Alternc  3.2
Alternc logiel libre pour l'hébergement
 All Data Structures Namespaces Files Functions Variables Pages
m_ftp.php
Go to the documentation of this file.
1 <?php
2 /*
3  ----------------------------------------------------------------------
4  AlternC - Web Hosting System
5  Copyright (C) 2000-2012 by the AlternC Development Team.
6  https://alternc.org/
7  ----------------------------------------------------------------------
8  LICENSE
9 
10  This program is free software; you can redistribute it and/or
11  modify it under the terms of the GNU General Public License (GPL)
12  as published by the Free Software Foundation; either version 2
13  of the License, or (at your option) any later version.
14 
15  This program is distributed in the hope that it will be useful,
16  but WITHOUT ANY WARRANTY; without even the implied warranty of
17  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18  GNU General Public License for more details.
19 
20  To read the license please visit http://www.gnu.org/copyleft/gpl.html
21  ----------------------------------------------------------------------
22  Purpose of file: Manage FTP accounts
23  ----------------------------------------------------------------------
24 */
25 
26 /**
27 * Classe de gestion des comptes FTP de l'hébergé.
28 */
29 class m_ftp {
30 
31 
32  var $srv_name;
33  /* ----------------------------------------------------------------- */
34  /**
35  * Constructeur
36  */
37  function m_ftp() {
38  global $L_FQDN;
39  $this->srv_name = variable_get('ftp_human_name', '%%FQDN%%','Human name for FTP server', array(array('desc'=>'Name','type'=>'string')));
40  }
41 
42 
43  /* ----------------------------------------------------------------- */
44  /**
45  * Password kind used in this class (hook for admin class)
46  */
48  return array("ftp"=>"FTP accounts");
49  }
50 
51  function hook_menu() {
52  global $quota;
53  $q = $quota->getquota("ftp");
54 
55  $obj = array(
56  'title' => _("FTP accounts"),
57  'ico' => 'images/ftp.png',
58  'link' => 'toggle',
59  'pos' => 60,
60  'links' => array(),
61  ) ;
62 
63  if ( $quota->cancreate("ftp") ) {
64  $obj['links'][] =
65  array (
66  'ico' => 'images/new.png',
67  'txt' => _("Create a new ftp account"),
68  'url' => "ftp_edit.php?create=1",
69  'class' => '',
70  );
71  }
72 
73  if ( $q['u'] > 0 ) { // if there are some FTP accounts
74  $obj['links'][] =
75  array (
76  'txt' => _("FTP accounts list"),
77  'url' => "ftp_list.php"
78  );
79  }
80 
81  return $obj;
82  }
83 
84  // Return the values needed to activate security access. See get_auth_class()
85  // in authip for more informations
86  function authip_class() {
87  $c = Array();
88  $c['name']="FTP";
89  $c['protocol']="ftp";
90  $c['values']=Array();
91 
92  $tt = $this->get_list();
93  if (empty($tt) || !is_array($tt)) return $c;
94  foreach ($this->get_list() as $v ) {
95  $c['values'][$v['id']]=$v['login'];
96  }
97 
98  return $c;
99  }
100 
101  // Switch enabled status of an account
102  function switch_enabled($id,$status=null) {
103  global $cuid, $db, $err;
104  if (! $jj = $this->get_ftp_details($id)) {
105  $err->raise('ftp', _("This account do not exist or is not of this account"));
106  return false;
107  }
108  if ( $status == null ){
109  if ($jj[0]['enabled'] == true ) { $status=0;}
110  else { $status=1; }
111  }
112 
113  // Be sure what is in $status, in case of it was a parameter
114  $status = ($status?'true':'false');
115 
116  if ( ! $db->query("UPDATE ftpusers SET enabled = $status WHERE uid = '$cuid' AND id = '$id' ;") ) {
117  $err->raise('ftp', _("Error during update"));
118  return false;
119  } else {
120  return true ;
121  }
122  }
123 
124 
125  /* ----------------------------------------------------------------- */
126  /** Retourne la liste des comptes FTP du compte hébergé
127  * Retourne la liste des comptes FTP sous forme de tableau indexé de
128  * tableaus associatifs comme suit :
129  * $a["id"]= ID du compte ftp
130  * $a["login"]= Nom de login du compte
131  * $a["dir"]= Dossier relatif à la racine du compte de l'utilisateur
132  * @return array Retourne le tableau des comptes ou FALSE si une erreur s'est produite.
133  */
134  function get_list() {
135  global $db,$err,$cuid, $bro;
136  $err->log("ftp","get_list");
137  $r=array();
138  $db->query("SELECT id, name, homedir, enabled FROM ftpusers WHERE uid='$cuid' ORDER BY name;");
139  if ($db->num_rows()) {
140  while ($db->next_record()) {
141  $r[]=array(
142  "id"=>$db->f("id"),
143  "login"=>$db->f("name"),
144  "enabled"=>$db->f("enabled"),
145  //"dir"=>$match[1]
146  "dir"=>$db->f("homedir")
147  );
148  }
149  return $r;
150  } else {
151  $err->raise("ftp",_("No FTP account found"));
152  return false;
153  }
154  }
155 
156  /* ----------------------------------------------------------------- */
157  /** Retourne les détails d'un compte FTP (voir get_list)
158  * Le tableau est celui du compte d'id spécifié
159  * @param integer $id Numéro du compte dont on souhaite obtenir les détails
160  * @return array Tableau associatif contenant les infos du comptes ftp
161  */
162  function get_ftp_details($id) {
163  global $db,$err,$cuid;
164  $err->log("ftp","get_ftp_details",$id);
165  $r=array();
166  $db->query("SELECT id, name, homedir, enabled FROM ftpusers WHERE uid='$cuid' AND id='$id';");
167  if ($db->num_rows()) {
168  $db->next_record();
169 
170  $regexp="/^".preg_quote(getuserpath(),"/")."\/(.*)$/";
171  $tr=preg_match($regexp, $db->f("homedir"),$match);
172 
173  $lg=explode("_",$db->f("name"));
174  if ((!is_array($lg)) || (count($lg)!=2)) {
175  $lg[0]=$db->f("name");
176  $lg[1]="";
177  }
178  $r[]=array(
179  "id"=>$db->f("id"),
180  "prefixe"=> $lg[0],
181  "login"=>$lg[1],
182  "dir"=>$match[1],
183  "enabled"=>$db->f("enabled")
184  );
185  return $r;
186  } else {
187  $err->raise("ftp",_("This FTP account does not exist"));
188  return false;
189  }
190  }
191 
192  /* ----------------------------------------------------------------- */
193  /** Retourne la liste des prefixes utilisables par le compte courant
194  * @return array tableau contenant la liste des prefixes (domaines + login)
195  * du compte actuel.
196  */
197  function prefix_list() {
198  global $db,$mem,$cuid;
199  $r=array();
200  $r[]=$mem->user["login"];
201  $db->query("SELECT domaine FROM domaines WHERE compte='$cuid' ORDER BY domaine;");
202  while ($db->next_record()) {
203  $r[]=$db->f("domaine");
204  }
205  return $r;
206  }
207 
208  // Check if the login is fine (syntax)
209  function check_login($l) {
210  global $err;
211 
212  // special chars and the max numbers of them allowed
213  // to be able to give a specific error
214  $vv = array('_'=>'1', ' '=>0);
215  foreach ($vv as $k=>$n) {
216  if (substr_count($l, $k) > $n ) { // if there is more than $n $k
217  $err->raise('ftp', sprintf(_("FTP login is incorrect: too many '%s'"), $k));
218  return false;
219  }
220  }
221 
222  // Explicitly look for only allowed chars
223  if ( ! preg_match("/^[A-Za-z0-9_\.\-]+$/", $l) ) {
224  $err->raise('ftp', _("FTP login is incorrect"));
225  return false;
226  }
227  return true;
228  }
229 
230  /* ----------------------------------------------------------------- */
231  /** Affiche (ECHO) la liste des prefixes disponibles sous forme de champs d'option
232  * Les champs sont affichés sous la forme <option>prefixe</option>...
233  * La valeur $current se voit affublée de la balise SELECTED.
234  * @param string $current Prefixe sélectionné par défaut
235  * @return boolean TRUE.
236  */
237  function select_prefix_list($current) {
238  $r=$this->prefix_list();
239  reset($r);
240  while (list($key,$val)=each($r)) {
241  if ($current==$val) $c=" selected=\"selected\""; else $c="";
242  echo "<option$c>$val</option>";
243  }
244  return true;
245  }
246 
247  /* ----------------------------------------------------------------- */
248  /** Modifie les paramètres du comptes FTP $id.
249  * @param integer $id Numéro du compte dont on veut modifier les paramètres
250  * @param string $prefixe Prefixe du compte FTP
251  * @param string $login login ajouté au préfixe ($prefixe_$login)
252  * @param string $pass mot de passe
253  * @param string $dir Répertoire racine du compte
254  * @return boolean TRUE si le compte a été modifié, FALSE si une erreur est survenue.
255  */
256  function put_ftp_details($id,$prefixe,$login,$pass,$dir) {
257  global $mem,$db,$err,$bro,$cuid,$admin;
258  $err->log("ftp","put_ftp_details",$id);
259  $db->query("SELECT count(*) AS cnt FROM ftpusers WHERE id='$id' and uid='$cuid';");
260  $db->next_record();
261  if (!$db->f("cnt")) {
262  $err->raise("ftp",_("This FTP account does not exist"));
263  return false;
264  }
265  $dir=$bro->convertabsolute($dir);
266  if (substr($dir,0,1)=="/") {
267  $dir=substr($dir,1);
268  }
269  $r=$this->prefix_list();
270  if (!in_array($prefixe,$r)) {
271  $err->raise("ftp",_("The chosen prefix is not allowed"));
272  return false;
273  }
274  $lo=$mem->user["login"];
275  $l=substr($lo,0,1);
276  $full_login=$prefixe;
277  if ($login) $full_login.="_".$login;
278  if (! $this->check_login($full_login) ) return false;
279  $db->query("SELECT COUNT(*) AS cnt FROM ftpusers WHERE id!='$id' AND name='$full_login';");
280  $db->next_record();
281  if ($db->f("cnt")) {
282  $err->raise("ftp",_("This FTP account already exists"));
283  return false;
284  }
285  $absolute=getuserpath()."/$dir";
286  if (!file_exists($absolute)) {
287  system("/bin/mkdir -p $absolute");
288  }
289  if (!is_dir($absolute)) {
290  $err->raise("ftp",_("The directory cannot be created"));
291  return false;
292  }
293  if (trim($pass)) {
294 
295  // Check this password against the password policy using common API :
296  if (is_callable(array($admin,"checkPolicy"))) {
297  if (!$admin->checkPolicy("ftp",$prefixe.$login,$pass)) {
298  return false; // The error has been raised by checkPolicy()
299  }
300  }
301  $encrypted_password = _md5cr($pass,strrev(microtime(true)));
302  $db->query("UPDATE ftpusers SET name='".$full_login."', password='', encrypted_password='$encrypted_password', homedir='$absolute', uid='$cuid' WHERE id='$id';");
303  } else {
304  $db->query("UPDATE ftpusers SET name='".$full_login."', homedir='$absolute', uid='$cuid' WHERE id='$id';");
305  }
306  return true;
307  }
308 
309 
310  /* ----------------------------------------------------------------- */
311  /** Efface le compte ftp spécifié.
312  * @param integer $id Numéro du compte FTP à supprimer.
313  * @return boolean TRUE si le compte a été effacé, FALSE sinon.
314  */
315  function delete_ftp($id) {
316  global $db,$err,$cuid;
317  $err->log("ftp","delete_ftp",$id);
318  $db->query("SELECT name FROM ftpusers WHERE id='$id' and uid='$cuid';");
319  $db->next_record();
320  $name=$db->f("name");
321  if (!$name) {
322  $err->raise("ftp",_("This FTP account does not exist"));
323  return false;
324  }
325  $db->query("DELETE FROM ftpusers WHERE id='$id'");
326  return $name;
327  }
328 
329  /* ----------------------------------------------------------------- */
330  /** Crée un nouveau compte FTP.
331  * @param string $prefixe Prefixe au login
332  * @param string $login Login ftp (login=prefixe_login)
333  * @param string $pass Mot de passe FTP
334  * @param string $dir Répertoire racine du compte relatif à la racine du membre
335  * @return boolean TRUE si le compte a été créé, FALSE sinon.
336  *
337  */
338  function add_ftp($prefixe,$login,$pass,$dir) {
339  global $mem,$db,$err,$quota,$bro,$cuid,$admin;
340  $err->log("ftp","add_ftp",$prefixe."_".$login);
341  $dir=$bro->convertabsolute($dir);
342  if (substr($dir,0,1)=="/") {
343  $dir=substr($dir,1);
344  }
345  $r=$this->prefix_list();
346  if (empty($pass)) {
347  $err->raise("ftp",_("Password can't be empty"));
348  return false;
349  }
350  if (!in_array($prefixe,$r) || $prefixe=="") {
351  $err->raise("ftp",_("The chosen prefix is not allowed"));
352  return false;
353  }
354  $full_login=$prefixe;
355  if ($login) $full_login.="_".$login;
356  if ( !$this->check_login($full_login) ) return false;
357  $db->query("SELECT count(*) AS cnt FROM ftpusers WHERE name='".$full_login."'");
358  $db->next_record();
359  if ($db->f("cnt")) {
360  $err->raise("ftp",_("This FTP account already exists"));
361  return false;
362  }
363  $db->query("SELECT login FROM membres WHERE uid='$cuid';");
364  $db->next_record();
365  $lo=$db->f("login");
366  $l=substr($lo,0,1);
367  $absolute=getuserpath()."/$dir";
368  if (!file_exists($absolute)) {
369  system("/bin/mkdir -p $absolute");
370  }
371  if (!is_dir($absolute)) {
372  $err->raise("ftp",_("The directory cannot be created"));
373  return false;
374  }
375 
376  // Check this password against the password policy using common API :
377  if (is_callable(array($admin,"checkPolicy"))) {
378  if (!$admin->checkPolicy("ftp",$full_login,$pass)) {
379  return false; // The error has been raised by checkPolicy()
380  }
381  }
382 
383  if ($quota->cancreate("ftp")) {
384  $encrypted_password = _md5cr($pass,strrev(microtime(true)));
385  $db->query("INSERT INTO ftpusers (name,password, encrypted_password,homedir,uid) VALUES ('".$full_login."', '', '$encrypted_password', '$absolute', '$cuid')");
386  return true;
387  } else {
388  $err->raise("ftp",_("Your FTP account quota is over. You cannot create more ftp accounts"));
389  return false;
390  }
391  }
392 
393  /* ----------------------------------------------------------------- */
394  /** Retourne TRUE si $dir possède un compte FTP
395  * @param string $dir Dossier à tester, relatif à la racine du compte courant
396  * @return boolean retourne TRUE si $dir à un compte FTP, FALSE sinon.
397  */
398  function is_ftp($dir) {
399  global $mem,$db,$err;
400  $err->log("ftp","is_ftp",$dir);
401  $lo=$mem->user["login"];
402  $l=substr($lo,0,1);
403  if (substr($dir,0,1)=="/") $dir=substr($dir,1);
404  $db->query("SELECT id FROM ftpusers WHERE homedir='".getuserpath()."/$dir';");
405  if ($db->num_rows()) {
406  $db->next_record();
407  return $db->f("id");
408  } else {
409  return false;
410  }
411  }
412 
413  /* ----------------------------------------------------------------- */
414  /** Fonction appellée par domains quand un domaine est supprimé pour le membre
415  * @param string $dom Domaine à détruire.
416  * @access private
417  */
419  global $db,$err,$cuid;
420  $err->log("ftp","alternc_del_domain",$dom);
421  $db->query("DELETE FROM ftpusers WHERE uid='$cuid' AND ( name LIKE '$dom\_%' OR name LIKE '$dom') ");
422  return true;
423  }
424 
425  /* ----------------------------------------------------------------- */
426  /** Fonction appellée par membres quand un membre est effacé.
427  * @param integer $uid Numéro de membre effacé.
428  * @access private
429  */
430  function alternc_del_member() {
431  global $db,$err,$cuid;
432  $err->log("ftp","alternc_del_member");
433  $db->query("DELETE FROM ftpusers WHERE uid='$cuid'");
434  return true;
435  }
436 
437  /* ----------------------------------------------------------------- */
438  /**
439  * Returns the used quota for the $name service for the current user.
440  * @param $name string name of the quota
441  * @return integer the number of service used or false if an error occured
442  * @access private
443  */
444  function hook_quota_get() {
445  global $db,$err,$cuid;
446  $err->log("ftp","getquota");
447  $q=Array("name"=>"ftp", "description"=>_("FTP accounts"), "used"=>0);
448  $db->query("SELECT COUNT(*) AS cnt FROM ftpusers WHERE uid='$cuid'");
449  if ($db->next_record()) {
450  $q['used']=$db->f("cnt");
451  }
452  return $q;
453  }
454 
455 
456  /* ----------------------------------------------------------------- */
457  /**
458  * Exporte toutes les informations ftp du compte AlternC
459  * @access private
460  * EXPERIMENTAL 'sid' function ;)
461  */
462  function alternc_export_conf() {
463  global $db,$err;
464  $err->log("ftp","export");
465  $f=$this->get_list();
466  $str=" <ftp>";
467  foreach ($f as $d=>$v) {
468  $str.=" <login>".($v["login"])."</login>\n";
469  $str.=" <password>".($v["encrypted_password"])."</password>\n";
470  $str.=" <directory>".($v["dir"])."<directory>\n";
471  }
472  $str.=" </ftp>\n";
473  return $str;
474  }
475 
476 
477  /* ----------------------------------------------------------------- */
478  /** hook function called by AlternC-upnp to know which open
479  * tcp or udp ports this class requires or suggests
480  * @return array a key => value list of port protocol name mandatory values
481  * @access private
482  */
483  function hook_upnp_list() {
484  return array(
485  "ftp" => array("port" => 21, "protocol" => "tcp", "mandatory" => 1),
486  );
487  }
488 
489 } /* Class m_ftp */
490