diff --git a/bureau/admin/adm_doms_def_type.php b/bureau/admin/adm_doms_def_type.php index 3cc2b613..76e4a9c7 100644 --- a/bureau/admin/adm_doms_def_type.php +++ b/bureau/admin/adm_doms_def_type.php @@ -2,92 +2,90 @@ require_once("../class/config.php"); if (!$admin->enabled) { - __("This page is restricted to authorized staff"); + __("This page is restricted to authorized staff"); exit(); - } +} - include_once("head.php"); +include_once("head.php"); +?> +
+"._("Uncompressing through TAR")."
"; - passthru("tar -xf ".escapeshellarg($file)." -C ".escapeshellarg($dest)." 2>&1", $ret); - } - if (substr($lfile,-4)==".zip") { - echo ""._("Uncompressing through UNZIP")."
"; - $cmd="unzip -o ".escapeshellarg($file)." -d ".escapeshellarg($dest)." 2>&1"; - passthru($cmd, $ret); - } - if (substr($lfile,-3)==".gz") { - echo ""._("Uncompressing through GUNZIP")."
"; - $cmd="gunzip ".escapeshellarg($file)." 2>&1"; - passthru($cmd, $ret); - } - echo ""; - if ($ret) { - $err->raise("bro",_("I cannot find a way to extract the file %s, it is an unsupported compressed format"), $file); - } - // fix the perms of the extracted archive TODO: does it work??? - $action->fix_dir($dest_to_fix); - return $ret; - } - - /** - * Copy many files from point A to point B - * - * @global m_err $err - * @param array $d List of files to move - * @param string $old - * @param string $new - * @return boolean - */ - function CopyFile($d,$old,$new) { - global $err; - $old=$this->convertabsolute($old,false); - if (!$old) { - $err->raise("bro",_("File or folder name is incorrect")); - return false; + /** + * Crée un fichier vide dans un dossier + * + * @global m_mysql $db + * @global m_err $err + * @global int $cuid + * @param string $dir Dossier dans lequel on veut crer un sous-dossier + * @param string $file Nom du dossier à créer + * @return boolean TRUE si le dossier a été créé, FALSE si une erreur s'est produite. + */ + function CreateFile($dir, $file) { + global $db, $err, $cuid; + $file = ssla($file); + $absolute = $this->convertabsolute($dir . "/" . $file, false); + if (!$absolute || file_exists($absolute)) { + $err->raise("bro", _("File or folder name is incorrect")); + return false; + } + if (!file_exists($absolute)) { + if (!@touch($absolute)) { + $err->raise("bro", _("Cannot create the requested file. Please check the permissions")); + return false; + } + } + $db->query("UPDATE browser SET crff=0 WHERE uid='$cuid';"); + return true; } - $new=$this->convertabsolute($new,false); - if (!$new) { - $err->raise("bro",_("File or folder name is incorrect")); - return false; + + /** + * Efface les fichiers du tableau $file_list dans le dossier $R + * + * @global m_err $err + * @global m_mem $mem + * @param array $file_list Liste des fichiers effacer. + * @param string $R Dossier dans lequel on efface les fichiers + * @return boolean TRUE si les fichiers ont t effacs, FALSE si une erreur s'est produite. + */ + function DeleteFile($file_list, $R) { + global $err; + $root = realpath(getuserpath()); + $absolute = $this->convertabsolute($R, false); + if (!$absolute && strpos($root, $absolute) === 0 && strlen($absolute) > (strlen($root) + 1)) { + $err->raise("bro", _("File or folder name is incorrect")); + return false; + } + for ($i = 0; $i < count($file_list); $i++) { + $file_list[$i] = ssla($file_list[$i]); + if (!strpos($file_list[$i], "/") && file_exists($absolute . "/" . $file_list[$i])) { // Character / forbidden in a FILE name + $this->_delete($absolute . "/" . $file_list[$i]); + } + } + return true; } - if ($old == $new) { - $err->raise("bro",_("You cannot move or copy a file to the same folder")); - return false; + + /** + * Renomme les fichier de $old du dossier $R en $new + * + * @global m_err $err + * @param string $R Dossier dans lequel se trouve les fichiers renommer. + * @param array $old Ancien nom des fichiers + * @param array $new Nouveau nom des fichiers + * @return boolean TRUE si les fichiers ont t renomms, FALSE si une erreur s'est produite. + */ + function RenameFile($R, $old, $new) { + global $err; + $absolute = $this->convertabsolute($R, false); + if (!$absolute) { + $err->raise("bro", _("File or folder name is incorrect")); + return false; + } + $alea = "." . time() . rand(1000, 9999); + for ($i = 0; $i < count($old); $i++) { + $old[$i] = ssla($old[$i]); // strip slashes if needed + $new[$i] = ssla($new[$i]); + if (!strpos($old[$i], "/") && !strpos($new[$i], "/")) { // caractre / interdit dans old ET dans new... + @rename($absolute . "/" . $old[$i], $absolute . "/" . $old[$i] . $alea); + } + } + for ($i = 0; $i < count($old); $i++) { + if (!strpos($old[$i], "/") && !strpos($new[$i], "/")) { // caractre / interdit dans old ET dans new... + @rename($absolute . "/" . $old[$i] . $alea, $absolute . "/" . $new[$i]); + } + } + + return true; } - for ($i=0;$iCopyOneFile($old."/".$d[$i],$new); - } + + /** + * Déplace les fichier de $d du dossier $old vers $new + * + * @global m_err $err + * @param array $d Liste des fichiers du dossier $old dplacer + * @param string $old Dossier dans lequel se trouve les fichiers dplacer. + * @param string $new Dossier vers lequel seront dplacs les fichiers. + * @return boolean TRUE si les fichiers ont t renomms, FALSE si une erreur s'est produite. + */ + function MoveFile($d, $old, $new) { + global $err; + $old = $this->convertabsolute($old, false); + if (!$old) { + $err->raise("bro", _("File or folder name is incorrect")); + return false; + } + + if ($new[0] != '/') { + $new = $old . '/' . $new; + } + $new = $this->convertabsolute($new, false); + + if (!$new) { + $err->raise("bro", _("File or folder name is incorrect")); + return false; + } + if ($old == $new) { + $err->raise("bro", _("You cannot move or copy a file to the same folder")); + return false; + } + for ($i = 0; $i < count($d); $i++) { + $d[$i] = ssla($d[$i]); // strip slashes if needed + if (!strpos($d[$i], "/") && file_exists($old . "/" . $d[$i]) && !file_exists($new . "/" . $d[$i])) { + if (!rename($old . "/" . $d[$i], $new . "/" . $d[$i])) { + $err->raise("bro", "error renaming $old/$d[$i] -> $new/$d[$i]"); + } + } + } + return true; } - return true; - } + /** + * Change les droits d'acces aux fichier de $d du dossier $R en $p + * + * @param string $R Dossier dans lequel se trouve les fichiers renommer. + * @param string $d list of files whose permission must be changed + * @param string $perm the permission to change + * @param boolean $verbose Shall we 'echo' what we did ? + * @return boolean TRUE Si les fichiers ont t renomms, FALSE si une erreur s'est produite. + */ + function ChangePermissions($R, $d, $perm, $verbose = false) { + global $err, $action; + $absolute = $this->convertabsolute($R, false); + if (!$absolute) { + $err->raise("bro", _("File or folder name is incorrect")); + return false; + } + for ($i = 0; $i < count($d); $i++) { + $d[$i] = ssla($d[$i]); // strip slashes if needed + if (!strpos($d[$i], "/")) { // caractre / interdit dans le nom du fichier + $m = fileperms($absolute . "/" . $d[$i]); - /** - * Copy a source to a destination by either copying recursively a - * directory or by downloading a file with a URL (only http:// is - * supported) - * - * Note that we assume that the inputs have been convertabsolute()'d - * - * @global m_err $err - * @param string $src Path or URL - * @param string $dest Absolute path inside the users directory - * @return boolean false on error - */ - function CopyOneFile($src, $dest) { - global $err; - exec("cp -Rpf ".escapeshellarg($src)." ".escapeshellarg($dest), $void, $ret); - if ($ret) { - $err->raise("bro","Errors happened while copying the source to destination. cp return value: %d", $ret); - return false; + // pour l'instant on se limite a "write" pour owner, puisque c'est le seul + // cas interessant compte tenu de la conf de Apache pour AlternC.. + if ($perm[$i]['w']) { + $m = $m | 0220; // ug+w + } else { + $m = $m & (~ 0222); // ugo-w + } + $action->chmod($absolute . "/" . $d[$i], $m); + if ($verbose) { + echo "chmod " . sprintf('%o', $m) . " file, was " . sprintf('%o', fileperms($absolute . "/" . $d[$i])) . " -- " . $perm[$i]['w']; + } + } + } + + return true; } - return true; - } - - /** - * Affiche le chemin et les liens de la racine au dossier $path - * Affiche autant de liens HTML (anchor) que le chemin $path contient de - * niveaux de dossier. Chaque lien est associ la page web $action - * laquelle on ajoute le paramtre R=+Le nom du dossier courant. - * - * @param string $path Dossier vers lequel on trace le chemin - * @param string $action Page web de destination des liens - * @param boolean $justparent - * @return string Le code HTML ainsi obtenu. - */ - function PathList($path,$action, $justparent=false) { - $path=$this->convertabsolute($path,true); - $a=explode("/",$path); - if (!is_array($a)) $a=array($a); - $c=''; - $R=''; - if ($justparent) { - return "↑"; + /** + * Recoit un champ file upload (Global) et le stocke dans le dossier $R + * Le champ file-upload originel doit s'appeler "userfile" et doit + * bien être un fichier d'upload. + * + * + * @global array $_FILES + * @global m_err $err + * @global int $cuid + * @global m_action $action + * @param string $R Dossier dans lequel on upload le fichier + * @returns string The path where the file resides or false if upload failed + */ + function UploadFile($R) { + global $_FILES, $err, $cuid, $action; + $absolute = $this->convertabsolute($R, false); + if (!$absolute) { + $err->raise("bro", _("File or folder name is incorrect")); + return false; + } + if (!strpos($_FILES['userfile']['name'], "/")) { + if ($_FILES['userfile']['error'] == UPLOAD_ERR_OK && is_uploaded_file($_FILES['userfile']['tmp_name'])) { + if (!file_exists($absolute . "/" . $_FILES['userfile']['name'])) { + @touch($absolute . "/" . $_FILES['userfile']['name']); + } + if (@move_uploaded_file($_FILES['userfile']['tmp_name'], $absolute . "/" . $_FILES['userfile']['name'])) { + $action->fix_file($absolute . "/" . $_FILES['userfile']['name']); + return $absolute . "/" . $_FILES['userfile']['name']; + } else { + $err->raise("bro", _("Cannot create the requested file. Please check the permissions")); + return false; + } + } else { + // there was an error, raise it + $err->log("bro", "uploadfile", "Problem when uploading a file"); + switch ($_FILES['userfile']['error']) { + case UPLOAD_ERR_INI_SIZE: + $erstr = _("The uploaded file exceeds the max file size allowed"); + break; + case UPLOAD_ERR_FORM_SIZE: + case UPLOAD_ERR_PARTIAL: + case UPLOAD_ERR_NO_FILE: + case UPLOAD_ERR_NO_TMP_DIR: + case UPLOAD_ERR_CANT_WRITE: + case UPLOAD_ERR_EXTENSION: + default: + $erstr = _("Undefined error ") . $_FILES['userfile']['error']; + break; + } + $err->raise("bro", _("Error during the upload of the file: ") . $erstr); + return false; + } + } + return $absolute . "/" . $_FILES['userfile']['name']; } - for($i=0;$i ".$a[$i]." / "; - } + + /** + * Extract an archive by using GNU and non-GNU tools + * + * @global m_err $err + * @global int $cuid + * @global m_mem $mem + * @global m_action $action + * @param string $file Full or relative path to the archive + * @param string $dest Path of the extract destination, the + * same directory as the archive by default + * @return integer|null != 0 on error + */ + function ExtractFile($file, $dest = null) { + global $err, $action; + $file = $this->convertabsolute($file, false); + if (is_null($dest)) { + $dest = dirname($file); + } else { + $dest = $this->convertabsolute($dest, false); + } + if (!$file || !$dest || !is_readable($file)) { + $err->raise("bro", _("File or folder name is incorrect")); + return 1; + } + $lfile = strtolower($file); + if (substr($lfile, -4) == ".tar" || substr($lfile, -8) == ".tar.bz2" || substr($lfile, -7) == ".tar.gz" || substr($lfile, -6) == ".tar.z") { + // TODO new version of tar supports `tar xf ...` so there is no + // need to specify the compression format + echo " " . _("Uncompressing through TAR") . "
"; + $ret = 0; + passthru("tar -xf " . escapeshellarg($file) . " -C " . escapeshellarg($dest) . " 2>&1", $ret); + } + if (substr($lfile, -4) == ".zip") { + echo "" . _("Uncompressing through UNZIP") . "
"; + $cmd = "unzip -o " . escapeshellarg($file) . " -d " . escapeshellarg($dest) . " 2>&1"; + passthru($cmd, $ret); + } + if (substr($lfile, -3) == ".gz") { + echo "" . _("Uncompressing through GUNZIP") . "
"; + $cmd = "gunzip " . escapeshellarg($file) . " 2>&1"; + passthru($cmd, $ret); + } + echo ""; + if ($ret) { + $err->raise("bro", _("I cannot find a way to extract the file %s, it is an unsupported compressed format"), $file); + } + // fix the perms of the extracted archive TODO: does it work??? | note: it was using a wrong variable name ! + $action->fix_dir($dest); + return $ret; } - return $c; - } - - /** - * Affiche le contenu d'un fichier pour un champ VALUE de textarea - * - * Affiche le contenu du fichier $file dans le dossier $R. Le contenu - * du fichier est reformat pour pouvoir entrer dans un champs TextArea - * - * @global m_err $err - * @param string $R Dossier dans lequel on cherche le fichier - * @param string $file Fichier dont on souhaite obtenir le contenu. - * @return string|false TRUE si le fichier a bien été mis sur - * echo, ou FALSE si une erreur est survenue. - */ - function content($R,$file) { - global $err; - $absolute=$this->convertabsolute($R,false); - if (!strpos($file,"/")) { - $absolute.="/".$file; - if (file_exists($absolute)) { - $std=str_replace("<","<",str_replace("&","&",file_get_contents($absolute))); - return $std; - } else { - $err->raise("bro",_("Cannot read the requested file. Please check the permissions")); - return false; - } - } else { - $err->raise("bro",_("File or folder name is incorrect")); - return false; + /** + * Copy many files from point A to point B + * + * @global m_err $err + * @param array $d List of files to move + * @param string $old + * @param string $new + * @return boolean + */ + function CopyFile($d, $old, $new) { + global $err; + $old = $this->convertabsolute($old, false); + if (!$old) { + $err->raise("bro", _("File or folder name is incorrect")); + return false; + } + $new = $this->convertabsolute($new, false); + if (!$new) { + $err->raise("bro", _("File or folder name is incorrect")); + return false; + } + if ($old == $new) { + $err->raise("bro", _("You cannot move or copy a file to the same folder")); + return false; + } + for ($i = 0; $i < count($d); $i++) { + $d[$i] = ssla($d[$i]); // strip slashes if needed + if (!strpos($d[$i], "/") && file_exists($old . "/" . $d[$i]) && !file_exists($new . "/" . $d[$i])) { + $this->CopyOneFile($old . "/" . $d[$i], $new); + } + } + return true; } - } + /** + * Copy a source to a destination by either copying recursively a + * directory or by downloading a file with a URL (only http:// is + * supported) + * + * Note that we assume that the inputs have been convertabsolute()'d + * + * @global m_err $err + * @param string $src Path or URL + * @param string $dest Absolute path inside the users directory + * @return boolean false on error + */ + function CopyOneFile($src, $dest) { + global $err; + exec("cp -Rpf " . escapeshellarg($src) . " " . escapeshellarg($dest), $void, $ret); + if ($ret) { + $err->raise("bro", "Errors happened while copying the source to destination. cp return value: %d", $ret); + return false; + } + return true; + } + /** + * Affiche le chemin et les liens de la racine au dossier $path + * Affiche autant de liens HTML (anchor) que le chemin $path contient de + * niveaux de dossier. Chaque lien est associ la page web $action + * laquelle on ajoute le paramtre R=+Le nom du dossier courant. + * + * @param string $path Dossier vers lequel on trace le chemin + * @param string $action Page web de destination des liens + * @param boolean $justparent + * @return string Le code HTML ainsi obtenu. + */ + function PathList($path, $action, $justparent = false) { + $path = $this->convertabsolute($path, true); + $a = explode("/", $path); + if (!is_array($a)) { + $a = array($a); + } + $c = ''; + $R = ''; + if ($justparent) { + return "↑"; + } + for ($i = 0; $i < count($a); $i++) { + if ($a[$i]) { + $R.=$a[$i] . "/"; + $c.="" . $a[$i] . " / "; + } + } + return $c; + } - /** - * Retourne une url de navigation pour le fichier $name du dossier $dir - * Les url sont mises en caches. Il se peut qu'aucune url n'existe, ou que - * celle-ci soit protge par un .htaccess. - * - * Return a browsing url if available. - * Maintain a url cache (positive AND negative(-) cache) - * - * @global m_mysql $db - * @global int $cuid - * - * @param string $dir Dossier concerné - * @param string $name Fichier dont on souhaite obtenir une URL - * @return string URL concerne, ou FALSE si aucune URL n'est disponible pour ce fichier - */ - function viewurl($dir,$name) { - global $db,$cuid; - // Is it in cache ? - if (substr($dir,0,1) == "/") $dir=substr($dir,1); - if (substr($dir,-1) == "/") $dir=substr($dir,0,-1); - $dir=str_replace("%2F", "/", urlencode($dir)); - $name=urlencode($name); - if (!@$this->cacheurl["d".$dir]) { - // On parcours $dir en remontant les / - $end=""; $beg=$dir; $tofind=true; - while ($tofind) { - $db->query("SELECT sub,domaine FROM sub_domaines WHERE compte='$cuid' + /** + * Affiche le contenu d'un fichier pour un champ VALUE de textarea + * + * Affiche le contenu du fichier $file dans le dossier $R. Le contenu + * du fichier est reformat pour pouvoir entrer dans un champs TextArea + * + * @global m_err $err + * @param string $R Dossier dans lequel on cherche le fichier + * @param string $file Fichier dont on souhaite obtenir le contenu. + * @return string|false TRUE si le fichier a bien été mis sur + * echo, ou FALSE si une erreur est survenue. + */ + function content($R, $file) { + global $err; + $absolute = $this->convertabsolute($R, false); + if (!strpos($file, "/")) { + $absolute.="/" . $file; + if (file_exists($absolute)) { + $std = str_replace("<", "<", str_replace("&", "&", file_get_contents($absolute))); + return $std; + } else { + $err->raise("bro", _("Cannot read the requested file. Please check the permissions")); + return false; + } + } else { + $err->raise("bro", _("File or folder name is incorrect")); + return false; + } + } + + /** + * Retourne une url de navigation pour le fichier $name du dossier $dir + * Les url sont mises en caches. Il se peut qu'aucune url n'existe, ou que + * celle-ci soit protge par un .htaccess. + * + * Return a browsing url if available. + * Maintain a url cache (positive AND negative(-) cache) + * + * @global m_mysql $db + * @global int $cuid + * + * @param string $dir Dossier concerné + * @param string $name Fichier dont on souhaite obtenir une URL + * @return string URL concerne, ou FALSE si aucune URL n'est disponible pour ce fichier + */ + function viewurl($dir, $name) { + global $db, $cuid; + // Is it in cache ? + if (substr($dir, 0, 1) == "/") { + $dir = substr($dir, 1); + } + if (substr($dir, -1) == "/") { + $dir = substr($dir, 0, -1); + } + $dir = str_replace("%2F", "/", urlencode($dir)); + $name = urlencode($name); + if (!@$this->cacheurl["d" . $dir]) { + // On parcours $dir en remontant les / + $end = ""; + $beg = $dir; + $tofind = true; + while ($tofind) { + $db->query("SELECT sub,domaine FROM sub_domaines WHERE compte='$cuid' AND type=0 AND (valeur='/$beg/' or valeur='/$beg');"); - $db->next_record(); - if ($db->num_rows()) { - $tofind=false; - $this->cacheurl["d".$dir]="http://".$db->f("sub").ife($db->f("sub"),".").$db->f("domaine").$end; - } - if (!$beg && $tofind) { - $tofind=false; - $this->cacheurl["d".$dir]="-"; - // We did not find it ;( - } - if (($tt=strrpos($beg,"/"))!==false) { - $end=substr($beg,$tt).$end; //=/topdir$end so $end starts AND ends with / - $beg=substr($beg,0,$tt); - } else { - $end="/".$beg.$end; - $beg="/"; - } - } - } - if ($this->cacheurl["d".$dir] && $this->cacheurl["d".$dir]!="-") { - return $this->cacheurl["d".$dir]."/".$name; - } else { - return false; - } - } - - /** - * - * @global m_mem $mem - * @global m_err $err - * @param string $dir - * @param string $name - * @return null|boolean - */ - function can_edit($dir,$name) { - global $mem,$err; - $absolute="$dir/$name"; - $absolute=$this->convertabsolute($absolute,false); - if (!$absolute) { - $err->raise('bro',_("File not in authorized directory")); - include('foot.php'); - exit; - } - $finfo=finfo_open(FILEINFO_MIME_TYPE); - $mime=finfo_file($finfo,$absolute); - if ( substr($mime,0,5) == "text/" || $mime == "application/x-empty" || $mime == "inode/x-empty") { - return true; - } - return false; - } - - - /** - * Return a HTML snippet representing an extraction function only if the mimetype of $name is supported - * - * @param string $name - * @return boolean - */ - function is_extractable($name) { - if ($parts=explode(".", $name)) { - $ext=array_pop($parts); - switch ($ext) { - case "gz": - case "bz": - case "bz2": - $ext=array_pop($parts).$ext; - /* FALLTHROUGH */ - case "tar.gz": - case "tar.bz": - case "tar.bz2": - case "tgz": - case "tbz": - case "tbz2": - case "tar": - case "Z": - case "zip": - return true; - } - } - return false; - } - - /** - * return true if file is a sql dump (end with .sql or .sql.gz) - * - * @param type $dir - * @param type $name - * @return boolean - */ - function is_sqlfile($name) { - if ($parts=explode(".", $name)) { - $ext=array_pop($parts); - $ext2=array_pop($parts).'.'.$ext; - if ( $ext == 'sql' or $ext2 == 'sql.gz') return true; - } - return false; - } - - /** - * - * @global m_err $err - * @param string $dir - * @param string $file - */ - function download_link($dir,$file){ - global $err; - $err->log("bro","download_link"); - header("Content-Disposition: attachment; filename=$file"); - header("Content-Type: application/force-download"); - header("Content-Transfer-Encoding: binary"); - $this->content_send($dir,$file); - } - - - /** - * Echoes the content of the file $file located in directory $R - * - * @global m_err $err - * @param string $R - * @param string $file - * @return null|false - */ - function content_send($R,$file) { - global $err; - $absolute=$this->convertabsolute($R,false); - if (!strpos($file,"/")) { - $absolute.="/".$file; - if (file_exists($absolute)) { - readfile($absolute); - } - } else { - $err->raise("bro",_("File or folder name is incorrect")); - return false; - } - } - - - /** - * Sauve le fichier $file dans le dossier $R avec pour contenu $texte - * le contenu est issu d'un textarea, et ne DOIT PAS contenir de \ ajouts - * automatiquement par addslashes - * - * @global m_err $err - * @param string $file Nom du fichier sauver. S'il existe déjà , il sera - * écrasé sans confirmation. - * @param string $R Dossier dans lequel on modifie le fichier - * @param string $texte Texte du fichier à sauver dedans - * @return false|null TRUE si tout s'est bien pass, FALSE si une erreur s'est produite. - */ - function save($file,$R,$texte) { - global $err; - $absolute=$this->convertabsolute($R,false); - if (!strpos($file,"/")) { - $absolute.="/".$file; - if (file_exists($absolute)) { - if (! file_put_contents($absolute, $texte ) ) { - $err->raise("bro",_("Cannot edit the requested file. Please check the permissions")); - return false; - } - } - } else { - $err->raise("bro",_("File or folder name is incorrect")); - return false; - } - } - - - /** - * Echo d'un flux .tar.Z contenant tout le contenu du dossier $dir - * - * @global m_mem $mem - * @param string $dir Dossier à dumper, relatif la racine du compte du membre. - * @return void NE RETOURNE RIEN, et il faut Quitter le script immdiatement aprs - */ - function DownloadZ($dir="") { - global $mem; - header("Content-Disposition: attachment; filename=".$mem->user["login"].".Z"); - header("Content-Type: application/x-Z"); - header("Content-Transfer-Encoding: binary"); - $d=escapeshellarg(".".$this->convertabsolute($dir,true)); - set_time_limit(0); - passthru("/bin/tar -cZ -C ".getuserpath()."/".$mem->user["login"]."/ $d"); - } - - - /** - * Echo d'un flux .tgz contenant tout le contenu du dossier $dir - * - * @global type $mem - * @param string $dir Dossier à dumper, relatif la racine du compte du membre. - * @return void NE RETOURNE RIEN, et il faut Quitter le script immdiatement aprs - */ - function DownloadTGZ($dir="") { - global $mem; - header("Content-Disposition: attachment; filename=".$mem->user["login"].".tgz"); - header("Content-Type: application/x-tgz"); - header("Content-Transfer-Encoding: binary"); - $d=escapeshellarg(".".$this->convertabsolute($dir,true)); - set_time_limit(0); - passthru("/bin/tar -cz -C ".getuserpath()."/ $d"); - } - - - /** - * Echo d'un flux .tar.bz2 contenant tout le contenu du dossier $dir - * - * @global type $mem - * @param string $dir Dossier à dumper, relatif la racine du compte du membre. - * @return void NE RETOURNE RIEN, et il faut Quitter le script immdiatement aprs - */ - function DownloadTBZ($dir="") { - global $mem; - header("Content-Disposition: attachment; filename=".$mem->user["login"].".tar.bz2"); - header("Content-Type: application/x-bzip2"); - header("Content-Transfer-Encoding: binary"); - $d=escapeshellarg(".".$this->convertabsolute($dir,true)); - set_time_limit(0); - passthru("/bin/tar -cj -C ".getuserpath()."/ $d"); - } - - - - /** - * Echo d'un flux .ZIP contenant tout le contenu du dossier $dir - * - * @global type $mem - * @param string $dir Dossier à dumper, relatif la racine du compte du membre. - * @return void NE RETOURNE RIEN, et il faut Quitter le script immdiatement aprs - */ - function DownloadZIP($dir="") { - global $mem; - header("Content-Disposition: attachment; filename=".$mem->user["login"].".zip"); - header("Content-Type: application/x-zip"); - header("Content-Transfer-Encoding: binary"); - $d=escapeshellarg($this->convertabsolute($dir,false)); - set_time_limit(0); - passthru("/usr/bin/zip -r - $d"); - } - - - /** - * Fonction de tri perso utilis par filelist. - * - * @access private - * @param string $a - * @param string $b - * @return int - */ - function _sort_filelist_name($a,$b) { - if ($a["type"] && !$b["type"]) return 1; - if ($b["type"] && !$a["type"]) return -1; - return $a["name"]>$b["name"]; - } - - - /** - * Efface $file et tous ses sous-dossiers s'il s'agit d'un dossier - * A UTILISER AVEC PRECAUTION !!! - * @param string $file Fichier ou dossier supprimer. - * @access private - */ - function _delete($file) { - global $err; - // permet d'effacer de nombreux fichiers - @set_time_limit(0); - //chmod($file,0777); - $err->log("bro", "_delete($file)"); - if (is_dir($file)) { - $handle=opendir($file); - while (($filename=readdir($handle)) !== false) { - if ($filename != "." && $filename != "..") { - $this->_delete($file."/".$filename); - } - } - closedir($handle); - rmdir($file); - } else { - unlink($file); - } - } - - - /** - * Function d'exportation de configuration appelé par la classe m_export via un hooks - * Produit en sorti un tableau formatté ( pour le moment) en HTML - * - * @global m_mysql $db - * @global m_err $err - * @return string - */ - function alternc_export_conf() { - global $db,$err; - $err->log("bro","export_conf"); - $str="
Browser \n"; - $str.="\n"; - $pref=$this->GetPrefs(); - - $i=1; - foreach ($pref as $k=>$v) { - if (($i % 2) == 0){ - $str.=" <$k>$v$k>\n"; - } - $i++; - } - $str.=" \n"; - - return $str; - } - - - /** - * Function d'exportation des données appelé par la classe m_export via un hooks - * - * @global m_mem $mem - * @global m_err $err - * @param string $dir Le chemin destination du tarball produit - * @return boolean|null - */ - function alternc_export_data($dir){ - global $mem,$err; - $err->log("bro","export_data"); - $dir.="html/"; - if(!is_dir($dir)){ - if(!mkdir($dir)) - $err->raise("bro",_("Cannot create the requested directory. Please check the permissions")); - } - $timestamp=date("H:i:s"); - - if(exec("/bin/tar cvf - ".escapeshellarg(getuserpath()."/")."| gzip -9c > ".escapeshellarg($dir."/".$mem->user['login']."_html_".$timestamp.".tar.gz"))) { - $err->log("bro","export_data_succes"); - } else { - $err->log("bro","export_data_failed"); + $db->next_record(); + if ($db->num_rows()) { + $tofind = false; + $this->cacheurl["d" . $dir] = "http://" . $db->f("sub") . ife($db->f("sub"), ".") . $db->f("domaine") . $end; + } + if (!$beg && $tofind) { + $tofind = false; + $this->cacheurl["d" . $dir] = "-"; + // We did not find it ;( + } + if (($tt = strrpos($beg, "/")) !== false) { + $end = substr($beg, $tt) . $end; //=/topdir$end so $end starts AND ends with / + $beg = substr($beg, 0, $tt); + } else { + $end = "/" . $beg . $end; + $beg = "/"; + } + } + } + if ($this->cacheurl["d" . $dir] && $this->cacheurl["d" . $dir] != "-") { + return $this->cacheurl["d" . $dir] . "/" . $name; + } else { + return false; + } } - } + /** + * + * @global m_mem $mem + * @global m_err $err + * @param string $dir + * @param string $name + * @return null|boolean + */ + function can_edit($dir, $name) { + global $err; + $absolute = "$dir/$name"; + $absolute = $this->convertabsolute($absolute, false); + if (!$absolute) { + $err->raise('bro', _("File not in authorized directory")); + include('foot.php'); + exit; + } + $finfo = finfo_open(FILEINFO_MIME_TYPE); + $mime = finfo_file($finfo, $absolute); + if (substr($mime, 0, 5) == "text/" || $mime == "application/x-empty" || $mime == "inode/x-empty") { + return true; + } + return false; + } - function getMaxAllowedUploadSize() { - return min(ini_get('post_max_size'), ini_get('upload_max_filesize')); - } + /** + * Return a HTML snippet representing an extraction function only if the mimetype of $name is supported + * + * @param string $name + * @return boolean + */ + function is_extractable($name) { + if (($parts = explode(".", $name))) { + $ext = array_pop($parts); + switch ($ext) { + case "gz": + case "bz": + case "bz2": + $ext = array_pop($parts) . $ext; + /* FALLTHROUGH */ + case "tar.gz": + case "tar.bz": + case "tar.bz2": + case "tgz": + case "tbz": + case "tbz2": + case "tar": + case "Z": + case "zip": + return true; + } + } + return false; + } -} /* Class Browser */ + /** + * return true if file is a sql dump (end with .sql or .sql.gz) + * + * @param type $dir + * @param type $name + * @return boolean + */ + function is_sqlfile($name) { + if (($parts = explode(".", $name))) { + $ext = array_pop($parts); + $ext2 = array_pop($parts) . '.' . $ext; + if ($ext == 'sql' or $ext2 == 'sql.gz') { + return true; + } + } + return false; + } + /** + * + * @global m_err $err + * @param string $dir + * @param string $file + */ + function download_link($dir, $file) { + global $err; + $err->log("bro", "download_link"); + header("Content-Disposition: attachment; filename=$file"); + header("Content-Type: application/force-download"); + header("Content-Transfer-Encoding: binary"); + $this->content_send($dir, $file); + } + + /** + * Echoes the content of the file $file located in directory $R + * + * @global m_err $err + * @param string $R + * @param string $file + * @return null|false + */ + function content_send($R, $file) { + global $err; + $absolute = $this->convertabsolute($R, false); + if (!strpos($file, "/")) { + $absolute.="/" . $file; + if (file_exists($absolute)) { + readfile($absolute); + } + } else { + $err->raise("bro", _("File or folder name is incorrect")); + return false; + } + } + + /** + * Sauve le fichier $file dans le dossier $R avec pour contenu $texte + * le contenu est issu d'un textarea, et ne DOIT PAS contenir de \ ajouts + * automatiquement par addslashes + * + * @global m_err $err + * @param string $file Nom du fichier sauver. S'il existe déjà , il sera + * écrasé sans confirmation. + * @param string $R Dossier dans lequel on modifie le fichier + * @param string $texte Texte du fichier à sauver dedans + * @return false|null TRUE si tout s'est bien pass, FALSE si une erreur s'est produite. + */ + function save($file, $R, $texte) { + global $err; + $absolute = $this->convertabsolute($R, false); + if (!strpos($file, "/")) { + $absolute.="/" . $file; + if (file_exists($absolute)) { + if (!file_put_contents($absolute, $texte)) { + $err->raise("bro", _("Cannot edit the requested file. Please check the permissions")); + return false; + } + } + } else { + $err->raise("bro", _("File or folder name is incorrect")); + return false; + } + } + + /** + * Echo d'un flux .tar.Z contenant tout le contenu du dossier $dir + * + * @global m_mem $mem + * @param string $dir Dossier à dumper, relatif la racine du compte du membre. + * @return void NE RETOURNE RIEN, et il faut Quitter le script immdiatement aprs + */ + function DownloadZ($dir = "") { + global $mem; + header("Content-Disposition: attachment; filename=" . $mem->user["login"] . ".Z"); + header("Content-Type: application/x-Z"); + header("Content-Transfer-Encoding: binary"); + $d = escapeshellarg("." . $this->convertabsolute($dir, true)); + set_time_limit(0); + passthru("/bin/tar -cZ -C " . getuserpath() . "/" . $mem->user["login"] . "/ $d"); + } + + /** + * Echo d'un flux .tgz contenant tout le contenu du dossier $dir + * + * @global type $mem + * @param string $dir Dossier à dumper, relatif la racine du compte du membre. + * @return void NE RETOURNE RIEN, et il faut Quitter le script immdiatement aprs + */ + function DownloadTGZ($dir = "") { + global $mem; + header("Content-Disposition: attachment; filename=" . $mem->user["login"] . ".tgz"); + header("Content-Type: application/x-tgz"); + header("Content-Transfer-Encoding: binary"); + $d = escapeshellarg("." . $this->convertabsolute($dir, true)); + set_time_limit(0); + passthru("/bin/tar -cz -C " . getuserpath() . "/ $d"); + } + + /** + * Echo d'un flux .tar.bz2 contenant tout le contenu du dossier $dir + * + * @global type $mem + * @param string $dir Dossier à dumper, relatif la racine du compte du membre. + * @return void NE RETOURNE RIEN, et il faut Quitter le script immdiatement aprs + */ + function DownloadTBZ($dir = "") { + global $mem; + header("Content-Disposition: attachment; filename=" . $mem->user["login"] . ".tar.bz2"); + header("Content-Type: application/x-bzip2"); + header("Content-Transfer-Encoding: binary"); + $d = escapeshellarg("." . $this->convertabsolute($dir, true)); + set_time_limit(0); + passthru("/bin/tar -cj -C " . getuserpath() . "/ $d"); + } + + /** + * Echo d'un flux .ZIP contenant tout le contenu du dossier $dir + * + * @global type $mem + * @param string $dir Dossier à dumper, relatif la racine du compte du membre. + * @return void NE RETOURNE RIEN, et il faut Quitter le script immdiatement aprs + */ + function DownloadZIP($dir = "") { + global $mem; + header("Content-Disposition: attachment; filename=" . $mem->user["login"] . ".zip"); + header("Content-Type: application/x-zip"); + header("Content-Transfer-Encoding: binary"); + $d = escapeshellarg($this->convertabsolute($dir, false)); + set_time_limit(0); + passthru("/usr/bin/zip -r - $d"); + } + + /** + * Fonction de tri perso utilis par filelist. + * + * @access private + * @param string $a + * @param string $b + * @return int + */ + function _sort_filelist_name($a, $b) { + if ($a["type"] && !$b["type"]) { + return 1; + } + if ($b["type"] && !$a["type"]) { + return -1; + } + return $a["name"] > $b["name"]; + } + + /** + * Efface $file et tous ses sous-dossiers s'il s'agit d'un dossier + * A UTILISER AVEC PRECAUTION !!! + * @param string $file Fichier ou dossier supprimer. + * @access private + */ + function _delete($file) { + global $err; + // permet d'effacer de nombreux fichiers + @set_time_limit(0); + //chmod($file,0777); + $err->log("bro", "_delete($file)"); + if (is_dir($file)) { + $handle = opendir($file); + while (($filename = readdir($handle)) !== false) { + if ($filename != "." && $filename != "..") { + $this->_delete($file . "/" . $filename); + } + } + closedir($handle); + rmdir($file); + } else { + unlink($file); + } + } + + /** + * Function d'exportation de configuration appelé par la classe m_export via un hooks + * Produit en sorti un tableau formatté ( pour le moment) en HTML + * + * @global m_mysql $db + * @global m_err $err + * @return string + */ + function alternc_export_conf() { + global $err; + $err->log("bro", "export_conf"); + $str = "
Browser \n"; + $str.="\n"; + $pref = $this->GetPrefs(); + + $i = 1; + foreach ($pref as $k => $v) { + if (($i % 2) == 0) { + $str.=" <$k>$v$k>\n"; + } + $i++; + } + $str.=" \n"; + + return $str; + } + + /** + * Function d'exportation des données appelé par la classe m_export via un hooks + * + * @global m_mem $mem + * @global m_err $err + * @param string $dir Le chemin destination du tarball produit + * @return boolean|null + */ + function alternc_export_data($dir) { + global $mem, $err; + $err->log("bro", "export_data"); + $dir.="html/"; + if (!is_dir($dir)) { + if (!mkdir($dir)) + $err->raise("bro", _("Cannot create the requested directory. Please check the permissions")); + } + $timestamp = date("H:i:s"); + + if (exec("/bin/tar cvf - " . escapeshellarg(getuserpath() . "/") . "| gzip -9c > " . escapeshellarg($dir . "/" . $mem->user['login'] . "_html_" . $timestamp . ".tar.gz"))) { + $err->log("bro", "export_data_succes"); + } else { + $err->log("bro", "export_data_failed"); + } + } + + function getMaxAllowedUploadSize() { + return min(ini_get('post_max_size'), ini_get('upload_max_filesize')); + } + +} + +/* Class Browser */ diff --git a/bureau/class/m_cron.php b/bureau/class/m_cron.php index d00ffc0b..bf6d0b0f 100644 --- a/bureau/class/m_cron.php +++ b/bureau/class/m_cron.php @@ -1,364 +1,404 @@ 1440, 'name'=>_("Daily")), - Array('unit'=>60, 'name'=>_("Hour")), - Array('unit'=>30, 'name'=>_("Half Hour")), - ); - } - - - /*---------------------------------------------------------------------------*/ - /** List the crontab for the current user. - * @return array an hash for each crontab. - */ - function lst_cron() { - global $cuid,$db,$err; - $err->log("cron","lst_cron"); - $db->query("SELECT * FROM cron WHERE uid = $cuid ORDER BY url;"); - $r=Array(); - while ($db->next_record()) { - $tmp=Array(); - $tmp['id']=$db->f('id'); - $tmp['url']=urldecode($db->f('url')); - $tmp['user']=urldecode($db->f('user')); - $tmp['password']=urldecode($db->f('password')); - $tmp['schedule']=$db->f('schedule'); - $tmp['email']=urldecode($db->f('email')); - $tmp['next_execution']=$db->f('next_execution'); - $r[]=$tmp; - } - return $r; - } - - function hook_menu() { - $obj = array( - 'title' => _("Scheduled tasks"), - 'ico' => 'images/schedule.png', - 'link' => 'cron.php', - 'pos' => 90, - ) ; - - return $obj; - } - - /*---------------------------------------------------------------------------*/ - /** update the crontab - * @param $arr array the crontab information, including its ID - * @return boolean TRUE if the crontab has been edited - */ - function update($arr) { - $ok=true; - foreach ($arr as $a) { - if (! isset($a['id'])) $a['id']=null; - if (empty($a['url']) && is_null($a['id'])) continue; - if (! $this->_update_one($a['url'], $a['user'], $a['password'], $a['email'], $a['schedule'], $a['id']) ) { - $ok=false; - } - } - return $ok; - } - - - /*---------------------------------------------------------------------------*/ - /** delete a crontab - * @param $id the id of the crontab to delete - * @return boolean TRUE if the crontab has been deleted - */ - function delete_one($id) { - global $db,$err,$cuid; - $err->log("cron","delete_one"); - return $db->query("DELETE FROM cron WHERE id=".intval($id)." AND uid=$cuid LIMIT 1;"); - } - - - /*---------------------------------------------------------------------------*/ - /** update a crontab, - * @return boolean TRUE if the crontab has been edited - */ - private function _update_one($url, $user, $password, $email, $schedule, $id=null) { - global $db,$err,$quota,$cuid; - $err->log("cron","update_one"); - - if (empty($url) && !is_null($id)) { - return $this->delete_one($id); + /** Constructor + */ + function m_cron() { + } - - if(filter_var($url,FILTER_VALIDATE_URL)===false){ - $err->raise("cron",_("URL not valid")); - return false; + function schedule() { + return Array( + Array('unit' => 1440, 'name' => _("Daily")), + Array('unit' => 60, 'name' => _("Hour")), + Array('unit' => 30, 'name' => _("Half Hour")), + ); } - $url=urlencode($url); - $user=urlencode($user); - if (empty($user)) $password=''; - $password=urlencode($password); - - //@todo remove checkmail cf functions.php - if (!empty($email) && ! checkmail($email) == 0 ){ - $err->raise("cron",_("Email address is not valid")); - return false; + + /* --------------------------------------------------------------------------- */ + + /** List the crontab for the current user. + * @return array an hash for each crontab. + */ + function lst_cron() { + global $cuid, $db, $err; + $err->log("cron", "lst_cron"); + $db->query("SELECT * FROM cron WHERE uid = $cuid ORDER BY url;"); + $r = Array(); + while ($db->next_record()) { + $tmp = Array(); + $tmp['id'] = $db->f('id'); + $tmp['url'] = urldecode($db->f('url')); + $tmp['user'] = urldecode($db->f('user')); + $tmp['password'] = urldecode($db->f('password')); + $tmp['schedule'] = $db->f('schedule'); + $tmp['email'] = urldecode($db->f('email')); + $tmp['next_execution'] = $db->f('next_execution'); + $r[] = $tmp; + } + return $r; } - $email=urlencode($email); - if (! $this->valid_schedule($schedule)) return false; - if (is_null($id)) { // if a new insert, quotacheck - $q = $quota->getquota("cron"); - if ( $q["u"] >= $q["t"] ) { - $err->raise("cron",_("You quota of cron entries is over. You cannot create more cron entries")); - return false; - } - } else { // if not a new insert, check the $cuid - $db->query("SELECT uid FROM cron WHERE id = $id;"); - if (! $db->next_record()) { - return "false"; - } // return false if pb - if ( $db->f('uid') != $cuid ) { - $err->raise("cron",_("Identity problem")); - return false; - } + function hook_menu() { + $obj = array( + 'title' => _("Scheduled tasks"), + 'ico' => 'images/schedule.png', + 'link' => 'cron.php', + 'pos' => 90, + ); + + return $obj; } - $query = "REPLACE INTO cron (id, uid, url, user, password, schedule, email) VALUES ('$id', '$cuid', '$url', '$user', '$password', '$schedule', '$email') ;"; - return $db->query("$query"); - } + /* --------------------------------------------------------------------------- */ - /*---------------------------------------------------------------------------*/ - /** validate a crontab schedule - * @param $s array schedule paramters - * @return boolean TRUE if the schedule is valid - */ - function valid_schedule($s) { - $s2 = intval($s); - if ($s2 != $s) return false; - $r=false; - foreach ($this->schedule() as $cs ) { - if ($cs['unit'] == $s) return true; + /** update the crontab + * @param $arr array the crontab information, including its ID + * @return boolean TRUE if the crontab has been edited + */ + function update($arr) { + $ok = true; + foreach ($arr as $a) { + if (!isset($a['id'])) { + $a['id'] = null; + } + if (empty($a['url']) && is_null($a['id'])) { + continue; + } + if (!$this->_update_one($a['url'], $a['user'], $a['password'], $a['email'], $a['schedule'], $a['id'])) { + $ok = false; + } + } + return $ok; } - return $r; - } - /*---------------------------------------------------------------------------*/ - /** hook for quota computation - */ - function hook_quota_get() { - global $cuid,$db,$err; - $err->log("cron","alternc_get_quota"); - $q=Array("name"=>"cron", "description"=>_("Scheduled tasks"), "used"=>0); - $db->query("select count(*) as cnt from cron where uid = $cuid;"); - if ($db->next_record()) { - $q['used']=$db->f('cnt'); + /* --------------------------------------------------------------------------- */ + + /** delete a crontab + * @param $id the id of the crontab to delete + * @return boolean TRUE if the crontab has been deleted + */ + function delete_one($id) { + global $db, $err, $cuid; + $err->log("cron", "delete_one"); + return $db->query("DELETE FROM cron WHERE id=" . intval($id) . " AND uid=$cuid LIMIT 1;"); } - return $q; - } - /*---------------------------------------------------------------------------*/ - /** - * Execute the required crontab of AlternC users - * this function EXIT at the end. - */ - function execute_cron() { - global $db; + /* --------------------------------------------------------------------------- */ - if (!isset($GLOBALS["DEBUG"])) $GLOBALS["DEBUG"]=false; - $db->query("SELECT id, url, email, schedule, user, password FROM cron WHERE next_execution <= NOW();"); - $urllist=array(); - - while ($db->next_record()) { - $db->Record["url"]=urldecode($db->Record["url"]); $db->Record["user"]=urldecode($db->Record["user"]); - $db->Record["email"]=urldecode($db->Record["email"]); $db->Record["password"]=urldecode($db->Record["password"]); + /** update a crontab, + * @return boolean TRUE if the crontab has been edited + */ + private function _update_one($url, $user, $password, $email, $schedule, $id = null) { + global $db, $err, $quota, $cuid; + $err->log("cron", "update_one"); - // we support only http or https schemes: - if (substr($db->Record["url"],0,7)=="http://" || substr($db->Record["url"],0,8)=="https://") { - $u=array( - "url" => $db->Record["url"], - "id" => $db->Record["id"], "email" =>$db->Record["email"], - ); - - if ($db->Record["user"] && $db->Record["password"]) { - $u["login"]=$db->Record["user"]; - $u["password"]=$db->Record["password"]; - } - if ($GLOBALS["DEBUG"]) echo "Will run cron :\n".print_r($u,true)."\n"; - $urllist[]=$u; - } - - if (empty($urllist)) { // nothing to do : - exit(0); - } - - // cron_callback($url, $content, $curlobj) will be called at the end of each http call. - $this->rolling_curl($urllist, array("m_cron","cron_callback")); + if (empty($url) && !is_null($id)) { + return $this->delete_one($id); + } + + + if (filter_var($url, FILTER_VALIDATE_URL) === false) { + $err->raise("cron", _("URL not valid")); + return false; + } + $url = urlencode($url); + $user = urlencode($user); + if (empty($user)) { + $password = ''; + } + $password = urlencode($password); + + //@todo remove checkmail cf functions.php + if (!empty($email) && !checkmail($email) == 0) { + $err->raise("cron", _("Email address is not valid")); + return false; + } + $email = urlencode($email); + if (!$this->valid_schedule($schedule)) { + return false; + } + + if (is_null($id)) { // if a new insert, quotacheck + $q = $quota->getquota("cron"); + if ($q["u"] >= $q["t"]) { + $err->raise("cron", _("You quota of cron entries is over. You cannot create more cron entries")); + return false; + } + } else { // if not a new insert, check the $cuid + $db->query("SELECT uid FROM cron WHERE id = $id;"); + if (!$db->next_record()) { + return "false"; + } // return false if pb + if ($db->f('uid') != $cuid) { + $err->raise("cron", _("Identity problem")); + return false; + } + } + $query = "REPLACE INTO cron (id, uid, url, user, password, schedule, email) VALUES ('$id', '$cuid', '$url', '$user', '$password', '$schedule', '$email') ;"; + return $db->query("$query"); } - } - - - /*---------------------------------------------------------------------------*/ - /** - * Callback function called by rolling_curl when a cron resulr has been received - * schedule it for next run and send the mail if needed - */ - function cron_callback($url,$content,$curl) { - global $db,$L_FQDN; - if (empty($url["id"])) return; // not normal... - $id=intval($url["id"]); + /* --------------------------------------------------------------------------- */ - if ($curl["http_code"]==200) { - $ok=true; - } else { - $ok=false; + /** validate a crontab schedule + * @param $s array schedule paramters + * @return boolean TRUE if the schedule is valid + */ + function valid_schedule($s) { + $s2 = intval($s); + if ($s2 != $s) { + return false; + } + $r = false; + foreach ($this->schedule() as $cs) { + if ($cs['unit'] == $s) { + return true; + } + } + return $r; } - if (isset($url["email"]) && $url["email"] && $content) { - if (!mail($url["email"],"AlternC Cron #$id - Report ".date("r"),"Please find below the stdout content produced by your cron task.\n------------------------------------------------------------\n\n".$content,"From: postmaster@$L_FQDN")) { - echo "Error sending mail for cron #$id to address '".$url["email"]."'\n"; - } + + /* --------------------------------------------------------------------------- */ + + /** hook for quota computation + */ + function hook_quota_get() { + global $cuid, $db, $err; + $err->log("cron", "alternc_get_quota"); + $q = Array("name" => "cron", "description" => _("Scheduled tasks"), "used" => 0); + $db->query("select count(*) as cnt from cron where uid = $cuid;"); + if ($db->next_record()) { + $q['used'] = $db->f('cnt'); + } + return $q; } - // now schedule it for next run: - $db->query("UPDATE cron SET next_execution=FROM_UNIXTIME( UNIX_TIMESTAMP(NOW()) + schedule * 60) WHERE id=$id"); - } - + /* --------------------------------------------------------------------------- */ - /*---------------------------------------------------------------------------*/ - /** - * Launch parallel (using MAX_SOCKETS sockets maximum) retrieval - * of URL using CURL - * @param $urls array of associative array, each having the following keys : - * url = url to get (of the form http[s]://login:password@host/path/file?querystring ) - * login & password = if set, tell the login and password to use as simple HTTP AUTH. - * - any other key will be sent as it is to the callback function - * @param $callback function called for each request when completing. First argument is the $url object, second is the content (output) - * third is the info structure from curl for the returned page. 200 for OK, 403 for AUTH FAILED, 0 for timeout, dump it to know it ;) - * this function should return as soon as possible to allow other curl calls to complete properly. - * @param $cursom_options array of custom CURL options for all transfers - */ - function rolling_curl($urls, $callback, $custom_options = null) { - // make sure the rolling window isn't greater than the # of urls - if (!isset($GLOBALS["DEBUG"])) $GLOBALS["DEBUG"]=false; - $rolling_window = m_cron::MAX_SOCKETS; - $rolling_window = (count($urls) < $rolling_window) ? count($urls) : $rolling_window; - - $master = curl_multi_init(); - $curl_arr = array(); - - // add additional curl options here - $std_options = array(CURLOPT_RETURNTRANSFER => true, - CURLOPT_FOLLOWLOCATION => false, - CURLOPT_CONNECTTIMEOUT => 5, - CURLOPT_TIMEOUT => 240, // 4 minutes timeout for a page - CURLOPT_USERAGENT => "AlternC (Cron Daemon)", - CURLOPT_MAXREDIRS => 0); + /** + * Execute the required crontab of AlternC users + * this function EXIT at the end. + */ + function execute_cron() { + global $db; - if ($GLOBALS["DEBUG"]) $std_options[CURLOPT_VERBOSE]=true; - $options = ($custom_options) ? ($std_options + $custom_options) : $std_options; - - // start the first batch of requests - for ($i = 0; $i < $rolling_window; $i++) { - $ch = curl_init(); - $options[CURLOPT_URL] = $urls[$i]["url"]; - if ($GLOBALS["DEBUG"]) echo "URL: ".$urls[$i]["url"]."\n"; - curl_setopt_array($ch,$options); - // Handle custom cafile for some https url - if (strtolower(substr($options[CURLOPT_URL],0,5))=="https") { - curl_setopt($ch,CURLOPT_CAINFO,m_cron::DEFAULT_CAFILE); - if ($GLOBALS["DEBUG"]) echo "cainfo set to DEFAULT\n"; - } - if (isset($urls[$i]["login"]) && isset($urls[$i]["password"])) { // set basic http authentication - curl_setopt($ch,CURLOPT_HTTPAUTH,CURLAUTH_BASIC); - curl_setopt($ch,CURLOPT_USERPWD,$urls[$i]["login"].":".$urls[$i]["password"]); - if ($GLOBALS["DEBUG"]) echo "set basic auth\n"; - } - curl_multi_add_handle($master, $ch); + if (!isset($GLOBALS["DEBUG"])) { + $GLOBALS["DEBUG"] = false; + } + $db->query("SELECT id, url, email, schedule, user, password FROM cron WHERE next_execution <= NOW();"); + $urllist = array(); + + while ($db->next_record()) { + $db->Record["url"] = urldecode($db->Record["url"]); + $db->Record["user"] = urldecode($db->Record["user"]); + $db->Record["email"] = urldecode($db->Record["email"]); + $db->Record["password"] = urldecode($db->Record["password"]); + + // we support only http or https schemes: + if (substr($db->Record["url"], 0, 7) == "http://" || substr($db->Record["url"], 0, 8) == "https://") { + $u = array( + "url" => $db->Record["url"], + "id" => $db->Record["id"], "email" => $db->Record["email"], + ); + + if ($db->Record["user"] && $db->Record["password"]) { + $u["login"] = $db->Record["user"]; + $u["password"] = $db->Record["password"]; + } + if ($GLOBALS["DEBUG"]) + echo "Will run cron :\n" . print_r($u, true) . "\n"; + $urllist[] = $u; + } + + if (empty($urllist)) { // nothing to do : + exit(0); + } + + // cron_callback($url, $content, $curlobj) will be called at the end of each http call. + $this->rolling_curl($urllist, array("m_cron", "cron_callback")); + } } - - do { - while(($execrun = curl_multi_exec($master, $running)) == CURLM_CALL_MULTI_PERFORM); - if($execrun != CURLM_OK) - break; - // a request was just completed -- find out which one - while($done = curl_multi_info_read($master)) { - $info = curl_getinfo($done['handle']); - // TODO : since ssl_verify_result is buggy, if we have [header_size] => 0 && [request_size] => 0 && [http_code] => 0, AND https, we can pretend the SSL certificate is buggy. - if ($GLOBALS["DEBUG"]) { echo "Info for ".$done['handle']." \n"; print_r($info); } - if ($info['http_code'] == 200) { - $output = curl_multi_getcontent($done['handle']); - } else { - // request failed. add error handling. - $output=""; - } - // request terminated. process output using the callback function. - // Pass the url array to the callback, so we need to search it - foreach($urls as $url) { - if ($url["url"]==$info["url"]) { - call_user_func($callback,$url,$output,$info); - break; - } - } - - // If there is more: start a new request - // (it's important to do this before removing the old one) - if ($iquery("UPDATE cron SET next_execution=FROM_UNIXTIME( UNIX_TIMESTAMP(NOW()) + schedule * 60) WHERE id=$id"); + } + + /* --------------------------------------------------------------------------- */ + + /** + * Launch parallel (using MAX_SOCKETS sockets maximum) retrieval + * of URL using CURL + * @param $urls array of associative array, each having the following keys : + * url = url to get (of the form http[s]://login:password@host/path/file?querystring ) + * login & password = if set, tell the login and password to use as simple HTTP AUTH. + * - any other key will be sent as it is to the callback function + * @param $callback function called for each request when completing. First argument is the $url object, second is the content (output) + * third is the info structure from curl for the returned page. 200 for OK, 403 for AUTH FAILED, 0 for timeout, dump it to know it ;) + * this function should return as soon as possible to allow other curl calls to complete properly. + * @param $cursom_options array of custom CURL options for all transfers + */ + function rolling_curl($urls, $callback, $custom_options = null) { + // make sure the rolling window isn't greater than the # of urls + if (!isset($GLOBALS["DEBUG"])) + $GLOBALS["DEBUG"] = false; + $rolling_window = m_cron::MAX_SOCKETS; + $rolling_window = (count($urls) < $rolling_window) ? count($urls) : $rolling_window; + + $master = curl_multi_init(); + + // add additional curl options here + $std_options = array(CURLOPT_RETURNTRANSFER => true, + CURLOPT_FOLLOWLOCATION => false, + CURLOPT_CONNECTTIMEOUT => 5, + CURLOPT_TIMEOUT => 240, // 4 minutes timeout for a page + CURLOPT_USERAGENT => "AlternC (Cron Daemon)", + CURLOPT_MAXREDIRS => 0); + + if ($GLOBALS["DEBUG"]) { + $std_options[CURLOPT_VERBOSE] = true; + } + $options = ($custom_options) ? ($std_options + $custom_options) : $std_options; + + // start the first batch of requests + for ($i = 0; $i < $rolling_window; $i++) { + $ch = curl_init(); + $options[CURLOPT_URL] = $urls[$i]["url"]; + if ($GLOBALS["DEBUG"]) { + echo "URL: " . $urls[$i]["url"] . "\n"; + } + curl_setopt_array($ch, $options); + // Handle custom cafile for some https url + if (strtolower(substr($options[CURLOPT_URL], 0, 5)) == "https") { + curl_setopt($ch, CURLOPT_CAINFO, m_cron::DEFAULT_CAFILE); + if ($GLOBALS["DEBUG"]) { + echo "cainfo set to DEFAULT\n"; + } + } + if (isset($urls[$i]["login"]) && isset($urls[$i]["password"])) { // set basic http authentication + curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_BASIC); + curl_setopt($ch, CURLOPT_USERPWD, $urls[$i]["login"] . ":" . $urls[$i]["password"]); + if ($GLOBALS["DEBUG"]) { + echo "set basic auth\n"; + } + } + curl_multi_add_handle($master, $ch); + } + + do { + while (($execrun = curl_multi_exec($master, $running)) == CURLM_CALL_MULTI_PERFORM); + if ($execrun != CURLM_OK) { + break; + } + // a request was just completed -- find out which one + while ($done = curl_multi_info_read($master)) { + $info = curl_getinfo($done['handle']); + // TODO : since ssl_verify_result is buggy, if we have [header_size] => 0 && [request_size] => 0 && [http_code] => 0, AND https, we can pretend the SSL certificate is buggy. + if ($GLOBALS["DEBUG"]) { + echo "Info for " . $done['handle'] . " \n"; + print_r($info); + } + if ($info['http_code'] == 200) { + $output = curl_multi_getcontent($done['handle']); + } else { + // request failed. add error handling. + $output = ""; + } + // request terminated. process output using the callback function. + // Pass the url array to the callback, so we need to search it + foreach ($urls as $url) { + if ($url["url"] == $info["url"]) { + call_user_func($callback, $url, $output, $info); + break; + } + } + + // If there is more: start a new request + // (it's important to do this before removing the old one) + if ($i < count($urls)) { + $ch = curl_init(); + $options[CURLOPT_URL] = $urls[$i++]; // increment i + curl_setopt_array($ch, $options); + if (strtolower(substr($options[CURLOPT_URL], 0, 5)) == "https") { + curl_setopt($ch, CURLOPT_CAINFO, m_cron::DEFAULT_CAFILE); + if ($GLOBALS["DEBUG"]) { + echo "cainfo set to DEFAULT\n"; + } + } + if (isset($urls[$i]["login"]) && isset($urls[$i]["password"])) { // set basic http authentication + curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_BASIC); + curl_setopt($ch, CURLOPT_USERPWD, urlencode($urls[$i]["login"]) . ":" . urlencode($urls[$i]["password"])); + if ($GLOBALS["DEBUG"]) { + echo "set basic auth\n"; + } + } + curl_multi_add_handle($master, $ch); + } + // remove the curl handle that just completed + curl_multi_remove_handle($master, $done['handle']); + } + } while ($running); + + curl_multi_close($master); + return true; + } + +} + +/* Class cron */ diff --git a/bureau/class/m_crypto.php b/bureau/class/m_crypto.php index 2fc844bc..de8d15fb 100644 --- a/bureau/class/m_crypto.php +++ b/bureau/class/m_crypto.php @@ -1,44 +1,31 @@ status=true; - ini_set('display_errors', '1'); + var $infos = ""; + var $status = false; + var $nb_sql_query = 0; + var $tps_sql_query = 0; + var $generation_started = null; + + /* --------------------------------------------------------------------------- */ + + /** Constructor + */ + function m_debug_alternc() { + if (isset($_COOKIE['alternc_debugme']) && $_COOKIE['alternc_debugme']) { + $this->status = true; + ini_set('display_errors', '1'); + } + $this->nb_sql_query = 0; + $this->tps_sql_query = 0; + $this->generation_started = microtime(true); } - $this->nb_sql_query=0; - $this->tps_sql_query=0; - $this->generation_started=microtime(true); - } - function activate() { - setcookie('alternc_debugme','1', time()+3600); // expire in 1 hour - $this->status=""; - return true; - } + function activate() { + setcookie('alternc_debugme', '1', time() + 3600); // expire in 1 hour + $this->status = ""; + return true; + } - function desactivate() { - setcookie('alternc_debugme','0'); - $this->status=false; - return true; - } + function desactivate() { + setcookie('alternc_debugme', '0'); + $this->status = false; + return true; + } - function add($txt) { - $this->infos .= "\n$txt"; - return true; - } + function add($txt) { + $this->infos .= "\n$txt"; + return true; + } - function dump() { - global $cuid; - if ( $cuid!=2000 ) return false; - if ( ! $this->status ) return false; + function dump() { + global $cuid; + if ($cuid != 2000) { + return false; + } + if (!$this->status) { + return false; + } - $generation_time = (microtime(true) - $this->generation_started) * 1000; + $generation_time = (microtime(true) - $this->generation_started) * 1000; - echo ""; - return true; - } + echo ""; + return true; + } -} /* Class debug_alternc */ +} -?> +/* Class debug_alternc */ diff --git a/bureau/class/m_dom.php b/bureau/class/m_dom.php index 90b1f9eb..be144747 100644 --- a/bureau/class/m_dom.php +++ b/bureau/class/m_dom.php @@ -85,7 +85,7 @@ class m_dom { * Constructeur */ function m_dom() { - global $L_FQDN; + global $L_FQDN; $this->tld_no_check_at_all = variable_get('tld_no_check_at_all', 0, 'Disable ALL check on the TLD (users will be able to add any domain)', array('desc' => 'Disabled', 'type' => 'boolean')); variable_get('mailname_bounce', $L_FQDN, 'FQDN of the mail server, used to create vhost virtual mail_adress.', array('desc' => 'FQDN', 'type' => 'string')); } @@ -105,16 +105,16 @@ class m_dom { * @param string $fqdn */ public static function get_sub_domain_id_and_member_by_name($fqdn) { - global $db, $err, $cuid; + global $db, $err; $err->log("dom", "get_sub_domain_by_name"); $fqdn = mysql_real_escape_string($fqdn); $db->query("select sd.* from sub_domaines sd where if(length(sd.sub)>0,concat_ws('.',sd.sub,sd.domaine),sd.domaine) = '$fqdn';"); - if (!$db->next_record()) + if (!$db->next_record()) { return false; + } return array('sub_id' => intval($db->f('id')), 'member_id' => intval($db->f('compte'))); } - function hook_menu() { global $quota; $obj = array( @@ -123,22 +123,20 @@ class m_dom { 'link' => 'toggle', 'pos' => 20, 'links' => array(), - ); + ); if ($quota->cancreate("dom")) { - $obj['links'][] = - array( - 'ico' => 'images/new.png', - 'txt' => _("Add a domain"), - 'url' => "dom_add.php", + $obj['links'][] = array( + 'ico' => 'images/new.png', + 'txt' => _("Add a domain"), + 'url' => "dom_add.php", ); } foreach ($this->enum_domains() as $d) { - $obj['links'][] = - array( - 'txt' => htmlentities($d), - 'url' => "dom_edit.php?domain=" . urlencode($d), + $obj['links'][] = array( + 'txt' => htmlentities($d), + 'url' => "dom_edit.php?domain=" . urlencode($d), ); } @@ -186,7 +184,7 @@ class m_dom { * @param integer $type */ function domains_type_target_values($type = null) { - global $db, $err, $cuid; + global $db, $err; $err->log("dom", "domains_type_target_values"); if (is_null($type)) { $db->query("desc domaines_type;"); @@ -202,8 +200,9 @@ class m_dom { return $r; } else { $db->query("select target from domaines_type where name='$type';"); - if (!$db->next_record()) + if (!$db->next_record()) { return false; + } return $db->f('target'); } } @@ -220,8 +219,9 @@ class m_dom { $val = array(); foreach (explode("\n", $zone) as $z) { $z = trim($z); - if (empty($z)) + if (empty($z)) { continue; + } $val[] = $this->import_manual_dns_entry($z, $domain, $detect_redirect, $save); } return $val; @@ -231,11 +231,12 @@ class m_dom { * @param string $zone */ function import_manual_dns_entry($zone, $domain, $detect_redirect = true, $save = false) { - global $cuid, $err; + global $err; $err->log("dom", "import_manual_dns_entry"); $zone = trim($zone); - if (empty($zone)) + if (empty($zone)) { return false; + } $domain = trim($domain); if (empty($domain)) { @@ -409,7 +410,6 @@ class m_dom { } private function import_manual_dns_entry_doit($entry) { - global $err; $entry['did_it'] = 0; if ($entry['status'] == 'err') { return $entry; @@ -417,14 +417,14 @@ class m_dom { $val = $entry['entry_new']; - if (empty($val['type'])) + if (empty($val['type'])) { return false; + } switch ($val['type']) { case "set_ttl": $entry['did_it'] = $this->set_ttl($this->get_domain_byname($val['domain']), $val['value']); return $entry; - break; } // If it is an unknown domains type @@ -435,8 +435,9 @@ class m_dom { } // If the subdomain is @, we want an empty subdomain - if ($val['sub'] == '@') + if ($val['sub'] == '@') { $val['sub'] = ''; + } $this->lock(); $entry['did_it'] = $this->set_sub_domain($val['domain'], $val['sub'], $val['type'], $val['value']); @@ -492,7 +493,6 @@ class m_dom { if ($result === false) { throw new Exception("Could not read data from {$url}"); - return false; } if (strstr($http_response_header[0], '301') || strstr($http_response_header[0], '302')) { // This is a redirection @@ -528,7 +528,7 @@ class m_dom { } function domains_type_get($name) { - global $db, $err, $cuid; + global $db; $name = mysql_real_escape_string($name); $db->query("select * from domaines_type where name='$name' ;"); $db->next_record(); @@ -536,14 +536,14 @@ class m_dom { } function domains_type_del($name) { - global $db, $err, $cuid; + global $db; $name = mysql_real_escape_string($name); $db->query("delete domaines_type where name='$name';"); return true; } function domains_type_update($name, $description, $target, $entry, $compatibility, $enable, $only_dns, $need_dns, $advanced, $create_tmpdir, $create_targetdir) { - global $err, $cuid, $db; + global $err, $db; // The name MUST contain only letter and digits, it's an identifier after all ... if (!preg_match("#^[a-z0-9]+$#", $name)) { $err->raise("dom", _("The name MUST contain only letter and digits")); @@ -565,12 +565,13 @@ class m_dom { } function sub_domain_change_status($sub_id, $status) { - global $db, $err, $cuid; + global $db, $err; $err->log("dom", "sub_domain_change_status"); $sub_id = intval($sub_id); $status = strtoupper($status); - if (!in_array($status, array('ENABLE', 'DISABLE'))) + if (!in_array($status, array('ENABLE', 'DISABLE'))) { return false; + } $jh = $this->get_sub_domain_all($sub_id); if ($status == 'ENABLE') { // check compatibility with existing sub_domains @@ -618,9 +619,7 @@ class m_dom { $dom = strtolower($dom); $db->query("UPDATE sub_domaines SET web_action='UPDATE' WHERE domaine='$dom';"); $this->set_dns_action($dom, 'UPDATE'); - # TODO : some work with domain sensitive classes - return true; } @@ -638,7 +637,7 @@ class m_dom { * @return boolean Retourne FALSE si une erreur s'est produite, TRUE sinon. */ function del_domain($dom) { - global $db, $err, $classes, $cuid, $hooks; + global $db, $err, $hooks; $err->log("dom", "del_domain", $dom); $dom = strtolower($dom); @@ -689,7 +688,7 @@ class m_dom { $ @return boolean Retourne FALSE si une erreur s'est produite, TRUE sinon. */ function add_domain($domain, $dns, $noerase = false, $force = false, $isslave = false, $slavedom = "") { - global $db, $err, $quota, $classes, $L_MX, $L_FQDN, $tld, $cuid, $bro, $hooks; + global $db, $err, $quota, $L_FQDN, $tld, $cuid, $hooks; $err->log("dom", "add_domain", $domain); // Locked ? @@ -740,14 +739,15 @@ class m_dom { return false; } - if ($dns) + if ($dns) { $dns = "1"; - else + } else { $dns = "0"; - + } // mode 5 : force DNS to NO. - if ($tld[$v] == 5) + if ($tld[$v] == 5) { $dns = 0; + } // It must be a real domain (no subdomain) if (!$dns) { $v = checkhostallow_nodns($domain); @@ -762,14 +762,16 @@ class m_dom { $err->raise("dom", _("Your domain quota is over, you cannot create more domain names")); return false; } - if ($noerase) + if ($noerase) { $noerase = "1"; - else + } else { $noerase = "0"; - if ($dns) + } + if ($dns) { $gesmx = "1"; - else + } else { $gesmx = "0"; // do not host mx by default if not hosting the DNS + } $db->query("INSERT INTO domaines (compte,domaine,gesdns,gesmx,noerase,dns_action) VALUES ('$cuid','$domain','$dns','$gesmx','$noerase','UPDATE');"); if (!($id = $db->lastid())) { $err->raise("dom", _("An unexpected error occured when creating the domain")); @@ -798,8 +800,9 @@ class m_dom { } // New Hooks: $hooks->invoke("hook_dom_add_domain", array($id)); - if ($gesmx) + if ($gesmx) { $hooks->invoke("hook_dom_add_mx_domain", array($id)); + } if ($isslave) { $hooks->invoke("hook_dom_add_slave_domain", array($id, $slavedom)); } @@ -834,13 +837,11 @@ class m_dom { * @param string $domain */ function domdefaultdir($domain) { - global $bro, $cuid; - $dest_root = $bro->get_userid_root($cuid); - # return $dest_root."/www/".$this->domshort($domain); return "/www/" . $this->domshort($domain); } function dump_axfr($domain, $ns = 'localhost') { + $axfr = array(); exec('/usr/bin/dig AXFR "' . escapeshellcmd($domain) . '" @"' . escapeshellcmd($ns) . '"', $axfr); return $axfr; } @@ -858,7 +859,7 @@ class m_dom { 'domain_type_parameter' => $db->f('domain_type_parameter'), 'concerned' => $db->f('concerned'), 'enabled' => $db->f('enabled') - ); + ); } return $c; @@ -869,12 +870,14 @@ class m_dom { $err->log("dom", "update_default_subdomains"); $ok = true; foreach ($arr as $a) { - if (!isset($a['id'])) + if (!isset($a['id'])) { $a['id'] = null; + } if (!empty($a['sub']) || !empty($a['domain_type_parameter'])) { - if (!isset($a['enabled'])) + if (!isset($a['enabled'])) { $a['enabled'] = 0; + } if (!$this->update_one_default($a['domain_type'], $a['sub'], $a['domain_type_parameter'], $a['concerned'], $a['enabled'], $a['id'])) { $ok = false; } @@ -887,10 +890,11 @@ class m_dom { global $db, $err; $err->log("dom", "update_one_default"); - if ($id == null) + if ($id == null) { $db->query("INSERT INTO default_subdomains values ('','" . addslashes($sub) . "','" . addslashes($domain_type) . "','" . addslashes($domain_type_parameter) . "','" . addslashes($concerned) . "','" . addslashes($enabled) . "');"); - else + } else { $db->query("UPDATE default_subdomains set sub='" . addslashes($sub) . "', domain_type='" . addslashes($domain_type) . "',domain_type_parameter='" . addslashes($domain_type_parameter) . "',concerned='" . addslashes($concerned) . "',enabled='" . addslashes($enabled) . "' where id=" . addslashes($id) . ";"); + } return true; //update } @@ -924,7 +928,7 @@ class m_dom { * */ function whois($domain) { - global $db, $err; + global $err; $err->log("dom", "whois", $domain); // pour ajouter un nouveau TLD, utiliser le code ci-dessous. // echo "whois : $domain
"; @@ -978,16 +982,19 @@ class m_dom { if (preg_match("#Name Server:#", $ligne)) { $found = true; $tmp = strtolower(str_replace(chr(10), "", str_replace(chr(13), "", str_replace(" ", "", str_replace("Name Server:", "", $ligne))))); - if ($tmp) + if ($tmp) { $serveurList[] = $tmp; + } } break; case "cx": $ligne = str_replace(chr(10), "", str_replace(chr(13), "", str_replace(" ", "", $ligne))); - if ($ligne == "" && $state == 1) + if ($ligne == "" && $state == 1) { $state = 2; - if ($state == 1) + } + if ($state == 1) { $serveurList[] = strtolower($ligne); + } if ($ligne == "Nameservers:" && $state == 0) { $state = 1; $found = true; @@ -996,10 +1003,12 @@ class m_dom { case "eu": case "be": $ligne = preg_replace("/^ *([^ ]*) \(.*\)$/", "\\1", trim($ligne)); - if ($found) + if ($found) { $tmp = trim($ligne); - if ($tmp) + } + if ($tmp) { $serveurList[] = $tmp; + } if ($ligne == "Nameservers:") { $state = 1; $found = true; @@ -1011,16 +1020,18 @@ class m_dom { // weird regexp (trailing garbage after name server), but I could not make it work otherwise $tmp = strtolower(preg_replace('/Name Server: ([^ ]+)\..$/', "\\1", $ligne)); $tmp = preg_replace('/[^-_a-z0-9\.]/', '', $tmp); - if ($tmp) + if ($tmp) { $serveurList[] = $tmp; + } } break; case "it": if (preg_match("#nserver:#", $ligne)) { $found = true; $tmp = strtolower(preg_replace("/nserver:\s*[^ ]*\s*([^\s]*)$/", "\\1", $ligne)); - if ($tmp) + if ($tmp) { $serveurList[] = $tmp; + } } break; case "fr": @@ -1028,8 +1039,9 @@ class m_dom { if (preg_match("#nserver:#", $ligne)) { $found = true; $tmp = strtolower(preg_replace("#nserver:\s*([^\s]*)\s*.*$#", "\\1", $ligne)); - if ($tmp) + if ($tmp) { $serveurList[] = $tmp; + } } break; case "ca": @@ -1082,6 +1094,8 @@ class m_dom { function checkmx($domaine, $ref_domain = '') { global $L_DEFAULT_MX, $L_DEFAULT_SECONDARY_MX; + $ref_mx = array(); + $mxhosts = array(); if (!empty($ref_domain)) { getmxrr($ref_domain, $ref_mx); } else { @@ -1114,9 +1128,6 @@ class m_dom { return 0; } -//checkmx - - /* ----------------------------------------------------------------- */ /** @@ -1245,7 +1256,7 @@ class m_dom { * @param string $value */ function check_type_value($type, $value) { - global $db, $err, $cuid; + global $err; // check the type we can have in domaines_type.target switch ($this->domains_type_target_values($type)) { @@ -1273,7 +1284,6 @@ class m_dom { return false; } return true; - break; case 'IP': if (checkip($value)) { return true; @@ -1309,14 +1319,10 @@ class m_dom { default: $err->raise("dom", _("Invalid domain type selected, please check")); return false; - break; } return false; } -//check_type_value - - /* ----------------------------------------------------------------- */ /** @@ -1328,13 +1334,14 @@ class m_dom { * @return boolean tell you if the subdomain can be installed there */ function can_create_subdomain($dom, $sub, $type, $sub_domain_id = 'null') { - global $db, $err, $cuid; + global $db, $err; $err->log("dom", "can_create_subdomain", $dom . "/" . $sub); // Get the compatibility list for this domain type $db->query("select upper(compatibility) as compatibility from domaines_type where upper(name)=upper('$type');"); - if (!$db->next_record()) + if (!$db->next_record()) { return false; + } $compatibility_lst = explode(",", $db->f('compatibility')); // Get the list of type of subdomains already here who have the same name @@ -1342,8 +1349,9 @@ class m_dom { #$db->query("select * from sub_domaines where sub='$sub' and domaine='$dom';"); while ($db->next_record()) { // And if there is a domain with a incompatible type, return false - if (!in_array(strtoupper($db->f('type')), $compatibility_lst)) + if (!in_array(strtoupper($db->f('type')), $compatibility_lst)) { return false; + } } // All is right, go ! Create ur domain ! @@ -1393,7 +1401,7 @@ class m_dom { } // On a épuré $dir des problémes eventuels ... On est en DESSOUS du dossier de l'utilisateur. - if ($t = checkfqdn($dom)) { + if (($t = checkfqdn($dom))) { $err->raise("dom", _("The domain name is syntaxically incorrect")); return false; } @@ -1415,7 +1423,7 @@ class m_dom { // Create TMP dir and TARGET dir if needed by the domains_type $dest_root = $bro->get_userid_root($cuid); - $domshort = $this->domshort($dom, $sub); + //$domshort = $this->domshort($dom, $sub); $db->query("select create_tmpdir, create_targetdir from domaines_type where name = '$type';"); $db->next_record(); if ($db->f('create_tmpdir')) { @@ -1444,9 +1452,6 @@ class m_dom { return true; } -// set_sub_domain - - /* ----------------------------------------------------------------- */ /** @@ -1456,7 +1461,7 @@ class m_dom { * */ function del_sub_domain($sub_domain_id) { - global $db, $err, $cuid; + global $db, $err; $err->log("dom", "del_sub_domain", $sub_domain_id); // Locked ? if (!$this->islocked) { @@ -1473,8 +1478,6 @@ class m_dom { return true; } -// del_sub_domain - /** * @param integer $dom_id */ @@ -1505,7 +1508,7 @@ class m_dom { * */ function edit_domain($dom, $dns, $gesmx, $force = false, $ttl = 86400) { - global $db, $err, $L_MX, $classes, $cuid, $hooks; + global $db, $err, $hooks; $err->log("dom", "edit_domain", $dom . "/" . $dns . "/" . $gesmx); // Locked ? if (!$this->islocked && !$force) { @@ -1544,8 +1547,9 @@ class m_dom { $err->raise("dom", _("The domain name %s does not exist"), $dom); return false; } - if ($dns != "1") + if ($dns != "1") { $dns = "0"; + } // On vérifie que des modifications ont bien eu lieu :) if ($r["dns"] == $dns && $r["mail"] == $gesmx && $r["zonettl"] == $ttl) { $err->raise("dom", _("No change has been requested...")); @@ -1579,14 +1583,10 @@ class m_dom { return true; } -// edit_domain - - /* * ************************* */ /* Slave dns ip managment */ /* * ************************* */ - /* ----------------------------------------------------------------- */ /** Return the list of ip addresses and classes that are allowed access to domain list @@ -1616,8 +1616,9 @@ class m_dom { return false; } $class = intval($class); - if ($class < 8 || $class > 32) + if ($class < 8 || $class > 32) { $class = 32; + } $db->query("SELECT * FROM slaveip WHERE ip='$ip' AND class='$class';"); if ($db->next_record()) { $err->raise("err", _("The requested domain is forbidden in this server, please contact the administrator")); @@ -1652,7 +1653,7 @@ class m_dom { /** Check for a slave account */ function check_slave_account($login, $pass) { - global $db, $err; + global $db; $db->query("SELECT * FROM slaveaccount WHERE login='$login' AND pass='$pass';"); if ($db->next_record()) { return true; @@ -1665,7 +1666,7 @@ class m_dom { /** Out (echo) the complete hosted domain list : */ function echo_domain_list($integrity = false) { - global $db, $err; + global $db; $db->query("SELECT domaine FROM domaines WHERE gesdns=1 ORDER BY domaine"); $tt = ""; while ($db->next_record()) { @@ -1684,7 +1685,7 @@ class m_dom { /** Returns the complete hosted domain list : */ function get_domain_list($uid = -1) { - global $db, $err; + global $db; $uid = intval($uid); $res = array(); $sql = ""; @@ -1703,7 +1704,7 @@ class m_dom { * @return array */ function get_domain_all_summary() { - global $db, $err; + global $db; $res = array(); $db->query("SELECT domaine, gesdns, gesmx, dns_action, zonettl FROM domaines ORDER BY domaine"); while ($db->next_record()) { @@ -1770,7 +1771,7 @@ class m_dom { /** Count all domains, for all users */ function count_domains_all() { - global $db, $err, $cuid; + global $db; $db->query("SELECT COUNT(*) AS count FROM domaines;"); if ($db->next_record()) { return $db->f('count'); @@ -1784,14 +1785,15 @@ class m_dom { /** Return the list of allowed slave accounts */ function enum_slave_account() { - global $db, $err; + global $db; $db->query("SELECT * FROM slaveaccount;"); $res = array(); while ($db->next_record()) { $res[] = $db->Record; } - if (!count($res)) + if (!count($res)) { return false; + } return $res; } @@ -1831,7 +1833,7 @@ class m_dom { * @access private */ function lock() { - global $db, $err; + global $err; $err->log("dom", "lock"); if ($this->islocked) { $err->raise("dom", _("--- Program error --- Lock already obtained!")); @@ -1850,7 +1852,7 @@ class m_dom { * @access private */ function unlock() { - global $db, $err; + global $err; $err->log("dom", "unlock"); if (!$this->islocked) { $err->raise("dom", _("--- Program error --- No lock on the domains!")); @@ -1914,10 +1916,10 @@ class m_dom { * No parameters needed * */ function alternc_export_conf() { - global $db, $err; + global $err; $err->log("dom", "export"); $this->enum_domains(); - $str=""; + $str = ""; foreach ($this->domains as $d) { $str.= "\n"; $str.=" " . $d . " \n"; @@ -1975,6 +1977,7 @@ class m_dom { if ($only_apache) { $params.=" and dt.only_dns is false "; } + // BUG BUG BUG FIXME // Suppression de comptes -> membres existe pas -> domaines a supprimer ne sont pas lister $db->query(" @@ -2017,8 +2020,9 @@ order by global $dom; $d = array(); foreach ($dom->domains_type_lst() as $k => $v) { - if ($v['only_dns'] == true) + if ($v['only_dns'] == true) { continue; + } if (!$j = file_get_contents(ALTERNC_APACHE2_GEN_TMPL_DIR . '/' . strtolower($k) . '.conf')) { die("Error: missing file for $k"); } @@ -2030,9 +2034,9 @@ order by // Launch old fashionned hooks as there was in AlternC 1.0 function generate_conf_oldhook($action, $lst_sub, $sub_obj = null) { - if (is_null($sub_obj)) + if (is_null($sub_obj)) { $sub_obj = $this->generation_parameters(null, false); - + } if (!isset($lst_sub[strtoupper($action)]) || empty($lst_sub[strtoupper($action)])) { return false; } @@ -2173,12 +2177,14 @@ order by // by subdomain $tmp = array(); foreach ($da['sub'] as $sub) { - if ($sub['web_action']!='OK') continue; + if ($sub['web_action'] != 'OK') { + continue; + } if (!$sub['only_dns']) { if (!isset($tmp[$sub['fqdn']])) { $tmp[$sub['fqdn']] = 0; } - $tmp[$sub['fqdn']]++; + $tmp[$sub['fqdn']] ++; if ($tmp[$sub['fqdn']] >= 2) { $errors[$sub['fqdn']] = sprintf(_("Problem on %s: there is more than 1 web configuration going to be generated for this sub-domain."), $sub['fqdn']); } diff --git a/bureau/class/m_err.php b/bureau/class/m_err.php index ed201cfc..f6e644c2 100644 --- a/bureau/class/m_err.php +++ b/bureau/class/m_err.php @@ -1,142 +1,144 @@ Cette classe gère les erreurs qui peuvent apparaitre lors d'appels -* à l'API d'AlternC. Ces erreurs sont stockées sous la forme de 2 nombres -* (Classe ID et Numéro d'erreur) ainsi qu'un texte facultatif associé. -* Des textes d'erreurs localisés sont aussi disponibles. -*Cette classe se charge aussi d'insérer les appels à l'API d'AlternC -* dans les logs du système dans /var/log/alternc/bureau.log -*
-* Copyleft {@link http://alternc.net/ AlternC Team} -* -* @copyright AlternC-Team 2002-11-01 http://alternc.net/ -*/ + * Classe de gestion des erreurs apparaissant lors d'appels API. + * + *Cette classe gère les erreurs qui peuvent apparaitre lors d'appels + * à l'API d'AlternC. Ces erreurs sont stockées sous la forme de 2 nombres + * (Classe ID et Numéro d'erreur) ainsi qu'un texte facultatif associé. + * Des textes d'erreurs localisés sont aussi disponibles.
+ *Cette classe se charge aussi d'insérer les appels à l'API d'AlternC + * dans les logs du système dans /var/log/alternc/bureau.log + *
+ * Copyleft {@link http://alternc.net/ AlternC Team} + * + * @copyright AlternC-Team 2002-11-01 http://alternc.net/ + */ class m_err { - /** Numero de classe d'erreur actuelle */ - var $clsid=0; + /** Numero de classe d'erreur actuelle */ + var $clsid = 0; - /** Dernière erreur enregistrée par la classe */ - var $error=0; + /** Dernière erreur enregistrée par la classe */ + var $error = 0; - /** Paramètre chaine eventuellement associé à la dernière erreur */ - var $param=""; + /** Paramètre chaine eventuellement associé à la dernière erreur */ + var $param = ""; - /** Emplacement du fichier de logs d'AlternC */ - var $logfile="/var/log/alternc/bureau.log"; + /** Emplacement du fichier de logs d'AlternC */ + var $logfile = "/var/log/alternc/bureau.log"; - /** - * Leve une erreur, signale celle-ci dans les logs et stocke le code erreur - * - * Cette fonction lance une erreur, l'ajoute dans les logs d'AlternC, - * et la met à disposition pour le bureau virtuel pour affichage ultérieur. - * - * @todo ne plus utiliser $error de façon numérique, nulle part - * - * @param integer $clsid Classe qui lève l'erreur - * @param mixed $error Numéro de l'erreur ou chaîne décrivant l'erreur - * @param string $param Paramètre chaine associé à l'erreur (facultatif) - * @return boolean TRUE si l'erreur est connue, FALSE sinon. - * - */ - function raise($clsid,$error,$param="") { - /* Leve une exception. Si elle existe, sinon, stocke un message d'erreur sur erreur ...*/ - if (_("err_".$clsid."_".$error)!="err_".$clsid."_".$error || is_string($error)) { - $this->clsid=$clsid; - $this->error=$error; - $args = func_get_args(); - $this->param=array_slice($args, 2); - $this->logerr(); - return true; - } else { - $this->clsid="err"; - $this->error=1; - $this->param="Error # $error in Class $clsid, Value is $param. (sorry, no text for this error in your language at the moment)"; - $this->logerr(); - return false; + /** + * Leve une erreur, signale celle-ci dans les logs et stocke le code erreur + * + * Cette fonction lance une erreur, l'ajoute dans les logs d'AlternC, + * et la met à disposition pour le bureau virtuel pour affichage ultérieur. + * + * @todo ne plus utiliser $error de façon numérique, nulle part + * + * @param integer $clsid Classe qui lève l'erreur + * @param mixed $error Numéro de l'erreur ou chaîne décrivant l'erreur + * @param string $param Paramètre chaine associé à l'erreur (facultatif) + * @return boolean TRUE si l'erreur est connue, FALSE sinon. + * + */ + function raise($clsid, $error, $param = "") { + /* Leve une exception. Si elle existe, sinon, stocke un message d'erreur sur erreur ... */ + if (_("err_" . $clsid . "_" . $error) != "err_" . $clsid . "_" . $error || is_string($error)) { + $this->clsid = $clsid; + $this->error = $error; + $args = func_get_args(); + $this->param = array_slice($args, 2); + $this->logerr(); + return true; + } else { + $this->clsid = "err"; + $this->error = 1; + $this->param = "Error # $error in Class $clsid, Value is $param. (sorry, no text for this error in your language at the moment)"; + $this->logerr(); + return false; + } } - } - /** - * Retourne la chaine d'erreur correspondant à la dernière erreur rencontrée - * - * Si la dernière erreur rencontrée est connue, retourne l'erreur en toute lettre - * dans la langue actuellement sélectionnée, ou en anglais par défaut. - * Si l'erreur n'est pas connue, retourne son numéro de classe et d'ereur. - * - * @return string Chaine d'erreur. - * - */ - function errstr() { - if (is_string($this->error)) { - // new way of handling errors: message directly in the class - $str = $this->error."\n"; - } else { - // old way: message in the locales files (ugly) - $str = _("err_".$this->clsid."_".$this->error)."\n"; + /** + * Retourne la chaine d'erreur correspondant à la dernière erreur rencontrée + * + * Si la dernière erreur rencontrée est connue, retourne l'erreur en toute lettre + * dans la langue actuellement sélectionnée, ou en anglais par défaut. + * Si l'erreur n'est pas connue, retourne son numéro de classe et d'ereur. + * + * @return string Chaine d'erreur. + * + */ + function errstr() { + if (is_string($this->error)) { + // new way of handling errors: message directly in the class + $str = $this->error . "\n"; + } else { + // old way: message in the locales files (ugly) + $str = _("err_" . $this->clsid . "_" . $this->error) . "\n"; + } + $args = $this->param; + if (is_array($args)) { + array_unshift($args, $str); + $msg = call_user_func_array("sprintf", $args); + return $msg; + } else { + return $args; + } } - $args = $this->param; - if (is_array($args)) { - array_unshift($args, $str); - $msg = call_user_func_array("sprintf", $args); - return $msg; - } else { - return $args; + + /** + * Envoi un log d'erreur dans /var/log/alternc/bureau.log + * + * Cette fonction Loggue la dernière erreur dans /var/log sur la machine, + * permettant ainsi aux admins de savoir ce qu'il se passe... + * Elle est appelée automatiquement par error + * @access private + */ + function logerr() { + global $mem; + @file_put_contents($this->logfile, date("d/m/Y H:i:s") . " - ERROR - " . $mem->user["login"] . " - " . $this->errstr(), FILE_APPEND); } - } - /** - * Envoi un log d'erreur dans /var/log/alternc/bureau.log - * - * Cette fonction Loggue la dernière erreur dans /var/log sur la machine, - * permettant ainsi aux admins de savoir ce qu'il se passe... - * Elle est appelée automatiquement par error - * @access private - */ - function logerr() { - global $mem; - @file_put_contents($this->logfile, date("d/m/Y H:i:s")." - ERROR - ".$mem->user["login"]." - ".$this->errstr(), FILE_APPEND ); - } + /** + * Envoi un log d'appel d'API dans /var/log/alternc/bureau.log + * + * Cette fonction loggue dans /var/log l'appel à la fonction de l'API + * d'AlternC. + * + * @param integer $clsid Numéro de la classe dont on a appelé une fonction + * @param string $function Nom de la fonction appelée + * @param string $param Paramètre (facultatif) passés à la fonction de l'API. + * @return boolean TRUE si le log a été ajouté, FALSE sinon + * + */ + function log($clsid, $function, $param = "") { + global $mem; + return @file_put_contents($this->logfile, date("d/m/Y H:i:s") . " - " . get_remote_ip() . " - CALL - " . $mem->user["login"] . " - $clsid - $function - $param\n", FILE_APPEND); + } - /** - * Envoi un log d'appel d'API dans /var/log/alternc/bureau.log - * - * Cette fonction loggue dans /var/log l'appel à la fonction de l'API - * d'AlternC. - * - * @param integer $clsid Numéro de la classe dont on a appelé une fonction - * @param string $function Nom de la fonction appelée - * @param string $param Paramètre (facultatif) passés à la fonction de l'API. - * @return boolean TRUE si le log a été ajouté, FALSE sinon - * - */ - function log($clsid,$function,$param="") { - global $mem,$cuid; - return @file_put_contents($this->logfile,date("d/m/Y H:i:s")." - " .get_remote_ip(). " - CALL - ".$mem->user["login"]." - $clsid - $function - $param\n", FILE_APPEND ); - } +} -}; /* Classe m_err */ - -?> +/* Classe m_err */ diff --git a/bureau/class/m_export.php b/bureau/class/m_export.php index 9289ea38..dfb0c2a4 100644 --- a/bureau/class/m_export.php +++ b/bureau/class/m_export.php @@ -1,29 +1,35 @@ invoke('alternc_export_conf'); + $config = $hooks->invoke('alternc_export_conf'); return $config; } -/** le repertoire de base est passé en paramettre puis en construit une arborescence de la forme -/ / / / invoke('alternc_export_data', Array($dir)); } -}// export Class end +} + +// export Class end diff --git a/bureau/class/m_ftp.php b/bureau/class/m_ftp.php index 56fe7d97..df536fdb 100644 --- a/bureau/class/m_ftp.php +++ b/bureau/class/m_ftp.php @@ -1,4 +1,5 @@ srv_name = variable_get('ftp_human_name', $L_FQDN,'Human name for FTP server', array('desc'=>'Name','type'=>'string')); - } + /* ----------------------------------------------------------------- */ - - /* ----------------------------------------------------------------- */ - /** - * Password kind used in this class (hook for admin class) - */ - function alternc_password_policy() { - return array("ftp"=>"FTP accounts"); - } - - function hook_menu() { - global $quota; - $q = $quota->getquota("ftp"); - - $obj = array( - 'title' => _("FTP accounts"), - 'ico' => 'images/ftp.png', - 'link' => 'toggle', - 'pos' => 60, - 'links' => array(), - ) ; - - if ( $quota->cancreate("ftp") ) { - $obj['links'][] = - array ( - 'ico' => 'images/new.png', - 'txt' => _("Create a new ftp account"), - 'url' => "ftp_edit.php?create=1", - 'class' => '', - ); - } - - if ( $q['u'] > 0 ) { // if there are some FTP accounts - $obj['links'][] = - array ( - 'txt' => _("FTP accounts list"), - 'url' => "ftp_list.php" - ); - } - - return $obj; - } - - // Return the values needed to activate security access. See get_auth_class() - // in authip for more informations - function authip_class() { - $c = Array(); - $c['name']="FTP"; - $c['protocol']="ftp"; - $c['values']=Array(); - - $tt = $this->get_list(); - if (empty($tt) || !is_array($tt)) return $c; - foreach ($this->get_list() as $v ) { - $c['values'][$v['id']]=$v['login']; + /** + * Constructeur + */ + function m_ftp() { + global $L_FQDN; + $this->srv_name = variable_get('ftp_human_name', $L_FQDN, 'Human name for FTP server', array('desc' => 'Name', 'type' => 'string')); } - return $c; - } + /* ----------------------------------------------------------------- */ - // Switch enabled status of an account - function switch_enabled($id,$status=null) { - global $cuid, $db, $err; - if (! $jj = $this->get_ftp_details($id)) { - $err->raise('ftp', _("This account do not exist or is not of this account")); - return false; - } - if ( $status == null ){ - if ($jj[0]['enabled'] == true ) { $status=0;} - else { $status=1; } - } - - // Be sure what is in $status, in case of it was a parameter - $status = ($status?'true':'false'); - - if ( ! $db->query("UPDATE ftpusers SET enabled = $status WHERE uid = '$cuid' AND id = '$id' ;") ) { - $err->raise('ftp', _("Error during update")); - return false; - } else { - return true ; - } - } - - - /* ----------------------------------------------------------------- */ - /** Retourne la liste des comptes FTP du compte hébergé - * Retourne la liste des comptes FTP sous forme de tableau indexé de - * tableaus associatifs comme suit : - * $a["id"]= ID du compte ftp - * $a["login"]= Nom de login du compte - * $a["dir"]= Dossier relatif à la racine du compte de l'utilisateur - * @return array Retourne le tableau des comptes - */ - function get_list() { - global $db,$err,$cuid, $bro; - $err->log("ftp","get_list"); - $r=array(); - $db->query("SELECT id, name, homedir, enabled FROM ftpusers WHERE uid='$cuid' ORDER BY name;"); - if ($db->num_rows()) { - while ($db->next_record()) { - $r[]=array( - "id"=>$db->f("id"), - "login"=>$db->f("name"), - "enabled"=>$db->f("enabled"), - //"dir"=>$match[1] - "dir"=>$db->f("homedir") - ); - } - return $r; - } else { - $err->raise("ftp",_("No FTP account found")); - return array(); - } - } - - /* ----------------------------------------------------------------- */ - /** Retourne les détails d'un compte FTP (voir get_list) - * Le tableau est celui du compte d'id spécifié - * @param integer $id Numéro du compte dont on souhaite obtenir les détails - * @return array Tableau associatif contenant les infos du comptes ftp - */ - function get_ftp_details($id) { - global $db,$err,$cuid; - $err->log("ftp","get_ftp_details",$id); - $r=array(); - $db->query("SELECT id, name, homedir, enabled FROM ftpusers WHERE uid='$cuid' AND id='$id';"); - if ($db->num_rows()) { - $db->next_record(); - - $regexp="/^".preg_quote(getuserpath(),"/")."\/(.*)$/"; - preg_match($regexp, $db->f("homedir"),$match); - - $lg=explode("_",$db->f("name")); - if ((!is_array($lg)) || (count($lg)!=2)) { - $lg[0]=$db->f("name"); - $lg[1]=""; - } - $r[]=array( - "id"=>$db->f("id"), - "prefixe"=> $lg[0], - "login"=>$lg[1], - "dir"=>$match[1], - "enabled"=>$db->f("enabled") - ); - return $r; - } else { - $err->raise("ftp",_("This FTP account does not exist")); - return false; - } - } - - /* ----------------------------------------------------------------- */ - /** Retourne la liste des prefixes utilisables par le compte courant - * @return array tableau contenant la liste des prefixes (domaines + login) - * du compte actuel. - */ - function prefix_list() { - global $db,$mem,$cuid; - $r=array(); - $r[]=$mem->user["login"]; - $db->query("SELECT domaine FROM domaines WHERE compte='$cuid' ORDER BY domaine;"); - while ($db->next_record()) { - $r[]=$db->f("domaine"); - } - return $r; - } - - /** - * Check if the login is fine (syntax) - * - * @param string $l - */ - function check_login($l) { - global $err; - - // special chars and the max numbers of them allowed - // to be able to give a specific error - $vv = array('_'=>'1', ' '=>0); - foreach ($vv as $k=>$n) { - if (substr_count($l, $k) > $n ) { // if there is more than $n $k - $err->raise('ftp', sprintf(_("FTP login is incorrect: too many '%s'"), $k)); - return false; - } + /** + * Password kind used in this class (hook for admin class) + */ + function alternc_password_policy() { + return array("ftp" => "FTP accounts"); } - // Explicitly look for only allowed chars - if ( ! preg_match("/^[A-Za-z0-9]+[A-Za-z0-9_\.\-]*$/", $l) ) { - $err->raise('ftp', _("FTP login is incorrect")); - return false; - } - return true; - } + function hook_menu() { + global $quota; + $q = $quota->getquota("ftp"); - /* ----------------------------------------------------------------- */ - /** Affiche (ECHO) la liste des prefixes disponibles sous forme de champs d'option - * Les champs sont affichés sous la forme ... - * La valeur $current se voit affublée de la balise SELECTED. - * @param string $current Prefixe sélectionné par défaut - * @return boolean TRUE. - */ - function select_prefix_list($current) { - $r=$this->prefix_list(); - reset($r); - while (list($key,$val)=each($r)) { - if ($current==$val) $c=" selected=\"selected\""; else $c=""; - echo ""; - } - return true; - } + $obj = array( + 'title' => _("FTP accounts"), + 'ico' => 'images/ftp.png', + 'link' => 'toggle', + 'pos' => 60, + 'links' => array(), + ); - /* ----------------------------------------------------------------- */ - /** Modifie les paramètres du comptes FTP $id. - * @param integer $id Numéro du compte dont on veut modifier les paramètres - * @param string $prefixe Prefixe du compte FTP - * @param string $login login ajouté au préfixe ($prefixe_$login) - * @param string $pass mot de passe - * @param string $dir Répertoire racine du compte - * @return boolean TRUE si le compte a été modifié, FALSE si une erreur est survenue. - */ - function put_ftp_details($id,$prefixe,$login,$pass,$dir) { - global $mem,$db,$err,$bro,$cuid,$admin; - $err->log("ftp","put_ftp_details",$id); - $db->query("SELECT count(*) AS cnt FROM ftpusers WHERE id='$id' and uid='$cuid';"); - $db->next_record(); - if (!$db->f("cnt")) { - $err->raise("ftp",_("This FTP account does not exist")); - return false; - } - $dir=$bro->convertabsolute($dir); - if (substr($dir,0,1)=="/") { - $dir=substr($dir,1); - } - $r=$this->prefix_list(); - if (!in_array($prefixe,$r)) { - $err->raise("ftp",_("The chosen prefix is not allowed")); - return false; - } - - $full_login=$prefixe; - if ($login) $full_login.="_".$login; - if (! $this->check_login($full_login) ) return false; - $db->query("SELECT COUNT(*) AS cnt FROM ftpusers WHERE id!='$id' AND name='$full_login';"); - $db->next_record(); - if ($db->f("cnt")) { - $err->raise("ftp",_("This FTP account already exists")); - return false; - } - $absolute=getuserpath()."/$dir"; - if (!file_exists($absolute)) { - system("/bin/mkdir -p $absolute"); - } - if (!is_dir($absolute)) { - $err->raise("ftp",_("The directory cannot be created")); - return false; - } - if (trim($pass)) { - - // Check this password against the password policy using common API : - if (is_callable(array($admin,"checkPolicy"))) { - if (!$admin->checkPolicy("ftp",$full_login,$pass)) { - return false; // The error has been raised by checkPolicy() + if ($quota->cancreate("ftp")) { + $obj['links'][] = array( + 'ico' => 'images/new.png', + 'txt' => _("Create a new ftp account"), + 'url' => "ftp_edit.php?create=1", + 'class' => '', + ); } - } - $encrypted_password = _md5cr($pass,strrev(microtime(true))); - $db->query("UPDATE ftpusers SET name='".$full_login."', password='', encrypted_password='$encrypted_password', homedir='$absolute', uid='$cuid' WHERE id='$id';"); - } else { - $db->query("UPDATE ftpusers SET name='".$full_login."', homedir='$absolute', uid='$cuid' WHERE id='$id';"); - } - return true; - } + if ($q['u'] > 0) { // if there are some FTP accounts + $obj['links'][] = array( + 'txt' => _("FTP accounts list"), + 'url' => "ftp_list.php" + ); + } - /* ----------------------------------------------------------------- */ - /** Efface le compte ftp spécifié. - * @param integer $id Numéro du compte FTP à supprimer. - * @return boolean TRUE si le compte a été effacé, FALSE sinon. - */ - function delete_ftp($id) { - global $db,$err,$cuid; - $err->log("ftp","delete_ftp",$id); - $db->query("SELECT name FROM ftpusers WHERE id='$id' and uid='$cuid';"); - $db->next_record(); - $name=$db->f("name"); - if (!$name) { - $err->raise("ftp",_("This FTP account does not exist")); - return false; - } - $db->query("DELETE FROM ftpusers WHERE id='$id'"); - return $name; - } - - /* ----------------------------------------------------------------- */ - /** Crée un nouveau compte FTP. - * @param string $prefixe Prefixe au login - * @param string $login Login ftp (login=prefixe_login) - * @param string $pass Mot de passe FTP - * @param string $dir Répertoire racine du compte relatif à la racine du membre - * @return boolean TRUE si le compte a été créé, FALSE sinon. - * - */ - function add_ftp($prefixe,$login,$pass,$dir) { - global $mem,$db,$err,$quota,$bro,$cuid,$admin; - $err->log("ftp","add_ftp",$prefixe."_".$login); - $dir=$bro->convertabsolute($dir); - if (substr($dir,0,1)=="/") { - $dir=substr($dir,1); - } - $r=$this->prefix_list(); - if (empty($pass)) { - $err->raise("ftp",_("Password can't be empty")); - return false; - } - if (!in_array($prefixe,$r) || $prefixe=="") { - $err->raise("ftp",_("The chosen prefix is not allowed")); - return false; - } - $full_login=$prefixe; - if ($login) $full_login.="_".$login; - if ( !$this->check_login($full_login) ) return false; - $db->query("SELECT count(*) AS cnt FROM ftpusers WHERE name='".$full_login."'"); - $db->next_record(); - if ($db->f("cnt")) { - $err->raise("ftp",_("This FTP account already exists")); - return false; - } - $db->query("SELECT login FROM membres WHERE uid='$cuid';"); - $db->next_record(); - $absolute=getuserpath()."/$dir"; - if (!file_exists($absolute)) { - system("/bin/mkdir -p $absolute"); // FIXME replace with action - } - if (!is_dir($absolute)) { - $err->raise("ftp",_("The directory cannot be created")); - return false; + return $obj; } - // Check this password against the password policy using common API : - if (is_callable(array($admin,"checkPolicy"))) { - if (!$admin->checkPolicy("ftp",$full_login,$pass)) { - return false; // The error has been raised by checkPolicy() - } + // Return the values needed to activate security access. See get_auth_class() + // in authip for more informations + function authip_class() { + $c = Array(); + $c['name'] = "FTP"; + $c['protocol'] = "ftp"; + $c['values'] = Array(); + + $tt = $this->get_list(); + if (empty($tt) || !is_array($tt)) { + return $c; + } + foreach ($this->get_list() as $v) { + $c['values'][$v['id']] = $v['login']; + } + + return $c; } - if ($quota->cancreate("ftp")) { - $encrypted_password = _md5cr($pass,strrev(microtime(true))); - $db->query("INSERT INTO ftpusers (name,password, encrypted_password,homedir,uid) VALUES ('".$full_login."', '', '$encrypted_password', '$absolute', '$cuid')"); - return true; - } else { - $err->raise("ftp",_("Your FTP account quota is over. You cannot create more ftp accounts")); - return false; + // Switch enabled status of an account + function switch_enabled($id, $status = null) { + global $cuid, $db, $err; + if (!$jj = $this->get_ftp_details($id)) { + $err->raise('ftp', _("This account do not exist or is not of this account")); + return false; + } + if ($status == null) { + if ($jj[0]['enabled'] == true) { + $status = 0; + } else { + $status = 1; + } + } + + // Be sure what is in $status, in case of it was a parameter + $status = ($status ? 'true' : 'false'); + + if (!$db->query("UPDATE ftpusers SET enabled = $status WHERE uid = '$cuid' AND id = '$id' ;")) { + $err->raise('ftp', _("Error during update")); + return false; + } else { + return true; + } } - } - /* ----------------------------------------------------------------- */ - /** Retourne TRUE si $dir possède un compte FTP - * @param string $dir Dossier à tester, relatif à la racine du compte courant - * @return boolean retourne TRUE si $dir à un compte FTP, FALSE sinon. - */ - function is_ftp($dir) { - global $mem,$db,$err; - $err->log("ftp","is_ftp",$dir); - if (substr($dir,0,1)=="/") $dir=substr($dir,1); - $db->query("SELECT id FROM ftpusers WHERE homedir='".getuserpath()."/$dir';"); - if ($db->num_rows()) { - $db->next_record(); - return $db->f("id"); - } else { - return false; + /* ----------------------------------------------------------------- */ + + /** Retourne la liste des comptes FTP du compte h�berg� + * Retourne la liste des comptes FTP sous forme de tableau index� de + * tableaus associatifs comme suit : + * $a["id"]= ID du compte ftp + * $a["login"]= Nom de login du compte + * $a["dir"]= Dossier relatif � la racine du compte de l'utilisateur + * @return array Retourne le tableau des comptes + */ + function get_list() { + global $db, $err, $cuid; + $err->log("ftp", "get_list"); + $r = array(); + $db->query("SELECT id, name, homedir, enabled FROM ftpusers WHERE uid='$cuid' ORDER BY name;"); + if ($db->num_rows()) { + while ($db->next_record()) { + $r[] = array( + "id" => $db->f("id"), + "login" => $db->f("name"), + "enabled" => $db->f("enabled"), + //"dir"=>$match[1] + "dir" => $db->f("homedir") + ); + } + return $r; + } else { + $err->raise("ftp", _("No FTP account found")); + return array(); + } } - } - /* ----------------------------------------------------------------- */ - /** Fonction appellée par domains quand un domaine est supprimé pour le membre - * @param string $dom Domaine à détruire. - * @access private - */ - function alternc_del_domain($dom) { - global $db,$err,$cuid; - $err->log("ftp","alternc_del_domain",$dom); - $db->query("DELETE FROM ftpusers WHERE uid='$cuid' AND ( name LIKE '$dom\_%' OR name LIKE '$dom') "); - return true; - } + /* ----------------------------------------------------------------- */ - /* ----------------------------------------------------------------- */ - /** Fonction appellée par membres quand un membre est effacé. - * @access private - */ - function alternc_del_member() { - global $db,$err,$cuid; - $err->log("ftp","alternc_del_member"); - $db->query("DELETE FROM ftpusers WHERE uid='$cuid'"); - return true; - } + /** Retourne les details d'un compte FTP (voir get_list) + * Le tableau est celui du compte d'id specifie + * @param integer $id Numero du compte dont on souhaite obtenir les d�tails + * @return array Tableau associatif contenant les infos du comptes ftp + */ + function get_ftp_details($id) { + global $db, $err, $cuid; + $err->log("ftp", "get_ftp_details", $id); + $r = array(); + $db->query("SELECT id, name, homedir, enabled FROM ftpusers WHERE uid='$cuid' AND id='$id';"); + if ($db->num_rows()) { + $db->next_record(); - /* ----------------------------------------------------------------- */ - /** - * Returns the used quota for the $name service for the current user. - * @param $name string name of the quota - * @return integer the number of service used or false if an error occured - * @access private - */ - function hook_quota_get() { - global $db,$err,$cuid; - $err->log("ftp","getquota"); - $q=Array("name"=>"ftp", "description"=>_("FTP accounts"), "used"=>0); - $db->query("SELECT COUNT(*) AS cnt FROM ftpusers WHERE uid='$cuid'"); - if ($db->next_record()) { - $q['used']=$db->f("cnt"); + $regexp = "/^" . preg_quote(getuserpath(), "/") . "\/(.*)$/"; + $match = array(); + preg_match($regexp, $db->f("homedir"), $match); + + $lg = explode("_", $db->f("name")); + if ((!is_array($lg)) || (count($lg) != 2)) { + $lg[0] = $db->f("name"); + $lg[1] = ""; + } + $r[] = array( + "id" => $db->f("id"), + "prefixe" => $lg[0], + "login" => $lg[1], + "dir" => $match[1], + "enabled" => $db->f("enabled") + ); + return $r; + } else { + $err->raise("ftp", _("This FTP account does not exist")); + return false; + } } - return $q; - } + /* ----------------------------------------------------------------- */ - /* ----------------------------------------------------------------- */ - /** - * Exporte toutes les informations ftp du compte AlternC - * @access private - * EXPERIMENTAL 'sid' function ;) - */ - function alternc_export_conf() { - global $db,$err; - $err->log("ftp","export"); - $f=$this->get_list(); - $str=" "; - foreach ($f as $d=>$v) { - $str.=" \n"; - return $str; - } + /** + * Check if the login is fine (syntax) + * + * @param string $l + */ + function check_login($l) { + global $err; - /* ----------------------------------------------------------------- */ - /** hook function called by AlternC-upnp to know which open - * tcp or udp ports this class requires or suggests - * @return array a key => value list of port protocol name mandatory values - * @access private - */ - function hook_upnp_list() { - return array( - "ftp" => array("port" => 21, "protocol" => "tcp", "mandatory" => 1), - ); - } - -} /* Class m_ftp */ + // special chars and the max numbers of them allowed + // to be able to give a specific error + $vv = array('_' => '1', ' ' => 0); + foreach ($vv as $k => $n) { + if (substr_count($l, $k) > $n) { // if there is more than $n $k + $err->raise('ftp', sprintf(_("FTP login is incorrect: too many '%s'"), $k)); + return false; + } + } + // Explicitly look for only allowed chars + if (!preg_match("/^[A-Za-z0-9]+[A-Za-z0-9_\.\-]*$/", $l)) { + $err->raise('ftp', _("FTP login is incorrect")); + return false; + } + return true; + } + + /* ----------------------------------------------------------------- */ + + /** Affiche (ECHO) la liste des prefixes disponibles sous forme de champs d'option + * Les champs sont affich�s sous la forme ... + * La valeur $current se voit affubl�e de la balise SELECTED. + * @param string $current Prefixe s�lectionn� par d�faut + * @return boolean TRUE. + */ + function select_prefix_list($current) { + $r = $this->prefix_list(); + reset($r); + while (list($key, $val) = each($r)) { + if ($current == $val) { + $c = " selected=\"selected\""; + } else { + $c = ""; + } + echo ""; + } + return true; + } + + /* ----------------------------------------------------------------- */ + + /** Modifie les param�tres du comptes FTP $id. + * @param integer $id Num�ro du compte dont on veut modifier les param�tres + * @param string $prefixe Prefixe du compte FTP + * @param string $login login ajout� au pr�fixe ($prefixe_$login) + * @param string $pass mot de passe + * @param string $dir R�pertoire racine du compte + * @return boolean TRUE si le compte a �t� modifi�, FALSE si une erreur est survenue. + */ + function put_ftp_details($id, $prefixe, $login, $pass, $dir) { + global $db, $err, $bro, $cuid, $admin; + $err->log("ftp", "put_ftp_details", $id); + $db->query("SELECT count(*) AS cnt FROM ftpusers WHERE id='$id' and uid='$cuid';"); + $db->next_record(); + if (!$db->f("cnt")) { + $err->raise("ftp", _("This FTP account does not exist")); + return false; + } + $dir = $bro->convertabsolute($dir); + if (substr($dir, 0, 1) == "/") { + $dir = substr($dir, 1); + } + $r = $this->prefix_list(); + if (!in_array($prefixe, $r)) { + $err->raise("ftp", _("The chosen prefix is not allowed")); + return false; + } + + $full_login = $prefixe; + if ($login) { + $full_login.="_" . $login; + } + if (!$this->check_login($full_login)) { + return false; + } + $db->query("SELECT COUNT(*) AS cnt FROM ftpusers WHERE id!='$id' AND name='$full_login';"); + $db->next_record(); + if ($db->f("cnt")) { + $err->raise("ftp", _("This FTP account already exists")); + return false; + } + $absolute = getuserpath() . "/$dir"; + if (!file_exists($absolute)) { + system("/bin/mkdir -p $absolute"); + } + if (!is_dir($absolute)) { + $err->raise("ftp", _("The directory cannot be created")); + return false; + } + if (trim($pass)) { + + // Check this password against the password policy using common API : + if (is_callable(array($admin, "checkPolicy"))) { + if (!$admin->checkPolicy("ftp", $full_login, $pass)) { + return false; // The error has been raised by checkPolicy() + } + } + $encrypted_password = _md5cr($pass, strrev(microtime(true))); + $db->query("UPDATE ftpusers SET name='" . $full_login . "', password='', encrypted_password='$encrypted_password', homedir='$absolute', uid='$cuid' WHERE id='$id';"); + } else { + $db->query("UPDATE ftpusers SET name='" . $full_login . "', homedir='$absolute', uid='$cuid' WHERE id='$id';"); + } + return true; + } + + /* ----------------------------------------------------------------- */ + + /** Efface le compte ftp specifie + * @param integer $id Numero du compte FTP a supprimer. + * @return boolean TRUE si le compte a ete efface, FALSE sinon. + */ + function delete_ftp($id) { + global $db, $err, $cuid; + $err->log("ftp", "delete_ftp", $id); + $db->query("SELECT name FROM ftpusers WHERE id='$id' and uid='$cuid';"); + $db->next_record(); + $name = $db->f("name"); + if (!$name) { + $err->raise("ftp", _("This FTP account does not exist")); + return false; + } + $db->query("DELETE FROM ftpusers WHERE id='$id'"); + return $name; + } + + /* ----------------------------------------------------------------- */ + + /** Cree un nouveau compte FTP. + * @param string $prefixe Prefixe au login + * @param string $login Login ftp (login=prefixe_login) + * @param string $pass Mot de passe FTP + * @param string $dir Repertoire racine du compte relatif à la racine du membre + * @return boolean TRUE si le compte a ete cree, FALSE sinon. + * + */ + function add_ftp($prefixe, $login, $pass, $dir) { + global $db, $err, $quota, $bro, $cuid, $admin; + $err->log("ftp", "add_ftp", $prefixe . "_" . $login); + $dir = $bro->convertabsolute($dir); + if (substr($dir, 0, 1) == "/") { + $dir = substr($dir, 1); + } + $r = $this->prefix_list(); + if (empty($pass)) { + $err->raise("ftp", _("Password can't be empty")); + return false; + } + if (!in_array($prefixe, $r) || $prefixe == "") { + $err->raise("ftp", _("The chosen prefix is not allowed")); + return false; + } + $full_login = $prefixe; + if ($login) { + $full_login.="_" . $login; + } + if (!$this->check_login($full_login)) { + return false; + } + $db->query("SELECT count(*) AS cnt FROM ftpusers WHERE name='" . $full_login . "'"); + $db->next_record(); + if ($db->f("cnt")) { + $err->raise("ftp", _("This FTP account already exists")); + return false; + } + $db->query("SELECT login FROM membres WHERE uid='$cuid';"); + $db->next_record(); + $absolute = getuserpath() . "/$dir"; + if (!file_exists($absolute)) { + system("/bin/mkdir -p $absolute"); // FIXME replace with action + } + if (!is_dir($absolute)) { + $err->raise("ftp", _("The directory cannot be created")); + return false; + } + + // Check this password against the password policy using common API : + if (is_callable(array($admin, "checkPolicy"))) { + if (!$admin->checkPolicy("ftp", $full_login, $pass)) { + return false; // The error has been raised by checkPolicy() + } + } + + if ($quota->cancreate("ftp")) { + $encrypted_password = _md5cr($pass, strrev(microtime(true))); + $db->query("INSERT INTO ftpusers (name,password, encrypted_password,homedir,uid) VALUES ('" . $full_login . "', '', '$encrypted_password', '$absolute', '$cuid')"); + return true; + } else { + $err->raise("ftp", _("Your FTP account quota is over. You cannot create more ftp accounts")); + return false; + } + } + + /* ----------------------------------------------------------------- */ + + /** Retourne TRUE si $dir possee un compte FTP + * @param string $dir Dossier a tester, relatif a la racine du compte courant + * @return boolean retourne TRUE si $dir a un compte FTP, FALSE sinon. + */ + function is_ftp($dir) { + global $db, $err; + $err->log("ftp", "is_ftp", $dir); + if (substr($dir, 0, 1) == "/") { + $dir = substr($dir, 1); + } + $db->query("SELECT id FROM ftpusers WHERE homedir='" . getuserpath() . "/$dir';"); + if ($db->num_rows()) { + $db->next_record(); + return $db->f("id"); + } else { + return false; + } + } + + /* ----------------------------------------------------------------- */ + + /** Fonction appellee par domains quand un domaine est supprime pour le membre + * @param string $dom Domaine à detruire. + * @access private + */ + function alternc_del_domain($dom) { + global $db, $err, $cuid; + $err->log("ftp", "alternc_del_domain", $dom); + $db->query("DELETE FROM ftpusers WHERE uid='$cuid' AND ( name LIKE '$dom\_%' OR name LIKE '$dom') "); + return true; + } + + /* ----------------------------------------------------------------- */ + + /** Fonction appellee par membres quand un membre est efface + * @access private + */ + function alternc_del_member() { + global $db, $err, $cuid; + $err->log("ftp", "alternc_del_member"); + $db->query("DELETE FROM ftpusers WHERE uid='$cuid'"); + return true; + } + + /* ----------------------------------------------------------------- */ + + /** + * Returns the used quota for the $name service for the current user. + * @param $name string name of the quota + * @return integer the number of service used or false if an error occured + * @access private + */ + function hook_quota_get() { + global $db, $err, $cuid; + $err->log("ftp", "getquota"); + $q = Array("name" => "ftp", "description" => _("FTP accounts"), "used" => 0); + $db->query("SELECT COUNT(*) AS cnt FROM ftpusers WHERE uid='$cuid'"); + if ($db->next_record()) { + $q['used'] = $db->f("cnt"); + } + return $q; + } + + /* ----------------------------------------------------------------- */ + + /** + * Exporte toutes les informations ftp du compte AlternC + * @access private + * EXPERIMENTAL 'sid' function ;) + */ + function alternc_export_conf() { + global $db, $err; + $err->log("ftp", "export"); + $f = $this->get_list(); + $str = "".($v["login"])." \n"; - $str.="".($v["encrypted_password"])." \n"; - $str.="".($v["dir"])." \n"; + /** Retourne la liste des prefixes utilisables par le compte courant + * @return array tableau contenant la liste des prefixes (domaines + login) + * du compte actuel. + */ + function prefix_list() { + global $db, $mem, $cuid; + $r = array(); + $r[] = $mem->user["login"]; + $db->query("SELECT domaine FROM domaines WHERE compte='$cuid' ORDER BY domaine;"); + while ($db->next_record()) { + $r[] = $db->f("domaine"); + } + return $r; } - $str.=" "; + foreach ($f as $d => $v) { + $str.=" \n"; + return $str; + } + + /* ----------------------------------------------------------------- */ + + /** hook function called by AlternC-upnp to know which open + * tcp or udp ports this class requires or suggests + * @return array a key => value list of port protocol name mandatory values + * @access private + */ + function hook_upnp_list() { + return array( + "ftp" => array("port" => 21, "protocol" => "tcp", "mandatory" => 1), + ); + } + +} + +/* Class m_ftp */ diff --git a/bureau/class/m_hooks.php b/bureau/class/m_hooks.php index 673b18d2..58a92b04 100644 --- a/bureau/class/m_hooks.php +++ b/bureau/class/m_hooks.php @@ -1,4 +1,5 @@ "Protected folders passwords"); - } - - /** - * - * @return array - */ - function hook_menu() { - $obj = array( - 'title' => _("Protected folders"), - 'ico' => 'images/password.png', - 'link' => 'hta_list.php', - 'pos' => 50, - ) ; - - return $obj; - } - + /** + * Constructor + */ + function m_webaccess() { + + } /** - * Create a protected folder (.htaccess et .htpasswd) - * @param string $dir Folder to protect (relative to user root) - * @return boolean TRUE if the folder has been protected, or FALSE if an error occurred - * - * @global m_mem $mem - * @global m_bro $bro - * @global m_err $err - * @param string $dir - * @return boolean - */ - function CreateDir($dir) { - global $mem,$bro,$err; - $err->log("hta","createdir",$dir); - $absolute = $bro->convertabsolute($dir,0); - if (!$absolute) { - $err->raise("hta",printf(_("The folder '%s' does not exist"),$dir)); - return false; + * Password kind used in this class (hook for admin class) + * + * @return array + */ + function alternc_password_policy() { + return array("hta" => "Protected folders passwords"); } - if (!file_exists($absolute)) { - @mkdir($absolute,00777); - } - if (!file_exists("$absolute/.htaccess")) { - if (!@touch("$absolute/.htaccess")) { - $err->raise("hta",_("File already exist")); - return false; - } - $file = @fopen("$absolute/.htaccess","r+"); - if (!$file) { - $err->raise("hta",_("File already exist")); - return false; - } - fseek($file,0); - $param = "AuthUserFile \"$absolute/.htpasswd\"\nAuthName \""._("Restricted area")."\"\nAuthType Basic\nrequire valid-user\n"; - fwrite($file, $param); - fclose($file); - } - if (!file_exists("$absolute/.htpasswd")) { - if (!touch("$absolute/.htpasswd")) { - $err->raise("hta",_("File already exist")); - return false; - } - return true; - } - return true; - } - /** - * Returns the list of all user folder currently protected by a .htpasswd file - * - * @global m_err $err - * @global m_mem $mem - * @return array Array containing user folder list - */ - function ListDir(){ - global$err,$mem; - $err->log("hta","listdir"); - $sortie = array(); - $absolute = ALTERNC_HTML."/".substr($mem->user["login"],0,1)."/".$mem->user["login"]; - exec("find ".escapeshellarg($absolute)." -name .htpasswd|sort",$sortie); - if(!count($sortie)){ - $err->raise("hta",_("No protected folder")); - return false; + * + * @return array + */ + function hook_menu() { + $obj = array( + 'title' => _("Protected folders"), + 'ico' => 'images/password.png', + 'link' => 'hta_list.php', + 'pos' => 50, + ); + + return $obj; } - $pattern = "/^".preg_quote(ALTERNC_HTML,"/")."\/.\/[^\/]*\/(.*)\/\.htpasswd/"; - - $r = array(); - for($i = 0;$i" . ($v["login"]) . " \n"; + $str.="" . ($v["encrypted_password"]) . " \n"; + $str.="" . ($v["dir"]) . " \n"; + } + $str.=" log("hta","is_protected",$dir); - $absolute = ALTERNC_HTML."/".substr($mem->user["login"],0,1)."/".$mem->user["login"]."/$dir"; - if (file_exists("$absolute/.htpasswd")){ - return true; - } else { - return false; + * Create a protected folder (.htaccess et .htpasswd) + * @param string $dir Folder to protect (relative to user root) + * @return boolean TRUE if the folder has been protected, or FALSE if an error occurred + * + * @global m_mem $mem + * @global m_bro $bro + * @global m_err $err + * @param string $dir + * @return boolean + */ + function CreateDir($dir) { + global $bro, $err; + $err->log("hta", "createdir", $dir); + $absolute = $bro->convertabsolute($dir, 0); + if (!$absolute) { + $err->raise("hta", printf(_("The folder '%s' does not exist"), $dir)); + return false; + } + if (!file_exists($absolute)) { + @mkdir($absolute, 00777); + } + if (!file_exists("$absolute/.htaccess")) { + if (!@touch("$absolute/.htaccess")) { + $err->raise("hta", _("File already exist")); + return false; + } + $file = @fopen("$absolute/.htaccess", "r+"); + if (!$file) { + $err->raise("hta", _("File already exist")); + return false; + } + fseek($file, 0); + $param = "AuthUserFile \"$absolute/.htpasswd\"\nAuthName \"" . _("Restricted area") . "\"\nAuthType Basic\nrequire valid-user\n"; + fwrite($file, $param); + fclose($file); + } + if (!file_exists("$absolute/.htpasswd")) { + if (!touch("$absolute/.htpasswd")) { + $err->raise("hta", _("File already exist")); + return false; + } + return true; + } + return true; } - } - /** - * Returns the list of login for a protected folder. - * - * @global m_mem $mem - * @global m_err $err - * @param string $dir The folder to lookup (relative to user root) - * @return array An array containing the list of logins from the .htpasswd file, or FALSE - */ - function get_hta_detail($dir) { - global $mem,$err; - $err->log("hta","get_hta_detail"); - $absolute = ALTERNC_HTML."/".substr($mem->user["login"],0,1)."/".$mem->user["login"]."/$dir"; - if (file_exists("$absolute/.htaccess")) { - /* if (!_reading_htaccess($absolute)) { - return false; - } - */ } - $file = @fopen("$absolute/.htpasswd","r"); - $i = 0; - $res = array(); - if (!$file) { - return false; - } - // TODO: Tester la validité du .htpasswd - while (!feof($file)) { - $s = fgets($file,1024); - $t = explode(":",$s); - if ($t[0]!=$s) { - $res[$i] = $t[0]; - $i = $i+1; - } - } - fclose($file); - return $res; - } + * Returns the list of all user folder currently protected by a .htpasswd file + * + * @global m_err $err + * @global m_mem $mem + * @return array Array containing user folder list + */ + function ListDir() { + global$err, $mem; + $err->log("hta", "listdir"); + $sortie = array(); + $absolute = ALTERNC_HTML . "/" . substr($mem->user["login"], 0, 1) . "/" . $mem->user["login"]; + exec("find " . escapeshellarg($absolute) . " -name .htpasswd|sort", $sortie); + if (!count($sortie)) { + $err->raise("hta", _("No protected folder")); + return false; + } + $pattern = "/^" . preg_quote(ALTERNC_HTML, "/") . "\/.\/[^\/]*\/(.*)\/\.htpasswd/"; + $r = array(); + for ($i = 0; $i < count($sortie); $i++) { + $matches = array(); + preg_match($pattern, $sortie[$i], $matches); + $tmpm = isset($matches[1]) ? '/' . $matches[1] : ''; + $r[$i] = $tmpm . "/"; + } + return $r; + } /** - * Unprotect a folder - * - * @global m_mem $mem - * @global m_bro $bro - * @global m_err $err - * @param string $dir Folder to unprotect, relative to user root - * @param boolean $skip For testing purpose mainly, skips the full user path search - * @return boolean TRUE if the folder has been unprotected, or FALSE if an error occurred - */ - function DelDir($dir,$skip = false) { - global $mem,$bro,$err; - $err->log("hta","deldir",$dir); - $dir = $bro->convertabsolute($dir,$skip); - if (!$dir) { - $err->raise("hta",printf(("The folder '%s' does not exist"),$dir)); - return false; + * Tells if a folder is protected. + * + * @global m_mem $mem + * @global m_err $err + * @param string $dir Folder to check + * @return boolean If the folder is protected, or FALSE if it is not + */ + function is_protected($dir) { + global $mem, $err; + $err->log("hta", "is_protected", $dir); + $absolute = ALTERNC_HTML . "/" . substr($mem->user["login"], 0, 1) . "/" . $mem->user["login"] . "/$dir"; + if (file_exists("$absolute/.htpasswd")) { + return true; + } else { + return false; + } } - $htaccess_file = "$dir/.htaccess"; - if( !is_readable($htaccess_file)){ - $err->raise("hta",printf(_("I cannot read the file '%s'"),$htaccess_file)); - } - $fileLines = file($htaccess_file); - $patternList = array( - "AuthUserFile.*$", - "AuthName.*$", - "AuthType Basic.*$", - "require valid-user.*$" - ); - $count_lines = 0; - foreach($fileLines as $key => $line){ - foreach ($patternList as $pattern) { - if(preg_match("/".$pattern."/", $line)){ - $count_lines++; - unset($fileLines[$key]); + + /** + * Returns the list of login for a protected folder. + * + * @global m_mem $mem + * @global m_err $err + * @param string $dir The folder to lookup (relative to user root) + * @return array An array containing the list of logins from the .htpasswd file, or FALSE + */ + function get_hta_detail($dir) { + global $mem, $err; + $err->log("hta", "get_hta_detail"); + $absolute = ALTERNC_HTML . "/" . substr($mem->user["login"], 0, 1) . "/" . $mem->user["login"] . "/$dir"; + if (file_exists("$absolute/.htaccess")) { + /* if (!_reading_htaccess($absolute)) { + return false; + } + */ + } + $file = @fopen("$absolute/.htpasswd", "r"); + $i = 0; + $res = array(); + if (!$file) { + return false; + } + // TODO: Tester la validité du .htpasswd + while (!feof($file)) { + $s = fgets($file, 1024); + $t = explode(":", $s); + if ($t[0] != $s) { + $res[$i] = $t[0]; + $i = $i + 1; } } + fclose($file); + return $res; } - // If no changes - if( ! $count_lines ){ - $err->raise("hta",printf(_("Unexpected: No changes made to '%s'"),$htaccess_file)); - } - // If file is empty, remove it - if( !count($fileLines)){ - if( ! unlink( $htaccess_file)){ - $err->raise("hta",printf(_("I could not delete the file '%s'"),$htaccess_file)); + + /** + * Unprotect a folder + * + * @global m_mem $mem + * @global m_bro $bro + * @global m_err $err + * @param string $dir Folder to unprotect, relative to user root + * @param boolean $skip For testing purpose mainly, skips the full user path search + * @return boolean TRUE if the folder has been unprotected, or FALSE if an error occurred + */ + function DelDir($dir, $skip = false) { + global $bro, $err; + $err->log("hta", "deldir", $dir); + $dir = $bro->convertabsolute($dir, $skip); + if (!$dir) { + $err->raise("hta", printf(("The folder '%s' does not exist"), $dir)); + return false; + } + $htaccess_file = "$dir/.htaccess"; + if (!is_readable($htaccess_file)) { + $err->raise("hta", printf(_("I cannot read the file '%s'"), $htaccess_file)); + } + $fileLines = file($htaccess_file); + $patternList = array( + "AuthUserFile.*$", + "AuthName.*$", + "AuthType Basic.*$", + "require valid-user.*$" + ); + $count_lines = 0; + foreach ($fileLines as $key => $line) { + foreach ($patternList as $pattern) { + if (preg_match("/" . $pattern . "/", $line)) { + $count_lines++; + unset($fileLines[$key]); + } + } + } + // If no changes + if (!$count_lines) { + $err->raise("hta", printf(_("Unexpected: No changes made to '%s'"), $htaccess_file)); + } + // If file is empty, remove it + if (!count($fileLines)) { + if (!unlink($htaccess_file)) { + $err->raise("hta", printf(_("I could not delete the file '%s'"), $htaccess_file)); + } + } else { + file_put_contents($htaccess_file, implode("\n", $fileLines)); + } + $htpasswd_file = "$dir/.htpasswd"; + if (!is_writable($htpasswd_file)) { + $err->raise("hta", printf(_("I cannot read the file '%s'"), $htpasswd_file)); + } else if (!unlink($htpasswd_file)) { + $err->raise("hta", printf(_("I cannot delete the file '%s/.htpasswd'"), $dir)); + return false; } - }else{ - file_put_contents($htaccess_file, implode("\n",$fileLines)); - } - $htpasswd_file = "$dir/.htpasswd"; - if( ! is_writable($htpasswd_file)){ - $err->raise("hta",printf(_("I cannot read the file '%s'"),$htpasswd_file)); - } - else if ( ! unlink($htpasswd_file)) { - $err->raise("hta",printf(_("I cannot delete the file '%s/.htpasswd'"),$dir)); - return false; - } - - return true; - } + return true; + } /** - * Add a user to a protected folder - * - * @global m_err $err - * @global m_bro $bro - * @global m_admin $admin - * @param string $user - * @param string $password - * @param string $dir - * @param string $password The password to add (cleartext) - * @param string $dir The folder we add it to (relative to user root). - * @return boolean TRUE if the user has been added, or FALSE if an error occurred - */ - function add_user($user,$password,$dir) { - global $err, $bro, $admin; - $err->log("hta","add_user",$user."/".$dir); - if (empty($user)) { - $err->raise('hta',_("Please enter a user")); - return false; - } - if (empty($password)) { - $err->raise('hta',_("Please enter a password")); - return false; - } - $absolute = $bro->convertabsolute($dir,0); - if (!file_exists($absolute)) { - $err->raise("hta",printf(("The folder '%s' does not exist"),$dir)); - return false; - } - // @todo delete cf!. functions.php checkloginemail definition - if (checkloginmail($user)){ - // Check this password against the password policy using common API : - if (is_callable(array($admin,"checkPolicy"))) { - if (!$admin->checkPolicy("hta",$user,$password)) { - return false; // The error has been raised by checkPolicy() - } - } + * Add a user to a protected folder + * + * @global m_err $err + * @global m_bro $bro + * @global m_admin $admin + * @param string $user + * @param string $password + * @param string $dir + * @param string $password The password to add (cleartext) + * @param string $dir The folder we add it to (relative to user root). + * @return boolean TRUE if the user has been added, or FALSE if an error occurred + */ + function add_user($user, $password, $dir) { + global $err, $bro, $admin; + $err->log("hta", "add_user", $user . "/" . $dir); + if (empty($user)) { + $err->raise('hta', _("Please enter a user")); + return false; + } + if (empty($password)) { + $err->raise('hta', _("Please enter a password")); + return false; + } + $absolute = $bro->convertabsolute($dir, 0); + if (!file_exists($absolute)) { + $err->raise("hta", printf(("The folder '%s' does not exist"), $dir)); + return false; + } + // @todo delete cf!. functions.php checkloginemail definition + if (checkloginmail($user)) { + // Check this password against the password policy using common API : + if (is_callable(array($admin, "checkPolicy"))) { + if (!$admin->checkPolicy("hta", $user, $password)) { + return false; // The error has been raised by checkPolicy() + } + } - $file = @fopen("$absolute/.htpasswd","a+"); - if (!$file) { - $err->raise("hta",_("File already exist")); - return false; - } - fseek($file,0); - while (!feof($file)) { - $s = fgets($file,1024); - $t = explode(":",$s); - if ($t[0]==$user) { - $err->raise("hta",_("The user '%s' already exist for this folder"),$user); - return false; - } - } - fseek($file,SEEK_END); - if ( empty($t[1]) || substr($t[1],-1)!="\n") { - fwrite($file,"\n"); - } - fwrite($file, "$user:"._md5cr($password)."\n"); - fclose($file); - return true; - } else { - $err->raise("hta",_("Please enter a valid username")); - return false; + $file = @fopen("$absolute/.htpasswd", "a+"); + if (!$file) { + $err->raise("hta", _("File already exist")); + return false; + } + fseek($file, 0); + while (!feof($file)) { + $s = fgets($file, 1024); + $t = explode(":", $s); + if ($t[0] == $user) { + $err->raise("hta", _("The user '%s' already exist for this folder"), $user); + return false; + } + } + fseek($file, SEEK_END); + if (empty($t[1]) || substr($t[1], -1) != "\n") { + fwrite($file, "\n"); + } + fwrite($file, "$user:" . _md5cr($password) . "\n"); + fclose($file); + return true; + } else { + $err->raise("hta", _("Please enter a valid username")); + return false; + } } - } - /** - */ - /** - * Delete a user from a protected folder. - * - * @global m_bro $bro - * @global m_err $err - * @param array $lst An array with login to delete. - * @param string $dir The folder, relative to user root, where we want to delete users. - * @return boolean TRUE if users has been deleted, or FALSE if an error occurred. - */ - function del_user($lst,$dir) { - global $bro,$err; - $err->log("hta","del_user",$lst."/".$dir); - $absolute = $bro->convertabsolute($dir,0); - if (!file_exists($absolute)) { - $err->raise("hta",printf(_("The folder '%s' does not exist"),$dir)); - return false; + * Delete a user from a protected folder. + * + * @global m_bro $bro + * @global m_err $err + * @param array $lst An array with login to delete. + * @param string $dir The folder, relative to user root, where we want to delete users. + * @return boolean TRUE if users has been deleted, or FALSE if an error occurred. + */ + function del_user($lst, $dir) { + global $bro, $err; + $err->log("hta", "del_user", $lst . "/" . $dir); + $absolute = $bro->convertabsolute($dir, 0); + if (!file_exists($absolute)) { + $err->raise("hta", printf(_("The folder '%s' does not exist"), $dir)); + return false; + } + touch("$absolute/.htpasswd.new"); + $file = fopen("$absolute/.htpasswd", "r"); + $newf = fopen("$absolute/.htpasswd.new", "a"); + if (!$file || !$newf) { + $err->raise("hta", _("File already exist")); + return false; + } + reset($lst); + fseek($file, 0); + while (!feof($file)) { + $s = fgets($file, 1024); + $t = explode(":", $s); + if (!in_array($t[0], $lst) && ($t[0] != "\n")) { + fseek($newf, 0); + fwrite($newf, "$s"); + } + } + fclose($file); + fclose($newf); + unlink("$absolute/.htpasswd"); + rename("$absolute/.htpasswd.new", "$absolute/.htpasswd"); + return true; } - touch("$absolute/.htpasswd.new"); - $file = fopen("$absolute/.htpasswd","r"); - $newf = fopen("$absolute/.htpasswd.new","a"); - if (!$file || !$newf) { - $err->raise("hta",_("File already exist")); - return false; - } - reset($lst); - fseek($file,0); - while (!feof($file)) { - $s = fgets($file,1024); - $t = explode(":",$s); - if (!in_array($t[0],$lst) && ($t[0]!="\n")) { - fseek($newf,0); - fwrite($newf, "$s"); - } - } - fclose($file); - fclose($newf); - unlink("$absolute/.htpasswd"); - rename("$absolute/.htpasswd.new", "$absolute/.htpasswd"); - return true; - } - /** - * @param string $user The users whose password should be changed - * @param string $newpass The new password of this user - * @param string $dir The folder, relative to user root, in which we will change a password - * @return boolean TRUE if the password has been changed, or FALSE if an error occurred - */ - /** - * Change the password of a user in a protected folder - * - * @global m_bro $bro - * @global m_err $err - * @global m_admin $admin - * @param string $user - * @param string $newpass - * @param string $dir - * @return boolean - */ - function change_pass($user,$newpass,$dir) { - global $bro,$err,$admin; - $err->log("hta","change_pass",$user."/".$dir); - $absolute = $bro->convertabsolute($dir,0); - if (!file_exists($absolute)) { - $err->raise("hta",printf(_("The folder '%s' does not exist"),$dir)); - return false; - } + * Change the password of a user in a protected folder + * @param string $user The users whose password should be changed + * @param string $newpass The new password of this user + * @param string $dir The folder, relative to user root, in which we will change a password + * @return boolean TRUE if the password has been changed, or FALSE if an error occurred + */ + function change_pass($user, $newpass, $dir) { + global $bro, $err, $admin; + $err->log("hta", "change_pass", $user . "/" . $dir); + $absolute = $bro->convertabsolute($dir, 0); + if (!file_exists($absolute)) { + $err->raise("hta", printf(_("The folder '%s' does not exist"), $dir)); + return false; + } - // Check this password against the password policy using common API : - if (is_callable(array($admin,"checkPolicy"))) { - if (!$admin->checkPolicy("hta",$user,$newpass)) { - return false; // The error has been raised by checkPolicy() - } - } + // Check this password against the password policy using common API : + if (is_callable(array($admin, "checkPolicy"))) { + if (!$admin->checkPolicy("hta", $user, $newpass)) { + return false; // The error has been raised by checkPolicy() + } + } - touch("$absolute/.htpasswd.new"); - $file = fopen("$absolute/.htpasswd","r"); - $newf = fopen("$absolute/.htpasswd.new","a"); - if (!$file || !$newf) { - $err->raise("hta",_("File already exist")); - return false; + touch("$absolute/.htpasswd.new"); + $file = fopen("$absolute/.htpasswd", "r"); + $newf = fopen("$absolute/.htpasswd.new", "a"); + if (!$file || !$newf) { + $err->raise("hta", _("File already exist")); + return false; + } + while (!feof($file)) { + $s = fgets($file, 1024); + $t = explode(":", $s); + if ($t[0] != $user) { + fwrite($newf, "$s"); + } + } + fwrite($newf, "$user:" . _md5cr($newpass) . "\n"); + fclose($file); + fclose($newf); + unlink("$absolute/.htpasswd"); + rename("$absolute/.htpasswd.new", "$absolute/.htpasswd"); + return true; } - while (!feof($file)) { - $s = fgets($file,1024); - $t = explode(":",$s); - if ($t[0]!=$user) { - fwrite($newf, "$s"); - } - } - fwrite($newf, "$user:"._md5cr($newpass)."\n"); - fclose($file); - fclose($newf); - unlink("$absolute/.htpasswd"); - rename("$absolute/.htpasswd.new", "$absolute/.htpasswd"); - return true; - } - /** - * Check that a .htaccess file is valid (for authentication) - * - * @global m_err $err - * @param type $absolute - * @param string $absolute Folder we want to check (relative to user root) - * @return boolean TRUE is the .htaccess is protecting this folder, or FALSE else - */ - private function _reading_htaccess($absolute) { - global $err; - $err->log("hta","_reading_htaccess",$absolute); - $file = fopen("$absolute/.htaccess","r+"); - $lignes = array(1,1,1); - $errr = 0; - if (!$file) { - return false; + * Check that a .htaccess file is valid (for authentication) + * + * @global m_err $err + * @param type $absolute + * @param string $absolute Folder we want to check (relative to user root) + * @return boolean TRUE is the .htaccess is protecting this folder, or FALSE else + */ + private function _reading_htaccess($absolute) { + global $err; + $err->log("hta", "_reading_htaccess", $absolute); + $file = fopen("$absolute/.htaccess", "r+"); + $lignes = array(1, 1, 1); + $errr = 0; + if (!$file) { + return false; + } + while (!feof($file) && !$errr) { + $s = fgets($file, 1024); + if (substr($s, 0, 12) != "RewriteCond " && substr($s, 0, 14) != "ErrorDocument " && substr($s, 0, 12) != "RewriteRule " && substr($s, 0, 14) != "RewriteEngine " && trim($s) != "") { + $errr = 1; + } + if (strtolower(trim($s)) == strtolower("authuserfile $absolute/.htpasswd")) { + $lignes[0] = 0; + $errr = 0; + } // authuserfile + if (strtolower(trim($s)) == "require valid-user") { + $lignes[1] = 0; + $errr = 0; + } //require + if (strtolower(trim($s)) == "authtype basic") { + $lignes[2] = 0; + $errr = 0; + } //authtype + } // Reading config file + fclose($file); + if ($errr || in_array(0, $lignes)) { + $err->raise("hta", _("An incompatible .htaccess file exists in this folder")); + return false; + } + return true; } - while (!feof($file) && !$errr) { - $s = fgets($file,1024); - if (substr($s,0,12)!="RewriteCond " && substr($s,0,14)!="ErrorDocument " && substr($s,0,12)!="RewriteRule " && substr($s,0,14)!="RewriteEngine " && trim($s)!="") { - $errr = 1; - } - if (strtolower(trim($s))==strtolower("authuserfile $absolute/.htpasswd")) { - $lignes[0] = 0; - $errr = 0; - } // authuserfile - if (strtolower(trim($s))=="require valid-user") { - $lignes[1] = 0; - $errr = 0; - } //require - if (strtolower(trim($s))=="authtype basic") { - $lignes[2] = 0; - $errr = 0; - } //authtype - } // Reading config file - fclose($file); - if ($errr || in_array(0,$lignes)) { - $err->raise("hta",_("An incompatible .htaccess file exists in this folder")); - return false; - } - return true; - } - -} /* CLASS m_hta */ +} +/* CLASS m_hta */ diff --git a/bureau/class/m_log.php b/bureau/class/m_log.php index 6c2bedaa..eabe4ad0 100644 --- a/bureau/class/m_log.php +++ b/bureau/class/m_log.php @@ -1,4 +1,5 @@ log("log","list_logs_directory"); - - $c=array(); - foreach( glob("${dir}/*log*") as $absfile) { - $c[]=array("name"=>basename($absfile), - "creation_date"=>date("F d Y H:i:s", filectime($absfile)), - "mtime" => filemtime($absfile), - "filesize"=>filesize($absfile), - "downlink"=>urlencode(basename($absfile)), - ); + function m_log() { + } - usort($c,"m_log::compare_logtime"); - return $c; - }//list_logs + function list_logs_directory($dir) { + global $cuid, $err; + $err->log("log", "list_logs_directory"); - // Used by list_logs_directory to sort - private function compare_logname($a, $b) { - return strcmp($a['name'],$b['name']); - } - - // Used by list_logs_directory to sort - private function compare_logtime($a, $b) { - return $b['mtime']-$a['mtime']; - } - - - function hook_menu() { - $obj = array( - 'title' => _("Logs"), - 'ico' => 'images/logs.png', - 'link' => 'logs_list.php', - 'pos' => 130, - ) ; - - return $obj; - } - - function list_logs_directory_all($dirs){ - global $err; - $err->log("log","get_logs_directory_all"); - $c=array(); - foreach($dirs as $dir=>$val){ - $c[$dir]=$this->list_logs_directory($val); + $c = array(); + foreach (glob("${dir}/*log*") as $absfile) { + $c[] = array("name" => basename($absfile), + "creation_date" => date("F d Y H:i:s", filectime($absfile)), + "mtime" => filemtime($absfile), + "filesize" => filesize($absfile), + "downlink" => urlencode(basename($absfile)), + ); + } + usort($c, "m_log::compare_logtime"); + return $c; } - return $c; - } - - function get_logs_directory(){ - global $cuid,$mem,$err; - $err->log("log","get_logs_directory"); - // Return an array to allow multiple directory in the future - if(defined('ALTERNC_LOGS_ARCHIVE')){ - $c=array("dir"=>ALTERNC_LOGS_ARCHIVE."/".$cuid."-".$mem->user["login"]); - }else{ - $c=array("dir"=>ALTERNC_LOGS."/".$cuid."-".$mem->user["login"]); + // Used by list_logs_directory to sort + private function compare_logname($a, $b) { + return strcmp($a['name'], $b['name']); } - return $c; - } - - function download_link($file){ - global $err,$mem; - $err->log("log","download_link"); - header("Content-Disposition: attachment; filename=".$file.""); - header("Content-Type: application/force-download"); - header("Content-Transfer-Encoding: binary"); - $f=$this->get_logs_directory(); - $ff=$f['dir']."/".basename($file); - set_time_limit(0); - readfile($ff); - } - function tail($file,$lines=20) { - global $err,$mem; - $err->log("log","tail"); - $lines=intval($lines); if ($lines<=0) $lines=20; - $f=$this->get_logs_directory(); - $ff=$f['dir']."/".basename($file); - unset($out); - exec("tail -".$lines." ".escapeshellarg($ff),$out); - return implode("\n",$out); - } + // Used by list_logs_directory to sort + private function compare_logtime($a, $b) { + return $b['mtime'] - $a['mtime']; + } + function hook_menu() { + $obj = array( + 'title' => _("Logs"), + 'ico' => 'images/logs.png', + 'link' => 'logs_list.php', + 'pos' => 130, + ); -} // end class + return $obj; + } + function list_logs_directory_all($dirs) { + global $err; + $err->log("log", "get_logs_directory_all"); + $c = array(); + foreach ($dirs as $dir => $val) { + $c[$dir] = $this->list_logs_directory($val); + } + return $c; + } + + function get_logs_directory() { + global $cuid, $mem, $err; + $err->log("log", "get_logs_directory"); + // Return an array to allow multiple directory in the future + if (defined('ALTERNC_LOGS_ARCHIVE')) { + $c = array("dir" => ALTERNC_LOGS_ARCHIVE . "/" . $cuid . "-" . $mem->user["login"]); + } else { + $c = array("dir" => ALTERNC_LOGS . "/" . $cuid . "-" . $mem->user["login"]); + } + return $c; + } + + function download_link($file) { + global $err; + $err->log("log", "download_link"); + header("Content-Disposition: attachment; filename=" . $file . ""); + header("Content-Type: application/force-download"); + header("Content-Transfer-Encoding: binary"); + $f = $this->get_logs_directory(); + $ff = $f['dir'] . "/" . basename($file); + set_time_limit(0); + readfile($ff); + } + + function tail($file, $lines = 20) { + global $err; + $err->log("log", "tail"); + $lines = intval($lines); + if ($lines <= 0) { + $lines = 20; + } + $f = $this->get_logs_directory(); + $ff = $f['dir'] . "/" . basename($file); + $out=array(); + exec("tail -" . $lines . " " . escapeshellarg($ff), $out); + return implode("\n", $out); + } + +} + +// end class diff --git a/bureau/class/m_lxc.php b/bureau/class/m_lxc.php index 8bba638c..89fb591c 100644 --- a/bureau/class/m_lxc.php +++ b/bureau/class/m_lxc.php @@ -1,197 +1,192 @@ IP = variable_get('lxc_ip', '', "IP address of the Alternc's LXC server. If empty, no LXC server.", array('desc'=>'IP address','type'=>'ip')); - $this->PORT = variable_get('lxc_port', '6504', "Port of the Alternc's LXC server", array('desc'=>'Port','type'=>'integer')); - $this->KEY = variable_get('lxc_key', '', "Shared key with the Alternc's LXC server", array('desc'=>'Shared key','type'=>'string')); - $this->maxtime = variable_get('lxc_maxtime', '4', "How many hours do we allow to have a server before shutting it down", array('desc'=>'Max time','type'=>'integer')); - } - - - /** - * HOOK: add the "Console Access" to AlternC's main menu - */ - function hook_menu() { - if ( empty($this->IP)) return ; // No menu if no server - - $obj = array( - 'title' => _("Console access"), - 'ico' => 'images/ssh.png', - 'link' => 'vm.php', - 'pos' => 95, - ) ; - - return $obj; - } - - - /** - * HOOK: remove VM history for AlternC account - */ - function hook_admin_del_member() { - global $db,$err,$cuid; - $err->log("lxc","alternc_del_member"); - $db->query("DELETE FROM vm_history WHERE uid='$cuid'"); - return true; - } - - - /** - * Send a message to a remote VM manager instance - * $params are the parameters to send as serialized data - * to the listening server. - * Return the unserialized response data, if the message has been sent successfully - * or FALSE if an error occurred. In that case $error[] is set. - */ - private function sendMessage($params) { - global $L_FQDN,$hooks; - $fp = @fsockopen($this->IP, $this->PORT, $errno, $errstr, $this->TIMEOUT); - if (!$fp) { - $this->error[] = 'Unable to connect'; - return FALSE; - } - // Authenticate: - $params['server']=$L_FQDN; - $params['key']=$this->KEY; - // MySQL Host for this user ? - $moreparams=$hooks->invoke("lxc_params",array($params)); - foreach($moreparams as $p) { - foreach($p as $k=>$v) - $params[$k]=$v; + /** + * Constructor, initialize the class informations from AlternC's variables + */ + function m_lxc() { + $this->IP = variable_get('lxc_ip', '', "IP address of the Alternc's LXC server. If empty, no LXC server.", array('desc' => 'IP address', 'type' => 'ip')); + $this->PORT = variable_get('lxc_port', '6504', "Port of the Alternc's LXC server", array('desc' => 'Port', 'type' => 'integer')); + $this->KEY = variable_get('lxc_key', '', "Shared key with the Alternc's LXC server", array('desc' => 'Shared key', 'type' => 'string')); + $this->maxtime = variable_get('lxc_maxtime', '4', "How many hours do we allow to have a server before shutting it down", array('desc' => 'Max time', 'type' => 'integer')); } - $msg = serialize($params); - if (fwrite ($fp, $msg."\n") < 0) { - $this->error[] = 'Unable to send data'; - return FALSE; + /** + * HOOK: add the "Console Access" to AlternC's main menu + */ + function hook_menu() { + if (empty($this->IP)) + return; // No menu if no server + + $obj = array( + 'title' => _("Console access"), + 'ico' => 'images/ssh.png', + 'link' => 'vm.php', + 'pos' => 95, + ); + + return $obj; } - $resp = fgets($fp, 8192); - fclose ($fp); - $data = @unserialize($resp); - - if (isset($data['error']) && $data['error']>0) { - $this->error[] = $data['msg']; - return FALSE; - } else { - return $resp; + /** + * HOOK: remove VM history for AlternC account + */ + function hook_admin_del_member() { + global $db, $err, $cuid; + $err->log("lxc", "alternc_del_member"); + $db->query("DELETE FROM vm_history WHERE uid='$cuid'"); + return true; } - } + /** + * Send a message to a remote VM manager instance + * $params are the parameters to send as serialized data + * to the listening server. + * Return the unserialized response data, if the message has been sent successfully + * or FALSE if an error occurred. In that case $error[] is set. + */ + private function sendMessage($params) { + global $L_FQDN, $hooks; + $fp = @fsockopen($this->IP, $this->PORT, $errno, $errstr, $this->TIMEOUT); + if (!$fp) { + $this->error[] = 'Unable to connect'; + return FALSE; + } + // Authenticate: + $params['server'] = $L_FQDN; + $params['key'] = $this->KEY; + // MySQL Host for this user ? + $moreparams = $hooks->invoke("lxc_params", array($params)); + foreach ($moreparams as $p) { + foreach ($p as $k => $v) { + $params[$k] = $v; + } + } - /** - * START a Virtual Machine on the remote VM manager - * for user $login having hashed password $pass and uid $uid - */ - public function start($login = FALSE, $pass = FALSE, $uid = FALSE) { - global $mem, $db, $err, $mysql; + $msg = serialize($params); + if (fwrite($fp, $msg . "\n") < 0) { + $this->error[] = 'Unable to send data'; + return FALSE; + } + $resp = fgets($fp, 8192); + fclose($fp); - if ($this->getvm() !== FALSE) { - $err->raise('lxc', _('VM already started')); - return FALSE; + $data = @unserialize($resp); + + if (isset($data['error']) && $data['error'] > 0) { + $this->error[] = $data['msg']; + return FALSE; + } else { + return $resp; + } } - unset($this->error); - $login = $login ? $login : $mem->user['login']; - $pass = $pass ? $pass : $mem->user['pass']; - $uid = $uid ? $uid : $mem->user['uid']; + /** + * START a Virtual Machine on the remote VM manager + * for user $login having hashed password $pass and uid $uid + */ + public function start($login = FALSE, $pass = FALSE, $uid = FALSE) { + global $mem, $db, $err, $mysql; - $msgg = array('action'=>'start', 'login'=>$login, 'pass' => $pass, 'uid'=> $uid); - $msgg['mysql_host'] = $mysql->dbus->Host; + if ($this->getvm() !== FALSE) { + $err->raise('lxc', _('VM already started')); + return FALSE; + } + unset($this->error); - $res = $this->sendMessage($msgg); - if ($res === FALSE) { - return $this->error; - } else { - $data = unserialize($res); - $error = (int)$data['error']; - $hostname = $data['hostname']; - $msg = $data['msg']; - $date_start = 'NOW()'; - $uid = $mem->user['uid']; + $login = $login ? $login : $mem->user['login']; + $pass = $pass ? $pass : $mem->user['pass']; + $uid = $uid ? $uid : $mem->user['uid']; - if ($error != 0) { - $err->raise('lxc', _($msg)); - return FALSE; - } - $db->query("INSERT INTO vm_history (ip,date_start,uid,serialized_object) VALUES ('$hostname', $date_start, '$uid', '$res')"); - return $res; + $msgg = array('action' => 'start', 'login' => $login, 'pass' => $pass, 'uid' => $uid); + $msgg['mysql_host'] = $mysql->dbus->Host; + + $res = $this->sendMessage($msgg); + if ($res === FALSE) { + return $this->error; + } else { + $data = unserialize($res); + $error = (int) $data['error']; + $hostname = $data['hostname']; + $msg = $data['msg']; + $date_start = 'NOW()'; + $uid = $mem->user['uid']; + + if ($error != 0) { + $err->raise('lxc', _($msg)); + return FALSE; + } + $db->query("INSERT INTO vm_history (ip,date_start,uid,serialized_object) VALUES ('$hostname', $date_start, '$uid', '$res')"); + return $res; + } } - } + /** + * + */ + public function getvm($login = FALSE) { + global $mem; - /** - * - */ - public function getvm($login = FALSE) { - global $db, $mem, $cuid; + $login = $login ? $login : $mem->user['login']; + $msgg = array('action' => 'get', 'login' => $login); + $res = $this->sendMessage($msgg); + if (!$res) { + return FALSE; + } + return unserialize($res); + } - $login = $login ? $login : $mem->user['login']; - $msgg = array('action'=>'get', 'login'=>$login); - $res = $this->sendMessage($msgg); - if (!$res) return FALSE; - return unserialize($res); - } + /** + * Stop the currently running VM + */ + public function stop() { + $vm = $this->getvm(); + if ($vm === FALSE) { + return FALSE; + } + if ($this->sendMessage(array('action' => 'stop', 'vm' => $vm['vm'])) === FALSE) { + return FALSE; + } + return TRUE; + } +} - /** - * Stop the currently running VM - */ - public function stop() { - global $db, $mem; - $vm = $this->getvm(); - if ($vm === FALSE) - return FALSE; - - if ($this->sendMessage(array('action' => 'stop', 'vm' => $vm['vm'])) === FALSE) - return FALSE; - return TRUE; - } - - - -} // class m_lxc +// class m_lxc diff --git a/bureau/class/m_mail.php b/bureau/class/m_mail.php index cfc4e47d..250f23aa 100644 --- a/bureau/class/m_mail.php +++ b/bureau/class/m_mail.php @@ -1,4 +1,5 @@ invoke in the code. -*/ + * This class handle emails (pop and/or aliases and even wrapper for internal + * classes) of hosted users. + * + * @copyright AlternC-Team 2012-09-01 http://alternc.com/ + * This class is directly using the following alternc MySQL tables: + * address = any used email address will be defined here, mailbox = pop/imap mailboxes, recipient = redirection from an email to another + * and indirectly the domain class, to know domain names from their id in the DB. + * This class is also defining a few hooks, search ->invoke in the code. + */ class m_mail { + /* ----------------------------------------------------------------- */ + /** domain list for this account + * @access private + */ + var $domains; - /* ----------------------------------------------------------------- */ - /** domain list for this account - * @access private - */ - var $domains; + /* ----------------------------------------------------------------- */ + /** If an email has those chars, 'not nice in shell env' ;) + * we don't store the email in $mail/u/{user}_domain, but in $mail/_/{address_id}_domain + * @access private + */ + var $specialchars = array('"', "'", '\\', '/'); - /* ----------------------------------------------------------------- */ - /** If an email has those chars, 'not nice in shell env' ;) - * we don't store the email in $mail/u/{user}_domain, but in $mail/_/{address_id}_domain - * @access private - */ - var $specialchars=array('"',"'",'\\','/'); + /* ----------------------------------------------------------------- */ + /** If an email has those chars, we will ONLY allow RECIPIENTS, NOT POP/IMAP for DOVECOT ! + * Since Dovecot doesn't allow those characters + * @access private + */ + var $forbiddenchars = array('"', "'", '\\', '/', '?', '!', '*', '$', '|', '#', '+'); - /* ----------------------------------------------------------------- */ - /** If an email has those chars, we will ONLY allow RECIPIENTS, NOT POP/IMAP for DOVECOT ! - * Since Dovecot doesn't allow those characters - * @access private - */ - var $forbiddenchars=array('"',"'",'\\','/','?','!','*','$','|','#','+'); + /* ----------------------------------------------------------------- */ + /** Number of results for a pager display + * @access public + */ + var $total; + // Human server name for help + var $srv_submission; + var $srv_smtp; + var $srv_smtps; + var $srv_imap; + var $srv_imaps; + var $srv_pop3; + var $srv_pop3s; + var $cache_domain_mail_size = array(); + var $enum_domains = array(); - /* ----------------------------------------------------------------- */ - /** Number of results for a pager display - * @access public - */ - var $total; + /* ----------------------------------------------------------------- */ - - // Human server name for help - var $srv_submission; - var $srv_smtp; - var $srv_smtps; - var $srv_imap; - var $srv_imaps; - var $srv_pop3; - var $srv_pop3s; - - var $cache_domain_mail_size = array(); - var $enum_domains=array(); - /* ----------------------------------------------------------------- */ - /** - * Constructeur - */ - function m_mail() { - global $L_FQDN; - $this->srv_submission = variable_get('mail_human_submission', $L_FQDN,'Human name for mail server (submission protocol), leave empty to disable help', array('desc'=>'Name','type'=>'string')); - $this->srv_smtp = variable_get('mail_human_smtp', $L_FQDN,'Human name for mail server (SMTP protocol), leave empty to disable help', array('desc'=>'Name','type'=>'string')); - $this->srv_smtps = variable_get('mail_human_smtps', $L_FQDN,'Human name for mail server (SMTPS protocol), leave empty to disable help', array('desc'=>'Name','type'=>'string')); - $this->srv_imap = variable_get('mail_human_imap', $L_FQDN,'Human name for IMAP mail server', array('desc'=>'Name','type'=>'string')); - $this->srv_imaps = variable_get('mail_human_imaps', $L_FQDN,'Human name for IMAPS mail server', array('desc'=>'Name','type'=>'string')); - $this->srv_pop3 = variable_get('mail_human_pop3', $L_FQDN,'Human name for POP3 mail server', array('desc'=>'Name','type'=>'string')); - $this->srv_pop3s = variable_get('mail_human_pop3s', $L_FQDN,'Human name for POP3s mail server', array('desc'=>'Name','type'=>'string')); - } - - function hook_menu() { - $obj = array( - 'title' => _("Email Addresses"), - 'ico' => 'images/mail.png', - 'link' => 'toggle', - 'pos' => 30, - 'links' => array(), - ) ; - - foreach ($this->enum_domains() as $d) { - $obj['links'][] = - array ( - 'txt' => htmlentities($d["domaine"]).' '.htmlentities("(".$d["nb_mail"].")"), - 'url' => "mail_list.php?domain_id=".urlencode($d['id']), - ); - } - - return $obj; - } - - function get_total_size_for_domain($domain) { - global $db; - if (empty($this->cache_domain_mail_size)) { - $db->query("SELECT SUBSTRING_INDEX(user,'@', -1) as domain, SUM(quota_dovecot) AS sum FROM dovecot_view group by domain ;"); - while ($db->next_record() ) { - $dd = $db->f('domain'); - $this->cache_domain_mail_size[ $dd ] = $db->f('sum'); - } - } - if ( isset( $this->cache_domain_mail_size[$domain]) ) return $this->cache_domain_mail_size[$domain]; - return 0; - } - - // FIXME documenter - - /** - * @param string $domain_id - */ - function catchall_getinfos($domain_id) { - global $dom, $db; - $rr=array( - 'mail_id'=>'', - 'domain' =>$dom->get_domain_byid($domain_id), - 'target' => '', - 'type' => '', - ); - - $db->query("select r.recipients as dst, a.id mail_id from address a, recipient r where a.domain_id = $domain_id and r.address_id = a.id and a.address='';"); - if ($db->next_record()) { - $rr['target'] = $db->f('dst'); - $rr['mail_id'] = $db->f('mail_id'); + /** + * Constructeur + */ + function m_mail() { + global $L_FQDN; + $this->srv_submission = variable_get('mail_human_submission', $L_FQDN, 'Human name for mail server (submission protocol), leave empty to disable help', array('desc' => 'Name', 'type' => 'string')); + $this->srv_smtp = variable_get('mail_human_smtp', $L_FQDN, 'Human name for mail server (SMTP protocol), leave empty to disable help', array('desc' => 'Name', 'type' => 'string')); + $this->srv_smtps = variable_get('mail_human_smtps', $L_FQDN, 'Human name for mail server (SMTPS protocol), leave empty to disable help', array('desc' => 'Name', 'type' => 'string')); + $this->srv_imap = variable_get('mail_human_imap', $L_FQDN, 'Human name for IMAP mail server', array('desc' => 'Name', 'type' => 'string')); + $this->srv_imaps = variable_get('mail_human_imaps', $L_FQDN, 'Human name for IMAPS mail server', array('desc' => 'Name', 'type' => 'string')); + $this->srv_pop3 = variable_get('mail_human_pop3', $L_FQDN, 'Human name for POP3 mail server', array('desc' => 'Name', 'type' => 'string')); + $this->srv_pop3s = variable_get('mail_human_pop3s', $L_FQDN, 'Human name for POP3s mail server', array('desc' => 'Name', 'type' => 'string')); } - // Does it redirect to a specific mail or to a domain - if (empty($rr['target'])) { - $rr['type']='none'; - } elseif (substr($rr['target'],0,1)=='@') { - $rr['type']='domain'; - } else { - $rr['type']='mail'; + function hook_menu() { + $obj = array( + 'title' => _("Email Addresses"), + 'ico' => 'images/mail.png', + 'link' => 'toggle', + 'pos' => 30, + 'links' => array(), + ); + + foreach ($this->enum_domains() as $d) { + $obj['links'][] = array( + 'txt' => htmlentities($d["domaine"]) . ' ' . htmlentities("(" . $d["nb_mail"] . ")"), + 'url' => "mail_list.php?domain_id=" . urlencode($d['id']), + ); + } + + return $obj; } - - return $rr; - } - /** - * @param string $domain_id - */ - function catchall_del($domain_id) { - $catch = $this->catchall_getinfos($domain_id); - if (empty($catch['mail_id'])) return false; - return $this->delete($catch['mail_id']); - } - - /** - * @param string $domain_id - * @param string $target - */ - function catchall_set($domain_id, $target) { - global $err; - // target : - $target=rtrim($target); - if ( substr_count($target,'@') == 0 ) { // Pas de @ - $target = '@'.$target; - } - - if ( substr($target,0,1) == '@' ) { // le premier caractere est un @ - // FIXME validate domain - } else { // ca doit être un mail - if (!filter_var($target,FILTER_VALIDATE_EMAIL)) { - $err->raise("mail",_("The email you entered is syntaxically incorrect")); - return false; - } + function get_total_size_for_domain($domain) { + global $db; + if (empty($this->cache_domain_mail_size)) { + $db->query("SELECT SUBSTRING_INDEX(user,'@', -1) as domain, SUM(quota_dovecot) AS sum FROM dovecot_view group by domain ;"); + while ($db->next_record()) { + $dd = $db->f('domain'); + $this->cache_domain_mail_size[$dd] = $db->f('sum'); + } + } + if (isset($this->cache_domain_mail_size[$domain])) { + return $this->cache_domain_mail_size[$domain]; + } + return 0; } - $this->catchall_del($domain_id); - $err->error=""; - return $this->create_alias($domain_id, '', $target, "catchall", true); - } + // FIXME documenter - /* ----------------------------------------------------------------- */ - /** get_quota (hook for quota class), returns the number of used - * service for a quota-bound service - * @param $name string the named quota we want - * @return the number of used service for the specified quota, - * or false if I'm not the one for the named quota - */ - function hook_quota_get() { - global $db,$err,$cuid; - $err->log("mail","getquota"); - $q=Array("name"=>"mail", "description"=>_("Email addresses"), "used"=>0); - $db->query("SELECT COUNT(*) AS cnt FROM address a, domaines d WHERE a.domain_id=d.id AND d.compte=$cuid AND a.type='';"); - if ($db->next_record()) { - $q['used']=$db->f("cnt"); + /** + * @param string $domain_id + */ + function catchall_getinfos($domain_id) { + global $dom, $db; + $rr = array( + 'mail_id' => '', + 'domain' => $dom->get_domain_byid($domain_id), + 'target' => '', + 'type' => '', + ); + + $db->query("select r.recipients as dst, a.id mail_id from address a, recipient r where a.domain_id = $domain_id and r.address_id = a.id and a.address='';"); + if ($db->next_record()) { + $rr['target'] = $db->f('dst'); + $rr['mail_id'] = $db->f('mail_id'); + } + + // Does it redirect to a specific mail or to a domain + if (empty($rr['target'])) { + $rr['type'] = 'none'; + } elseif (substr($rr['target'], 0, 1) == '@') { + $rr['type'] = 'domain'; + } else { + $rr['type'] = 'mail'; + } + + return $rr; } - return $q; - } + /** + * @param string $domain_id + */ + function catchall_del($domain_id) { + $catch = $this->catchall_getinfos($domain_id); + if (empty($catch['mail_id'])) { + return false; + } + return $this->delete($catch['mail_id']); + } - /* ----------------------------------------------------------------- */ - /** Password policy kind used in this class (hook for admin class) - * @return array an array of policykey => "policy name (for humans)" - */ - function alternc_password_policy() { - return array("pop"=>_("Email account password")); - } + /** + * @param string $domain_id + * @param string $target + */ + function catchall_set($domain_id, $target) { + global $err; + $target = rtrim($target); + if (substr_count($target, '@') == 0) { // Pas de @ + $target = '@' . $target; + } + if (substr($target, 0, 1) == '@') { // le premier caractere est un @ + // FIXME validate domain + } else { // ca doit être un mail + if (!filter_var($target, FILTER_VALIDATE_EMAIL)) { + $err->raise("mail", _("The email you entered is syntaxically incorrect")); + return false; + } + } + $this->catchall_del($domain_id); + $err->error = ""; + return $this->create_alias($domain_id, '', $target, "catchall", true); + } - /* ----------------------------------------------------------------- */ - /** Returns the list of mail-hosting domains for a user - * @return array indexed array of hosted domains - */ - function enum_domains($uid=-1) { - global $db,$err,$cuid; - $err->log("mail","enum_domains"); - if ($uid == -1) { $uid = $cuid; } - $db->query(" + /* ----------------------------------------------------------------- */ + + /** get_quota (hook for quota class), returns the number of used + * service for a quota-bound service + * @param $name string the named quota we want + * @return the number of used service for the specified quota, + * or false if I'm not the one for the named quota + */ + function hook_quota_get() { + global $db, $err, $cuid; + $err->log("mail", "getquota"); + $q = Array("name" => "mail", "description" => _("Email addresses"), "used" => 0); + $db->query("SELECT COUNT(*) AS cnt FROM address a, domaines d WHERE a.domain_id=d.id AND d.compte=$cuid AND a.type='';"); + if ($db->next_record()) { + $q['used'] = $db->f("cnt"); + } + return $q; + } + + /* ----------------------------------------------------------------- */ + + /** Password policy kind used in this class (hook for admin class) + * @return array an array of policykey => "policy name (for humans)" + */ + function alternc_password_policy() { + return array("pop" => _("Email account password")); + } + + /* ----------------------------------------------------------------- */ + + /** Returns the list of mail-hosting domains for a user + * @return array indexed array of hosted domains + */ + function enum_domains($uid = -1) { + global $db, $err, $cuid; + $err->log("mail", "enum_domains"); + if ($uid == -1) { + $uid = $cuid; + } + $db->query(" SELECT d.id, d.domaine, @@ -244,793 +247,832 @@ ORDER BY d.domaine ; "); - $this->enum_domains=array(); - while($db->next_record()){ - $this->enum_domains[]=$db->Record; - } - return $this->enum_domains; - } - - - /* ----------------------------------------------------------------- */ - /** available: tells if an email address can be installed in the server - * check the domain part (is it mine too), the syntax, and the availability. - * @param $mail string email to check - * @return boolean true if the email can be installed on the server - */ - function available($mail){ - global $db,$err,$dom; - $err->log("mail","available"); - list($login,$domain)=explode("@",$mail,2); - // Validate the domain ownership & syntax - if (!($dom_id=$dom->get_domain_byname($domain))) { - return false; + $this->enum_domains = array(); + while ($db->next_record()) { + $this->enum_domains[] = $db->Record; + } + return $this->enum_domains; } - // Validate the email syntax: - if (!filter_var($mail,FILTER_VALIDATE_EMAIL)) { - $err->raise("mail",_("The email you entered is syntaxically incorrect")); - return false; + + /* ----------------------------------------------------------------- */ + + /** available: tells if an email address can be installed in the server + * check the domain part (is it mine too), the syntax, and the availability. + * @param $mail string email to check + * @return boolean true if the email can be installed on the server + */ + function available($mail) { + global $db, $err, $dom; + $err->log("mail", "available"); + list($login, $domain) = explode("@", $mail, 2); + // Validate the domain ownership & syntax + if (!($dom_id = $dom->get_domain_byname($domain))) { + return false; + } + // Validate the email syntax: + if (!filter_var($mail, FILTER_VALIDATE_EMAIL)) { + $err->raise("mail", _("The email you entered is syntaxically incorrect")); + return false; + } + // Check the availability + $db->query("SELECT a.id FROM address a WHERE a.domain_id=" . $dom_id . " AND a.address='" . addslashes($login) . "';"); + if ($db->next_record()) { + return false; + } else { + return true; + } } - // Check the availability - $db->query("SELECT a.id FROM address a WHERE a.domain_id=".$dom_id." AND a.address='".addslashes($login)."';"); - if ($db->next_record()) { - return false; - } else { - return true; - } - } + /* ----------------------------------------------------------------- */ + /* function used to list every mail address hosted on a domain. + * @param $dom_id integer the domain id. + * @param $search string search that string in recipients or address. + * @param $offset integer skip THAT much emails in the result. + * @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. + */ - /* ----------------------------------------------------------------- */ - /* function used to list every mail address hosted on a domain. - * @param $dom_id integer the domain id. - * @param $search string search that string in recipients or address. - * @param $offset integer skip THAT much emails in the result. - * @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. - */ - function enum_domain_mails($dom_id = null, $search="", $offset=0, $count=30, $show_systemmails=false){ - global $db,$err,$cuid,$hooks; - $err->log("mail","enum_domains_mail"); + function enum_domain_mails($dom_id = null, $search = "", $offset = 0, $count = 30, $show_systemmails = false) { + global $db, $err, $hooks; + $err->log("mail", "enum_domains_mail"); - $search=trim($search); + $search = trim($search); - $where="a.domain_id=$dom_id"; - if ($search) $where.=" AND (a.address LIKE '%".addslashes($search)."%' OR r.recipients LIKE '%".addslashes($search)."%')"; - if (!$show_systemmails) $where.=" AND type='' "; - $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(); - $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, a.domain_id + $where = "a.domain_id=$dom_id"; + if ($search) { + $where.=" AND (a.address LIKE '%" . addslashes($search) . "%' OR r.recipients LIKE '%" . addslashes($search) . "%')"; + } + if (!$show_systemmails) { + $where.=" AND type='' "; + } + $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(); + $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, a.domain_id 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 $limit ;"); - if (! $db->next_record()) { - $err->raise("mail",_("No email found for this query")); - return array(); - } - $res=array(); - do { - $details=$db->Record; - // if necessary, fill the typedata with data from hooks ... - if ($details["type"]) { - $result=$hooks->invoke("hook_mail_get_details",array($details)); // Will fill typedata if necessary - $details["typedata"]=implode("
",$result); - } - $res[]=$details; - } while ($db->next_record()); - return $res; - } - - - function hook_mail_get_details($detail) { - if ($detail['type']=='catchall') return _(sprintf("Special mail address for catch-all. Click here to manage it.",$detail['domain_id'])); - } - - - /* ----------------------------------------------------------------- */ - /** Function used to insert a new mail into the db - * should be used by the web interface, not by third-party programs. - * - * This function calls the hook "hooks_mail_cancreate" - * which must return FALSE if the user can't create this email, and raise and error accordingly - * - * @param $dom_id integer A domain_id (owned by the user) - * (will be the part at the right of the @ in the email) - * @param $mail string the left part of the email to create (something@dom_id) - * @return an hashtable containing the database id of the newly created mail, - * or false if an error occured ($err is filled accordingly) - */ - function create($dom_id, $mail,$type="",$dontcheck=false){ - global $err,$db,$cuid,$quota,$dom,$hooks; - $err->log("mail","create",$mail); - - // Validate the domain id - if (!($domain=$dom->get_domain_byid($dom_id))) { - return false; - } - - // Validate the email syntax: - $m=$mail."@".$domain; - if (!filter_var($m,FILTER_VALIDATE_EMAIL) && !$dontcheck) { - $err->raise("mail",_("The email you entered is syntaxically incorrect")); - return false; - } - - // Call other classes to check we can create it: - $cancreate=$hooks->invoke("hook_mail_cancreate",array($dom_id,$mail)); - if (in_array(false,$cancreate,true)) { - return false; - } - - // Check the quota: - if (!$quota->cancreate("mail")) { - $err->raise("mail",_("You cannot create email addresses: your quota is over")); - return false; - } - // Already exists? - $db->query("SELECT * FROM address WHERE domain_id=".$dom_id." AND address='".addslashes($mail)."';"); - if ($db->next_record()) { - $err->raise("mail",_("This email address already exists")); - return false; - } - // Create it now - $db->query("INSERT INTO address (domain_id, address,type) VALUES ($dom_id, '".addslashes($mail)."','$type');"); - if (!($id=$db->lastid())) { - $err->raise("mail",_("An unexpected error occured when creating the email")); - return false; - } - return $id; - } - - - /* ----------------------------------------------------------------- */ - /** function used to get every information we can on a mail - * @param $mail_id integer - * @return array a hashtable with all the informations for that email - */ - function get_details($mail_id) { - global $db, $err, $cuid, $hooks; - $err->log("mail","get_details"); - - $mail_id=intval($mail_id); - // Validate that this email is owned by me... - if (!($mail=$this->is_it_my_mail($mail_id))) { - return false; - } - - // We fetch all the informations for that email: these will fill the hastable : - $db->query("SELECT a.id, a.address, a.password, a.enabled, d.domaine AS domain, m.path, m.quota, m.quota*1024*1024 AS quotabytes, m.bytes AS used, NOT ISNULL(m.id) AS islocal, a.type, r.recipients, m.lastlogin, a.mail_action, m.mail_action AS mailbox_action 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 a.id=".$mail_id." AND d.id=a.domain_id;"); - if (! $db->next_record()) return false; - $details=$db->Record; - // if necessary, fill the typedata with data from hooks ... - if ($details["type"]) { - $result=$hooks->invoke("hook_mail_get_details",array($mail_id)); // Will fill typedata if necessary - $details["typedata"]=implode("
",$result); - } - return $details; - } - - - private $isitmy_cache=array(); - - /* ----------------------------------------------------------------- */ - /** Check if an email is mine ... - * - * @param $mail_id integer the number of the email to check - * @return string the complete email address if that's mine, false if not - * ($err is filled accordingly) - */ - function is_it_my_mail($mail_id){ - global $err,$db,$cuid; - $mail_id=intval($mail_id); - // cache it (may be called more than one time in the same page). - if (isset($this->isitmy_cache[$mail_id])) return $this->isitmy_cache[$mail_id]; - - $db->query("SELECT concat(a.address,'@',d.domaine) AS email FROM address a, domaines d WHERE d.id=a.domain_id AND a.id=$mail_id AND d.compte=$cuid;"); - if ($db->next_record()) { - return $this->isitmy_cache[$mail_id]=$db->f("email"); - } else { - $err->raise("mail",_("This email is not yours, you can't change anything on it")); - return $this->isitmy_cache[$mail_id]=false; - } - } - - - /* ----------------------------------------------------------------- */ - /** Hook called when the DOMAIN class will delete a domain. - * - * @param $dom integer the number of the email to delete - * @return boolean 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) { - global $db; - $list=$this->enum_domain_mails($dom_id,"",0,-1); - if (is_array($list)) { - foreach($list as $one) { - $this->delete($one["id"]); - } - } - $db->query("SELECT domaine FROM domaines WHERE id=$domain_id;"); - if ($db->next_record()) { - $db->query("UPDATE sub_domaines SET web_action='DELETE' WHERE domaine='".addslashes($db->Record["domaine"])."' AND type='txt' AND (sub='' AND valeur LIKE 'v=spf1 %') OR (sub='_dmarc' AND valeur LIKE 'v=dmarc1;%');"); - $db->query("UPDATE domaines SET dns_action='UPDATE' WHERE id=$domain_id;"); - } - - return true; - } - - // return the alternc account's ID of the mail_id - function get_account_by_mail_id($mail_id) { - global $db,$err; - $db->query("select compte as uid from domaines d, address a where a.domain_id = d.id and a.id = $mail_id"); - if ( !$db->next_record()) { - return false; - } - return $db->f('uid'); - } - - - /* ----------------------------------------------------------------- */ - /** Function used to delete a mail from the db - * should be used by the web interface, not by third-party programs. - * - * @param $mail_id integer the number of the email to delete - * @return boolean if the email has been properly deleted - * or false if an error occured ($err is filled accordingly) - */ - function delete($mail_id){ - global $err,$db,$cuid,$quota,$dom,$hooks; - $err->log("mail","delete"); - - $mail_id=intval($mail_id); - - if (!$mail_id) { - $err->raise("mail",_("The email you entered is syntaxically incorrect")); - return false; - } - // Validate that this email is owned by me... - if (!($mail=$this->is_it_my_mail($mail_id))) { - return false; - } - - $mailinfos=$this->get_details($mail_id); - $hooks->invoke('hook_mail_delete', array($mail_id, $mailinfos['address'].'@'.$mailinfos['domain'] )); - - // Search for that address: - $db->query("SELECT a.id, a.type, a.mail_action, m.mail_action AS mailbox_action, NOT ISNULL(m.id) AS islocal FROM address a LEFT JOIN mailbox m ON m.address_id=a.id WHERE a.id='$mail_id';"); - if (!$db->next_record()) { - $err->raise("mail",_("The email %s does not exist, it can't be deleted"),$mail); - return false; - } - if ($db->f("mail_action")!="OK" || ($db->f("islocal") && $db->f("mailbox_action")!="OK")) { // will be deleted soon ... - $err->raise("mail",_("The email %s is already marked for deletion, it can't be deleted"),$mail); - return false; - } - $mail_id=$db->f("id"); - - if ($db->f("islocal")) { - // If it's a pop/imap mailbox, mark it for deletion - $db->query("UPDATE address SET mail_action='DELETE', enabled=0 WHERE id='$mail_id';"); - $db->query("UPDATE mailbox SET mail_action='DELETE' WHERE address_id='$mail_id';"); - $err->raise("mail",_("The email %s has been marked for deletion"),$mail); - } else { - // If it's only aliases, delete it NOW. - $db->query("DELETE FROM address WHERE id='$mail_id';"); - $db->query("DELETE FROM mailbox WHERE address_id='$mail_id';"); - $db->query("DELETE FROM recipient WHERE address_id='$mail_id';"); - $err->raise("mail",_("The email %s has been successfully deleted"),$mail); - } - return true; - } - - - /* ----------------------------------------------------------------- */ - /** Function used to undelete a pending deletion mail from the db - * should be used by the web interface, not by third-party programs. - * - * @param $mail_id integer the email id - * @return boolean if the email has been properly undeleted - * or false if an error occured ($err is filled accordingly) - */ - function undelete($mail_id){ - global $err,$db,$cuid,$quota,$dom,$hooks; - $err->log("mail","undelete"); - - $mail_id=intval($mail_id); - - if (!$mail_id) { - $err->raise("mail",_("The email you entered is syntaxically incorrect")); - return false; - } - // Validate that this email is owned by me... - if (!($mail=$this->is_it_my_mail($mail_id))) { - return false; - } - - // Search for that address: - $db->query("SELECT a.id, a.type, a.mail_action, m.mail_action AS mailbox_action, NOT ISNULL(m.id) AS islocal FROM address a LEFT JOIN mailbox m ON m.address_id=a.id WHERE a.id='$mail_id';"); - if (!$db->next_record()) { - $err->raise("mail",_("The email %s does not exist, it can't be undeleted"),$mail); - return false; - } - if ($db->f("type")!="") { // Technically special : mailman, sympa ... - $err->raise("mail",_("The email %s is special, it can't be undeleted"),$mail); - return false; - } - if ($db->f("mailbox_action")!="DELETE" || $db->f("mail_action")!="DELETE") { // will be deleted soon ... - $err->raise("mail",_("Sorry, deletion of email %s is already in progress, or not marked for deletion, it can't be undeleted"),$mail); - return false; - } - $mail_id=$db->f("id"); - - if ($db->f("islocal")) { - // If it's a pop/imap mailbox, mark it for deletion - $db->query("UPDATE address SET mail_action='OK', `enabled`=1 WHERE id='$mail_id';"); - $db->query("UPDATE mailbox SET mail_action='OK' WHERE address_id='$mail_id';"); - $err->raise("mail",_("The email %s has been undeleted"),$mail); - return true; - } else { - $err->raise("mail",_("-- Program Error -- The email %s can't be undeleted"),$mail); - return false; - } - } - - - /* ----------------------------------------------------------------- */ - /** set the password of an email address. - * @param $mail_id integer email ID - * @param $pass string the new password. - * @return boolean true if the password has been set, false else, raise an error. - */ - function set_passwd($mail_id,$pass){ - global $db,$err,$admin; - $err->log("mail","setpasswd"); - - if (!($email=$this->is_it_my_mail($mail_id))) return false; - if (!$admin->checkPolicy("pop",$email,$pass)) return false; - if (!$db->query("UPDATE address SET password='"._md5cr($pass)."' where id=$mail_id;")) return false; - return true; - } - - - /* ----------------------------------------------------------------- */ - /** Enables an email address. - * @param $mail_id integer Email ID - * @return boolean true if the email has been enabled. - */ - function enable($mail_id){ - global $db,$err; - $err->log("mail","enable"); - if (!($email=$this->is_it_my_mail($mail_id))) return false; - if (!$db->query("UPDATE address SET `enabled`=1 where id=$mail_id;")) return false; - return true; - } - - - /* ----------------------------------------------------------------- */ - /** Disables an email address. - * @param $mail_id integer Email ID - * @return boolean true if the email has been enabled. - */ - function disable($mail_id){ - global $db,$err; - $err->log("mail","disable"); - if (!($email=$this->is_it_my_mail($mail_id))) return false; - if (!$db->query("UPDATE address SET `enabled`=0 where id=$mail_id;")) return false; - return true; - } - - - /* ----------------------------------------------------------------- */ - /** Function used to update an email settings - * should be used by the web interface, not by third-party programs. - * - * @param $mail_id integer the number of the email to delete - * @param integer $islocal boolean is it a POP/IMAP mailbox ? - * @param integer $quotamb integer if islocal=1, quota in MB - * @param string $recipients string recipients, one mail per line. - * @return boolean if the email has been properly edited - * or false if an error occured ($err is filled accordingly) - */ - function set_details($mail_id, $islocal, $quotamb, $recipients,$delivery="dovecot",$dontcheck=false) { - global $err,$db,$cuid,$quota,$dom,$hooks; - $delivery=mysql_real_escape_string($delivery); - $err->log("mail","set_details"); - if (!($me=$this->get_details($mail_id))) { - return false; - } - if ($me["islocal"] && !$islocal) { - // delete pop - $db->query("UPDATE mailbox SET mail_action='DELETE' WHERE address_id=".$mail_id.";"); - } - if (!$me["islocal"] && $islocal) { - // create pop - $path=""; - if($delivery=="dovecot"){ - $path=ALTERNC_MAIL."/".substr($me["address"]."_",0,1)."/".$me["address"]."_".$me["domain"]; - } - foreach($this->forbiddenchars as $str) { - if (strpos($me["address"],$str)!==false) { - $err->raise("mail",_("There is forbidden characters in your email address. You can't make it a POP/IMAP account, you can only use it as redirection to other emails")); - return false; - break; + if (!$db->next_record()) { + $err->raise("mail", _("No email found for this query")); + return array(); } - } - foreach($this->specialchars as $str) { - if (strpos($me["address"],$str)!==false) { - $path=ALTERNC_MAIL."/_/".$me["id"]."_".$me["domain"]; - break; - } - } - $db->query("INSERT INTO mailbox SET address_id=$mail_id, delivery='$delivery', path='".addslashes($path)."';"); - } - if ($me["islocal"] && $islocal && $me["mailbox_action"]=="DELETE") { - $db->query("UPDATE mailbox SET mail_action='OK' WHERE mail_action='DELETE' AND address_id=".$mail_id.";"); + $res = array(); + do { + $details = $db->Record; + // if necessary, fill the typedata with data from hooks ... + if ($details["type"]) { + $result = $hooks->invoke("hook_mail_get_details", array($details)); // Will fill typedata if necessary + $details["typedata"] = implode("
", $result); + } + $res[] = $details; + } while ($db->next_record()); + return $res; } - if ($islocal) { - if ($quotamb!=0 && $quotamb<(intval($me["used"]/1024/1024)+1)) { - $quotamb=intval($me["used"]/1024/1024)+1; - $err->raise("mail",_("You set a quota smaller than the current mailbox size. Since it's not allowed, we set the quota to the current mailbox size")); - } - $db->query("UPDATE mailbox SET quota=".intval($quotamb)." WHERE address_id=".$mail_id.";"); + function hook_mail_get_details($detail) { + if ($detail['type'] == 'catchall') { + return _(sprintf("Special mail address for catch-all. Click here to manage it.", $detail['domain_id'])); + } } - $recipients=preg_replace('/[\r\t\s]/', "\n", $recipients); // Handle space AND new line - $r=explode("\n",$recipients); - $red=""; - foreach($r as $m) { - $m=trim($m); - if ($m && ( filter_var($m,FILTER_VALIDATE_EMAIL) || $dontcheck) // Recipient Email is valid - && $m!=($me["address"]."@".$me["domain"])) { // And not myself (no loop allowed easily ;) ) - $red.=$m."\n"; - } + /* ----------------------------------------------------------------- */ + + /** Function used to insert a new mail into the db + * should be used by the web interface, not by third-party programs. + * + * This function calls the hook "hooks_mail_cancreate" + * which must return FALSE if the user can't create this email, and raise and error accordingly + * + * @param $dom_id integer A domain_id (owned by the user) + * (will be the part at the right of the @ in the email) + * @param $mail string the left part of the email to create (something@dom_id) + * @return an hashtable containing the database id of the newly created mail, + * or false if an error occured ($err is filled accordingly) + */ + function create($dom_id, $mail, $type = "", $dontcheck = false) { + global $err, $db, $quota, $dom, $hooks; + $err->log("mail", "create", $mail); + + // Validate the domain id + if (!($domain = $dom->get_domain_byid($dom_id))) { + return false; + } + + // Validate the email syntax: + $m = $mail . "@" . $domain; + if (!filter_var($m, FILTER_VALIDATE_EMAIL) && !$dontcheck) { + $err->raise("mail", _("The email you entered is syntaxically incorrect")); + return false; + } + + // Call other classes to check we can create it: + $cancreate = $hooks->invoke("hook_mail_cancreate", array($dom_id, $mail)); + if (in_array(false, $cancreate, true)) { + return false; + } + + // Check the quota: + if (!$quota->cancreate("mail")) { + $err->raise("mail", _("You cannot create email addresses: your quota is over")); + return false; + } + // Already exists? + $db->query("SELECT * FROM address WHERE domain_id=" . $dom_id . " AND address='" . addslashes($mail) . "';"); + if ($db->next_record()) { + $err->raise("mail", _("This email address already exists")); + return false; + } + // Create it now + $db->query("INSERT INTO address (domain_id, address,type) VALUES ($dom_id, '" . addslashes($mail) . "','$type');"); + if (!($id = $db->lastid())) { + $err->raise("mail", _("An unexpected error occured when creating the email")); + return false; + } + return $id; } - $db->query("DELETE FROM recipient WHERE address_id=".$mail_id.";"); - if (isset($red) && $red) { - $db->query("INSERT INTO recipient SET address_id=".$mail_id.", recipients='".addslashes($red)."';"); + + /* ----------------------------------------------------------------- */ + + /** function used to get every information we can on a mail + * @param $mail_id integer + * @return array a hashtable with all the informations for that email + */ + function get_details($mail_id) { + global $db, $err, $hooks; + $err->log("mail", "get_details"); + + $mail_id = intval($mail_id); + // Validate that this email is owned by me... + if (!($mail = $this->is_it_my_mail($mail_id))) { + return false; + } + + // We fetch all the informations for that email: these will fill the hastable : + $db->query("SELECT a.id, a.address, a.password, a.enabled, d.domaine AS domain, m.path, m.quota, m.quota*1024*1024 AS quotabytes, m.bytes AS used, NOT ISNULL(m.id) AS islocal, a.type, r.recipients, m.lastlogin, a.mail_action, m.mail_action AS mailbox_action 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 a.id=" . $mail_id . " AND d.id=a.domain_id;"); + if (!$db->next_record()) { + return false; + } + $details = $db->Record; + // if necessary, fill the typedata with data from hooks ... + if ($details["type"]) { + $result = $hooks->invoke("hook_mail_get_details", array($mail_id)); // Will fill typedata if necessary + $details["typedata"] = implode("
", $result); + } + return $details; } - return true; - } - /* ----------------------------------------------------------------- */ - /** A wrapper used by mailman class to create it's needed addresses - * @ param : $dom_id , the domain id associated to a given address - * @ param : $m , the left part of the mail address being created - * @ param : $delivery , the delivery used to deliver the mail - */ + private $isitmy_cache = array(); - function add_wrapper($dom_id,$m,$delivery){ - global $err,$db,$mail; - $err->log("mail","add_wrapper","creating $delivery $m address"); + /* ----------------------------------------------------------------- */ - $mail_id=$mail->create($dom_id,$m,$delivery); - $this->set_details($mail_id,1,0,'',$delivery); - // FIXME return error code - } - - /* ----------------------------------------------------------------- */ - /** A function used to create an alias for a specific address - * @ param : $dom_id , the domain sql identifier - * @ param : $m , the alias we want to create - * @ param : $alias , the already existing aliased address - * @ param : $type, the type of the alias created - * @param string $m - * @param string $alias - * @param string $dom_id - */ - function create_alias($dom_id,$m,$alias,$type="",$dontcheck=false) { - global $err,$db,$mail; - $err->log("mail","create_alias","creating $m alias for $alias type $type"); - - $mail_id=$mail->create($dom_id,$m,$type,$dontcheck); - if (!$mail_id) return false; - $this->set_details($mail_id,0,0,$alias,"dovecot",$dontcheck); - return true; - } - - - - /* ----------------------------------------------------------------- */ - /** A wrapper used by mailman class to create it's needed addresses - * @ param : $mail_id , the mysql id of the mail address we want to delete - * of the email for the current acccount. - */ - function del_wrapper($mail_id){ - global $err,$db; - $err->log("mail","del_wrapper"); - $this->delete($mail_id); - } - - /* ----------------------------------------------------------------- */ - /** Export the mail information of an account - * @return: str, string containing the complete configuration - * of the email for the current acccount. - */ - function alternc_export_conf() { - global $db,$err,$mail_localbox; - $err->log("mail","export"); - $domain=$this->enum_domains(); - $str="\n"; - foreach ($domain as $d) { - $str.=" \n"; - return $str; - } - - - /* ----------------------------------------------------------------- */ - /** - * Return the list of allowed slave accounts (secondary-mx) - * @return array - */ - function enum_slave_account() { - global $db,$err; - $db->query("SELECT login,pass FROM mxaccount;"); - $res=array(); - while ($db->next_record()) { - $res[]=$db->Record; + /** Check if an email is mine ... + * + * @param $mail_id integer the number of the email to check + * @return string the complete email address if that's mine, false if not + * ($err is filled accordingly) + */ + function is_it_my_mail($mail_id) { + global $err, $db, $cuid; + $mail_id = intval($mail_id); + // cache it (may be called more than one time in the same page). + if (isset($this->isitmy_cache[$mail_id])) { + return $this->isitmy_cache[$mail_id]; + } + $db->query("SELECT concat(a.address,'@',d.domaine) AS email FROM address a, domaines d WHERE d.id=a.domain_id AND a.id=$mail_id AND d.compte=$cuid;"); + if ($db->next_record()) { + return $this->isitmy_cache[$mail_id] = $db->f("email"); + } else { + $err->raise("mail", _("This email is not yours, you can't change anything on it")); + return $this->isitmy_cache[$mail_id] = false; + } } - if (!count($res)) return false; - return $res; - } - /* ----------------------------------------------------------------- */ - /** - * Check for a slave account (secondary mx) - * @param string $login the login to check - * @param string $pass the password to check - * @return boolean TRUE if the password is correct, or FALSE if an error occurred. - */ - function check_slave_account($login,$pass) { - global $db,$err; - $login=mysql_real_escape_string($login); - $pass=mysql_real_escape_string($pass); - $db->query("SELECT * FROM mxaccount WHERE login='$login' AND pass='$pass';"); - if ($db->next_record()) { + /* ----------------------------------------------------------------- */ + + /** Hook called when the DOMAIN class will delete a domain. + * + * @param $dom integer the number of the email to delete + * @return boolean 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) { + global $db; + $list = $this->enum_domain_mails($dom_id, "", 0, -1); + if (is_array($list)) { + foreach ($list as $one) { + $this->delete($one["id"]); + } + } + $db->query("SELECT domaine FROM domaines WHERE id=$domain_id;"); + if ($db->next_record()) { + $db->query("UPDATE sub_domaines SET web_action='DELETE' WHERE domaine='" . addslashes($db->Record["domaine"]) . "' AND type='txt' AND (sub='' AND valeur LIKE 'v=spf1 %') OR (sub='_dmarc' AND valeur LIKE 'v=dmarc1;%');"); + $db->query("UPDATE domaines SET dns_action='UPDATE' WHERE id=$domain_id;"); + } + return true; } - return false; - } - - /* ----------------------------------------------------------------- */ - /** Out (echo) the complete hosted domain list : - */ - function echo_domain_list($format=null) { - global $db,$err; - $db->query("SELECT domaine FROM domaines WHERE gesmx=1 ORDER BY domaine"); - $lst=array(); - $tt=""; - while ($db->next_record()) { - $lst[]=$db->f("domaine"); - $tt.=$db->f("domaine"); + // return the alternc account's ID of the mail_id + function get_account_by_mail_id($mail_id) { + global $db; + $db->query("select compte as uid from domaines d, address a where a.domain_id = d.id and a.id = $mail_id"); + if (!$db->next_record()) { + return false; + } + return $db->f('uid'); } - # Generate an integrity check - $obj=array('integrity'=>md5($tt),'items'=>$lst); + /* ----------------------------------------------------------------- */ - switch($format) { - case "json": - return json_encode($obj); - break; - default: - foreach ($lst as $l) { echo $l."\n"; } + /** Function used to delete a mail from the db + * should be used by the web interface, not by third-party programs. + * + * @param $mail_id integer the number of the email to delete + * @return boolean if the email has been properly deleted + * or false if an error occured ($err is filled accordingly) + */ + function delete($mail_id) { + global $err, $db, $hooks; + $err->log("mail", "delete"); + + $mail_id = intval($mail_id); + + if (!$mail_id) { + $err->raise("mail", _("The email you entered is syntaxically incorrect")); + return false; + } + // Validate that this email is owned by me... + if (!($mail = $this->is_it_my_mail($mail_id))) { + return false; + } + + $mailinfos = $this->get_details($mail_id); + $hooks->invoke('hook_mail_delete', array($mail_id, $mailinfos['address'] . '@' . $mailinfos['domain'])); + + // Search for that address: + $db->query("SELECT a.id, a.type, a.mail_action, m.mail_action AS mailbox_action, NOT ISNULL(m.id) AS islocal FROM address a LEFT JOIN mailbox m ON m.address_id=a.id WHERE a.id='$mail_id';"); + if (!$db->next_record()) { + $err->raise("mail", _("The email %s does not exist, it can't be deleted"), $mail); + return false; + } + if ($db->f("mail_action") != "OK" || ($db->f("islocal") && $db->f("mailbox_action") != "OK")) { // will be deleted soon ... + $err->raise("mail", _("The email %s is already marked for deletion, it can't be deleted"), $mail); + return false; + } + $mail_id = $db->f("id"); + + if ($db->f("islocal")) { + // If it's a pop/imap mailbox, mark it for deletion + $db->query("UPDATE address SET mail_action='DELETE', enabled=0 WHERE id='$mail_id';"); + $db->query("UPDATE mailbox SET mail_action='DELETE' WHERE address_id='$mail_id';"); + $err->raise("mail", _("The email %s has been marked for deletion"), $mail); + } else { + // If it's only aliases, delete it NOW. + $db->query("DELETE FROM address WHERE id='$mail_id';"); + $db->query("DELETE FROM mailbox WHERE address_id='$mail_id';"); + $db->query("DELETE FROM recipient WHERE address_id='$mail_id';"); + $err->raise("mail", _("The email %s has been successfully deleted"), $mail); + } return true; - break; - } // switch - } - - - /* ----------------------------------------------------------------- */ - /** - * Add a slave account that will be allowed to access the mxdomain list - * @param string $login the login to add - * @param string $pass the password to add - * @return boolean TRUE if the account has been created, or FALSE if an error occurred. - */ - function add_slave_account($login,$pass) { - global $db,$err; - $login=mysql_real_escape_string($login); - $pass=mysql_real_escape_string($pass); - $db->query("SELECT * FROM mxaccount WHERE login='$login'"); - if ($db->next_record()) { - $err->raise("mail",_("The slave MX account was not found")); - return false; } - $db->query("INSERT INTO mxaccount (login,pass) VALUES ('$login','$pass')"); - return true; - } + /* ----------------------------------------------------------------- */ - /* ----------------------------------------------------------------- */ - /** - * Remove a slave account - * @param string $login the login to delete - */ - function del_slave_account($login) { - global $db,$err; - $login=mysql_real_escape_string($login); - $db->query("DELETE FROM mxaccount WHERE login='$login'"); - return true; - } + /** Function used to undelete a pending deletion mail from the db + * should be used by the web interface, not by third-party programs. + * + * @param $mail_id integer the email id + * @return boolean if the email has been properly undeleted + * or false if an error occured ($err is filled accordingly) + */ + function undelete($mail_id) { + global $err, $db; + $err->log("mail", "undelete"); - /* ----------------------------------------------------------------- */ - /** hook function called by AlternC when a domain is created for - * the current user account using the SLAVE DOMAIN feature - * This function create a CATCHALL to the master domain - * @param string $domain_id Domain that has just been created - * @param string $target_domain Master domain - * @access private - */ - function hook_dom_add_slave_domain($domain_id,$target_domain) { - global $err; - $err->log("mail","hook_dom_add_slave_domain",$domain_id); - $this->catchall_set($domain_id,'@'.$target_domain); - return true; - } + $mail_id = intval($mail_id); - /* ----------------------------------------------------------------- */ - /** hook function called by AlternC when a domain is created for - * the current user account - * This function create a postmaster mail which is an alias to LOGIN @ FQDN - * wich is a dynamic alias to the alternc's account mail - * @param string $domain_id Domain that has just been created - * @access private - */ - function hook_dom_add_mx_domain($domain_id) { - global $err, $mem, $L_FQDN,$db; - $err->log("mail","hook_dom_add_mx_domain",$domain_id); + if (!$mail_id) { + $err->raise("mail", _("The email you entered is syntaxically incorrect")); + return false; + } + // Validate that this email is owned by me... + if (!($mail = $this->is_it_my_mail($mail_id))) { + return false; + } - $db->query("SELECT value FROM variable where name='mailname_bounce';"); - if (!$db->next_record()) { - $err->raise("mail",_("Problem: can't create default bounce mail")); - return false; + // Search for that address: + $db->query("SELECT a.id, a.type, a.mail_action, m.mail_action AS mailbox_action, NOT ISNULL(m.id) AS islocal FROM address a LEFT JOIN mailbox m ON m.address_id=a.id WHERE a.id='$mail_id';"); + if (!$db->next_record()) { + $err->raise("mail", _("The email %s does not exist, it can't be undeleted"), $mail); + return false; + } + if ($db->f("type") != "") { // Technically special : mailman, sympa ... + $err->raise("mail", _("The email %s is special, it can't be undeleted"), $mail); + return false; + } + if ($db->f("mailbox_action") != "DELETE" || $db->f("mail_action") != "DELETE") { // will be deleted soon ... + $err->raise("mail", _("Sorry, deletion of email %s is already in progress, or not marked for deletion, it can't be undeleted"), $mail); + return false; + } + $mail_id = $db->f("id"); + + if ($db->f("islocal")) { + // If it's a pop/imap mailbox, mark it for deletion + $db->query("UPDATE address SET mail_action='OK', `enabled`=1 WHERE id='$mail_id';"); + $db->query("UPDATE mailbox SET mail_action='OK' WHERE address_id='$mail_id';"); + $err->raise("mail", _("The email %s has been undeleted"), $mail); + return true; + } else { + $err->raise("mail", _("-- Program Error -- The email %s can't be undeleted"), $mail); + return false; + } } - $mailname=$db->f("value"); - // set spf & dmarc for this domain - $db->query("SELECT domaine FROM domaines WHERE id=$domain_id;"); - if ($db->next_record()) { - if ($spf=variable_get("default_spf_value")) { - $this->set_dns_spf($db->Record["domaine"],$spf); - } - if ($dmarc=variable_get("default_dmarc_value")) { - $this->set_dns_dmarc($db->Record["domaine"],$dmarc); - } + + /* ----------------------------------------------------------------- */ + + /** set the password of an email address. + * @param $mail_id integer email ID + * @param $pass string the new password. + * @return boolean true if the password has been set, false else, raise an error. + */ + function set_passwd($mail_id, $pass) { + global $db, $err, $admin; + $err->log("mail", "setpasswd"); + + if (!($email = $this->is_it_my_mail($mail_id))) { + return false; + } + if (!$admin->checkPolicy("pop", $email, $pass)) { + return false; + } + if (!$db->query("UPDATE address SET password='" . _md5cr($pass) . "' where id=$mail_id;")) { + return false; + } + return true; } - return $this->create_alias($domain_id, 'postmaster', $mem->user['login'].'@'.$mailname ); - } + /* ----------------------------------------------------------------- */ - /* ----------------------------------------------------------------- */ - /** hook function called by variables when a variable is changed - * @access private - */ - function hook_variable_set($name,$old,$new) { - global $err, $db; - $err->log("mail","hook_variable_set($name,$old,$new)"); + /** Enables an email address. + * @param $mail_id integer Email ID + * @return boolean true if the email has been enabled. + */ + function enable($mail_id) { + global $db, $err; + $err->log("mail", "enable"); + if (!($email = $this->is_it_my_mail($mail_id))) { + return false; + } + if (!$db->query("UPDATE address SET `enabled`=1 where id=$mail_id;")) { + return false; + } + return true; + } - if ($name=="default_spf_value") { - $new=trim($new); - $old=trim($old); - $db->query("SELECT domaine,login,compte FROM domaines, membres WHERE gesdns=1 AND gesmx=1 and membres.uid=domaines.compte;"); - while ($db->next_record()) { - $this->set_dns_spf($db->Record["domaine"],$new,$old,$db->Record["compte"],$db->Record["login"]); - } - } + /* ----------------------------------------------------------------- */ - if ($name=="default_dmarc_value") { - $new=trim($new); - $old=trim($old); - $db->query("SELECT domaine,login,compte FROM domaines, membres WHERE gesdns=1 AND gesmx=1 and membres.uid=domaines.compte;"); - while ($db->next_record()) { - $this->set_dns_dmarc($db->Record["domaine"],$new,$old,$db->Record["compte"],$db->Record["login"]); - } - } - - } + /** Disables an email address. + * @param $mail_id integer Email ID + * @return boolean true if the email has been enabled. + */ + function disable($mail_id) { + global $db, $err; + $err->log("mail", "disable"); + if (!($email = $this->is_it_my_mail($mail_id))) { + return false; + } + if (!$db->query("UPDATE address SET `enabled`=0 where id=$mail_id;")) { + return false; + } + return true; + } + /* ----------------------------------------------------------------- */ - /* ----------------------------------------------------------------- */ - /** Set or UPDATE the DNS record for the domain $dom(str) to be $spf - * account's login is current and if not it's $login. - * don't change spf if current value is not $old - * @access private - */ - function set_dns_spf($domain, $spf, $previous=-1, $uid=-1, $login=-1) { - global $db, $cuid, $dom, $mem; - // defaults - if ($uid===-1) $uid=intval($cuid); else $uid=intval($uid); - if ($login===-1) $login=$mem->user["login"]; - // Search for the record in sub_domaines table - $db->query("SELECT * FROM sub_domaines WHERE compte=$uid AND domaine='".addslashes($domain)."' AND sub='' AND type='txt' AND valeur LIKE 'v=spf1 %' AND web_action!='DELETE';"); - if ($db->next_record()) { - if ($previous!==-1 && $db->Record["valeur"]=="v=spf1 ".$spf) { - return; // skip, no change asked. - } - $db->query("UPDATE sub_domaines SET web_action='DELETE' WHERE id='".$db->Record["id"]."';"); - } - $db->query("INSERT INTO sub_domaines SET compte=$uid, domaine='".addslashes($domain)."', sub='', type='txt', valeur='".addslashes("v=spf1 ".$spf)."', web_action='UPDATE';"); - $db->query("UPDATE domaines SET dns_action='UPDATE' WHERE domaine='".addslashes($domain)."';"); - } + /** Function used to update an email settings + * should be used by the web interface, not by third-party programs. + * + * @param $mail_id integer the number of the email to delete + * @param integer $islocal boolean is it a POP/IMAP mailbox ? + * @param integer $quotamb integer if islocal=1, quota in MB + * @param string $recipients string recipients, one mail per line. + * @return boolean if the email has been properly edited + * or false if an error occured ($err is filled accordingly) + */ + function set_details($mail_id, $islocal, $quotamb, $recipients, $delivery = "dovecot", $dontcheck = false) { + global $err, $db; + $delivery = mysql_real_escape_string($delivery); + $err->log("mail", "set_details"); + if (!($me = $this->get_details($mail_id))) { + return false; + } + if ($me["islocal"] && !$islocal) { + // delete pop + $db->query("UPDATE mailbox SET mail_action='DELETE' WHERE address_id=" . $mail_id . ";"); + } + if (!$me["islocal"] && $islocal) { + // create pop + $path = ""; + if ($delivery == "dovecot") { + $path = ALTERNC_MAIL . "/" . substr($me["address"] . "_", 0, 1) . "/" . $me["address"] . "_" . $me["domain"]; + } + foreach ($this->forbiddenchars as $str) { + if (strpos($me["address"], $str) !== false) { + $err->raise("mail", _("There is forbidden characters in your email address. You can't make it a POP/IMAP account, you can only use it as redirection to other emails")); + return false; + } + } + foreach ($this->specialchars as $str) { + if (strpos($me["address"], $str) !== false) { + $path = ALTERNC_MAIL . "/_/" . $me["id"] . "_" . $me["domain"]; + break; + } + } + $db->query("INSERT INTO mailbox SET address_id=$mail_id, delivery='$delivery', path='" . addslashes($path) . "';"); + } + if ($me["islocal"] && $islocal && $me["mailbox_action"] == "DELETE") { + $db->query("UPDATE mailbox SET mail_action='OK' WHERE mail_action='DELETE' AND address_id=" . $mail_id . ";"); + } + if ($islocal) { + if ($quotamb != 0 && $quotamb < (intval($me["used"] / 1024 / 1024) + 1)) { + $quotamb = intval($me["used"] / 1024 / 1024) + 1; + $err->raise("mail", _("You set a quota smaller than the current mailbox size. Since it's not allowed, we set the quota to the current mailbox size")); + } + $db->query("UPDATE mailbox SET quota=" . intval($quotamb) . " WHERE address_id=" . $mail_id . ";"); + } + $recipients = preg_replace('/[\r\t\s]/', "\n", $recipients); // Handle space AND new line + $r = explode("\n", $recipients); + $red = ""; + foreach ($r as $m) { + $m = trim($m); + if ($m && ( filter_var($m, FILTER_VALIDATE_EMAIL) || $dontcheck) // Recipient Email is valid + && $m != ($me["address"] . "@" . $me["domain"])) { // And not myself (no loop allowed easily ;) ) + $red.=$m . "\n"; + } + } + $db->query("DELETE FROM recipient WHERE address_id=" . $mail_id . ";"); + if (isset($red) && $red) { + $db->query("INSERT INTO recipient SET address_id=" . $mail_id . ", recipients='" . addslashes($red) . "';"); + } + return true; + } - /* ----------------------------------------------------------------- */ - /** Set or UPDATE the DNS record for the domain $dom(str) to be $dmarc - * account's login is current and if not it's $login. - * don't change dmarc if current value is not $old - * @access private - */ - function set_dns_dmarc($domain, $dmarc, $previous=-1, $uid=-1, $login=-1) { - global $db, $cuid, $dom, $mem, $L_FQDN; - // defaults - if ($uid===-1) $uid=intval($cuid); else $uid=intval($uid); - if ($login===-1) $login=$mem->user["login"]; - $dmarc=str_replace("%%ADMINMAIL%%","admin@".$L_FQDN,$dmarc); - $dmarc=str_replace("%%USERMAIL%%",$login."@".$L_FQDN,$dmarc); + /* ----------------------------------------------------------------- */ - // Search for the record in sub_domaines table - $db->query("SELECT * FROM sub_domaines WHERE compte=$uid AND domaine='".addslashes($domain)."' AND sub='_dmarc' AND type='txt' AND valeur LIKE 'v=dmarc1;%' AND web_action!='DELETE';"); - if ($db->next_record()) { - if ($previous!==-1 && $db->Record["valeur"]=="v=dmarc1;".$dmarc) { - return; // skip, no change asked. - } - $db->query("UPDATE sub_domaines SET web_action='DELETE' WHERE id='".$db->Record["id"]."';"); - } - $db->query("INSERT INTO sub_domaines SET compte=$uid, domaine='".addslashes($domain)."', sub='_dmarc', type='txt', valeur='".addslashes("v=dmarc1;".$dmarc)."', web_action='UPDATE';"); - $db->query("UPDATE domaines SET dns_action='UPDATE' WHERE domaine='".addslashes($domain)."';"); - } + /** A wrapper used by mailman class to create it's needed addresses + * @ param : $dom_id , the domain id associated to a given address + * @ param : $m , the left part of the mail address being created + * @ param : $delivery , the delivery used to deliver the mail + */ + function add_wrapper($dom_id, $m, $delivery) { + global $err, $mail; + $err->log("mail", "add_wrapper", "creating $delivery $m address"); + $mail_id = $mail->create($dom_id, $m, $delivery); + $this->set_details($mail_id, 1, 0, '', $delivery); + // FIXME return error code + } + /* ----------------------------------------------------------------- */ + /** A function used to create an alias for a specific address + * @ param : $dom_id , the domain sql identifier + * @ param : $m , the alias we want to create + * @ param : $alias , the already existing aliased address + * @ param : $type, the type of the alias created + * @param string $m + * @param string $alias + * @param string $dom_id + */ + function create_alias($dom_id, $m, $alias, $type = "", $dontcheck = false) { + global $err, $mail; + $err->log("mail", "create_alias", "creating $m alias for $alias type $type"); - /* ----------------------------------------------------------------- */ - /** hook function called by AlternC-upnp to know which open - * tcp or udp ports this class requires or suggests - * @return array a key => value list of port protocol name mandatory values - * @access private - */ - function hook_upnp_list() { - return array( - "imap" => array("port" => 143, "protocol" => "tcp", "mandatory" => 1), - "imaps" => array("port" => 993, "protocol" => "tcp", "mandatory" => 1), - "pop" => array("port" => 110, "protocol" => "tcp", "mandatory" => 1), - "pops" => array("port" => 995, "protocol" => "tcp", "mandatory" => 1), - "smtp" => array("port" => 25, "protocol" => "tcp", "mandatory" => 1), - "sieve" => array("port" => 2000, "protocol" => "tcp", "mandatory" => 1), - "submission" => array("port" => 587, "protocol" => "tcp", "mandatory" => 0), - ); - } + $mail_id = $mail->create($dom_id, $m, $type, $dontcheck); + if (!$mail_id) { + return false; + } + $this->set_details($mail_id, 0, 0, $alias, "dovecot", $dontcheck); + return true; + } - + /* ----------------------------------------------------------------- */ -} /* Class m_mail */ + /** A wrapper used by mailman class to create it's needed addresses + * @ param : $mail_id , the mysql id of the mail address we want to delete + * of the email for the current acccount. + */ + function del_wrapper($mail_id) { + global $err; + $err->log("mail", "del_wrapper"); + $this->delete($mail_id); + } + /* ----------------------------------------------------------------- */ + /** Export the mail information of an account + * @return: str, string containing the complete configuration + * of the email for the current acccount. + */ + function alternc_export_conf() { + global $err; + $err->log("mail", "export"); + $domain = $this->enum_domains(); + $str = "\n \n"; - } - $str.="".xml_entities($d["domain"])." \n"; - $s=$this->enum_domain_mails($d["id"]); - if (count($s)) { - while (list($key,$val)=each($s)){ - $str.=" \n"; - $str.="".xml_entities($val["address"])." \n"; - $str.="".xml_entities($val["enabled"])." \n"; - if(is_array($val["islocal"])){ - $str.="1 \n"; - $str.="".$val["quota"]." \n"; - $str.="".$val["path"]." \n"; - }else{ - $str.="0 \n"; - } - if(!empty($val["recipients"])){ - $r=explode("\n",$val["recipients"]); - foreach($r as $recip){ - $str.="".$recip." \n"; - } - } - $str.="\n"; - } - } - $str.=" \n"; + foreach ($domain as $d) { + $str.=" \n"; + return $str; + } + + /* ----------------------------------------------------------------- */ + + /** + * Return the list of allowed slave accounts (secondary-mx) + * @return array + */ + function enum_slave_account() { + global $db; + $db->query("SELECT login,pass FROM mxaccount;"); + $res = array(); + while ($db->next_record()) { + $res[] = $db->Record; + } + if (!count($res)) { + return false; + } + return $res; + } + + /* ----------------------------------------------------------------- */ + + /** + * Check for a slave account (secondary mx) + * @param string $login the login to check + * @param string $pass the password to check + * @return boolean TRUE if the password is correct, or FALSE if an error occurred. + */ + function check_slave_account($login, $pass) { + global $db; + $login = mysql_real_escape_string($login); + $pass = mysql_real_escape_string($pass); + $db->query("SELECT * FROM mxaccount WHERE login='$login' AND pass='$pass';"); + if ($db->next_record()) { + return true; + } + return false; + } + + /* ----------------------------------------------------------------- */ + + /** Out (echo) the complete hosted domain list : + */ + function echo_domain_list($format = null) { + global $db; + $db->query("SELECT domaine FROM domaines WHERE gesmx=1 ORDER BY domaine"); + $lst = array(); + $tt = ""; + while ($db->next_record()) { + $lst[] = $db->f("domaine"); + $tt.=$db->f("domaine"); + } + + # Generate an integrity check + $obj = array('integrity' => md5($tt), 'items' => $lst); + + switch ($format) { + case "json": + return json_encode($obj); + default: + foreach ($lst as $l) { + echo $l . "\n"; + } + return true; + } // switch + } + + /* ----------------------------------------------------------------- */ + + /** + * Add a slave account that will be allowed to access the mxdomain list + * @param string $login the login to add + * @param string $pass the password to add + * @return boolean TRUE if the account has been created, or FALSE if an error occurred. + */ + function add_slave_account($login, $pass) { + global $db, $err; + $login = mysql_real_escape_string($login); + $pass = mysql_real_escape_string($pass); + $db->query("SELECT * FROM mxaccount WHERE login='$login'"); + if ($db->next_record()) { + $err->raise("mail", _("The slave MX account was not found")); + return false; + } + $db->query("INSERT INTO mxaccount (login,pass) VALUES ('$login','$pass')"); + return true; + } + + /* ----------------------------------------------------------------- */ + + /** + * Remove a slave account + * @param string $login the login to delete + */ + function del_slave_account($login) { + global $db; + $login = mysql_real_escape_string($login); + $db->query("DELETE FROM mxaccount WHERE login='$login'"); + return true; + } + + /* ----------------------------------------------------------------- */ + + /** hook function called by AlternC when a domain is created for + * the current user account using the SLAVE DOMAIN feature + * This function create a CATCHALL to the master domain + * @param string $domain_id Domain that has just been created + * @param string $target_domain Master domain + * @access private + */ + function hook_dom_add_slave_domain($domain_id, $target_domain) { + global $err; + $err->log("mail", "hook_dom_add_slave_domain", $domain_id); + $this->catchall_set($domain_id, '@' . $target_domain); + return true; + } + + /* ----------------------------------------------------------------- */ + + /** hook function called by AlternC when a domain is created for + * the current user account + * This function create a postmaster mail which is an alias to LOGIN @ FQDN + * wich is a dynamic alias to the alternc's account mail + * @param string $domain_id Domain that has just been created + * @access private + */ + function hook_dom_add_mx_domain($domain_id) { + global $err, $mem, $db; + $err->log("mail", "hook_dom_add_mx_domain", $domain_id); + + $db->query("SELECT value FROM variable where name='mailname_bounce';"); + if (!$db->next_record()) { + $err->raise("mail", _("Problem: can't create default bounce mail")); + return false; + } + $mailname = $db->f("value"); + // set spf & dmarc for this domain + $db->query("SELECT domaine FROM domaines WHERE id=$domain_id;"); + if ($db->next_record()) { + if ($spf = variable_get("default_spf_value")) { + $this->set_dns_spf($db->Record["domaine"], $spf); + } + if ($dmarc = variable_get("default_dmarc_value")) { + $this->set_dns_dmarc($db->Record["domaine"], $dmarc); + } + } + return $this->create_alias($domain_id, 'postmaster', $mem->user['login'] . '@' . $mailname); + } + + /* ----------------------------------------------------------------- */ + + /** hook function called by variables when a variable is changed + * @access private + */ + function hook_variable_set($name, $old, $new) { + global $err, $db; + $err->log("mail", "hook_variable_set($name,$old,$new)"); + + if ($name == "default_spf_value") { + $new = trim($new); + $old = trim($old); + $db->query("SELECT domaine,login,compte FROM domaines, membres WHERE gesdns=1 AND gesmx=1 and membres.uid=domaines.compte;"); + while ($db->next_record()) { + $this->set_dns_spf($db->Record["domaine"], $new, $old, $db->Record["compte"], $db->Record["login"]); + } + } + + if ($name == "default_dmarc_value") { + $new = trim($new); + $old = trim($old); + $db->query("SELECT domaine,login,compte FROM domaines, membres WHERE gesdns=1 AND gesmx=1 and membres.uid=domaines.compte;"); + while ($db->next_record()) { + $this->set_dns_dmarc($db->Record["domaine"], $new, $old, $db->Record["compte"], $db->Record["login"]); + } + } + } + + /* ----------------------------------------------------------------- */ + + /** Set or UPDATE the DNS record for the domain $dom(str) to be $spf + * account's login is current and if not it's $login. + * don't change spf if current value is not $old + * @access private + */ + function set_dns_spf($domain, $spf, $previous = -1, $uid = -1, $login = -1) { + global $db, $cuid, $mem; + // defaults + if ($uid === -1) { + $uid = intval($cuid); + } else { + $uid = intval($uid); + } + if ($login === -1) { + $login = $mem->user["login"]; + } + // Search for the record in sub_domaines table + $db->query("SELECT * FROM sub_domaines WHERE compte=$uid AND domaine='" . addslashes($domain) . "' AND sub='' AND type='txt' AND valeur LIKE 'v=spf1 %' AND web_action!='DELETE';"); + if ($db->next_record()) { + if ($previous !== -1 && $db->Record["valeur"] == "v=spf1 " . $spf) { + return; // skip, no change asked. + } + $db->query("UPDATE sub_domaines SET web_action='DELETE' WHERE id='" . $db->Record["id"] . "';"); + } + $db->query("INSERT INTO sub_domaines SET compte=$uid, domaine='" . addslashes($domain) . "', sub='', type='txt', valeur='" . addslashes("v=spf1 " . $spf) . "', web_action='UPDATE';"); + $db->query("UPDATE domaines SET dns_action='UPDATE' WHERE domaine='" . addslashes($domain) . "';"); + } + + /* ----------------------------------------------------------------- */ + + /** Set or UPDATE the DNS record for the domain $dom(str) to be $dmarc + * account's login is current and if not it's $login. + * don't change dmarc if current value is not $old + * @access private + */ + function set_dns_dmarc($domain, $dmarc, $previous = -1, $uid = -1, $login = -1) { + global $db, $cuid, $mem, $L_FQDN; + // defaults + if ($uid === -1) { + $uid = intval($cuid); + } else { + $uid = intval($uid); + } + if ($login === -1) { + $login = $mem->user["login"]; + } + $dmarc = str_replace("%%ADMINMAIL%%", "admin@" . $L_FQDN, $dmarc); + $dmarc = str_replace("%%USERMAIL%%", $login . "@" . $L_FQDN, $dmarc); + + // Search for the record in sub_domaines table + $db->query("SELECT * FROM sub_domaines WHERE compte=$uid AND domaine='" . addslashes($domain) . "' AND sub='_dmarc' AND type='txt' AND valeur LIKE 'v=dmarc1;%' AND web_action!='DELETE';"); + if ($db->next_record()) { + if ($previous !== -1 && $db->Record["valeur"] == "v=dmarc1;" . $dmarc) { + return; // skip, no change asked. + } + $db->query("UPDATE sub_domaines SET web_action='DELETE' WHERE id='" . $db->Record["id"] . "';"); + } + $db->query("INSERT INTO sub_domaines SET compte=$uid, domaine='" . addslashes($domain) . "', sub='_dmarc', type='txt', valeur='" . addslashes("v=dmarc1;" . $dmarc) . "', web_action='UPDATE';"); + $db->query("UPDATE domaines SET dns_action='UPDATE' WHERE domaine='" . addslashes($domain) . "';"); + } + + /* ----------------------------------------------------------------- */ + + /** hook function called by AlternC-upnp to know which open + * tcp or udp ports this class requires or suggests + * @return array a key => value list of port protocol name mandatory values + * @access private + */ + function hook_upnp_list() { + return array( + "imap" => array("port" => 143, "protocol" => "tcp", "mandatory" => 1), + "imaps" => array("port" => 993, "protocol" => "tcp", "mandatory" => 1), + "pop" => array("port" => 110, "protocol" => "tcp", "mandatory" => 1), + "pops" => array("port" => 995, "protocol" => "tcp", "mandatory" => 1), + "smtp" => array("port" => 25, "protocol" => "tcp", "mandatory" => 1), + "sieve" => array("port" => 2000, "protocol" => "tcp", "mandatory" => 1), + "submission" => array("port" => 587, "protocol" => "tcp", "mandatory" => 0), + ); + } + +} + +/* Class m_mail */ diff --git a/bureau/class/m_mem.php b/bureau/class/m_mem.php index b2d433ca..8952bdff 100644 --- a/bureau/class/m_mem.php +++ b/bureau/class/m_mem.php @@ -1,454 +1,483 @@ "AlternC's account password"); - } + /** Tableau contenant les champs de la table "local" du membre courant + * Ce tableau est utilisable globalement par toutes les classes filles. + * Note : les champs de "local" sont specifiques a l'hebergeur. + */ + var $local; - function hook_menu() { - $obj = array( - 'title' => _("Settings"), - 'ico' => 'images/settings.png', - 'link' => 'mem_param.php', - 'pos' => 160, - ) ; + /* ----------------------------------------------------------------- */ - return $obj; - } - - /* ----------------------------------------------------------------- */ - /** Check that the current user is an admnistrator. - * @return boolean TRUE if we are super user, or FALSE if we are not. - */ - function checkright() { - return ($this->user["su"]=="1"); - } - - /* ----------------------------------------------------------------- */ - /** Start a session in the web desktop. Check username and password. - * Note : If the user entered a bas password, the failure will be logged - * and told to the corresponding user on next successfull login. - * @param $username string Username that want to get connected. - * @param $password string User Password. - * @return boolean TRUE if the user has been successfully connected, or FALSE if an error occured. - */ - function login($username,$password,$restrictip=0,$authip_token=false) { - global $db,$err,$cuid,$authip,$admin; - $err->log("mem","login",$username); - // $username=addslashes($username); - // $password=addslashes($password); - $db->query("select * from membres where login='$username';"); - if ($db->num_rows()==0) { - $err->raise("mem",_("User or password incorrect")); - return false; - } - $db->next_record(); - if (_md5cr($password,$db->f("pass"))!=$db->f("pass")) { - $db->query("UPDATE membres SET lastfail=lastfail+1 WHERE uid='".$db->f("uid")."';"); - $err->raise("mem",_("User or password incorrect")); - return false; - } - if (!$db->f("enabled")) { - $err->raise("mem",_("This account is locked, contact the administrator.")); - return false; - } - $this->user=$db->Record; - $cuid=$db->f("uid"); - - if (panel_islocked() && $cuid != 2000) { - $err->raise("mem",_("This website is currently under maintenance, login is currently disabled.")); - return false; + /** + * Constructeur + */ + function m_mem() { + } - // AuthIP - $allowed_ip=false; - if ( $authip_token ) $allowed_ip = $this->authip_tokencheck($authip_token); + /* ----------------------------------------------------------------- */ - $aga = $authip->get_allowed('panel'); - foreach ($aga as $k=>$v ) { - if ( $authip->is_in_subnet(get_remote_ip(), $v['ip'], $v['subnet']) ) $allowed=true ; + /** + * Password kind used in this class (hook for admin class) + */ + function alternc_password_policy() { + return array("mem" => "AlternC's account password"); } - // Error if there is rules, the IP is not allowed and it's not in the whitelisted IP - if ( sizeof($aga)>1 && !$allowed_ip && !$authip->is_wl(get_remote_ip()) ) { - $err->raise("mem",_("Your IP isn't allowed to connect")); - return false; + function hook_menu() { + $obj = array( + 'title' => _("Settings"), + 'ico' => 'images/settings.png', + 'link' => 'mem_param.php', + 'pos' => 160, + ); + + return $obj; } - // End AuthIP - if ($restrictip) { - $ip="'".get_remote_ip()."'"; - } else $ip="''"; - /* Close sessions that are more than 2 days old. */ - $db->query("DELETE FROM sessions WHERE DATE_ADD(ts,INTERVAL 2 DAY)\n \n"; + } + $str.="" . xml_entities($d["domain"]) . " \n"; + $s = $this->enum_domain_mails($d["id"]); + if (count($s)) { + while (list($key, $val) = each($s)) { + $str.=" \n"; + $str.="" . xml_entities($val["address"]) . " \n"; + $str.="" . xml_entities($val["enabled"]) . " \n"; + if (is_array($val["islocal"])) { + $str.="1 \n"; + $str.="" . $val["quota"] . " \n"; + $str.="" . $val["path"] . " \n"; + } else { + $str.="0 \n"; + } + if (!empty($val["recipients"])) { + $r = explode("\n", $val["recipients"]); + foreach ($r as $recip) { + $str.="" . $recip . " \n"; + } + } + $str.="\n"; + } + } + $str.=" query("insert into sessions (sid,ip,uid) values ('$sess',$ip,'$cuid');"); - setcookie("session",$sess,0,"/"); - $err->error=0; - /* Fill in $local */ - $db->query("SELECT * FROM local WHERE uid='$cuid';"); - if ($db->num_rows()) { - $db->next_record(); - $this->local=$db->Record; + /* ----------------------------------------------------------------- */ + + /** Check that the current user is an admnistrator. + * @return boolean TRUE if we are super user, or FALSE if we are not. + */ + function checkright() { + return ($this->user["su"] == "1"); } - return true; - } - /* ----------------------------------------------------------------- */ - /** Start a session as another user from an administrator account. - * This function is not the same as su. setid connect the current user in the destination - * account (for good), and su allow any user to become another account for some commands only. - * (del_user, add_user ...) and allow to bring back admin rights with unsu - * - * @param $id integer User id where we will connect to. - * @return boolean TRUE if the user has been successfully connected, FALSE else. - */ - function setid($id) { - global $db,$err,$cuid,$mysql,$quota; - $err->log("mem","setid",$id); - $db->query("select * from membres where uid='$id';"); - if ($db->num_rows()==0) { - $err->raise("mem",_("User or password incorrect")); - return false; + /* ----------------------------------------------------------------- */ + + /** Start a session in the web desktop. Check username and password. + * Note : If the user entered a bas password, the failure will be logged + * and told to the corresponding user on next successfull login. + * @param $username string Username that want to get connected. + * @param $password string User Password. + * @return boolean TRUE if the user has been successfully connected, or FALSE if an error occured. + */ + function login($username, $password, $restrictip = 0, $authip_token = false) { + global $db, $err, $cuid, $authip; + $err->log("mem", "login", $username); + // $username=addslashes($username); + // $password=addslashes($password); + $db->query("select * from membres where login='$username';"); + if ($db->num_rows() == 0) { + $err->raise("mem", _("User or password incorrect")); + return false; + } + $db->next_record(); + if (_md5cr($password, $db->f("pass")) != $db->f("pass")) { + $db->query("UPDATE membres SET lastfail=lastfail+1 WHERE uid='" . $db->f("uid") . "';"); + $err->raise("mem", _("User or password incorrect")); + return false; + } + if (!$db->f("enabled")) { + $err->raise("mem", _("This account is locked, contact the administrator.")); + return false; + } + $this->user = $db->Record; + $cuid = $db->f("uid"); + + if (panel_islocked() && $cuid != 2000) { + $err->raise("mem", _("This website is currently under maintenance, login is currently disabled.")); + return false; + } + + // AuthIP + $allowed_ip = false; + if ($authip_token) { + $allowed_ip = $this->authip_tokencheck($authip_token); + } + + $aga = $authip->get_allowed('panel'); + foreach ($aga as $k => $v) { + if ($authip->is_in_subnet(get_remote_ip(), $v['ip'], $v['subnet'])) { + $allowed = true; + } + } + + // Error if there is rules, the IP is not allowed and it's not in the whitelisted IP + if (sizeof($aga) > 1 && !$allowed_ip && !$authip->is_wl(get_remote_ip())) { + $err->raise("mem", _("Your IP isn't allowed to connect")); + return false; + } + // End AuthIP + + if ($restrictip) { + $ip = "'" . get_remote_ip() . "'"; + } else { + $ip = "''"; + } + /* Close sessions that are more than 2 days old. */ + $db->query("DELETE FROM sessions WHERE DATE_ADD(ts,INTERVAL 2 DAY) query("insert into sessions (sid,ip,uid) values ('$sess',$ip,'$cuid');"); + setcookie("session", $sess, 0, "/"); + $err->error = 0; + /* Fill in $local */ + $db->query("SELECT * FROM local WHERE uid='$cuid';"); + if ($db->num_rows()) { + $db->next_record(); + $this->local = $db->Record; + } + return true; } - $db->next_record(); - $this->user=$db->Record; - $cuid=$db->f("uid"); - // And recreate the $db->dbus - $mysql->reload_dbus(); - $ip=get_remote_ip(); - $sess=md5(uniqid(mt_rand())); - $_REQUEST["session"]=$sess; - $db->query("insert into sessions (sid,ip,uid) values ('$sess','$ip','$cuid');"); - setcookie("session",$sess,0,"/"); - $err->error=0; - /* Fill in $local */ - $db->query("SELECT * FROM local WHERE uid='$cuid';"); - if ($db->num_rows()) { - $db->next_record(); - $this->local=$db->Record; + /* ----------------------------------------------------------------- */ + + /** Start a session as another user from an administrator account. + * This function is not the same as su. setid connect the current user in the destination + * account (for good), and su allow any user to become another account for some commands only. + * (del_user, add_user ...) and allow to bring back admin rights with unsu + * + * @param $id integer User id where we will connect to. + * @return boolean TRUE if the user has been successfully connected, FALSE else. + */ + function setid($id) { + global $db, $err, $cuid, $mysql, $quota; + $err->log("mem", "setid", $id); + $db->query("select * from membres where uid='$id';"); + if ($db->num_rows() == 0) { + $err->raise("mem", _("User or password incorrect")); + return false; + } + $db->next_record(); + $this->user = $db->Record; + $cuid = $db->f("uid"); + // And recreate the $db->dbus + $mysql->reload_dbus(); + + $ip = get_remote_ip(); + $sess = md5(uniqid(mt_rand())); + $_REQUEST["session"] = $sess; + $db->query("insert into sessions (sid,ip,uid) values ('$sess','$ip','$cuid');"); + setcookie("session", $sess, 0, "/"); + $err->error = 0; + /* Fill in $local */ + $db->query("SELECT * FROM local WHERE uid='$cuid';"); + if ($db->num_rows()) { + $db->next_record(); + $this->local = $db->Record; + } + $quota->getquota('', true); + return true; } - $quota->getquota('', true); - return true; - } - /* ----------------------------------------------------------------- */ - /** Suite à la connexion de l'utilisateur, réinitialise ses paramètres de dernière connexion - */ - function resetlast() { - global $db,$cuid; - $ip=addslashes(getenv("REMOTE_HOST")); - if (!$ip) $ip=addslashes(get_remote_ip()); - $db->query("UPDATE membres SET lastlogin=NOW(), lastfail=0, lastip='$ip' WHERE uid='$cuid';"); - } + /* ----------------------------------------------------------------- */ - function authip_token($bis=false) { - global $db,$cuid; - $db->query("select pass from membres where uid='$cuid';"); - $db->next_record(); - $i=intval(time()/3600); - if ($bis) ++$i; - return md5("$i--".$db->f('pass')); - } + /** Suite � la connexion de l'utilisateur, r�initialise ses param�tres de derni�re connexion + */ + function resetlast() { + global $db, $cuid; + $ip = addslashes(getenv("REMOTE_HOST")); + if (!$ip) { + $ip = addslashes(get_remote_ip()); + } + $db->query("UPDATE membres SET lastlogin=NOW(), lastfail=0, lastip='$ip' WHERE uid='$cuid';"); + } - /** - * @param boolean $t - */ - function authip_tokencheck($t) { - if ($t==$this->authip_token() || $t==$this->authip_token(true) ) return true; - return false; - } + function authip_token($bis = false) { + global $db, $cuid; + $db->query("select pass from membres where uid='$cuid';"); + $db->next_record(); + $i = intval(time() / 3600); + if ($bis) { + ++$i; + } + return md5("$i--" . $db->f('pass')); + } -/* Faut finir de l'implémenter :) * / - function authip_class() { - global $cuid; - $c = Array(); - $c['name']="Panel access"; - $c['protocol']="mem"; - $c['values']=Array($cuid=>''); + /** + * @param boolean $t + */ + function authip_tokencheck($t) { + return ($t == $this->authip_token() || $t == $this->authip_token(true)); + } - return $c; - } -/* */ + /* Faut finir de l'implementer :) * / + function authip_class() { + global $cuid; + $c = Array(); + $c['name']="Panel access"; + $c['protocol']="mem"; + $c['values']=Array($cuid=>''); - /* ----------------------------------------------------------------- */ - /** Vérifie que la session courante est correcte (cookie ok et ip valide). - * Si besoin, et si réception des champs username & password, crée une nouvelle - * session pour l'utilisateur annoncé. - * Cette fonction doit être appellée à chaque page devant être authentifiée. - * et AVANT d'émettre des données. (un cookie peut être envoyé) - * @global string $session Le cookie de session eventuel - * @global string $username/password le login/pass de l'utilisateur - * @return boolean TRUE si la session est correcte, FALSE sinon. - */ - function checkid() { - global $db,$err,$cuid,$restrictip,$authip; - if (isset($_REQUEST["username"])) { - if ( empty($_REQUEST['password']) ) { - $err->raise("mem",_("Missing password")); - return false; + return $c; } - if ($_REQUEST["username"] && $_REQUEST["password"]) { - return $this->login($_REQUEST["username"],$_REQUEST["password"], (isset($_REQUEST["restrictip"])?$_REQUEST["restrictip"]:0) ); - } - } // end isset - $_COOKIE["session"]=isset($_COOKIE["session"])?addslashes($_COOKIE["session"]):""; - if (strlen($_COOKIE["session"])!=32) { - $err->raise("mem",_("Identity lost or unknown, please login")); - return false; - } - $ip=get_remote_ip(); - $db->query("select uid,'$ip' as me,ip from sessions where sid='".$_COOKIE["session"]."'"); - if ($db->num_rows()==0) { - $err->raise("mem",_("Session unknown, contact the administrator")); - return false; - } - $db->next_record(); - if ($db->f("ip")) { - if ($db->f("me")!=$db->f("ip")) { - $err->raise("mem",_("IP address incorrect, please contact the administrator")); - return false; - } - } - $cuid=$db->f("uid"); + /* */ - if (panel_islocked() && $cuid != 2000) { - $err->raise("mem",_("This website is currently under maintenance, login is currently disabled.")); - return false; + /* ----------------------------------------------------------------- */ + + /** Verifie que la session courante est correcte (cookie ok et ip valide). + * Si besoin, et si reception des champs username & password, cree une nouvelle + * session pour l'utilisateur annonce. + * Cette fonction doit etre appellee a chaque page devant etre authentifiee. + * et AVANT d'emettre des donnees. (un cookie peut etre envoye) + * @global string $session Le cookie de session eventuel + * @global string $username/password le login/pass de l'utilisateur + * @return boolean TRUE si la session est correcte, FALSE sinon. + */ + function checkid() { + global $db, $err, $cuid; + if (isset($_REQUEST["username"])) { + if (empty($_REQUEST['password'])) { + $err->raise("mem", _("Missing password")); + return false; + } + if ($_REQUEST["username"] && $_REQUEST["password"]) { + return $this->login($_REQUEST["username"], $_REQUEST["password"], (isset($_REQUEST["restrictip"]) ? $_REQUEST["restrictip"] : 0)); + } + } // end isset + $_COOKIE["session"] = isset($_COOKIE["session"]) ? addslashes($_COOKIE["session"]) : ""; + if (strlen($_COOKIE["session"]) != 32) { + $err->raise("mem", _("Identity lost or unknown, please login")); + return false; + } + $ip = get_remote_ip(); + $db->query("select uid,'$ip' as me,ip from sessions where sid='" . $_COOKIE["session"] . "'"); + if ($db->num_rows() == 0) { + $err->raise("mem", _("Session unknown, contact the administrator")); + return false; + } + $db->next_record(); + if ($db->f("ip")) { + if ($db->f("me") != $db->f("ip")) { + $err->raise("mem", _("IP address incorrect, please contact the administrator")); + return false; + } + } + $cuid = $db->f("uid"); + + if (panel_islocked() && $cuid != 2000) { + $err->raise("mem", _("This website is currently under maintenance, login is currently disabled.")); + return false; + } + + $db->query("select * from membres where uid='$cuid';"); + $db->next_record(); + $this->user = $db->Record; + $err->error = 0; + /* Remplissage de $local */ + $db->query("SELECT * FROM local WHERE uid='$cuid';"); + if ($db->num_rows()) { + $db->next_record(); + $this->local = $db->Record; + } + return true; } - $db->query("select * from membres where uid='$cuid';"); - $db->next_record(); - $this->user=$db->Record; - $err->error=0; - /* Remplissage de $local */ - $db->query("SELECT * FROM local WHERE uid='$cuid';"); - if ($db->num_rows()) { - $db->next_record(); - $this->local=$db->Record; - } - return true; - } + /* ----------------------------------------------------------------- */ - /* ----------------------------------------------------------------- */ - /** Change l'identité d'un utilisateur temporairement. - * @global string $uid Utilisateur dont on prends l'identité - * @return TRUE si la session est correcte, FALSE sinon. - */ - function su($uid) { - global $cuid,$db,$err,$mysql; - if (!$this->olduid) - $this->olduid=$cuid; - $db->query("select * from membres where uid='$uid';"); - if ($db->num_rows()==0) { - $err->raise("mem",_("User or password incorrect")); - return false; - } - $db->next_record(); - $this->user=$db->Record; - $cuid=$db->f("uid"); + /** Change l'identite d'un utilisateur temporairement. + * @global string $uid Utilisateur dont on prends l'identite + * @return TRUE si la session est correcte, FALSE sinon. + */ + function su($uid) { + global $cuid, $db, $err, $mysql; + if (!$this->olduid) { + $this->olduid = $cuid; + } + $db->query("select * from membres where uid='$uid';"); + if ($db->num_rows() == 0) { + $err->raise("mem", _("User or password incorrect")); + return false; + } + $db->next_record(); + $this->user = $db->Record; + $cuid = $db->f("uid"); - // And recreate the $db->dbus - $mysql->reload_dbus(); - return true; - } + // And recreate the $db->dbus + $mysql->reload_dbus(); + return true; + } - /* ----------------------------------------------------------------- */ - /** Retourne a l'identite d'origine de l'utilisateur apres su. - * @return boolean TRUE si la session est correcte, FALSE sinon. - */ - function unsu() { - global $cuid,$mysql; - if (!$this->olduid) - return false; - $this->su($this->olduid); - $this->olduid=0; - // And recreate the $db->dbus - $mysql->reload_dbus(); - return true; - } + /* ----------------------------------------------------------------- */ + /** Retourne a l'identite d'origine de l'utilisateur apres su. + * @return boolean TRUE si la session est correcte, FALSE sinon. + */ + function unsu() { + global $mysql; + if (!$this->olduid) { + return false; + } + $this->su($this->olduid); + $this->olduid = 0; + // And recreate the $db->dbus + $mysql->reload_dbus(); + return true; + } - /* ----------------------------------------------------------------- */ - /** Termine une session du bureau virtuel (logout) - * @return boolean TRUE si la session a bien été détruite, FALSE sinon. - */ - function del_session() { - global $db,$user,$err,$cuid,$classes,$hooks; - $_COOKIE["session"]=addslashes(isset($_COOKIE["session"])?$_COOKIE["session"]:''); - setcookie("session","",0,"/"); - setcookie("oldid","",0,"/"); - if ($_COOKIE["session"]=="") { - $err->error=0; - return true; - } - if (strlen($_COOKIE["session"])!=32) { - $err->raise("mem",_("Cookie incorrect, please accept the session cookie")); - return false; - } - $ip=get_remote_ip(); - $db->query("select uid,'$ip' as me,ip from sessions where sid='".$_COOKIE["session"]."'"); - if ($db->num_rows()==0) { - $err->raise("mem",_("Session unknown, contact the administrator")); - return false; - } - $db->next_record(); - if ($db->f("me")!=$db->f("ip")) { - $err->raise("mem",_("IP address incorrect, please contact the administrator")); - return false; - } - $cuid=$db->f("uid"); - $db->query("delete from sessions where sid='".$_COOKIE["session"]."';"); - $err->error=0; - - # Invoker le logout dans toutes les autres classes - /* - foreach($classes as $c) { - if (method_exists($GLOBALS[$c],"alternc_del_session")) { - $GLOBALS[$c]->alternc_del_session(); - } - } - */ - $hooks->invoke("alternc_del_session"); - - session_unset(); - @session_destroy(); - return true; - } + /* ----------------------------------------------------------------- */ - /* ----------------------------------------------------------------- */ - /** Change le mot de passe de l'utilisateur courant. - * @param string $oldpass Ancien mot de passe. - * @param string $newpass Nouveau mot de passe - * @param string $newpass2 Nouveau mot de passe (à nouveau) - * @return boolean TRUE si le mot de passe a été changé, FALSE sinon. - */ - function passwd($oldpass,$newpass,$newpass2) { - global $db,$err,$cuid,$admin; - $err->log("mem","passwd"); - $oldpass=stripslashes($oldpass); - $newpass=stripslashes($newpass); - $newpass2=stripslashes($newpass2); - if (!$this->user["canpass"]) { - $err->raise("mem",_("You are not allowed to change your password.")); - return false; - } - if ($this->user["pass"]!=_md5cr($oldpass,$this->user["pass"])) { - $err->raise("mem",_("The old password is incorrect")); - return false; - } - if ($newpass!=$newpass2) { - $err->raise("mem",_("The new passwords are differents, please retry")); - return false; - } - $db->query("SELECT login FROM membres WHERE uid='$cuid';"); - $db->next_record(); - $login=$db->Record["login"]; - if (!$admin->checkPolicy("mem",$login,$newpass)) { - return false; // The error has been raised by checkPolicy() - } - $newpass=_md5cr($newpass); - $db->query("UPDATE membres SET pass='$newpass' WHERE uid='$cuid';"); - $err->error=0; - return true; - } + /** Termine une session du bureau virtuel (logout) + * @return boolean TRUE si la session a bien ete detruite, FALSE sinon. + */ + function del_session() { + global $db, $user, $err, $cuid, $hooks; + $_COOKIE["session"] = addslashes(isset($_COOKIE["session"]) ? $_COOKIE["session"] : ''); + setcookie("session", "", 0, "/"); + setcookie("oldid", "", 0, "/"); + if ($_COOKIE["session"] == "") { + $err->error = 0; + return true; + } + if (strlen($_COOKIE["session"]) != 32) { + $err->raise("mem", _("Cookie incorrect, please accept the session cookie")); + return false; + } + $ip = get_remote_ip(); + $db->query("select uid,'$ip' as me,ip from sessions where sid='" . $_COOKIE["session"] . "'"); + if ($db->num_rows() == 0) { + $err->raise("mem", _("Session unknown, contact the administrator")); + return false; + } + $db->next_record(); + if ($db->f("me") != $db->f("ip")) { + $err->raise("mem", _("IP address incorrect, please contact the administrator")); + return false; + } + $cuid = $db->f("uid"); + $db->query("delete from sessions where sid='" . $_COOKIE["session"] . "';"); + $err->error = 0; - /* ----------------------------------------------------------------- */ - /** Change les préférences administrateur d'un compte - * @param integer $admlist Mode de visualisation des membres (0=large 1=courte) - * @return boolean TRUE si les préférences ont été changées, FALSE sinon. - */ - function adminpref($admlist) { - global $db,$err,$cuid; - $err->log("mem","admlist"); - if (!$this->user["su"]) { - $err->raise("mem",_("You must be a system administrator to do this.")); - return false; - } - $db->query("UPDATE membres SET admlist='$admlist' WHERE uid='$cuid';"); - $err->error=0; - return true; - } + # Invoker le logout dans toutes les autres classes + /* + foreach($classes as $c) { + if (method_exists($GLOBALS[$c],"alternc_del_session")) { + $GLOBALS[$c]->alternc_del_session(); + } + } + */ + $hooks->invoke("alternc_del_session"); - /* ----------------------------------------------------------------- */ - /** Envoie en mail le mot de passe d'un compte. - * Note : On ne peut demander le mot de passe qu'une seule fois par jour. - * TODO : Translate this mail into the localization program. - * TODO : Check this function's ! - * @return boolean TRUE si le mot de passe a été envoyé avec succès, FALSE sinon. - */ - function send_pass($login) { - global $err,$db,$L_HOSTING,$L_FQDN; - $err->log("mem","send_pass"); - $db->query("SELECT * FROM membres WHERE login='$login';"); - if (!$db->num_rows()) { - $err->raise("mem",_("This account is locked, contact the administrator.")); - return false; + session_unset(); + @session_destroy(); + return true; } - $db->next_record(); - if (time()-$db->f("lastaskpass")<86400) { - $err->raise("mem",_("The new passwords are differents, please retry")); - return false; + + /* ----------------------------------------------------------------- */ + + /** Change le mot de passe de l'utilisateur courant. + * @param string $oldpass Ancien mot de passe. + * @param string $newpass Nouveau mot de passe + * @param string $newpass2 Nouveau mot de passe (a nouveau) + * @return boolean TRUE si le mot de passe a ete change, FALSE sinon. + */ + function passwd($oldpass, $newpass, $newpass2) { + global $db, $err, $cuid, $admin; + $err->log("mem", "passwd"); + $oldpass = stripslashes($oldpass); + $newpass = stripslashes($newpass); + $newpass2 = stripslashes($newpass2); + if (!$this->user["canpass"]) { + $err->raise("mem", _("You are not allowed to change your password.")); + return false; + } + if ($this->user["pass"] != _md5cr($oldpass, $this->user["pass"])) { + $err->raise("mem", _("The old password is incorrect")); + return false; + } + if ($newpass != $newpass2) { + $err->raise("mem", _("The new passwords are differents, please retry")); + return false; + } + $db->query("SELECT login FROM membres WHERE uid='$cuid';"); + $db->next_record(); + $login = $db->Record["login"]; + if (!$admin->checkPolicy("mem", $login, $newpass)) { + return false; // The error has been raised by checkPolicy() + } + $newpass = _md5cr($newpass); + $db->query("UPDATE membres SET pass='$newpass' WHERE uid='$cuid';"); + $err->error = 0; + return true; } - $txt=sprintf(_("Hello, + + /* ----------------------------------------------------------------- */ + + /** Change les preferences administrateur d'un compte + * @param integer $admlist Mode de visualisation des membres (0=large 1=courte) + * @return boolean TRUE si les preferences ont ete changees, FALSE sinon. + */ + function adminpref($admlist) { + global $db, $err, $cuid; + $err->log("mem", "admlist"); + if (!$this->user["su"]) { + $err->raise("mem", _("You must be a system administrator to do this.")); + return false; + } + $db->query("UPDATE membres SET admlist='$admlist' WHERE uid='$cuid';"); + $err->error = 0; + return true; + } + + /* ----------------------------------------------------------------- */ + + /** Envoie en mail le mot de passe d'un compte. + * Note : On ne peut demander le mot de passe qu'une seule fois par jour. + * TODO : Translate this mail into the localization program. + * TODO : Check this function's ! + * @return boolean TRUE si le mot de passe a ete envoye avec succes, FALSE sinon. + */ + function send_pass($login) { + global $err, $db, $L_HOSTING, $L_FQDN; + $err->log("mem", "send_pass"); + $db->query("SELECT * FROM membres WHERE login='$login';"); + if (!$db->num_rows()) { + $err->raise("mem", _("This account is locked, contact the administrator.")); + return false; + } + $db->next_record(); + if (time() - $db->f("lastaskpass") < 86400) { + $err->raise("mem", _("The new passwords are differents, please retry")); + return false; + } + $txt = sprintf(_("Hello, You requested the modification of your password for your account %s on %s @@ -467,33 +496,34 @@ If it happens again, please contact your server's Administrator. Cordially. "), $login, $L_HOSTING, $db->f("login"), $db->f("pass")); - mail($db->f("mail"),"Your password on $L_HOSTING",$txt,"From: postmaster@$L_FQDN\nReply-to: postmaster@$L_FQDN"); - $db->query("UPDATE membres SET lastaskpass=".time()." WHERE login='$login';"); - return true; - } - - /* ----------------------------------------------------------------- */ - /** Change le mail d'un membre (première etape, envoi du CookiE) - * TODO : insert this mail string into the localization system - * @param string $newmail Nouveau mail souhaité pour le membre. - * @return string le cookie si le mail a bien été envoyé, FALSE sinon - */ - function ChangeMail1($newmail) { - global $err,$db,$L_HOSTING,$L_FQDN,$cuid; - $err->log("mem","changemail1",$newmail); - $db->query("SELECT * FROM membres WHERE uid='$cuid';"); - if (!$db->num_rows()) { - $err->raise("mem",_("This account is locked, contact the administrator.")); - return false; + mail($db->f("mail"), "Your password on $L_HOSTING", $txt, "From: postmaster@$L_FQDN\nReply-to: postmaster@$L_FQDN"); + $db->query("UPDATE membres SET lastaskpass=" . time() . " WHERE login='$login';"); + return true; } - $db->next_record(); - // un cookie de 20 caractères pour le mail - $COOKIE=substr(md5(uniqid(rand(), true)),0,20); - // et de 6 pour la clé à entrer. ca me semble suffisant... - $KEY=substr(md5(uniqid(rand(), true)),0,6); - $link="https://$L_FQDN/mem_cm.php?usr=$cuid&cookie=$COOKIE"; - $txt=sprintf(_("Hello, + /* ----------------------------------------------------------------- */ + + /** Change le mail d'un membre (premiere etape, envoi du CookiE) + * TODO : insert this mail string into the localization system + * @param string $newmail Nouveau mail souhaite pour le membre. + * @return string le cookie si le mail a bien ete envoye, FALSE sinon + */ + function ChangeMail1($newmail) { + global $err, $db, $L_HOSTING, $L_FQDN, $cuid; + $err->log("mem", "changemail1", $newmail); + $db->query("SELECT * FROM membres WHERE uid='$cuid';"); + if (!$db->num_rows()) { + $err->raise("mem", _("This account is locked, contact the administrator.")); + return false; + } + $db->next_record(); + + // un cookie de 20 caract�res pour le mail + $COOKIE = substr(md5(uniqid(rand(), true)), 0, 20); + // et de 6 pour la cl� � entrer. ca me semble suffisant... + $KEY = substr(md5(uniqid(rand(), true)), 0, 6); + $link = "https://$L_FQDN/mem_cm.php?usr=$cuid&cookie=$COOKIE"; + $txt = sprintf(_("Hello, Someone (maybe you) requested an email's address modification of the account %s on %s @@ -512,152 +542,160 @@ again, please contact your server's administrator. Cordially. "), $db->f("login"), $L_HOSTING, $link); - mail($newmail,"Email modification request on $L_HOSTING",$txt,"From: postmaster@$L_FQDN\nReply-to: postmaster@$L_FQDN"); - // Supprime les demandes précédentes de ce compte ! - $db->query("DELETE FROM chgmail WHERE uid='$cuid';"); - $db->query("INSERT INTO chgmail (cookie,ckey,uid,mail,ts) VALUES ('$COOKIE','$KEY','$cuid','$newmail',".time().");"); - // Supprime les cookies de la veille :) - $lts=time()-86400; - $db->query("DELETE FROM chgmail WHERE ts<'$lts';"); - return $KEY; - } - - /* ----------------------------------------------------------------- */ - /** Change le mail d'un membre (seconde etape, CookiE+clé = application) - * @param string $COOKIE Cookie envoyé par mail - * @param string $KEY clé affichée à l'écran - * @param integer $uid Utilisateur concerné (on est hors session) - * @return boolean TRUE si le mail a bien été modifié, FALSE sinon - */ - function ChangeMail2($COOKIE,$KEY,$uid) { - global $err,$db,$L_HOSTING,$L_FQDN; - $err->log("mem","changemail2",$uid); - $db->query("SELECT * FROM chgmail WHERE cookie='$COOKIE' and ckey='$KEY' and uid='$uid';"); - if (!$db->num_rows()) { - $err->raise("mem",_("The information you entered is incorrect.")); - return false; - } - $db->next_record(); - - // met à jour le compte : - $db->query("UPDATE membres SET mail='".$db->f("mail")."' WHERE uid='$uid';"); - - $db->query("DELETE FROM chgmail WHERE uid='$uid';"); - // Supprime les cookies de la veille :) - $lts=time()-86400; - $db->query("DELETE FROM chgmail WHERE ts<'$lts';"); - return true; + mail($newmail, "Email modification request on $L_HOSTING", $txt, "From: postmaster@$L_FQDN\nReply-to: postmaster@$L_FQDN"); + // Supprime les demandes pr�c�dentes de ce compte ! + $db->query("DELETE FROM chgmail WHERE uid='$cuid';"); + $db->query("INSERT INTO chgmail (cookie,ckey,uid,mail,ts) VALUES ('$COOKIE','$KEY','$cuid','$newmail'," . time() . ");"); + // Supprime les cookies de la veille :) + $lts = time() - 86400; + $db->query("DELETE FROM chgmail WHERE ts<'$lts';"); + return $KEY; } /* ----------------------------------------------------------------- */ - /** Modifie le paramètre d'aide en ligne (1/0) + + /** Change le mail d'un membre (seconde etape, CookiE+cle = application) + * @param string $COOKIE Cookie envoye par mail + * @param string $KEY cle affichee a l'ecran + * @param integer $uid Utilisateur concerne (on est hors session) + * @return boolean TRUE si le mail a bien ete modifie, FALSE sinon + */ + function ChangeMail2($COOKIE, $KEY, $uid) { + global $err, $db; + $err->log("mem", "changemail2", $uid); + $db->query("SELECT * FROM chgmail WHERE cookie='$COOKIE' and ckey='$KEY' and uid='$uid';"); + if (!$db->num_rows()) { + $err->raise("mem", _("The information you entered is incorrect.")); + return false; + } + $db->next_record(); + + // met a jour le compte : + $db->query("UPDATE membres SET mail='" . $db->f("mail") . "' WHERE uid='$uid';"); + + $db->query("DELETE FROM chgmail WHERE uid='$uid';"); + // Supprime les cookies de la veille :) + $lts = time() - 86400; + $db->query("DELETE FROM chgmail WHERE ts<'$lts';"); + return true; + } + + /* ----------------------------------------------------------------- */ + + /** Modifie le parametre d'aide en ligne (1/0) * @param integer $show Faut-il (1) ou non (0) afficher l'aide en ligne */ function set_help_param($show) { - global $db,$err,$cuid; - $err->log("mem","set_help_param",$show); - $db->query("UPDATE membres SET show_help='$show' WHERE uid='$cuid';"); + global $db, $err, $cuid; + $err->log("mem", "set_help_param", $show); + $db->query("UPDATE membres SET show_help='$show' WHERE uid='$cuid';"); } /* ----------------------------------------------------------------- */ - /** Dit si l'aide en ligne est demandée - * @return boolean TRUE si l'aide en ligne est demandée, FALSE sinon. + + /** Dit si l'aide en ligne est demandee + * @return boolean TRUE si l'aide en ligne est demandee, FALSE sinon. */ function get_help_param() { - return $this->user["show_help"]; + return $this->user["show_help"]; } /* ----------------------------------------------------------------- */ + /** Affiche (echo) l'aide contextuelle - * @param integer $file Numéro de fichier d'aide à afficher. - * @return boolean TRUE si l'aide contextuelle a été trouvée, FALSE sinon + * @param integer $file Numero de fichier d'aide a afficher. + * @return boolean TRUE si l'aide contextuelle a ete trouvee, FALSE sinon */ - function show_help($file,$force=false) { - global $err; - if ($this->user["show_help"] || $force) { - $hlp=_("hlp_$file"); - if ($hlp!="hlp_$file") { - $hlp=preg_replace( - "#HELPID_([0-9]*)#", - " ",$hlp); - echo "
".$hlp."
"; - return true; - } - return false; - } else { - return true; - } - } - - /** - * @param integer $uid - */ - function get_creator_by_uid($uid) { - global $db,$err; - $err->log("dom","get_creator_by_uid"); - $uid=mysql_real_escape_string(intval($uid)); - $db->query("select creator from membres where uid = '$uid';"); - if (! $db->next_record()) return false; - return intval($db->f('creator') ); - } - - - /* ----------------------------------------------------------------- */ - /** - * Exports all the personnal user related information for an account. - * @access private - */ - function alternc_export_conf() { - global $db,$err; - $err->log("mem","export"); - $str="\n"; - $users=$this->user; - $str.=" \n"; - return $str; - } - - function session_tempo_params_get($v) { - global $uid; - if (empty($_COOKIE['session'])) return false; - $sid=$_COOKIE['session']; - if ( empty($_SESSION[$sid.'-'.$uid]) ) { // si pas de session de params tempo - return false; - } - $j=$_SESSION[$sid.'-'.$uid]; - $j=json_decode($j, true); - if ( ! empty($j[$v] ) ) { // si on a bien qque chose a retourner :) - return $j[$v]; - } - return false; - } - - function session_tempo_params_set($k, $v, $ecrase=false) { - global $uid; - if (empty($_COOKIE['session'])) return false; - $sid=$_COOKIE['session']; - $p=Array(); - if ( ! empty($_SESSION[$sid.'-'.$uid]) ) { - $p = json_decode($_SESSION[$sid.'-'.$uid], true); - } - if (! $ecrase && (isset($p[$k]) && is_array($p[$k])) && is_array($v) ) { - $v=array_merge($p[$k], $v); // overwrite entry with the same name + function show_help($file, $force = false) { + if ($this->user["show_help"] || $force) { + $hlp = _("hlp_$file"); + if ($hlp != "hlp_$file") { + $hlp = preg_replace( + "#HELPID_([0-9]*)#", "".$users["uid"]." \n"; - $str.="".$users["login"]." \n"; - $str.="".$users["enabled"]." \n"; - $str.="".$users["su"]." \n"; - $str.="".$users["pass"]." \n"; - $str.="".$users["mail"]." \n"; - $str.="".$users["created"]." \n"; - $str.="".$users["lastip"]." \n"; - $str.="".$users["lastlogin"]." \n"; - $str.="".$users["lastfail"]." \n"; - $str.="", $hlp); + echo "
" . $hlp . "
"; + return true; + } + return false; + } else { + return true; + } } - $p[$k]=$v; - $_SESSION[$sid.'-'.$uid]=json_encode($p); - return true; - } + /** + * @param integer $uid + */ + function get_creator_by_uid($uid) { + global $db, $err; + $err->log("dom", "get_creator_by_uid"); + $uid = mysql_real_escape_string(intval($uid)); + $db->query("select creator from membres where uid = '$uid';"); + if (!$db->next_record()) { + return false; + } + return intval($db->f('creator')); + } -} /* Classe Membre */ + /* ----------------------------------------------------------------- */ -?> + /** + * Exports all the personal user related information for an account. + * @access private + */ + function alternc_export_conf() { + global $db, $err; + $err->log("mem", "export"); + $str = "\n"; + $users = $this->user; + $str.=" \n"; + return $str; + } + + function session_tempo_params_get($v) { + global $uid; + if (empty($_COOKIE['session'])) { + return false; + } + $sid = $_COOKIE['session']; + if (empty($_SESSION[$sid . '-' . $uid])) { // si pas de session de params tempo + return false; + } + $j = $_SESSION[$sid . '-' . $uid]; + $j = json_decode($j, true); + if (!empty($j[$v])) { // si on a bien qque chose a retourner :) + return $j[$v]; + } + return false; + } + + function session_tempo_params_set($k, $v, $ecrase = false) { + global $uid; + if (empty($_COOKIE['session'])) { + return false; + } + $sid = $_COOKIE['session']; + $p = Array(); + if (!empty($_SESSION[$sid . '-' . $uid])) { + $p = json_decode($_SESSION[$sid . '-' . $uid], true); + } + if (!$ecrase && (isset($p[$k]) && is_array($p[$k])) && is_array($v)) { + $v = array_merge($p[$k], $v); // overwrite entry with the same name + } + + $p[$k] = $v; + $_SESSION[$sid . '-' . $uid] = json_encode($p); + return true; + } + +} + +/* Classe Membre */ diff --git a/bureau/class/m_menu.php b/bureau/class/m_menu.php index 131e98a5..597a1027 100644 --- a/bureau/class/m_menu.php +++ b/bureau/class/m_menu.php @@ -1,4 +1,5 @@ getquota("",true); // rebuild quota - - // Get menu objects - $lsto = $hooks->invoke('hook_menu'); - - // Get system menu - $sm = $this->system_menu(); - - // Merge it ! - $lst = array_merge($sm,$lsto); - - // Sort it - uasort($lst, 'm_menu::order_menu'); - - // Get user specific menu visibility options - $mop = $mem->session_tempo_params_get('menu_toggle') ; - - foreach( $lst as $k => $v ) { - - if (empty($v)) { - unset($lst[$k]); - continue; - } - - // Set the javascript toggle link for menu asking for it - if ($v['link'] == 'toggle') { - $lst[$k]['link'] = 'javascript:menu_toggle(\'menu-'.$k.'\');'; - } - - // Be sure that the default visibility is true - if (! isset($lst[$k]['visibility'])) $lst[$k]['visibility'] = true; - - // Set the user's specific visibility option - if (isset($mop["menu-$k"])) { - if ($mop["menu-$k"] == "hidden") $lst[$k]['visibility'] = false; - if ($mop["menu-$k"] == "visible") $lst[$k]['visibility'] = true; - } - - if ( isset($mesq[$k])) { // if there are some quota for this class - // Hide the menu if there are none and not allowed to create - if ( $mesq[$k]['t'] < 1 && $mesq[$k]['u'] < 1 ) { - unset($lst[$k]); - continue; - } - - // Set the quota in the menu object - $lst[$k]['quota_used'] = $mesq[$k]['u'] ; - $lst[$k]['quota_total'] = $mesq[$k]['t'] ; - - } // end if there are some quota for this class - + /** Constructor + * menu([$mid]) Constructeur de la classe menu, ne fait rien pour le moment + */ + function m_menu() { + } - return $lst; - } //getmenu + function getmenu() { + global $hooks, $quota, $mem; - function order_menu($a, $b) { - // Use to order the menu with a usort - return $a['pos'] > $b['pos']; - } + // Force rebuilding quota, in case of add or edit of the quota and cache not up-to-date + $mesq = $quota->getquota("", true); // rebuild quota + // Get menu objects + $lsto = $hooks->invoke('hook_menu'); - function system_menu() { - // Here some needed menu who don't have a class - global $help_baseurl, $lang_translation, $locales; + // Get system menu + $sm = $this->system_menu(); - $m = - array( - 'home' => - array( - 'title' => _("Home / Information"), - 'ico' => 'images/home.png', - 'link' => 'main.php', - 'pos' => 0, - ), - 'logout' => - array( - 'title' => _("Logout"), - 'ico' => 'images/exit.png', - 'link' => 'mem_logout.php', - 'pos' => 170, - ), - 'help' => - array( - 'title' => _("Online help"), - 'ico' => 'images/help.png', - 'target' => 'help', - 'link' => $help_baseurl, - 'pos' => 140, - ), - 'lang' => - array( - 'title' => _("Languages"), - 'ico' => '/images/lang.png', - 'visibility' => false, - 'link' => 'toggle', - 'links' => array(), - 'pos' => 150, - ) - ) ; - foreach($locales as $l) { - $m['lang']['links'][] = array ( 'txt' => (isset($lang_translation[$l]))?$lang_translation[$l]:$l, 'url' => "/login.php?setlang=$l"); - } - return $m; + // Merge it ! + $lst = array_merge($sm, $lsto); + // Sort it + uasort($lst, 'm_menu::order_menu'); -/* + // Get user specific menu visibility options + $mop = $mem->session_tempo_params_get('menu_toggle'); - + foreach ($lst as $k => $v) { + if (empty($v)) { + unset($lst[$k]); + continue; + } + // Set the javascript toggle link for menu asking for it + if ($v['link'] == 'toggle') { + $lst[$k]['link'] = 'javascript:menu_toggle(\'menu-' . $k . '\');'; + } + // Be sure that the default visibility is true + if (!isset($lst[$k]['visibility'])) { + $lst[$k]['visibility'] = true; + } + // Set the user's specific visibility option + if (isset($mop["menu-$k"])) { + if ($mop["menu-$k"] == "hidden") { + $lst[$k]['visibility'] = false; + } + if ($mop["menu-$k"] == "visible") { + $lst[$k]['visibility'] = true; + } + } -*/ - } //system_menu + if (isset($mesq[$k])) { // if there are some quota for this class + // Hide the menu if there are none and not allowed to create + if ($mesq[$k]['t'] < 1 && $mesq[$k]['u'] < 1) { + unset($lst[$k]); + continue; + } -} /* Class menu */ + // Set the quota in the menu object + $lst[$k]['quota_used'] = $mesq[$k]['u']; + $lst[$k]['quota_total'] = $mesq[$k]['t']; + } // end if there are some quota for this class + } + return $lst; + } + + function order_menu($a, $b) { + // Use to order the menu with a usort + return $a['pos'] > $b['pos']; + } + + function system_menu() { + // Here some needed menu who don't have a class + global $help_baseurl, $lang_translation, $locales; + + $m = array( + 'home' => + array( + 'title' => _("Home / Information"), + 'ico' => 'images/home.png', + 'link' => 'main.php', + 'pos' => 0, + ), + 'logout' => + array( + 'title' => _("Logout"), + 'ico' => 'images/exit.png', + 'link' => 'mem_logout.php', + 'pos' => 170, + ), + 'help' => + array( + 'title' => _("Online help"), + 'ico' => 'images/help.png', + 'target' => 'help', + 'link' => $help_baseurl, + 'pos' => 140, + ), + 'lang' => + array( + 'title' => _("Languages"), + 'ico' => '/images/lang.png', + 'visibility' => false, + 'link' => 'toggle', + 'links' => array(), + 'pos' => 150, + ) + ); + foreach ($locales as $l) { + $m['lang']['links'][] = array('txt' => (isset($lang_translation[$l])) ? $lang_translation[$l] : $l, 'url' => "/login.php?setlang=$l"); + } + return $m; + } + +} + +/* Class menu */ diff --git a/bureau/class/m_mysql.php b/bureau/class/m_mysql.php index a9b10bbf..3971379f 100644 --- a/bureau/class/m_mysql.php +++ b/bureau/class/m_mysql.php @@ -1,1158 +1,1150 @@ query("select db_servers.* from db_servers, membres where membres.uid=$cuid and membres.db_server_id=db_servers.id;"); - if (!$db->next_record()) { - $err->raise('db_user', _("There are no databases in db_servers for this user. Please contact your administrator.")); - die(); + /** + * Creator + */ + function DB_users($empty = false) { // Sometimes we need to create this object with empty parameters, but by default we fill them with those of the current user's DB + global $cuid, $db, $err; + + if (!$empty) { + $db->query("select db_servers.* from db_servers, membres where membres.uid=$cuid and membres.db_server_id=db_servers.id;"); + if (!$db->next_record()) { + $err->raise('db_user', _("There are no databases in db_servers for this user. Please contact your administrator.")); + die(); + } + + # Create the object + $this->HumanHostname = $db->f('name'); + $this->Host = $db->f('host'); + $this->User = $db->f('login'); + $this->Password = $db->f('password'); + $this->Client = $db->f('client'); + + $this->Database = "mysql"; # We have to define a dabatase when we connect, and the database must exist. + } else { + # Create the object without any parameter + $this->HumanHostname = ""; + $this->Host = ""; + $this->User = ""; + $this->Password = ""; + $this->Client = ""; + + $this->Database = "mysql"; # We have to define a dabatase when we connect, and the database must exist. + } } - # Create the object - $this->HumanHostname = $db->f('name'); - $this->Host = $db->f('host'); - $this->User = $db->f('login'); - $this->Password = $db->f('password'); - $this->Client = $db->f('client'); - - $this->Database = "mysql"; # We have to define a dabatase when we connect, and the database must exist. - }else{ - # Create the object without any parameter - $this->HumanHostname = ""; - $this->Host = ""; - $this->User = ""; - $this->Password = ""; - $this->Client = ""; - - $this->Database = "mysql"; # We have to define a dabatase when we connect, and the database must exist. - } - } - } - class m_mysql { - var $dbus; - /*---------------------------------------------------------------------------*/ - /** Constructor - * m_mysql([$mid]) Constructeur de la classe m_mysql, initialise le membre concerne - */ - function m_mysql() { - global $cuid; - if (!empty($cuid)) { - $this->dbus = new DB_users(); - } - variable_get('sql_allow_users_backups', 1,'Set 1 to allow users to configure backup of their databases, 0 if you want do disable this feature. Warning: it will not stop configured backup made by sqlbackup.sh'); - } + var $dbus; - function reload_dbus() { - $this->dbus = new DB_users(); - } + /* --------------------------------------------------------------------------- */ - function list_db_servers() { - global $db; - $db->query("select d.*, IFNULL(count(m.uid),0) as nb_users from db_servers d left join membres m on d.id = m.db_server_id group by d.id,m.db_server_id order by d.name,d.id;"); - $c=array(); - while ($db->next_record()) { - $c[]=$db->Record; - } - return $c; - } - - function hook_menu() { - global $quota; - $q = $quota->getquota("mysql"); - - $obj = array( - 'title' => _("MySQL"), - 'ico' => 'images/mysql.png', - 'link' => 'toggle', - 'pos' => 100, - 'links' => array(), - ) ; - - $obj['links'][] = - array ( - 'txt' => _("Databases"), - 'url' => "sql_list.php", - ); - $obj['links'][] = - array ( - 'txt' => _("MySQL Users"), - 'url' => "sql_users_list.php", - ); - if ($q["u"] > 0 ) { - $obj['links'][] = - array ( - 'txt' => _("PhpMyAdmin"), - 'url' => "sql_pma_sso.php", - 'target' => '_blank', - ); - } - return $obj; - } - - - - /* ----------------------------------------------------------------- */ - /** - * Password kind used in this class (hook for admin class) - */ - function alternc_password_policy() { - return array("mysql"=>"MySQL users"); - } - - /*---------------------------------------------------------------------------*/ - /** Get the list of the database for the current user. - * @return array returns an associative array as follow :" . $users["uid"] . " \n"; + $str.="" . $users["login"] . " \n"; + $str.="" . $users["enabled"] . " \n"; + $str.="" . $users["su"] . " \n"; + $str.="" . $users["pass"] . " \n"; + $str.="" . $users["mail"] . " \n"; + $str.="" . $users["created"] . " \n"; + $str.="" . $users["lastip"] . " \n"; + $str.="" . $users["lastlogin"] . " \n"; + $str.="" . $users["lastfail"] . " \n"; + $str.="
- * "db" => database name "bck" => backup mode for this db - * "dir" => Backup folder. - * Returns an array (empty) if no databases - */ - function get_dblist() { - global $db,$err,$bro,$cuid; - $err->log("mysql","get_dblist"); - $db->free(); - $db->query("SELECT login,pass,db, bck_mode, bck_dir FROM db WHERE uid='$cuid' ORDER BY db;"); - $c=array(); - while ($db->next_record()) { - list($dbu,$dbn)=split_mysql_database_name($db->f("db")); - $c[]=array("db"=>$db->f("db"), "name"=>$db->f('db'),"bck"=>$db->f("bck_mode"), "dir"=>$db->f("bck_dir"), "login"=>$db->f("login"), "pass"=>$db->f("pass")); - } - return $c; - } - - /*---------------------------------------------------------------------------*/ - /** Get the login and password of the special user able to connect to phpmyadmin - * @return array returns an associative array with login and password - * Returns FALSE if error - */ - function php_myadmin_connect(){ - global $db,$cuid,$err; - $err->log("mysql","php_myadmin_connect"); - $db->query("SELECT dbu.name,dbu.password, dbs.host FROM dbusers dbu, db_servers dbs, membres m WHERE dbu.uid='$cuid' and enable='ADMIN' and dbs.id=m.db_server_id and m.uid='$cuid';"); - if (!$db->num_rows()) { - $err->raise("mysql",_("Cannot connect to PhpMyAdmin")); - return false; - } - $db->next_record(); - $info=array( - "login"=>$db->f("name"), - "pass"=>$db->f("password"), - "host"=>$db->f("host") - ); - return $info; - } - - - /*---------------------------------------------------------------------------*/ - /** Returns the details of a user's database. - * $dbn is the name of the database (after the _) or nothing for the database "$user" - * @return string returns an associative array as follow : - * "db" => Name of the database - * "bck" => Current bckup mode - * "dir" => Backup directory - * "size" => Size of the database (in bytes) - * "pass" => Password of the user - * "history" => Number of backup we keep - * "gzip" => Does we compress the dumps ? - * Returns FALSE if the user has no database of if the database does not exist. - */ - function get_mysql_details($dbn) { - global $db,$err,$bro,$mem,$cuid; - $root=getuserpath(); - $err->log("mysql","get_mysql_details"); - $pos=strpos($dbn,'_'); - if($pos === false){ - $dbname=$dbn; - }else{ - $dbncomp=explode('_',$dbn); - $dbname=$dbn; - $dbn=$dbncomp[1]; - } - $size=$this->get_db_size($dbname); - $db->query("SELECT login,pass,db, bck_mode, bck_gzip, bck_dir, bck_history FROM db WHERE uid='$cuid' AND db='$dbname';"); - if (!$db->num_rows()) { - $err->raise("mysql",_("Database %s not found"),$dbn); - return array("enabled"=>false); - } - $db->next_record(); - list($dbu,$dbn)=split_mysql_database_name($db->f("db")); - return array("enabled"=>true,"login"=>$db->f("login"),"db"=>$db->f("db"), "name"=>$dbn,"bck"=>$db->f("bck_mode"), "dir"=>substr($db->f("bck_dir"),strlen($root)), "size"=>$size, "pass"=>$db->f("pass"), "history"=>$db->f("bck_history"), "gzip"=>$db->f("bck_gzip")); - } - - function test_get_param($dbname){ - global $db,$err,$cuid; - $db->query("SELECT "); - - - } - - /*---------------------------------------------------------------------------*/ - /** Create a new database for the current user. - * @param $dbn string Database name ($user_$dbn is the mysql db name) - * @return boolean if the database $user_$db has been successfully created, or FALSE if - * an error occured, such as over quota user. - */ - function add_db($dbn) { - global $db,$err,$quota,$mem,$cuid,$admin; - $err->log("mysql","add_db",$dbn); - $password_user=""; - if (!$quota->cancreate("mysql")) { - $err->raise("mysql",_("Your databases quota is over. You cannot create more databases")); - return false; - } - $pos=strpos($dbn,'_'); - if($pos === false){ - $dbname=$dbn; - }else{ - $dbncomp=explode('_',$dbn); - $dbname=$dbn; - $dbn=$dbncomp[1]; - if (empty($dbn)) { // If nothing after the '_' - $err->raise("mysql",_("Database can't have empty suffix")); - return false; - } - } - if (!preg_match("#^[0-9a-z]*$#",$dbn)) { - $err->raise("mysql",_("Database name can contain only letters and numbers")); - return false; - } - - - if (strlen($dbname) > 64) { - $err->raise("mysql",_("Database name cannot exceed 64 characters")); - return false; - } - $db->query("SELECT * FROM db WHERE db='$dbname';"); - if ($db->num_rows()) { - $err->raise("mysql",_("Database %s already exists"),$dbn); - return false; - } - - $db->query("SELECT name from dbusers where name='".$dbname."' and enable='ACTIVATED' ;"); - if(!$db->num_rows()){ - $password_user=create_pass(8); - if(!$this->add_user($dbn,$password_user,$password_user)){ - } - } - - //checking for the phpmyadmin user - $db->query("SELECT * FROM dbusers WHERE uid=$cuid AND enable='ADMIN';"); - if ($db->num_rows()) { - $db->next_record(); - $myadm=$db->f("name"); - $password=$db->f("password"); - }else{ - $err->raise("mysql",_("There is a problem with the special PhpMyAdmin user. Contact the administrator")); - return false; - } - - //Grant the special user every rights. - if ($this->dbus->query("CREATE DATABASE `$dbname`;")) { - $err->log("mysql","add_db_succes",$dbn); - // Ok, database does not exist, quota is ok and dbname is compliant. Let's proceed - $db->query("INSERT INTO db (uid,login,pass,db,bck_mode) VALUES ('$cuid','$myadm','$password','$dbname',0);"); - $dbuser=$dbname; - $dbname=str_replace('_','\_',$dbname); - $this->grant($dbname,$myadm,"ALL PRIVILEGES",$password); - if(!empty($password_user)){ - $this->grant($dbname,$dbuser,"ALL PRIVILEGES",$password_user); - } - $this->dbus->query("FLUSH PRIVILEGES;"); - return true; - } else { - $err->log("mysql","add_db",$dbn); - $err->raise("mysql",_("An error occured. The database could not be created")); - return false; - } - } - - - /*---------------------------------------------------------------------------*/ - /** Delete a database for the current user. - * @param $dbn string Name of the database to delete. The db name is $user_$dbn - * @return boolean if the database $user_$db has been successfully deleted, or FALSE if - * an error occured, such as db does not exist. - */ - function del_db($dbn) { - global $db,$err,$mem,$cuid; - $err->log("mysql","del_db",$dbn); - $dbname=addslashes($dbn); - $db->query("SELECT uid FROM db WHERE db='$dbname';"); - if (!$db->num_rows()) { - $err->raise("mysql",_("The database was not found. I can't delete it")); - return false; - } - $db->next_record(); - - // Ok, database exists and dbname is compliant. Let's proceed - $db->query("DELETE FROM size_db WHERE db='$dbname';"); - $db->query("DELETE FROM db WHERE uid='$cuid' AND db='$dbname';"); - $this->dbus->query("DROP DATABASE `$dbname`;"); - - $db_esc=str_replace('_','\_',$dbname); - $this->dbus->query("DELETE FROM mysql.db WHERE Db='$db_esc';"); - - #We test if the user created with the database is associated with more than 1 database. - $this->dbus->query("select User from mysql.db where User='".$dbname."' and (Select_priv='Y' or Insert_priv='Y' or Update_priv='Y' or Delete_priv='Y' or Create_priv='Y' or Drop_priv='Y' or References_priv='Y' or Index_priv='Y' or Alter_priv='Y' or Create_tmp_table_priv='Y' or Lock_tables_priv='Y');"); - if(($this->dbus->num_rows()) == 0){ - #If not we can delete it. - $this->del_user($dbname); - } - return true; - } - - - - - /*---------------------------------------------------------------------------*/ - /** Set the backup parameters for the database $db - * @param $db string database name - * @param $bck_mode integer Backup mode (0 = none 1 = daily 2 = weekly) - * @param $bck_history integer How many backup should we keep ? - * @param $bck_gzip boolean shall we compress the backup ? - * @param $bck_dir string Directory relative to the user account where the backup will be stored - * @return boolean true if the backup parameters has been successfully changed, false if not. - */ - function put_mysql_backup($dbn,$bck_mode,$bck_history,$bck_gzip,$bck_dir) { - global $db,$err,$mem,$bro,$cuid; - $err->log("mysql","put_mysql_backup"); - - if ( ! variable_get('sql_allow_users_backups') ) { - $err->raise("mysql",_("User aren't allowed to configure their backups")); - return false; - } - - $pos=strpos($dbn,'_'); - if($pos === false){ - $dbname=$dbn; - }else{ - $dbncomp=explode('_',$dbn); - $dbname=$dbn; - $dbn=$dbncomp[1]; - } - if (!preg_match("#^[0-9a-z]*$#",$dbn)) { - $err->raise("mysql",_("Database name can contain only letters and numbers")); - return false; - } - $db->query("SELECT * FROM db WHERE uid='$cuid' AND db='$dbname';"); - if (!$db->num_rows()) { - $err->raise("mysql",_("Database %s not found"),$dbn); - return false; - } - $db->next_record(); - $bck_mode=intval($bck_mode); - $bck_history=intval($bck_history); - if ($bck_gzip) - $bck_gzip="1"; - else - $bck_gzip="0"; - if (!$bck_mode) - $bck_mode="0"; - if (!$bck_history) { - $err->raise("mysql",_("You have to choose how many backups you want to keep")); - return false; - } - if (($bck_dir=$bro->convertabsolute($bck_dir,0))===false) { // return a full path or FALSE - $err->raise("mysql",_("Directory does not exist")); - return false; - } - $db->query("UPDATE db SET bck_mode='$bck_mode', bck_history='$bck_history', bck_gzip='$bck_gzip', bck_dir='$bck_dir' WHERE uid='$cuid' AND db='$dbname';"); - return true; - } - - - /*---------------------------------------------------------------------------*/ - /** Change the password of the user in MySQL - * @param $password string new password (cleartext) - * @return boolean TRUE if the password has been successfully changed, FALSE else. - */ - function put_mysql_details($password) { - global $db,$err,$mem,$cuid,$admin; - $err->log("mysql","put_mysql_details"); - $db->query("SELECT * FROM db WHERE uid='$cuid';"); - if (!$db->num_rows()) { - $err->raise("mysql",_("Database not found")); - return false; - } - $db->next_record(); - $login=$db->f("login"); - - if (!$password) { - $err->raise("mysql",_("The password is mandatory")); - return false; - } - - if (strlen($password)>16) { - $err->raise("mysql",_("MySQL password cannot exceed 16 characters")); - return false; - } - - // Check this password against the password policy using common API : - if (is_callable(array($admin,"checkPolicy"))) { - if (!$admin->checkPolicy("mysql",$login,$password)) { - return false; // The error has been raised by checkPolicy() - } - } - - // Update all the "pass" fields for this user : - $db->query("UPDATE db SET pass='$password' WHERE uid='$cuid';"); - $this->dbus->query("SET PASSWORD FOR ".$login."@".$this->dbus->Client." = PASSWORD('$password');"); - return true; - } - - /** - * Function used to grant SQL rights to users: - * @base :database - * @user : database user - * @rights : rights to apply ( optional, every rights apply given if missing - * @pass : user password ( optional, if not given the pass stays the same, else it takes the new value ) - * @table : sql tables to apply rights - **/ - function grant($base,$user,$rights=null,$pass=null,$table='*'){ - global $err,$db; - $err->log("mysql","grant",$base."-".$rights."-".$user); - - if(!preg_match("#^[0-9a-z_\\*\\\\]*$#",$base)){ - $err->raise("mysql",_("Database name can contain only letters and numbers")); - return false; - } elseif (!$this->dbus->query("select db from db where db='$base';")){ - $err->raise("mysql",_("Database not found")); - return false; - } - - if($rights==null){ - $rights='ALL PRIVILEGES'; - }elseif(!preg_match("#^[a-zA-Z,\s]*$#",$rights)){ - $err->raise("mysql",_("Databases rights are not correct")); - return false; - } - - if (!preg_match("#^[0-9a-z]#",$user)) { - $err->raise("mysql",_("The username can contain only letters and numbers.")); - return false; - } - $db->query("select name from dbusers where name='".$user."' ;"); - - if(!$db->num_rows()){ - $err->raise("mysql",_("Database user not found")); - return false; - } - - # Protect database name if not wildcard - if ($base != '*' ) $base = "`".$base."`" ; - - $grant="grant ".$rights." on ".$base.".".$table." to '".$user."'@'".$this->dbus->Client."'" ; - - if($pass){ - $grant .= " identified by '".$pass."';"; - }else{ - $grant .= ";"; - } - if(!$this->dbus->query($grant)){ - $err->raise("mysql",_("Could not grant rights")); - return false; - } - return true; - - } - - - /* ----------------------------------------------------------------- */ - /** Restore a sql database. - * @param $file string The filename, relative to the user root dir, which contains a sql dump - * @param $stdout boolean shall-we dump the error to stdout ? - * @param $id integer The ID of the database to dump to. - * @return boolean TRUE if the database has been restored, or FALSE if an error occurred - */ - function restore($file,$stdout,$id) { - global $err,$bro,$mem,$L_MYSQL_HOST,$db; - if (empty($file)) { - $err->raise("mysql",_("No file specified")); - return false; - } - if (!$r=$this->get_mysql_details($id)) { - return false; - } - if (!($fi=$bro->convertabsolute($file,0))) { - $err->raise("mysql",_("File not found")); - return false; - } - if (!file_exists($fi)) { - $err->raise("mysql",_("File not found")); - return false; - } - - if (substr($fi,-3)==".gz") { - $exe="/bin/gzip -d -c <".escapeshellarg($fi)." | /usr/bin/mysql -h".escapeshellarg($this->dbus->Host)." -u".escapeshellarg($r["login"])." -p".escapeshellarg($r["pass"])." ".escapeshellarg($r["db"]); - } elseif (substr($fi,-4)==".bz2") { - $exe="/usr/bin/bunzip2 -d -c <".escapeshellarg($fi)." | /usr/bin/mysql -h".escapeshellarg($this->dbus->Host)." -u".escapeshellarg($r["login"])." -p".escapeshellarg($r["pass"])." ".escapeshellarg($r["db"]); - } else { - $exe="/usr/bin/mysql -h".escapeshellarg($this->dbus->Host)." -u".escapeshellarg($r["login"])." -p".escapeshellarg($r["pass"])." ".escapeshellarg($r["db"])." <".escapeshellarg($fi); - } - $exe .= " 2>&1"; - - echo "" ; - if ($ret != 0) { - return false ; - } else { - return true ; - } - } - - - /* ----------------------------------------------------------------- */ - /** Get the size of a database - * @param $dbname name of the database - * @return integer database size - * @access private - */ - function get_db_size($dbname) { - global $db,$err; - - $this->dbus->query("SHOW TABLE STATUS FROM `$dbname`;"); - $size = 0; - while ($db->next_record()) { - $size += $db->f('Data_length') + $db->f('Index_length'); - if ( $db->f('Engine') != 'InnoDB') $size += $db->f('Data_free'); - } - return $size; - } - - - /* ------------------------------------------------------------ */ - /** - * Returns the list of database users of an account - **/ - function get_userslist($all=null) { - global $db,$err,$bro,$cuid; - $err->log("mysql","get_userslist"); - $c=array(); - if(!$all){ - $db->query("SELECT name FROM dbusers WHERE uid='$cuid' and enable not in ('ADMIN','HIDDEN') ORDER BY name;"); - }else{ - $db->query("SELECT name FROM dbusers WHERE uid='$cuid' ORDER BY name;"); - } - while ($db->next_record()) { - $pos=strpos($db->f("name"),"_"); - if($pos === false){ - $c[]=array("name"=>($db->f("name"))); - }else{ - $c[]=array("name"=>($db->f("name"))); - //$c[]=array("name"=>substr($db->f("name"),strpos($db->f("name"),"_")+1)); - } - } - - return $c; - } - - function get_defaultsparam($dbn){ - global $db,$err,$bro,$cuid; - $err->log("mysql","getdefaults"); - - $dbu=$dbn; - $r=array(); - $dbn=str_replace('_','\_',$dbn); - $this->dbus->query("Select * from mysql.db where Db='".$dbn."' and User!='".$cuid."_myadm';"); - - if(!$db->num_rows()){ - return $r; - } - while ($db->next_record()) { - $variable = $db->Record; - if($variable['User'] == $dbu){ - $r['Host']=$db->f('Host'); - - if($db->f('Select_priv') !== "Y"){ - return $r; - } - if($db->f('Insert_priv') !== "Y"){ - return $r; - } - if($db->f('Update_priv') !== "Y"){ - return $r; - } - if($db->f('Delete_priv') !== "Y"){ - return $r; - } - if($db->f('Create_priv') !== "Y"){ - return $r; - } - if($db->f('Drop_priv') !== "Y"){ - return $r; - } - if($db->f('References_priv') !== "Y"){ - return $r; - } - if($db->f('Index_priv') !== "Y"){ - return $r; - } - if($db->f('Alter_priv') !== "Y"){ - return $r; - } - if($db->f('Create_tmp_table_priv') !== "Y"){ - return $r; - } - if($db->f('Lock_tables_priv') !== "Y"){ - return $r; - } - if($db->f('Create_view_priv') !== "Y"){ - return $r; - } - if($db->f('Show_view_priv') !== "Y"){ - return $r; - } - if($db->f('Create_routine_priv') !== "Y"){ - return $r; - } - if($db->f('Alter_routine_priv') !== "Y"){ - return $r; - } - if($db->f('Execute_priv') !== "Y"){ - return $r; - } - if($db->f('Event_priv') !== "Y"){ - return $r; - } - if($db->f('Trigger_priv') !== "Y"){ - return $r; - } - } - }//endwhile - if(!$db->query("SELECT name,password from dbusers where name='".$dbu."';")){ - return $r; - } - - if(!$db->num_rows()){ - return $r; - } - $db->next_record(); - $r['user']=$db->f('name'); - $r['password']=$db->f('password'); - return $r; - - } - - - /* ------------------------------------------------------------ */ - /** - * Create a new user in MySQL rights tables - * @param $usern the username (we will add _[alternc-account] to it) - * @param string $password The password for this username - * @param string $passconf The password confirmation - * @return boolean if the user has been created in MySQL or FALSE if an error occurred - **/ - function add_user($usern,$password,$passconf) { - global $db,$err,$quota,$mem,$cuid,$admin; - $err->log("mysql","add_user",$usern); - - $usern=trim($usern); - $login=$mem->user["login"]; - if($login != $usern){ - $user=addslashes($login."_".$usern); - }else{ - $user=$usern; - } - $pass=addslashes($password); - - if (!$usern) { - $err->raise("mysql",_("The username is mandatory")); - return false; - } - if (!$pass) { - $err->raise("mysql",_("The password is mandatory")); - return false; - } - if (!preg_match("#^[0-9a-z]#",$usern)) { - $err->raise("mysql",_("The username can contain only letters and numbers")); - return false; - } - - - // We check the length of the COMPLETE username, not only the part after _ - if (strlen($user) > 16) { - $err->raise("mysql",_("MySQL username cannot exceed 16 characters")); - return false; - } - $db->query("SELECT * FROM dbusers WHERE name='$user';"); - if ($db->num_rows()) { - $err->raise("mysql",_("The database user already exists")); - return false; - } - if ($password != $passconf || !$password) { - $err->raise("mysql",_("The passwords do not match")); - return false; - } - - // Check this password against the password policy using common API : - if (is_callable(array($admin,"checkPolicy"))) { - if (!$admin->checkPolicy("mysql",$user,$password)) { - return false; // The error has been raised by checkPolicy() - } - } - - // We add him to the user table - $db->query("INSERT INTO dbusers (uid,name,password,enable) VALUES($cuid,'$user','$password','ACTIVATED');"); - - $this->grant("*",$user,"USAGE",$pass); - return true; - } - - /* ------------------------------------------------------------ */ - /** - * Change a user's MySQL password - * @param $usern the username - * @param $password The password for this username - * @param $passconf The password confirmation - * @return boolean if the password has been changed in MySQL or FALSE if an error occurred - **/ - function change_user_password($usern,$password,$passconf) { - global $db,$err,$quota,$mem,$cuid,$admin; - $err->log("mysql","change_user_pass",$usern); - - $usern=trim($usern); - $user=addslashes($usern); - $pass=addslashes($password); - if ($password != $passconf || !$password) { - $err->raise("mysql",_("The passwords do not match")); - return false; - } - - // Check this password against the password policy using common API : - if (is_callable(array($admin,"checkPolicy"))) { - if (!$admin->checkPolicy("mysql",$user,$password)) { - return false; // The error has been raised by checkPolicy() - } - } - $this->dbus->query("SET PASSWORD FOR '".$user."'@'".$this->dbus->Client."' = PASSWORD('".$pass."');"); - $db->query("UPDATE dbusers set password='".$pass."' where name='".$usern."' and uid=$cuid ;"); - return true; - } - - - - /* ------------------------------------------------------------ */ - /** - * Delete a user in MySQL rights tables - * @param $user the username (we will add "[alternc-account]_" to it) to delete - * @param integer $all - * @return boolean if the user has been deleted in MySQL or FALSE if an error occurred - **/ - function del_user($user,$all=null) { - global $db,$err,$mem,$cuid; - $err->log("mysql","del_user",$user); - if (!preg_match("#^[0-9a-z]#",$user)) { - $err->raise("mysql",_("The username can contain only letters and numbers")); - return false; - } - if(!$all){ - $db->query("SELECT name FROM dbusers WHERE name='".$user."' and enable not in ('ADMIN','HIDDEN');"); - }else{ - $db->query("SELECT name FROM dbusers WHERE uid='".$cuid."' ;"); - } - - if (!$db->num_rows()) { - $err->raise("mysql",_("The username was not found")); - return false; - } - $db->next_record(); - $login=$db->f("name"); - - // Ok, database exists and dbname is compliant. Let's proceed - $this->dbus->query("REVOKE ALL PRIVILEGES ON *.* FROM '".$user."'@'".$this->dbus->Client."';"); - $this->dbus->query("DELETE FROM mysql.db WHERE User='".$user."' AND Host='".$this->dbus->Client."';"); - $this->dbus->query("DELETE FROM mysql.user WHERE User='".$user."' AND Host='".$this->dbus->Client."';"); - $this->dbus->query("FLUSH PRIVILEGES"); - - $db->query("DELETE FROM dbusers WHERE uid='$cuid' AND name='".$user."';"); - return true; - } - - - /* ------------------------------------------------------------ */ - /** - * Return the list of the database rights of user $user - * @param $user the username - * @return array An array of database name and rights - **/ - - function get_user_dblist($user){ - global $db,$err,$mem,$cuid; - - $this->dbus->query("SELECT * FROM mysql.user WHERE User='".$user."' AND Host='".$this->dbus->Client."';"); - if (!$this->dbus->next_record() ) { - $err->raise('mysql',_("This user does not exist in the MySQL/User database")); - return false; - } - - $r=array(); - $db->free(); - $dblist=$this->get_dblist(); - foreach($dblist as $tab){ - $pos=strpos($tab['db'],"_"); - if($pos === false){ - $this->dbus->query("SELECT * FROM mysql.db WHERE User='".$user."' AND Host='".$this->dbus->Client."' AND Db='".$tab["db"]."';"); - }else{ - $dbname=str_replace('_','\_',$tab['db']); - $this->dbus->query("SELECT * FROM mysql.db WHERE User='".$user."' AND Host='".$this->dbus->Client."' AND Db='".$dbname."';"); - } - if ($this->dbus->next_record()){ - $r[]=array("db"=>$tab["db"], "select"=>$this->dbus->f("Select_priv"), "insert"=>$this->dbus->f("Insert_priv"), "update"=>$this->dbus->f("Update_priv"), "delete"=>$this->dbus->f("Delete_priv"), "create"=>$this->dbus->f("Create_priv"), "drop"=>$this->dbus->f("Drop_priv"), "references"=>$this->dbus->f("References_priv"), "index"=>$this->dbus->f("Index_priv"), "alter"=>$this->dbus->f("Alter_priv"), "create_tmp"=>$this->dbus->f("Create_tmp_table_priv"), "lock"=>$this->dbus->f("Lock_tables_priv"), - "create_view"=>$this->dbus->f("Create_view_priv"), - "show_view"=>$this->dbus->f("Show_view_priv"), - "create_routine"=>$this->dbus->f("Create_routine_priv"), - "alter_routine"=>$this->dbus->f("Alter_routine_priv"), - "execute"=>$this->dbus->f("Execute_priv"), - "event"=>$this->dbus->f("Event_priv"), - "trigger"=>$this->dbus->f("Trigger_priv") - ); - }else{ - $r[]=array("db"=>$tab['db'], "select"=>"N", "insert"=>"N", "update"=>"N", "delete"=>"N", "create"=>"N", "drop"=>"N", "references"=>"N", "index"=>"N", "alter"=>"N", "create_tmp"=>"N", "lock"=>"N","create_view"=>"N","show_view"=>"N","create_routine"=>"N","alter_routine"=>"N","execute"=>"N","event"=>"N","trigger"=>"N"); - - } - - } - return $r; - } - - /* ------------------------------------------------------------ */ - /** - * Set the access rights of user $user to database $dbn to be rights $rights - * @param $user the username to give rights to - * @param $dbn The database to give rights to - * @param $rights The rights as an array of MySQL keywords (insert, select ...) - * @return boolean TRUE if the rights has been applied or FALSE if an error occurred - * - **/ - function set_user_rights($user,$dbn,$rights) { - global $mem,$err,$db; - $err->log("mysql","set_user_rights"); - - $usern=addslashes($user); - $dbname=addslashes($dbn); - $dbname=str_replace('_','\_',$dbname); - // On génère les droits en fonction du tableau de droits - $strrights=""; - for( $i=0 ; $i
" ; - if ($stdout) { - passthru($exe,$ret); - } else { - exec ($exe,$ret); - } - echo "dbus->query("SELECT * FROM mysql.db WHERE User = '$usern' AND Db = '$dbname';"); - if($this->dbus->num_rows()) - $this->dbus->query("REVOKE ALL PRIVILEGES ON `$dbname`.* FROM '$usern'@'".$this->dbus->Client."';"); - if( $strrights ){ - $strrights=substr($strrights,0,strlen($strrights)-1); - $this->grant($dbname,$usern,$strrights); - } - $this->dbus->query("FLUSH PRIVILEGES"); - return TRUE; - } - - function available_sql_rights(){ - return Array('select','insert','update','delete','create','drop','references','index','alter','create_tmp','lock','create_view','show_view','create_routine','alter_routine','execute','event','trigger'); - - - } - - - /* ----------------------------------------------------------------- */ - /** Hook function called by the lxc class to set mysql_host and port - * parameters - * @param $name string name of the quota - * @return integer the number of service used or false if an error occured - * @access private - */ - function hook_lxc_params($params) { - global $err; - $err->log("mysql","alternc_get_quota"); - $p=array(); - if (isset($this->dbus["Host"]) && $this->dbus["Host"]!="") { - $p["mysql_host"] = $this->dbus["Host"]; - $p["mysql_port"] = 3306; - } - return $p; - } - - - /* ----------------------------------------------------------------- */ - /** Hook function called by the quota class to compute user used quota - * Returns the used quota for the $name service for the current user. - * @param $name string name of the quota - * @return integer the number of service used or false if an error occured - * @access private - */ - function hook_quota_get() { - global $err,$db,$cuid; - $err->log("mysql","alternc_get_quota"); - $q=Array("name"=>"mysql", "description"=>_("MySQL Databases"), "used"=>0); - $c=$this->get_dblist(); - if (is_array($c)) { - $q['used']=count($c); - } - return $q; - } - - /* ----------------------------------------------------------------- */ - /** Hook function called when a user is created. - * AlternC's standard function that create a member - * @access private - */ - function alternc_add_member() { - global $db,$err,$cuid,$mem; - $err->log("mysql","alternc_add_member"); - //checking for the phpmyadmin user - $db->query("SELECT name,password FROM dbusers WHERE uid=$cuid AND Type='ADMIN';"); - if ($db->num_rows()) { - $myadm=$db->f("name"); - $password=$db->f("password"); - }else{ - $myadm=$cuid."_myadm"; - $password=create_pass(8); - } - - - $db->query("INSERT INTO dbusers (uid,name,password,enable) VALUES ('$cuid','$myadm','$password','ADMIN');"); - - return true; - } - - - - - /* ----------------------------------------------------------------- */ - /** Hook function called when a user is deleted. - * AlternC's standard function that delete a member - * @access private - */ - function alternc_del_member() { - global $db,$err,$cuid; - $err->log("mysql","alternc_del_member"); - $c=$this->get_dblist(); - if (is_array($c)) { - for($i=0;$i del_db($c[$i]["name"]); - } - } - $d=$this->get_userslist(1); - if (!empty($d)) { - for($i=0;$i del_user($d[$i]["name"],1); - } - } - return true; - } - - - /* ----------------------------------------------------------------- */ - /** Hook function called when a user is logged out. - * We just remove the cookie created in admin/sql_admin.php - a @access private - */ - function alternc_del_session() { - $_SESSION['PMA_single_signon_user'] = ''; - $_SESSION['PMA_single_signon_password'] = ''; - $_SESSION['PMA_single_signon_host'] = ''; - } - - - /* ----------------------------------------------------------------- */ - /** - * Exporte all the mysql information of an account - * @access private - * EXPERIMENTAL 'sid' function ;) - */ - function alternc_export_conf() { - //TODO don't work with separated sql server for dbusers - global $db,$err,$cuid; - $err->log("mysql","export"); - $db->query("SELECT login, pass, db, bck_mode, bck_dir, bck_history, bck_gzip FROM db WHERE uid='$cuid';"); - $str=""; - if ($db->next_record()) { - $str.=" \n"; - $str.=" \n"; + variable_get('sql_allow_users_backups', 1, 'Set 1 to allow users to configure backup of their databases, 0 if you want do disable this feature. Warning: it will not stop configured backup made by sqlbackup.sh'); } - return $str; - } - /* ----------------------------------------------------------------- */ - /** - * Exporte all the mysql databases a of give account to $dir directory - * @access private - * EXPERIMENTAL 'sid' function ;) - */ - function alternc_export_data ($dir){ - global $db, $err, $cuid,$mem; - $err->log("mysql","export_data"); - $db->query("SELECT db.login, db.pass, db.db, dbusers.name FROM db,dbusers WHERE db.uid='$cuid' AND dbusers.uid=db.uid;"); - $dir.="sql/"; - if(!is_dir($dir)){ - if(!mkdir($dir)){ - $err->raise('mysql',_("The directory could not be created")); - } + function reload_dbus() { + $this->dbus = new DB_users(); } - // on exporte toutes les bases utilisateur. - while($db->next_record()){ - $filename=$dir."mysql.".$db->Record["db"].".".date("H:i:s").".sql.gz"; - exec ("/usr/bin/mysqldump --defaults-file=/etc/alternc/my.cnf --add-drop-table --allow-keywords -Q -f -q -a -e ".escapeshellarg($db->Record["db"])." |/bin/gzip >".escapeshellarg($filename)); + + function list_db_servers() { + global $db; + $db->query("select d.*, IFNULL(count(m.uid),0) as nb_users from db_servers d left join membres m on d.id = m.db_server_id group by d.id,m.db_server_id order by d.name,d.id;"); + $c = array(); + while ($db->next_record()) { + $c[] = $db->Record; + } + return $c; } - } - /* ----------------------------------------------------------------- */ - /** - * Return the size of each databases in a SQL Host given in parameter - * @param $db_name the human name of the host - * @param $db_host the host hosting the SQL databases - * @param $db_login the login to access the SQL db - * @param $db_password the password to access the SQL db - * @param $db_client the client to access the SQL db - * @return an array associating the name of the databases to their sizes : array(dbname=>size) - */ - function get_dbus_size($db_name,$db_host,$db_login,$db_password,$db_client) { - global $db,$err; - $err->log("mysql","get_dbus_size",$db_host); + function hook_menu() { + global $quota; + $q = $quota->getquota("mysql"); - # We create the object with empty parameters - $this->dbus=new DB_users(true); - # Modify the object with right parameters - $this->dbus->HumanHostname = $db_name; - $this->dbus->Host = $db_host; - $this->dbus->User = $db_login; - $this->dbus->Password = $db_password; - $this->dbus->Client = $db_client; + $obj = array( + 'title' => _("MySQL"), + 'ico' => 'images/mysql.png', + 'link' => 'toggle', + 'pos' => 100, + 'links' => array(), + ); - $this->dbus->query("show databases;"); - $res=array(); - while($this->dbus->next_record()) { - $dbname=$this->dbus->f("Database"); - $c=mysql_query("SHOW TABLE STATUS FROM $dbname;"); - $size=0; - while ($d=mysql_fetch_array($c)) { - $size+=$d["Data_length"]+$d["Index_length"]; - } - $res["$dbname"]="$size"; + $obj['links'][] = array( + 'txt' => _("Databases"), + 'url' => "sql_list.php", + ); + $obj['links'][] = array( + 'txt' => _("MySQL Users"), + 'url' => "sql_users_list.php", + ); + if ($q["u"] > 0) { + $obj['links'][] = array( + 'txt' => _("PhpMyAdmin"), + 'url' => "sql_pma_sso.php", + 'target' => '_blank', + ); + } + return $obj; } - return $res; - } -} /* Class m_mysql */ + /* ----------------------------------------------------------------- */ -?> + /** + * Password kind used in this class (hook for admin class) + */ + function alternc_password_policy() { + return array("mysql" => "MySQL users"); + } + + /* --------------------------------------------------------------------------- */ + + /** Get the list of the database for the current user. + * @return array returns an associative array as follow :".$db->Record["login"]." \n"; - $str.="".$db->Record["pass"]." \n"; - do { - $filename=$tmpdir."/mysql.".$db->Record["db"].".sql.gz"; // FIXME not used - $str.="".($db->Record["db"])." \n"; - $str.="".($db->Record["pass"])." \n"; - if ($s["bck_mode"]!=0) { // FIXME what is $s ? - $str.="".($db->Record["bck_mode"])." \n"; - $str.="".($db->Record["bck_dir"])." \n"; - $str.="".($db->Record["bck_history"])." \n"; - $str.="".($db->Record["bck_gzip"])." \n"; + /** Constructor + * m_mysql([$mid]) Constructeur de la classe m_mysql, initialise le membre concerne + */ + function m_mysql() { + global $cuid; + if (!empty($cuid)) { + $this->dbus = new DB_users(); } - } while ($db->next_record()); - $str.="
+ * "db" => database name "bck" => backup mode for this db + * "dir" => Backup folder. + * Returns an array (empty) if no databases + */ + function get_dblist() { + global $db, $err, $bro, $cuid; + $err->log("mysql", "get_dblist"); + $db->free(); + $db->query("SELECT login,pass,db, bck_mode, bck_dir FROM db WHERE uid='$cuid' ORDER BY db;"); + $c = array(); + while ($db->next_record()) { + list($dbu, $dbn) = split_mysql_database_name($db->f("db")); + $c[] = array("db" => $db->f("db"), "name" => $db->f('db'), "bck" => $db->f("bck_mode"), "dir" => $db->f("bck_dir"), "login" => $db->f("login"), "pass" => $db->f("pass")); + } + return $c; + } + + /* --------------------------------------------------------------------------- */ + + /** Get the login and password of the special user able to connect to phpmyadmin + * @return array returns an associative array with login and password + * Returns FALSE if error + */ + function php_myadmin_connect() { + global $db, $cuid, $err; + $err->log("mysql", "php_myadmin_connect"); + $db->query("SELECT dbu.name,dbu.password, dbs.host FROM dbusers dbu, db_servers dbs, membres m WHERE dbu.uid='$cuid' and enable='ADMIN' and dbs.id=m.db_server_id and m.uid='$cuid';"); + if (!$db->num_rows()) { + $err->raise("mysql", _("Cannot connect to PhpMyAdmin")); + return false; + } + $db->next_record(); + $info = array( + "login" => $db->f("name"), + "pass" => $db->f("password"), + "host" => $db->f("host") + ); + return $info; + } + + /* --------------------------------------------------------------------------- */ + + /** Returns the details of a user's database. + * $dbn is the name of the database (after the _) or nothing for the database "$user" + * @return string returns an associative array as follow : + * "db" => Name of the database + * "bck" => Current bckup mode + * "dir" => Backup directory + * "size" => Size of the database (in bytes) + * "pass" => Password of the user + * "history" => Number of backup we keep + * "gzip" => Does we compress the dumps ? + * Returns FALSE if the user has no database of if the database does not exist. + */ + function get_mysql_details($dbn) { + global $db, $err, $cuid; + $root = getuserpath(); + $err->log("mysql", "get_mysql_details"); + $pos = strpos($dbn, '_'); + if ($pos === false) { + $dbname = $dbn; + } else { + $dbncomp = explode('_', $dbn); + $dbname = $dbn; + $dbn = $dbncomp[1]; + } + $size = $this->get_db_size($dbname); + $db->query("SELECT login,pass,db, bck_mode, bck_gzip, bck_dir, bck_history FROM db WHERE uid='$cuid' AND db='$dbname';"); + if (!$db->num_rows()) { + $err->raise("mysql", _("Database %s not found"), $dbn); + return array("enabled" => false); + } + $db->next_record(); + list($dbu, $dbn) = split_mysql_database_name($db->f("db")); + return array("enabled" => true, "login" => $db->f("login"), "db" => $db->f("db"), "name" => $dbn, "bck" => $db->f("bck_mode"), "dir" => substr($db->f("bck_dir"), strlen($root)), "size" => $size, "pass" => $db->f("pass"), "history" => $db->f("bck_history"), "gzip" => $db->f("bck_gzip")); + } + + /* --------------------------------------------------------------------------- */ + + /** Create a new database for the current user. + * @param $dbn string Database name ($user_$dbn is the mysql db name) + * @return boolean if the database $user_$db has been successfully created, or FALSE if + * an error occured, such as over quota user. + */ + function add_db($dbn) { + global $db, $err, $quota, $cuid; + $err->log("mysql", "add_db", $dbn); + $password_user = ""; + if (!$quota->cancreate("mysql")) { + $err->raise("mysql", _("Your databases quota is over. You cannot create more databases")); + return false; + } + $pos = strpos($dbn, '_'); + if ($pos === false) { + $dbname = $dbn; + } else { + $dbncomp = explode('_', $dbn); + $dbname = $dbn; + $dbn = $dbncomp[1]; + if (empty($dbn)) { // If nothing after the '_' + $err->raise("mysql", _("Database can't have empty suffix")); + return false; + } + } + if (!preg_match("#^[0-9a-z]*$#", $dbn)) { + $err->raise("mysql", _("Database name can contain only letters and numbers")); + return false; + } + + if (strlen($dbname) > 64) { + $err->raise("mysql", _("Database name cannot exceed 64 characters")); + return false; + } + $db->query("SELECT * FROM db WHERE db='$dbname';"); + if ($db->num_rows()) { + $err->raise("mysql", _("Database %s already exists"), $dbn); + return false; + } + + $db->query("SELECT name from dbusers where name='" . $dbname . "' and enable='ACTIVATED' ;"); + if (!$db->num_rows()) { + $password_user = create_pass(8); + if (!$this->add_user($dbn, $password_user, $password_user)) { + + } + } + + //checking for the phpmyadmin user + $db->query("SELECT * FROM dbusers WHERE uid=$cuid AND enable='ADMIN';"); + if ($db->num_rows()) { + $db->next_record(); + $myadm = $db->f("name"); + $password = $db->f("password"); + } else { + $err->raise("mysql", _("There is a problem with the special PhpMyAdmin user. Contact the administrator")); + return false; + } + + //Grant the special user every rights. + if ($this->dbus->query("CREATE DATABASE `$dbname`;")) { + $err->log("mysql", "add_db_succes", $dbn); + // Ok, database does not exist, quota is ok and dbname is compliant. Let's proceed + $db->query("INSERT INTO db (uid,login,pass,db,bck_mode) VALUES ('$cuid','$myadm','$password','$dbname',0);"); + $dbuser = $dbname; + $dbname = str_replace('_', '\_', $dbname); + $this->grant($dbname, $myadm, "ALL PRIVILEGES", $password); + if (!empty($password_user)) { + $this->grant($dbname, $dbuser, "ALL PRIVILEGES", $password_user); + } + $this->dbus->query("FLUSH PRIVILEGES;"); + return true; + } else { + $err->log("mysql", "add_db", $dbn); + $err->raise("mysql", _("An error occured. The database could not be created")); + return false; + } + } + + /* --------------------------------------------------------------------------- */ + + /** Delete a database for the current user. + * @param $dbn string Name of the database to delete. The db name is $user_$dbn + * @return boolean if the database $user_$db has been successfully deleted, or FALSE if + * an error occured, such as db does not exist. + */ + function del_db($dbn) { + global $db, $err, $cuid; + $err->log("mysql", "del_db", $dbn); + $dbname = addslashes($dbn); + $db->query("SELECT uid FROM db WHERE db='$dbname';"); + if (!$db->num_rows()) { + $err->raise("mysql", _("The database was not found. I can't delete it")); + return false; + } + $db->next_record(); + + // Ok, database exists and dbname is compliant. Let's proceed + $db->query("DELETE FROM size_db WHERE db='$dbname';"); + $db->query("DELETE FROM db WHERE uid='$cuid' AND db='$dbname';"); + $this->dbus->query("DROP DATABASE `$dbname`;"); + + $db_esc = str_replace('_', '\_', $dbname); + $this->dbus->query("DELETE FROM mysql.db WHERE Db='$db_esc';"); + + #We test if the user created with the database is associated with more than 1 database. + $this->dbus->query("select User from mysql.db where User='" . $dbname . "' and (Select_priv='Y' or Insert_priv='Y' or Update_priv='Y' or Delete_priv='Y' or Create_priv='Y' or Drop_priv='Y' or References_priv='Y' or Index_priv='Y' or Alter_priv='Y' or Create_tmp_table_priv='Y' or Lock_tables_priv='Y');"); + if (($this->dbus->num_rows()) == 0) { + #If not we can delete it. + $this->del_user($dbname); + } + return true; + } + + /* --------------------------------------------------------------------------- */ + + /** Set the backup parameters for the database $db + * @param $dbn string database name + * @param $bck_mode integer Backup mode (0 = none 1 = daily 2 = weekly) + * @param $bck_history integer How many backup should we keep ? + * @param $bck_gzip boolean shall we compress the backup ? + * @param $bck_dir string Directory relative to the user account where the backup will be stored + * @return boolean true if the backup parameters has been successfully changed, false if not. + */ + function put_mysql_backup($dbn, $bck_mode, $bck_history, $bck_gzip, $bck_dir) { + global $db, $err, $bro, $cuid; + $err->log("mysql", "put_mysql_backup"); + + if (!variable_get('sql_allow_users_backups')) { + $err->raise("mysql", _("User aren't allowed to configure their backups")); + return false; + } + + $pos = strpos($dbn, '_'); + if ($pos === false) { + $dbname = $dbn; + } else { + $dbncomp = explode('_', $dbn); + $dbname = $dbn; + $dbn = $dbncomp[1]; + } + if (!preg_match("#^[0-9a-z]*$#", $dbn)) { + $err->raise("mysql", _("Database name can contain only letters and numbers")); + return false; + } + $db->query("SELECT * FROM db WHERE uid='$cuid' AND db='$dbname';"); + if (!$db->num_rows()) { + $err->raise("mysql", _("Database %s not found"), $dbn); + return false; + } + $db->next_record(); + $bck_mode = intval($bck_mode); + $bck_history = intval($bck_history); + if ($bck_gzip) { + $bck_gzip = "1"; + } else { + $bck_gzip = "0"; + } + if (!$bck_mode) { + $bck_mode = "0"; + } + if (!$bck_history) { + $err->raise("mysql", _("You have to choose how many backups you want to keep")); + return false; + } + if (($bck_dir = $bro->convertabsolute($bck_dir, 0)) === false) { // return a full path or FALSE + $err->raise("mysql", _("Directory does not exist")); + return false; + } + $db->query("UPDATE db SET bck_mode='$bck_mode', bck_history='$bck_history', bck_gzip='$bck_gzip', bck_dir='$bck_dir' WHERE uid='$cuid' AND db='$dbname';"); + return true; + } + + /* --------------------------------------------------------------------------- */ + + /** Change the password of the user in MySQL + * @param $password string new password (cleartext) + * @return boolean TRUE if the password has been successfully changed, FALSE else. + */ + function put_mysql_details($password) { + global $db, $err, $cuid, $admin; + $err->log("mysql", "put_mysql_details"); + $db->query("SELECT * FROM db WHERE uid='$cuid';"); + if (!$db->num_rows()) { + $err->raise("mysql", _("Database not found")); + return false; + } + $db->next_record(); + $login = $db->f("login"); + + if (!$password) { + $err->raise("mysql", _("The password is mandatory")); + return false; + } + + if (strlen($password) > 16) { + $err->raise("mysql", _("MySQL password cannot exceed 16 characters")); + return false; + } + + // Check this password against the password policy using common API : + if (is_callable(array($admin, "checkPolicy"))) { + if (!$admin->checkPolicy("mysql", $login, $password)) { + return false; // The error has been raised by checkPolicy() + } + } + + // Update all the "pass" fields for this user : + $db->query("UPDATE db SET pass='$password' WHERE uid='$cuid';"); + $this->dbus->query("SET PASSWORD FOR " . $login . "@" . $this->dbus->Client . " = PASSWORD('$password');"); + return true; + } + + /** + * Function used to grant SQL rights to users: + * @base :database + * @user : database user + * @rights : rights to apply ( optional, every rights apply given if missing + * @pass : user password ( optional, if not given the pass stays the same, else it takes the new value ) + * @table : sql tables to apply rights + * */ + function grant($base, $user, $rights = null, $pass = null, $table = '*') { + global $err, $db; + $err->log("mysql", "grant", $base . "-" . $rights . "-" . $user); + + if (!preg_match("#^[0-9a-z_\\*\\\\]*$#", $base)) { + $err->raise("mysql", _("Database name can contain only letters and numbers")); + return false; + } elseif (!$this->dbus->query("select db from db where db='$base';")) { + $err->raise("mysql", _("Database not found")); + return false; + } + + if ($rights == null) { + $rights = 'ALL PRIVILEGES'; + } elseif (!preg_match("#^[a-zA-Z,\s]*$#", $rights)) { + $err->raise("mysql", _("Databases rights are not correct")); + return false; + } + + if (!preg_match("#^[0-9a-z]#", $user)) { + $err->raise("mysql", _("The username can contain only letters and numbers.")); + return false; + } + $db->query("select name from dbusers where name='" . $user . "' ;"); + + if (!$db->num_rows()) { + $err->raise("mysql", _("Database user not found")); + return false; + } + + # Protect database name if not wildcard + if ($base != '*') { + $base = "`" . $base . "`"; + } + $grant = "grant " . $rights . " on " . $base . "." . $table . " to '" . $user . "'@'" . $this->dbus->Client . "'"; + + if ($pass) { + $grant .= " identified by '" . $pass . "';"; + } else { + $grant .= ";"; + } + if (!$this->dbus->query($grant)) { + $err->raise("mysql", _("Could not grant rights")); + return false; + } + return true; + } + + /* ----------------------------------------------------------------- */ + + /** Restore a sql database. + * @param $file string The filename, relative to the user root dir, which contains a sql dump + * @param $stdout boolean shall-we dump the error to stdout ? + * @param $id integer The ID of the database to dump to. + * @return boolean TRUE if the database has been restored, or FALSE if an error occurred + */ + function restore($file, $stdout, $id) { + global $err, $bro; + if (empty($file)) { + $err->raise("mysql", _("No file specified")); + return false; + } + if (!$r = $this->get_mysql_details($id)) { + return false; + } + if (!($fi = $bro->convertabsolute($file, 0))) { + $err->raise("mysql", _("File not found")); + return false; + } + if (!file_exists($fi)) { + $err->raise("mysql", _("File not found")); + return false; + } + + if (substr($fi, -3) == ".gz") { + $exe = "/bin/gzip -d -c <" . escapeshellarg($fi) . " | /usr/bin/mysql -h" . escapeshellarg($this->dbus->Host) . " -u" . escapeshellarg($r["login"]) . " -p" . escapeshellarg($r["pass"]) . " " . escapeshellarg($r["db"]); + } elseif (substr($fi, -4) == ".bz2") { + $exe = "/usr/bin/bunzip2 -d -c <" . escapeshellarg($fi) . " | /usr/bin/mysql -h" . escapeshellarg($this->dbus->Host) . " -u" . escapeshellarg($r["login"]) . " -p" . escapeshellarg($r["pass"]) . " " . escapeshellarg($r["db"]); + } else { + $exe = "/usr/bin/mysql -h" . escapeshellarg($this->dbus->Host) . " -u" . escapeshellarg($r["login"]) . " -p" . escapeshellarg($r["pass"]) . " " . escapeshellarg($r["db"]) . " <" . escapeshellarg($fi); + } + $exe .= " 2>&1"; + + echo ""; + if ($ret != 0) { + return false; + } else { + return true; + } + } + + /* ----------------------------------------------------------------- */ + + /** Get the size of a database + * @param $dbname name of the database + * @return integer database size + * @access private + */ + function get_db_size($dbname) { + global $db; + + $this->dbus->query("SHOW TABLE STATUS FROM `$dbname`;"); + $size = 0; + while ($db->next_record()) { + $size += $db->f('Data_length') + $db->f('Index_length'); + if ($db->f('Engine') != 'InnoDB') { + $size += $db->f('Data_free'); + } + } + return $size; + } + + /* ------------------------------------------------------------ */ + + /** + * Returns the list of database users of an account + * */ + function get_userslist($all = null) { + global $db, $err, $cuid; + $err->log("mysql", "get_userslist"); + $c = array(); + if (!$all) { + $db->query("SELECT name FROM dbusers WHERE uid='$cuid' and enable not in ('ADMIN','HIDDEN') ORDER BY name;"); + } else { + $db->query("SELECT name FROM dbusers WHERE uid='$cuid' ORDER BY name;"); + } + while ($db->next_record()) { + $pos = strpos($db->f("name"), "_"); + if ($pos === false) { + $c[] = array("name" => ($db->f("name"))); + } else { + $c[] = array("name" => ($db->f("name"))); + //$c[]=array("name"=>substr($db->f("name"),strpos($db->f("name"),"_")+1)); + } + } + + return $c; + } + + function get_defaultsparam($dbn) { + global $db, $err, $cuid; + $err->log("mysql", "getdefaults"); + + $dbu = $dbn; + $r = array(); + $dbn = str_replace('_', '\_', $dbn); + $this->dbus->query("Select * from mysql.db where Db='" . $dbn . "' and User!='" . $cuid . "_myadm';"); + + if (!$db->num_rows()) { + return $r; + } + while ($db->next_record()) { + $variable = $db->Record; + if ($variable['User'] == $dbu) { + $r['Host'] = $db->f('Host'); + + if ($db->f('Select_priv') !== "Y") { + return $r; + } + if ($db->f('Insert_priv') !== "Y") { + return $r; + } + if ($db->f('Update_priv') !== "Y") { + return $r; + } + if ($db->f('Delete_priv') !== "Y") { + return $r; + } + if ($db->f('Create_priv') !== "Y") { + return $r; + } + if ($db->f('Drop_priv') !== "Y") { + return $r; + } + if ($db->f('References_priv') !== "Y") { + return $r; + } + if ($db->f('Index_priv') !== "Y") { + return $r; + } + if ($db->f('Alter_priv') !== "Y") { + return $r; + } + if ($db->f('Create_tmp_table_priv') !== "Y") { + return $r; + } + if ($db->f('Lock_tables_priv') !== "Y") { + return $r; + } + if ($db->f('Create_view_priv') !== "Y") { + return $r; + } + if ($db->f('Show_view_priv') !== "Y") { + return $r; + } + if ($db->f('Create_routine_priv') !== "Y") { + return $r; + } + if ($db->f('Alter_routine_priv') !== "Y") { + return $r; + } + if ($db->f('Execute_priv') !== "Y") { + return $r; + } + if ($db->f('Event_priv') !== "Y") { + return $r; + } + if ($db->f('Trigger_priv') !== "Y") { + return $r; + } + } + } //endwhile + if (!$db->query("SELECT name,password from dbusers where name='" . $dbu . "';")) { + return $r; + } + + if (!$db->num_rows()) { + return $r; + } + $db->next_record(); + $r['user'] = $db->f('name'); + $r['password'] = $db->f('password'); + return $r; + } + + /* ------------------------------------------------------------ */ + + /** + * Create a new user in MySQL rights tables + * @param $usern the username (we will add _[alternc-account] to it) + * @param string $password The password for this username + * @param string $passconf The password confirmation + * @return boolean if the user has been created in MySQL or FALSE if an error occurred + * */ + function add_user($usern, $password, $passconf) { + global $db, $err, $mem, $cuid, $admin; + $err->log("mysql", "add_user", $usern); + + $usern = trim($usern); + $login = $mem->user["login"]; + if ($login != $usern) { + $user = addslashes($login . "_" . $usern); + } else { + $user = $usern; + } + $pass = addslashes($password); + + if (!$usern) { + $err->raise("mysql", _("The username is mandatory")); + return false; + } + if (!$pass) { + $err->raise("mysql", _("The password is mandatory")); + return false; + } + if (!preg_match("#^[0-9a-z]#", $usern)) { + $err->raise("mysql", _("The username can contain only letters and numbers")); + return false; + } + + // We check the length of the COMPLETE username, not only the part after _ + if (strlen($user) > 16) { + $err->raise("mysql", _("MySQL username cannot exceed 16 characters")); + return false; + } + $db->query("SELECT * FROM dbusers WHERE name='$user';"); + if ($db->num_rows()) { + $err->raise("mysql", _("The database user already exists")); + return false; + } + if ($password != $passconf || !$password) { + $err->raise("mysql", _("The passwords do not match")); + return false; + } + + // Check this password against the password policy using common API : + if (is_callable(array($admin, "checkPolicy"))) { + if (!$admin->checkPolicy("mysql", $user, $password)) { + return false; // The error has been raised by checkPolicy() + } + } + + // We add him to the user table + $db->query("INSERT INTO dbusers (uid,name,password,enable) VALUES($cuid,'$user','$password','ACTIVATED');"); + + $this->grant("*", $user, "USAGE", $pass); + return true; + } + + /* ------------------------------------------------------------ */ + + /** + * Change a user's MySQL password + * @param $usern the username + * @param $password The password for this username + * @param $passconf The password confirmation + * @return boolean if the password has been changed in MySQL or FALSE if an error occurred + * */ + function change_user_password($usern, $password, $passconf) { + global $db, $err, $cuid, $admin; + $err->log("mysql", "change_user_pass", $usern); + + $usern = trim($usern); + $user = addslashes($usern); + $pass = addslashes($password); + if ($password != $passconf || !$password) { + $err->raise("mysql", _("The passwords do not match")); + return false; + } + + // Check this password against the password policy using common API : + if (is_callable(array($admin, "checkPolicy"))) { + if (!$admin->checkPolicy("mysql", $user, $password)) { + return false; // The error has been raised by checkPolicy() + } + } + $this->dbus->query("SET PASSWORD FOR '" . $user . "'@'" . $this->dbus->Client . "' = PASSWORD('" . $pass . "');"); + $db->query("UPDATE dbusers set password='" . $pass . "' where name='" . $usern . "' and uid=$cuid ;"); + return true; + } + + /* ------------------------------------------------------------ */ + + /** + * Delete a user in MySQL rights tables + * @param $user the username (we will add "[alternc-account]_" to it) to delete + * @param integer $all + * @return boolean if the user has been deleted in MySQL or FALSE if an error occurred + * */ + function del_user($user, $all = null) { + global $db, $err, $cuid; + $err->log("mysql", "del_user", $user); + if (!preg_match("#^[0-9a-z]#", $user)) { + $err->raise("mysql", _("The username can contain only letters and numbers")); + return false; + } + if (!$all) { + $db->query("SELECT name FROM dbusers WHERE name='" . $user . "' and enable not in ('ADMIN','HIDDEN');"); + } else { + $db->query("SELECT name FROM dbusers WHERE uid='" . $cuid . "' ;"); + } + + if (!$db->num_rows()) { + $err->raise("mysql", _("The username was not found")); + return false; + } + $db->next_record(); + $login = $db->f("name"); + + // Ok, database exists and dbname is compliant. Let's proceed + $this->dbus->query("REVOKE ALL PRIVILEGES ON *.* FROM '" . $user . "'@'" . $this->dbus->Client . "';"); + $this->dbus->query("DELETE FROM mysql.db WHERE User='" . $user . "' AND Host='" . $this->dbus->Client . "';"); + $this->dbus->query("DELETE FROM mysql.user WHERE User='" . $user . "' AND Host='" . $this->dbus->Client . "';"); + $this->dbus->query("FLUSH PRIVILEGES"); + + $db->query("DELETE FROM dbusers WHERE uid='$cuid' AND name='" . $user . "';"); + return true; + } + + /* ------------------------------------------------------------ */ + + /** + * Return the list of the database rights of user $user + * @param $user the username + * @return array An array of database name and rights + * */ + function get_user_dblist($user) { + global $db, $err; + + $this->dbus->query("SELECT * FROM mysql.user WHERE User='" . $user . "' AND Host='" . $this->dbus->Client . "';"); + if (!$this->dbus->next_record()) { + $err->raise('mysql', _("This user does not exist in the MySQL/User database")); + return false; + } + + $r = array(); + $db->free(); + $dblist = $this->get_dblist(); + foreach ($dblist as $tab) { + $pos = strpos($tab['db'], "_"); + if ($pos === false) { + $this->dbus->query("SELECT * FROM mysql.db WHERE User='" . $user . "' AND Host='" . $this->dbus->Client . "' AND Db='" . $tab["db"] . "';"); + } else { + $dbname = str_replace('_', '\_', $tab['db']); + $this->dbus->query("SELECT * FROM mysql.db WHERE User='" . $user . "' AND Host='" . $this->dbus->Client . "' AND Db='" . $dbname . "';"); + } + if ($this->dbus->next_record()) { + $r[] = array("db" => $tab["db"], "select" => $this->dbus->f("Select_priv"), "insert" => $this->dbus->f("Insert_priv"), "update" => $this->dbus->f("Update_priv"), "delete" => $this->dbus->f("Delete_priv"), "create" => $this->dbus->f("Create_priv"), "drop" => $this->dbus->f("Drop_priv"), "references" => $this->dbus->f("References_priv"), "index" => $this->dbus->f("Index_priv"), "alter" => $this->dbus->f("Alter_priv"), "create_tmp" => $this->dbus->f("Create_tmp_table_priv"), "lock" => $this->dbus->f("Lock_tables_priv"), + "create_view" => $this->dbus->f("Create_view_priv"), + "show_view" => $this->dbus->f("Show_view_priv"), + "create_routine" => $this->dbus->f("Create_routine_priv"), + "alter_routine" => $this->dbus->f("Alter_routine_priv"), + "execute" => $this->dbus->f("Execute_priv"), + "event" => $this->dbus->f("Event_priv"), + "trigger" => $this->dbus->f("Trigger_priv") + ); + } else { + $r[] = array("db" => $tab['db'], "select" => "N", "insert" => "N", "update" => "N", "delete" => "N", "create" => "N", "drop" => "N", "references" => "N", "index" => "N", "alter" => "N", "create_tmp" => "N", "lock" => "N", "create_view" => "N", "show_view" => "N", "create_routine" => "N", "alter_routine" => "N", "execute" => "N", "event" => "N", "trigger" => "N"); + } + } + return $r; + } + + /* ------------------------------------------------------------ */ + + /** + * Set the access rights of user $user to database $dbn to be rights $rights + * @param $user the username to give rights to + * @param $dbn The database to give rights to + * @param $rights The rights as an array of MySQL keywords (insert, select ...) + * @return boolean TRUE if the rights has been applied or FALSE if an error occurred + * + * */ + function set_user_rights($user, $dbn, $rights) { + global $err; + $err->log("mysql", "set_user_rights"); + + $usern = addslashes($user); + $dbname = addslashes($dbn); + $dbname = str_replace('_', '\_', $dbname); + // On genere les droits en fonction du tableau de droits + $strrights = ""; + for ($i = 0; $i < count($rights); $i++) { + switch ($rights[$i]) { + case "select": + $strrights.="SELECT,"; + break; + case "insert": + $strrights.="INSERT,"; + break; + case "update": + $strrights.="UPDATE,"; + break; + case "delete": + $strrights.="DELETE,"; + break; + case "create": + $strrights.="CREATE,"; + break; + case "drop": + $strrights.="DROP,"; + break; + case "references": + $strrights.="REFERENCES,"; + break; + case "index": + $strrights.="INDEX,"; + break; + case "alter": + $strrights.="ALTER,"; + break; + case "create_tmp": + $strrights.="CREATE TEMPORARY TABLES,"; + break; + case "lock": + $strrights.="LOCK TABLES,"; + break; + case "create_view": + $strrights.="CREATE VIEW,"; + break; + case "show_view": + $strrights.="SHOW VIEW,"; + break; + case "create_routine": + $strrights.="CREATE ROUTINE,"; + break; + case "alter_routine": + $strrights.="ALTER ROUTINE,"; + break; + case "execute": + $strrights.="EXECUTE,"; + break; + case "event": + $strrights.="EVENT,"; + break; + case "trigger": + $strrights.="TRIGGER,"; + break; + } + } + + // We reset all user rights on this DB : + $this->dbus->query("SELECT * FROM mysql.db WHERE User = '$usern' AND Db = '$dbname';"); + if ($this->dbus->num_rows()) { + $this->dbus->query("REVOKE ALL PRIVILEGES ON `$dbname`.* FROM '$usern'@'" . $this->dbus->Client . "';"); + } + if ($strrights) { + $strrights = substr($strrights, 0, strlen($strrights) - 1); + $this->grant($dbname, $usern, $strrights); + } + $this->dbus->query("FLUSH PRIVILEGES"); + return TRUE; + } + + function available_sql_rights() { + return Array('select', 'insert', 'update', 'delete', 'create', 'drop', 'references', 'index', 'alter', 'create_tmp', 'lock', 'create_view', 'show_view', 'create_routine', 'alter_routine', 'execute', 'event', 'trigger'); + } + + /* ----------------------------------------------------------------- */ + + /** Hook function called by the lxc class to set mysql_host and port + * parameters + * @access private + */ + function hook_lxc_params($params) { + global $err; + $err->log("mysql", "alternc_get_quota"); + $p = array(); + if (isset($this->dbus["Host"]) && $this->dbus["Host"] != "") { + $p["mysql_host"] = $this->dbus["Host"]; + $p["mysql_port"] = 3306; + } + return $p; + } + + /* ----------------------------------------------------------------- */ + + /** Hook function called by the quota class to compute user used quota + * Returns the used quota for the $name service for the current user. + * @param $name string name of the quota + * @return integer the number of service used or false if an error occured + * @access private + */ + function hook_quota_get() { + global $err; + $err->log("mysql", "alternc_get_quota"); + $q = Array("name" => "mysql", "description" => _("MySQL Databases"), "used" => 0); + $c = $this->get_dblist(); + if (is_array($c)) { + $q['used'] = count($c); + } + return $q; + } + + /* ----------------------------------------------------------------- */ + + /** Hook function called when a user is created. + * AlternC's standard function that create a member + * @access private + */ + function alternc_add_member() { + global $db, $err, $cuid, $mem; + $err->log("mysql", "alternc_add_member"); + //checking for the phpmyadmin user + $db->query("SELECT name,password FROM dbusers WHERE uid=$cuid AND Type='ADMIN';"); + if ($db->num_rows()) { + $myadm = $db->f("name"); + $password = $db->f("password"); + } else { + $myadm = $cuid . "_myadm"; + $password = create_pass(8); + } + + + $db->query("INSERT INTO dbusers (uid,name,password,enable) VALUES ('$cuid','$myadm','$password','ADMIN');"); + + return true; + } + + /* ----------------------------------------------------------------- */ + + /** Hook function called when a user is deleted. + * AlternC's standard function that delete a member + * @access private + */ + function alternc_del_member() { + global $err; + $err->log("mysql", "alternc_del_member"); + $c = $this->get_dblist(); + if (is_array($c)) { + for ($i = 0; $i < count($c); $i++) { + $this->del_db($c[$i]["name"]); + } + } + $d = $this->get_userslist(1); + if (!empty($d)) { + for ($i = 0; $i < count($d); $i++) { + $this->del_user($d[$i]["name"], 1); + } + } + return true; + } + + /* ----------------------------------------------------------------- */ + + /** Hook function called when a user is logged out. + * We just remove the cookie created in admin/sql_admin.php + a @access private + */ + function alternc_del_session() { + $_SESSION['PMA_single_signon_user'] = ''; + $_SESSION['PMA_single_signon_password'] = ''; + $_SESSION['PMA_single_signon_host'] = ''; + } + + /* ----------------------------------------------------------------- */ + + /** + * Exporte all the mysql information of an account + * @access private + * EXPERIMENTAL 'sid' function ;) + */ + function alternc_export_conf() { + //TODO don't work with separated sql server for dbusers + global $db, $err, $cuid; + $err->log("mysql", "export"); + $db->query("SELECT login, pass, db, bck_mode, bck_dir, bck_history, bck_gzip FROM db WHERE uid='$cuid';"); + $str = ""; + if ($db->next_record()) { + $str.="
"; + $ret = 0; + if ($stdout) { + passthru($exe, $ret); + } else { + exec($exe, $ret); + } + echo "\n"; + $str.=" \n"; + } + return $str; + } + + /* ----------------------------------------------------------------- */ + + /** + * Exporte all the mysql databases a of give account to $dir directory + * @access private + * EXPERIMENTAL 'sid' function ;) + */ + function alternc_export_data($dir) { + global $db, $err, $cuid; + $err->log("mysql", "export_data"); + $db->query("SELECT db.login, db.pass, db.db, dbusers.name FROM db,dbusers WHERE db.uid='$cuid' AND dbusers.uid=db.uid;"); + $dir.="sql/"; + if (!is_dir($dir)) { + if (!mkdir($dir)) { + $err->raise('mysql', _("The directory could not be created")); + } + } + // on exporte toutes les bases utilisateur. + while ($db->next_record()) { + $filename = $dir . "mysql." . $db->Record["db"] . "." . date("H:i:s") . ".sql.gz"; + exec("/usr/bin/mysqldump --defaults-file=/etc/alternc/my.cnf --add-drop-table --allow-keywords -Q -f -q -a -e " . escapeshellarg($db->Record["db"]) . " |/bin/gzip >" . escapeshellarg($filename)); + } + } + + /* ----------------------------------------------------------------- */ + + /** + * Return the size of each databases in a SQL Host given in parameter + * @param $db_name the human name of the host + * @param $db_host the host hosting the SQL databases + * @param $db_login the login to access the SQL db + * @param $db_password the password to access the SQL db + * @param $db_client the client to access the SQL db + * @return an array associating the name of the databases to their sizes : array(dbname=>size) + */ + function get_dbus_size($db_name, $db_host, $db_login, $db_password, $db_client) { + global $err; + $err->log("mysql", "get_dbus_size", $db_host); + + # We create the object with empty parameters + $this->dbus = new DB_users(true); + # Modify the object with right parameters + $this->dbus->HumanHostname = $db_name; + $this->dbus->Host = $db_host; + $this->dbus->User = $db_login; + $this->dbus->Password = $db_password; + $this->dbus->Client = $db_client; + + $this->dbus->query("show databases;"); + $res = array(); + while ($this->dbus->next_record()) { + $dbname = $this->dbus->f("Database"); + $c = mysql_query("SHOW TABLE STATUS FROM $dbname;"); + $size = 0; + while ($d = mysql_fetch_array($c)) { + $size+=$d["Data_length"] + $d["Index_length"]; + } + $res["$dbname"] = "$size"; + } + return $res; + } + +} + +/* Class m_mysql */ diff --git a/bureau/class/m_quota.php b/bureau/class/m_quota.php index 68ea15cb..8554cf91 100644 --- a/bureau/class/m_quota.php +++ b/bureau/class/m_quota.php @@ -1,4 +1,5 @@ disk_quota_enable = variable_get('disk_quota_enable', 1,'Are disk quota enabled for this server', array('desc'=>'Enabled','type'=>'boolean')); - if ( $this->disk_quota_enable ) { - $this->disk = Array( "web"=>"web" ); + /** + * Constructor + */ + function m_quota() { + $this->disk_quota_enable = variable_get('disk_quota_enable', 1, 'Are disk quota enabled for this server', array('desc' => 'Enabled', 'type' => 'boolean')); + if ($this->disk_quota_enable) { + $this->disk = Array("web" => "web"); + } } - } - - private function dummy_for_translation() { - _("quota_web"); - } - - function hook_menu() { - $obj = array( - 'title' => _("Show my quotas"), - 'ico' => 'images/quota.png', - 'link' => 'toggle', - 'pos' => 110, - 'divclass' => 'menu-quota', - 'links' => array(), - ) ; - - $q=$this->getquota(); - - foreach ( array('web', 'bw_web') as $key ) { - if ( ! isset($q[$key]["u"]) || empty($q[$key]["t"])) continue; - $usage_percent = (int) ($q[$key]["u"] / $q[$key]["t"] * 100); - $obj['links'][] = array( 'txt'=>_("quota_".$key) . " " . sprintf(_("%s%% of %s"),$usage_percent,format_size($q[$key]["t"]*1024)), 'url'=>($key == 'bw_web' ? 'stats_show_per_month.php' : 'quota_show.php') ); - $obj['links'][] = array( 'txt'=>'progressbar', 'total' => $q[$key]["t"], 'used' => $q[$key]["u"]); - } - - return $obj; - } - - /* ----------------------------------------------------------------- */ - /** Check if a user can use a ressource. - * @param string $ressource the ressource name (a named quota) - * @Return TRUE if the user can create a ressource (= is there any quota left ?) - * @return boolean - */ - function cancreate($ressource="") { - $t=$this->getquota($ressource); - return $t["u"]<$t["t"]; - } - - - /* ----------------------------------------------------------------- */ - /** List the quota-managed services in the server - * @Return array the quota names and description (translated) - */ - function qlist() { - global $classes,$hooks; - $qlist=array(); - reset($this->disk); - while (list($key,$val)=each($this->disk)) { - $qlist[$key]=_("quota_".$key); // those are specific disks quotas. + private function dummy_for_translation() { + _("quota_web"); } - foreach($this->getquota() as $qq) { - if (isset($qq['name'])) { - $qlist[$qq['name']]=$qq['description']; - } - } - return $qlist; - } - + function hook_menu() { + $obj = array( + 'title' => _("Show my quotas"), + 'ico' => 'images/quota.png', + 'link' => 'toggle', + 'pos' => 110, + 'divclass' => 'menu-quota', + 'links' => array(), + ); - /** - * Synchronise the quotas of the users with the quota of the - * user's profile. - * If the user have a greater quota than the profile, no change. - * If the quota entry doesn't exist for the user, create it with - * the defaults value. - */ - function synchronise_user_profile() { - global $db,$err; - $err->log("quota","synchronise_user_profile"); - $q="insert into quotas select m.uid as uid, d.quota as name, d.value as total from membres m, defquotas d left join quotas q on q.name=d.quota where m.type=d.type ON DUPLICATE KEY UPDATE total = greatest(d.value, quotas.total);"; - if (!$db->query($q)) return false; - return true; - } + $q = $this->getquota(); - /* - * Create default quota in the profile - * when a new quota appear - * - */ - function create_missing_quota_profile() { - global $db,$quota,$err; - $err->log("quota","create_missing_quota_profile"); - $qt=$quota->getquota('',true); - $type=$quota->listtype(); - foreach($type as $t) { - foreach($qt as $q=>$vv) { - $db->query("INSERT IGNORE defquotas (value,quota,type) VALUES (0,'$q','$t');"); - } - } - return true; - } - - /* ----------------------------------------------------------------- */ - /** Return a ressource usage (u) and total quota (t) - * @param string $ressource ressource to get quota of - * @Return array the quota used and total for this ressource (or for all ressource if unspecified) - */ - function getquota($ressource="",$recheck=false) { - global $db,$err,$cuid,$get_quota_cache,$hooks,$mem; - $err->log("quota","getquota",$ressource); - if ($recheck) { // rebuilding quota - $get_quota_cache=null; - $this->quotas=array(); - } - if (! empty($get_quota_cache[$cuid]) ) { - // This function is called many time each webpage, so I cache the result - $this->quotas = $get_quota_cache[$cuid]; - } else { - $res=$hooks->invoke("hook_quota_get"); - foreach($res as $r) { - $this->quotas[$r['name']]=$r; - $this->quotas[$r['name']]['u']=$r['used']; // retrocompatibilité - $this->quotas[$r['name']]['t']=0; // Default quota = 0 - } - reset($this->disk); - - if (!empty ($this->disk)) { // Check if there are some disk quota to check - // Look if there are some cached value - $disk_cached = $mem->session_tempo_params_get('quota_cache_disk'); - - while (list($key,$val)=each($this->disk)) { - $a=array(); - if ( - isset($disk_cached[$val]) - && !empty($disk_cached[$val]) - && $disk_cached[$val]['uid'] == $cuid - && $disk_cached[$val]['timestamp'] > ( time() - (90) ) // Cache, en seconde - ) { - // If there is a cached value - $a = $disk_cached[$val]; - } else { - exec("/usr/lib/alternc/quota_get ".intval($cuid) ,$ak); - $a['u']=intval($ak[0]); - $a['t']=@intval($ak[1]); - $a['timestamp'] = time(); - $a['uid'] = $cuid; - $disk_cached = $mem->session_tempo_params_set('quota_cache_disk', array($val=>$a)); + foreach (array('web', 'bw_web') as $key) { + if (!isset($q[$key]["u"]) || empty($q[$key]["t"])) { + continue; } - $this->quotas[$val]=array("name"=>"$val", 'description'=>_("quota_".$val), "t"=>$a['t'],"u"=>$a['u']); - } + $usage_percent = (int) ($q[$key]["u"] / $q[$key]["t"] * 100); + $obj['links'][] = array('txt' => _("quota_" . $key) . " " . sprintf(_("%s%% of %s"), $usage_percent, format_size($q[$key]["t"] * 1024)), 'url' => ($key == 'bw_web' ? 'stats_show_per_month.php' : 'quota_show.php')); + $obj['links'][] = array('txt' => 'progressbar', 'total' => $q[$key]["t"], 'used' => $q[$key]["u"]); } - // Get the allowed quota from database. - $db->query("select name, total from quotas where uid='$cuid';"); - while ( $db->next_record() ) { - $this->quotas[$db->f('name')]['t']=$db->f('total'); + return $obj; + } + + /* ----------------------------------------------------------------- */ + + /** Check if a user can use a ressource. + * @param string $ressource the ressource name (a named quota) + * @Return TRUE if the user can create a ressource (= is there any quota left ?) + * @return boolean + */ + function cancreate($ressource = "") { + $t = $this->getquota($ressource); + return $t["u"] < $t["t"]; + } + + /* ----------------------------------------------------------------- */ + + /** List the quota-managed services in the server + * @Return array the quota names and description (translated) + */ + function qlist() { + $qlist = array(); + reset($this->disk); + while (list($key, $val) = each($this->disk)) { + $qlist[$key] = _("quota_" . $key); // those are specific disks quotas. } - $get_quota_cache[$cuid] = $this->quotas; - } - - if ($ressource) { - if (isset($this->quotas[$ressource]) ) { - return $this->quotas[$ressource]; - } else { - return 0; - } - } else { - return $this->quotas; - } - } - - - /* ----------------------------------------------------------------- */ - /** Set the quota for a user (and for a ressource) - * @param string $ressource ressource to set quota of - * @param integer size of the quota (available or used) - */ - function setquota($ressource,$size) { - global $err,$db,$cuid; - $err->log("quota","setquota",$ressource."/".$size); - if (floatval($size)==0) $size="0"; - if (isset($this->disk[$ressource])) { - // It's a disk resource, update it with shell command - exec("sudo /usr/lib/alternc/quota_edit ".intval($cuid)." ".intval($size)." &> /dev/null &"); - // Now we check that the value has been written properly : - exec("sudo /usr/lib/alternc/quota_get ".intval($cuid)." &> /dev/null &",$a); - if (!isset($a[1]) || $size!=$a[1]) { - $err->raise("quota",_("Error writing the quota entry!")); - return false; - } - } - // We check that this ressource exists for this client : - $db->query("SELECT * FROM quotas WHERE uid='$cuid' AND name='$ressource'"); - if ($db->num_rows()) { - $db->query("UPDATE quotas SET total='$size' WHERE uid='$cuid' AND name='$ressource';"); - } else { - $db->query("INSERT INTO quotas (uid,name,total) VALUES ('$cuid','$ressource','$size');"); - } - return true; - } - - - /* ----------------------------------------------------------------- */ - /** - * Erase all quota information about the user. - */ - function delquotas() { - global $db,$err,$cuid; - $err->log("quota","delquota"); - $db->query("DELETE FROM quotas WHERE uid='$cuid';"); - return true; - } - - - /* ----------------------------------------------------------------- */ - /** Get the default quotas as an associative array - * @return array the array of the default quotas - */ - function getdefaults() { - global $db; - $c=array(); - - $db->query("SELECT type,quota FROM defquotas WHERE type='default'"); - if(!$db->next_record()) - $this->addtype('default'); - - $db->query("SELECT value,quota,type FROM defquotas ORDER BY type,quota"); - while($db->next_record()) { - $type = $db->f("type"); - $c[$type][$db->f("quota")] = $db->f("value"); - } - return $c; - } - - - /* ----------------------------------------------------------------- */ - /** Set the default quotas - * @param array associative array of quota (key=>val) - */ - function setdefaults($newq) { - global $db; - $qlist=$this->qlist(); - - foreach($newq as $type => $quotas) { - foreach($quotas as $qname => $value) { - if(array_key_exists($qname, $qlist)) { - if(!$db->query("REPLACE INTO defquotas (value,quota,type) VALUES ($value,'$qname','$type');")) - return false; - } - } - } - return true; - } - - - /* ----------------------------------------------------------------- */ - /** Add an account type for quotas - * @param string $type account type to be added - * @return boolean true if all went ok - */ - function addtype($type) { - global $db,$err; - $qlist=$this->qlist(); - if(empty($type)) return false; - $type=strtolower($type); - if (!preg_match("#^[a-z0-9]*$#",$type)) { - $err->raise("quota", "Type can only contains characters a-z and 0-9"); - return false; - } - while (list($key,$val)=each($qlist)) { - if(!$db->query("INSERT IGNORE INTO defquotas (quota,type) VALUES('$key', '$type');") - || $db->affected_rows() == 0) - return false; - } - return true; - } - - - /* ----------------------------------------------------------------- */ - /** List for quotas - * @return array - */ - function listtype() { - global $db; - $db->query("SELECT distinct(type) FROM defquotas ORDER by type"); - $t=array(); - while ($db->next_record()) { - $t[] = $db->f("type"); - } - return $t; - } - - - /* ----------------------------------------------------------------- */ - /** Delete an account type for quotas - * @param string $type account type to be deleted - * @return boolean true if all went ok - */ - function deltype($type) { - global $db; - - if($db->query("UPDATE membres SET type='default' WHERE type='$type'") && - $db->query("DELETE FROM defquotas WHERE type='$type'")) { - return true; - } else { - return false; - } - } - - - /* ----------------------------------------------------------------- */ - /** Create default quotas entries for a new user. - * The user we are talking about is in the global $cuid. - */ - function addquotas() { - global $db,$err,$cuid; - $err->log("quota","addquota"); - $ql=$this->qlist(); - reset($ql); - - $db->query("SELECT type,quota FROM defquotas WHERE type='default'"); - if(!$db->next_record()) - $this->addtype('default'); - - $db->query("SELECT type FROM membres WHERE uid='$cuid'"); - $db->next_record(); - $t = $db->f("type"); - - foreach($ql as $res => $val) { - $db->query("SELECT value FROM defquotas WHERE quota='$res' AND type='$t'"); - $q = $db->next_record() ? $db->f("value") : 0; - $this->setquota($res, $q); - } - return true; - } - - - /* ----------------------------------------------------------------- */ - /** Return a quota value with its unit (when it is a space quota) - * in MB, GB, TB ... - * @param string $type The quota type - * @param integer $value The quota value - * @return string a quota value with its unit. - */ - function display_val($type, $value) { - switch ($type) { - case 'bw_web': - return format_size($value); - case 'web': - return format_size($value*1024); - default: - return $value; - } - } - - - /* get size_xx function (filled by spoolsize.php) */ - function _get_sum_sql($sql) { - global $db,$err,$cuid; - $db->query($sql); - if ($db->num_rows() == 0) { - return -1; - } else { - $db->next_record(); - $r = $db->Record; - return $r['sum']; - } - } - - function _get_count_sql($sql) { - global $db,$err,$cuid; - $db->query($sql); - if ($db->num_rows() == 0) { - return 0; - } else { - $db->next_record(); - $r = $db->Record; - return $r['count']; - } - } - - function _get_size_and_record_sql($sql) { - global $db,$err,$cuid; - $db->query($sql); - if ($db->num_rows() == 0) { - return array(); - } else { - $ret = array(); - while ($db->next_record()) { - $ret[] = $db->Record; - } - return $ret; - } - } - - /* sum of websites sizes from all users */ - function get_size_web_sum_all() { - return $this->_get_sum_sql("SELECT SUM(size) AS sum FROM size_web;"); - } - - /* sum of websites sizes from one user */ - function get_size_web_sum_user($u) { - return $this->_get_sum_sql("SELECT SUM(size) AS sum FROM size_web WHERE uid='$u';"); - } - - /* sum of mailbox sizes from all domains */ - function get_size_mail_sum_all() { - return $this->_get_sum_sql("SELECT SUM(bytes) AS sum FROM mailbox;"); - } - - /* sum of mailbox sizes for one domain */ - function get_size_mail_sum_domain($dom) { - global $mail; - return $mail->get_total_size_for_domain($dom); - } - - /* count of mailbox sizes from all domains */ - function get_size_mail_count_all() { - return $this->_get_count_sql("SELECT COUNT(*) AS count FROM mailbox;"); - } - - /* count of mailbox for one domain */ - function get_size_mail_count_domain($dom) { - return $this->_get_count_sql("SELECT COUNT(*) AS count FROM dovecot_view WHERE user LIKE '%@{$dom}'"); - } - - /* get list of mailbox alias and size for one domain */ - function get_size_mail_details_domain($dom) { - return $this->_get_size_and_record_sql("SELECT user as alias,quota_dovecot as size FROM dovecot_view WHERE user LIKE '%@{$dom}' ORDER BY alias;"); - } - - /* sum of mailman lists sizes from all domains */ - function get_size_mailman_sum_all() { - return $this->_get_sum_sql("SELECT SUM(size) AS sum FROM size_mailman;"); - } - - /* sum of mailman lists sizes for one domain */ - function get_size_mailman_sum_domain($dom) { - return $this->_get_sum_sql("SELECT SUM(size) AS sum FROM size_mailman WHERE list LIKE '%@{$dom}'"); - } - - /* sum of mailman lists for one user */ - function get_size_mailman_sum_user($u) { - return $this->_get_sum_sql("SELECT SUM(size) AS sum FROM size_mailman WHERE uid = '{$u}'"); - } - - /* count of mailman lists sizes from all domains */ - function get_size_mailman_count_all() { - return $this->_get_count_sql("SELECT COUNT(*) AS count FROM size_mailman;"); - } - - /* count of mailman lists for one user */ - function get_size_mailman_count_user($u) { - return $this->_get_count_sql("SELECT COUNT(*) AS count FROM size_mailman WHERE uid = '{$u}'"); - } - - /* get list of mailman list and size for one user */ - function get_size_mailman_details_user($u) { - return $this->_get_size_and_record_sql("SELECT s.size,CONCAT(m.list,'@',m.domain) as list FROM size_mailman s LEFT JOIN mailman m ON s.list=m.name WHERE s.uid='{$u}' ORDER BY s.list ASC"); - } - - /* sum of databases sizes from all users */ - function get_size_db_sum_all() { - return $this->_get_sum_sql("SELECT SUM(size) AS sum FROM size_db;"); - } - - /* sum of databases sizes for one user */ - function get_size_db_sum_user($u) { - return $this->_get_sum_sql("SELECT SUM(size) AS sum FROM size_db WHERE db = '{$u}' OR db LIKE '{$u}\_%'"); - } - - /* count of databases from all users */ - function get_size_db_count_all() { - return $this->_get_count_sql("SELECT COUNT(*) AS count FROM size_db;"); - } - - /* count of databases for one user */ - function get_size_db_count_user($u) { - return $this->_get_count_sql("SELECT COUNT(*) AS count FROM size_db WHERE db = '{$u}' OR db LIKE '{$u}\_%'"); - } - - /* get list of databases name and size for one user */ - function get_size_db_details_user($u) { - return $this->_get_size_and_record_sql("SELECT db,size FROM size_db WHERE db='{$u}' OR db LIKE '{$u}\_%';"); - } - - /* Return appropriate value and unit of a size given in Bytes (e.g. 1024 Bytes -> return 1 KB) */ - function get_size_unit($size) { - $units=array(1073741824=>_("GB"), 1048576=>_("MB"), 1024=>_("KB"), 0=>_("B")); - foreach($units as $value=>$unit){ - if($size>=$value){ - $size=str_pad(round($size/($value ? $value : 1), 1), 5, ' ', STR_PAD_LEFT); - return array('size'=>$size, 'unit'=>$unit); - } - } - } - - // Affiche des barres de progression - // color_type : - // 0 = Pas de changement de couleur - // 1 = Progression du vert vers le rouge en fonction du porcentage - // 2 = Progression du rouge vers le vert en fonction du porcentage - function quota_displaybar($usage, $color_type=1) { - if ($color_type == 1) { - $csscolor = " background-color:".PercentToColor($usage); - } elseif ($color_type == 2) { - $csscolor = " background-color:".PercentToColor(100-$usage); - } else { - $csscolor = ""; + foreach ($this->getquota() as $qq) { + if (isset($qq['name'])) { + $qlist[$qq['name']] = $qq['description']; + } + } + return $qlist; } - - echo ' '; - } - - - /* ==== Hook functions ==== */ - - /* ----------------------------------------------------------------- */ - /** Hook function call when a user is deleted - * AlternC's standard function called when a user is deleted - * globals $cuid is the appropriate user - */ - function hook_admin_del_member() { - $this->delquotas(); - } - - - /* ----------------------------------------------------------------- */ - /** Hook function called when a user is created - * This function initialize the user's quotas. - * globals $cuid is the appropriate user - */ - function hook_admin_add_member() { - global $err; - $err->log("quota","hook_admin_add_member"); - $this->addquotas(); - $this->getquota('',true); // actualise quota - } - - - /* ----------------------------------------------------------------- */ - /** Exports all the quota related information for an account. - * @access private - * EXPERIMENTAL function ;) - */ - function alternc_export_conf() { - global $db,$err; - $err->log("quota","export"); - $str="" . $db->Record["login"] . " \n"; + $str.="" . $db->Record["pass"] . " \n"; + do { + $filename = $tmpdir . "/mysql." . $db->Record["db"] . ".sql.gz"; // FIXME not used + $str.="" . ($db->Record["db"]) . " \n"; + $str.="" . ($db->Record["pass"]) . " \n"; + if ($s["bck_mode"] != 0) { // FIXME what is $s ? + $str.="" . ($db->Record["bck_mode"]) . " \n"; + $str.="" . ($db->Record["bck_dir"]) . " \n"; + $str.="" . ($db->Record["bck_history"]) . " \n"; + $str.="" . ($db->Record["bck_gzip"]) . " \n"; + } + } while ($db->next_record()); + $str.=""; - - $q=$this->getquota(); - foreach ($q as $k=>$v) { - $str.=" <$k>\n"; - $str.=" \n"; - return $str; - } + + /* + * Create default quota in the profile + * when a new quota appear + * + */ + + function create_missing_quota_profile() { + global $db, $quota, $err; + $err->log("quota", "create_missing_quota_profile"); + $qt = $quota->getquota('', true); + $type = $quota->listtype(); + foreach ($type as $t) { + foreach ($qt as $q => $vv) { + $db->query("INSERT IGNORE defquotas (value,quota,type) VALUES (0,'$q','$t');"); + } + } + return true; + } + + /* ----------------------------------------------------------------- */ + + /** Return a ressource usage (u) and total quota (t) + * @param string $ressource ressource to get quota of + * @Return array the quota used and total for this ressource (or for all ressource if unspecified) + */ + function getquota($ressource = "", $recheck = false) { + global $db, $err, $cuid, $get_quota_cache, $hooks, $mem; + $err->log("quota", "getquota", $ressource); + if ($recheck) { // rebuilding quota + $get_quota_cache = null; + $this->quotas = array(); + } + if (!empty($get_quota_cache[$cuid])) { + // This function is called many time each webpage, so I cache the result + $this->quotas = $get_quota_cache[$cuid]; + } else { + $res = $hooks->invoke("hook_quota_get"); + foreach ($res as $r) { + $this->quotas[$r['name']] = $r; + $this->quotas[$r['name']]['u'] = $r['used']; // retrocompatibilité + $this->quotas[$r['name']]['t'] = 0; // Default quota = 0 + } + reset($this->disk); + + if (!empty($this->disk)) { // Check if there are some disk quota to check + // Look if there are some cached value + $disk_cached = $mem->session_tempo_params_get('quota_cache_disk'); + + while (list($key, $val) = each($this->disk)) { + $a = array(); + if ( + isset($disk_cached[$val]) && !empty($disk_cached[$val]) && $disk_cached[$val]['uid'] == $cuid && $disk_cached[$val]['timestamp'] > ( time() - (90) ) // Cache, en seconde + ) { + // If there is a cached value + $a = $disk_cached[$val]; + } else { + exec("/usr/lib/alternc/quota_get " . intval($cuid), $ak); + $a['u'] = intval($ak[0]); + $a['t'] = @intval($ak[1]); + $a['timestamp'] = time(); + $a['uid'] = $cuid; + $disk_cached = $mem->session_tempo_params_set('quota_cache_disk', array($val => $a)); + } + $this->quotas[$val] = array("name" => "$val", 'description' => _("quota_" . $val), "t" => $a['t'], "u" => $a['u']); + } + } + + // Get the allowed quota from database. + $db->query("select name, total from quotas where uid='$cuid';"); + while ($db->next_record()) { + $this->quotas[$db->f('name')]['t'] = $db->f('total'); + } + + $get_quota_cache[$cuid] = $this->quotas; + } + + if ($ressource) { + if (isset($this->quotas[$ressource])) { + return $this->quotas[$ressource]; + } else { + return 0; + } + } else { + return $this->quotas; + } + } + + /* ----------------------------------------------------------------- */ + + /** Set the quota for a user (and for a ressource) + * @param string $ressource ressource to set quota of + * @param integer size of the quota (available or used) + */ + function setquota($ressource, $size) { + global $err, $db, $cuid; + $err->log("quota", "setquota", $ressource . "/" . $size); + if (floatval($size) == 0) { + $size = "0"; + } + if (isset($this->disk[$ressource])) { + // It's a disk resource, update it with shell command + exec("sudo /usr/lib/alternc/quota_edit " . intval($cuid) . " " . intval($size) . " &> /dev/null &"); + // Now we check that the value has been written properly : + $a = array(); + exec("sudo /usr/lib/alternc/quota_get " . intval($cuid) . " &> /dev/null &", $a); + if (!isset($a[1]) || $size != $a[1]) { + $err->raise("quota", _("Error writing the quota entry!")); + return false; + } + } + // We check that this ressource exists for this client : + $db->query("SELECT * FROM quotas WHERE uid='$cuid' AND name='$ressource'"); + if ($db->num_rows()) { + $db->query("UPDATE quotas SET total='$size' WHERE uid='$cuid' AND name='$ressource';"); + } else { + $db->query("INSERT INTO quotas (uid,name,total) VALUES ('$cuid','$ressource','$size');"); + } + return true; + } + + /* ----------------------------------------------------------------- */ + + /** + * Erase all quota information about the user. + */ + function delquotas() { + global $db, $err, $cuid; + $err->log("quota", "delquota"); + $db->query("DELETE FROM quotas WHERE uid='$cuid';"); + return true; + } + + /* ----------------------------------------------------------------- */ + + /** Get the default quotas as an associative array + * @return array the array of the default quotas + */ + function getdefaults() { + global $db; + $c = array(); + + $db->query("SELECT type,quota FROM defquotas WHERE type='default'"); + if (!$db->next_record()) { + $this->addtype('default'); + } + $db->query("SELECT value,quota,type FROM defquotas ORDER BY type,quota"); + while ($db->next_record()) { + $type = $db->f("type"); + $c[$type][$db->f("quota")] = $db->f("value"); + } + return $c; + } + + /* ----------------------------------------------------------------- */ + + /** Set the default quotas + * @param array associative array of quota (key=>val) + */ + function setdefaults($newq) { + global $db; + $qlist = $this->qlist(); + + foreach ($newq as $type => $quotas) { + foreach ($quotas as $qname => $value) { + if (array_key_exists($qname, $qlist)) { + if (!$db->query("REPLACE INTO defquotas (value,quota,type) VALUES ($value,'$qname','$type');")) { + return false; + } + } + } + } + return true; + } + + /* ----------------------------------------------------------------- */ + + /** Add an account type for quotas + * @param string $type account type to be added + * @return boolean true if all went ok + */ + function addtype($type) { + global $db, $err; + $qlist = $this->qlist(); + if (empty($type)) { + return false; + } + $type = strtolower($type); + if (!preg_match("#^[a-z0-9]*$#", $type)) { + $err->raise("quota", "Type can only contains characters a-z and 0-9"); + return false; + } + while (list($key, $val) = each($qlist)) { + if (!$db->query("INSERT IGNORE INTO defquotas (quota,type) VALUES('$key', '$type');") || $db->affected_rows() == 0) { + return false; + } + } + return true; + } + + /* ----------------------------------------------------------------- */ + + /** List for quotas + * @return array + */ + function listtype() { + global $db; + $db->query("SELECT distinct(type) FROM defquotas ORDER by type"); + $t = array(); + while ($db->next_record()) { + $t[] = $db->f("type"); + } + return $t; + } + + /* ----------------------------------------------------------------- */ + + /** Delete an account type for quotas + * @param string $type account type to be deleted + * @return boolean true if all went ok + */ + function deltype($type) { + global $db; + + if ($db->query("UPDATE membres SET type='default' WHERE type='$type'") && + $db->query("DELETE FROM defquotas WHERE type='$type'")) { + return true; + } else { + return false; + } + } + + /* ----------------------------------------------------------------- */ + + /** Create default quotas entries for a new user. + * The user we are talking about is in the global $cuid. + */ + function addquotas() { + global $db, $err, $cuid; + $err->log("quota", "addquota"); + $ql = $this->qlist(); + reset($ql); + + $db->query("SELECT type,quota FROM defquotas WHERE type='default'"); + if (!$db->next_record()) { + $this->addtype('default'); + } + $db->query("SELECT type FROM membres WHERE uid='$cuid'"); + $db->next_record(); + $t = $db->f("type"); + + foreach ($ql as $res => $val) { + $db->query("SELECT value FROM defquotas WHERE quota='$res' AND type='$t'"); + $q = $db->next_record() ? $db->f("value") : 0; + $this->setquota($res, $q); + } + return true; + } + + /* ----------------------------------------------------------------- */ + + /** Return a quota value with its unit (when it is a space quota) + * in MB, GB, TB ... + * @param string $type The quota type + * @param integer $value The quota value + * @return string a quota value with its unit. + */ + function display_val($type, $value) { + switch ($type) { + case 'bw_web': + return format_size($value); + case 'web': + return format_size($value * 1024); + default: + return $value; + } + } + + /* get size_xx function (filled by spoolsize.php) */ + + function _get_sum_sql($sql) { + global $db; + $db->query($sql); + if ($db->num_rows() == 0) { + return -1; + } else { + $db->next_record(); + $r = $db->Record; + return $r['sum']; + } + } + + function _get_count_sql($sql) { + global $db; + $db->query($sql); + if ($db->num_rows() == 0) { + return 0; + } else { + $db->next_record(); + $r = $db->Record; + return $r['count']; + } + } + + function _get_size_and_record_sql($sql) { + global $db; + $db->query($sql); + if ($db->num_rows() == 0) { + return array(); + } else { + $ret = array(); + while ($db->next_record()) { + $ret[] = $db->Record; + } + return $ret; + } + } + + /* sum of websites sizes from all users */ + + function get_size_web_sum_all() { + return $this->_get_sum_sql("SELECT SUM(size) AS sum FROM size_web;"); + } + + /* sum of websites sizes from one user */ + + function get_size_web_sum_user($u) { + return $this->_get_sum_sql("SELECT SUM(size) AS sum FROM size_web WHERE uid='$u';"); + } + + /* sum of mailbox sizes from all domains */ + + function get_size_mail_sum_all() { + return $this->_get_sum_sql("SELECT SUM(bytes) AS sum FROM mailbox;"); + } + + /* sum of mailbox sizes for one domain */ + + function get_size_mail_sum_domain($dom) { + global $mail; + return $mail->get_total_size_for_domain($dom); + } + + /* count of mailbox sizes from all domains */ + + function get_size_mail_count_all() { + return $this->_get_count_sql("SELECT COUNT(*) AS count FROM mailbox;"); + } + + /* count of mailbox for one domain */ + + function get_size_mail_count_domain($dom) { + return $this->_get_count_sql("SELECT COUNT(*) AS count FROM dovecot_view WHERE user LIKE '%@{$dom}'"); + } + + /* get list of mailbox alias and size for one domain */ + + function get_size_mail_details_domain($dom) { + return $this->_get_size_and_record_sql("SELECT user as alias,quota_dovecot as size FROM dovecot_view WHERE user LIKE '%@{$dom}' ORDER BY alias;"); + } + + /* sum of mailman lists sizes from all domains */ + + function get_size_mailman_sum_all() { + return $this->_get_sum_sql("SELECT SUM(size) AS sum FROM size_mailman;"); + } + + /* sum of mailman lists sizes for one domain */ + + function get_size_mailman_sum_domain($dom) { + return $this->_get_sum_sql("SELECT SUM(size) AS sum FROM size_mailman WHERE list LIKE '%@{$dom}'"); + } + + /* sum of mailman lists for one user */ + + function get_size_mailman_sum_user($u) { + return $this->_get_sum_sql("SELECT SUM(size) AS sum FROM size_mailman WHERE uid = '{$u}'"); + } + + /* count of mailman lists sizes from all domains */ + + function get_size_mailman_count_all() { + return $this->_get_count_sql("SELECT COUNT(*) AS count FROM size_mailman;"); + } + + /* count of mailman lists for one user */ + + function get_size_mailman_count_user($u) { + return $this->_get_count_sql("SELECT COUNT(*) AS count FROM size_mailman WHERE uid = '{$u}'"); + } + + /* get list of mailman list and size for one user */ + + function get_size_mailman_details_user($u) { + return $this->_get_size_and_record_sql("SELECT s.size,CONCAT(m.list,'@',m.domain) as list FROM size_mailman s LEFT JOIN mailman m ON s.list=m.name WHERE s.uid='{$u}' ORDER BY s.list ASC"); + } + + /* sum of databases sizes from all users */ + + function get_size_db_sum_all() { + return $this->_get_sum_sql("SELECT SUM(size) AS sum FROM size_db;"); + } + + /* sum of databases sizes for one user */ + + function get_size_db_sum_user($u) { + return $this->_get_sum_sql("SELECT SUM(size) AS sum FROM size_db WHERE db = '{$u}' OR db LIKE '{$u}\_%'"); + } + + /* count of databases from all users */ + + function get_size_db_count_all() { + return $this->_get_count_sql("SELECT COUNT(*) AS count FROM size_db;"); + } + + /* count of databases for one user */ + + function get_size_db_count_user($u) { + return $this->_get_count_sql("SELECT COUNT(*) AS count FROM size_db WHERE db = '{$u}' OR db LIKE '{$u}\_%'"); + } + + /* get list of databases name and size for one user */ + + function get_size_db_details_user($u) { + return $this->_get_size_and_record_sql("SELECT db,size FROM size_db WHERE db='{$u}' OR db LIKE '{$u}\_%';"); + } + + /* Return appropriate value and unit of a size given in Bytes (e.g. 1024 Bytes -> return 1 KB) */ + + function get_size_unit($size) { + $units = array(1073741824 => _("GB"), 1048576 => _("MB"), 1024 => _("KB"), 0 => _("B")); + foreach ($units as $value => $unit) { + if ($size >= $value) { + $size = str_pad(round($size / ($value ? $value : 1), 1), 5, ' ', STR_PAD_LEFT); + return array('size' => $size, 'unit' => $unit); + } + } + } + + // Affiche des barres de progression + // color_type : + // 0 = Pas de changement de couleur + // 1 = Progression du vert vers le rouge en fonction du porcentage + // 2 = Progression du rouge vers le vert en fonction du porcentage + function quota_displaybar($usage, $color_type = 1) { + if ($color_type == 1) { + $csscolor = " background-color:" . PercentToColor($usage); + } elseif ($color_type == 2) { + $csscolor = " background-color:" . PercentToColor(100 - $usage); + } else { + $csscolor = ""; + } -} /* Class m_quota */ + echo ' '; + } + + /* ==== Hook functions ==== */ + + /* ----------------------------------------------------------------- */ + + /** Hook function call when a user is deleted + * AlternC's standard function called when a user is deleted + * globals $cuid is the appropriate user + */ + function hook_admin_del_member() { + $this->delquotas(); + } + + /* ----------------------------------------------------------------- */ + + /** Hook function called when a user is created + * This function initialize the user's quotas. + * globals $cuid is the appropriate user + */ + function hook_admin_add_member() { + global $err; + $err->log("quota", "hook_admin_add_member"); + $this->addquotas(); + $this->getquota('', true); // actualise quota + } + + /* ----------------------------------------------------------------- */ + + /** Exports all the quota related information for an account. + * @access private + * EXPERIMENTAL function ;) + */ + function alternc_export_conf() { + global $err; + $err->log("quota", "export"); + $str = "".($v["u"])." \n"; - $str.="".($v["t"])." \n"; - $str.=" $k>\n"; + /** + * Synchronise the quotas of the users with the quota of the + * user's profile. + * If the user have a greater quota than the profile, no change. + * If the quota entry doesn't exist for the user, create it with + * the defaults value. + */ + function synchronise_user_profile() { + global $db, $err; + $err->log("quota", "synchronise_user_profile"); + $q = "insert into quotas select m.uid as uid, d.quota as name, d.value as total from membres m, defquotas d left join quotas q on q.name=d.quota where m.type=d.type ON DUPLICATE KEY UPDATE total = greatest(d.value, quotas.total);"; + if (!$db->query($q)) { + return false; + } + return true; } - $str.=""; + + $q = $this->getquota(); + foreach ($q as $k => $v) { + $str.=" <$k>\n"; + $str.=" \n"; + return $str; + } + +} + +/* Class m_quota */" . ($v["u"]) . " \n"; + $str.="" . ($v["t"]) . " \n"; + $str.=" $k>\n"; + } + $str.="