m_ftp.php 16 KB
Newer Older
1
2
<?php
/*
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
  ----------------------------------------------------------------------
  AlternC - Web Hosting System
  Copyright (C) 2000-2012 by the AlternC Development Team.
  https://alternc.org/
  ----------------------------------------------------------------------
  LICENSE
  
  This program is free software; you can redistribute it and/or
  modify it under the terms of the GNU General Public License (GPL)
  as published by the Free Software Foundation; either version 2
  of the License, or (at your option) any later version.
  
  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.
  
  To read the license please visit http://www.gnu.org/copyleft/gpl.html
  ----------------------------------------------------------------------
  Purpose of file: Manage FTP accounts
  ----------------------------------------------------------------------
24
*/
25

26
27
28
29
30
/**
* Classe de gestion des comptes FTP de l'hberg.
*/
class m_ftp {

31

32
  var $srv_name;
33
34
35
36
37
  /* ----------------------------------------------------------------- */
  /**
   * Constructeur
   */
  function m_ftp() {
38
    global $L_FQDN;
Alan Garcia's avatar
Alan Garcia committed
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
46
47
48
49
50

  /* ----------------------------------------------------------------- */
  /**
   * Password kind used in this class (hook for admin class)
   */
  function alternc_password_policy() {
    return array("ftp"=>"FTP accounts");
  }

51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
  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;
  }
83

84
85
86
87
88
89
90
91
  // 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();

92
93
    $tt = $this->get_list();
    if (empty($tt) || !is_array($tt)) return $c;
94
95
96
97
98
99
100
    foreach ($this->get_list() as $v ) {
      $c['values'][$v['id']]=$v['login'];
    }

    return $c;
  }

101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
  // 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 ;
    }
  }


125
126
127
128
129
130
131
132
133
134
  /* ----------------------------------------------------------------- */
  /** Retourne la liste des comptes FTP du compte hberg
   * 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 ou FALSE si une erreur s'est produite.
   */
  function get_list() {
135
    global $db,$err,$cuid, $bro;
136
137
    $err->log("ftp","get_list");
    $r=array();
138
    $db->query("SELECT id, name, homedir, enabled FROM ftpusers WHERE uid='$cuid' ORDER BY name;");
139
140
    if ($db->num_rows()) {
      while ($db->next_record()) {
141
142
143
	      $r[]=array(
		        "id"=>$db->f("id"),
		        "login"=>$db->f("name"),
144
		        "enabled"=>$db->f("enabled"),
145
146
		        //"dir"=>$match[1]
		        "dir"=>$db->f("homedir")
147
148
149
150
		   );
      }
      return $r;
    } else {
151
      $err->raise("ftp",_("No FTP account found"));
152
153
154
155
156
157
158
159
160
161
162
163
164
165
      return false;
    }
  }

  /* ----------------------------------------------------------------- */
  /** Retourne les dtails d'un compte FTP (voir get_list)
   * Le tableau est celui du compte d'id spcifi
   * @param integer $id Numro du compte dont on souhaite obtenir les dtails
   * @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();
166
    $db->query("SELECT id, name, homedir, enabled FROM ftpusers WHERE uid='$cuid' AND id='$id';");
167
168
    if ($db->num_rows()) {
      $db->next_record();
169
170
171
172

      $regexp="/^".preg_quote(getuserpath(),"/")."\/(.*)$/";
      $tr=preg_match($regexp, $db->f("homedir"),$match);

173
      $lg=explode("_",$db->f("name"));
174
      if ((!is_array($lg)) || (count($lg)!=2)) {
175
176
	      $lg[0]=$db->f("name");
	      $lg[1]="";
177
      }
178
      $r[]=array(
179
180
181
		   "id"=>$db->f("id"),
		   "prefixe"=> $lg[0],
		   "login"=>$lg[1],
182
183
		   "dir"=>$match[1],
		   "enabled"=>$db->f("enabled")
184
		   );
185
	return $r;
186
    } else {
187
      $err->raise("ftp",_("This FTP account does not exist"));
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
      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;
  }

Alan Garcia's avatar
Alan Garcia committed
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
  // Check if the login is fine (syntax)
  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;
     }
    }

    // Explicitly look for only allowed chars
    if ( ! preg_match("/^[A-Za-z0-9_\.\-]+$/", $l) ) { 
      $err->raise('ftp', _("FTP login is incorrect"));
      return false;
    }
    return true;
  }

230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
  /* ----------------------------------------------------------------- */
  /** Affiche (ECHO) la liste des prefixes disponibles sous forme de champs d'option
   * Les champs sont affichs sous la forme <option>prefixe</option>...
   * La valeur $current se voit affuble de la balise SELECTED.
   * @param string $current Prefixe slectionn par dfaut
   * @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 "<option$c>$val</option>";
    }
    return true;
  }

  /* ----------------------------------------------------------------- */
  /** Modifie les paramtres du comptes FTP $id.
   * @param integer $id Numro du compte dont on veut modifier les paramtres
   * @param string $prefixe Prefixe du compte FTP
   * @param string $login login ajout au prfixe ($prefixe_$login)
   * @param string $pass mot de passe
   * @param string $dir Rpertoire 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) {
257
    global $mem,$db,$err,$bro,$cuid,$admin;
258
259
260
261
    $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")) {
262
      $err->raise("ftp",_("This FTP account does not exist"));
263
264
265
266
267
268
269
270
      return false;
    }
    $dir=$bro->convertabsolute($dir);
    if (substr($dir,0,1)=="/") {
      $dir=substr($dir,1);
    }
    $r=$this->prefix_list();
    if (!in_array($prefixe,$r)) {
Alan Garcia's avatar
Alan Garcia committed
271
      $err->raise("ftp",_("The chosen prefix is not allowed"));
272
273
274
275
      return false;
    }
    $lo=$mem->user["login"];
    $l=substr($lo,0,1);
Alan Garcia's avatar
Alan Garcia committed
276
277
278
279
    $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';");
280
281
    $db->next_record();
    if ($db->f("cnt")) {
282
      $err->raise("ftp",_("This FTP account already exists"));
283
284
      return false;
    }
285
    $absolute=getuserpath()."/$dir";
286
287
288
289
    if (!file_exists($absolute)) {
      system("/bin/mkdir -p $absolute");
    }
    if (!is_dir($absolute)) {
290
      $err->raise("ftp",_("The directory cannot be created"));
291
292
      return false;
    }
293
    if (trim($pass)) {
294
295
296

      // Check this password against the password policy using common API : 
      if (is_callable(array($admin,"checkPolicy"))) {
297
298
299
        if (!$admin->checkPolicy("ftp",$prefixe.$login,$pass)) {
          return false; // The error has been raised by checkPolicy()
        }
300
      }
Alan Garcia's avatar
Alan Garcia committed
301
      $encrypted_password = _md5cr($pass,strrev(microtime(true)));
Alan Garcia's avatar
Alan Garcia committed
302
      $db->query("UPDATE ftpusers SET name='".$full_login."', password='', encrypted_password='$encrypted_password', homedir='$absolute', uid='$cuid' WHERE id='$id';");
303
    } else {
Alan Garcia's avatar
Alan Garcia committed
304
      $db->query("UPDATE ftpusers SET name='".$full_login."', homedir='$absolute', uid='$cuid' WHERE id='$id';");
305
306
307
308
    }
    return true;
  }

309

310
311
312
313
314
315
316
317
318
319
320
321
  /* ----------------------------------------------------------------- */
  /** Efface le compte ftp spcifi.
   * @param integer $id Numro 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) {
322
      $err->raise("ftp",_("This FTP account does not exist"));
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
      return false;
    }
    $db->query("DELETE FROM ftpusers WHERE id='$id'");
    return $name;
  }

  /* ----------------------------------------------------------------- */
  /** Cre 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 Rpertoire 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) {
339
    global $mem,$db,$err,$quota,$bro,$cuid,$admin;
340
341
342
343
344
345
    $err->log("ftp","add_ftp",$prefixe."_".$login);
    $dir=$bro->convertabsolute($dir);
    if (substr($dir,0,1)=="/") {
      $dir=substr($dir,1);
    }
    $r=$this->prefix_list();
346
347
348
349
    if (empty($pass)) {
      $err->raise("ftp",_("Password can't be empty"));
      return false;
    }
350
    if (!in_array($prefixe,$r) || $prefixe=="") {
Alan Garcia's avatar
Alan Garcia committed
351
      $err->raise("ftp",_("The chosen prefix is not allowed"));
352
353
      return false;
    }
Alan Garcia's avatar
Alan Garcia committed
354
355
356
357
    $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."'");
358
359
    $db->next_record();
    if ($db->f("cnt")) {
360
      $err->raise("ftp",_("This FTP account already exists"));
361
362
363
364
365
366
      return false;
    }
    $db->query("SELECT login FROM membres WHERE uid='$cuid';");
    $db->next_record();
    $lo=$db->f("login");
    $l=substr($lo,0,1);
367
    $absolute=getuserpath()."/$dir";
368
369
370
371
    if (!file_exists($absolute)) {
      system("/bin/mkdir -p $absolute");
    }
    if (!is_dir($absolute)) {
372
      $err->raise("ftp",_("The directory cannot be created"));
373
374
      return false;
    }
375
376
377

    // Check this password against the password policy using common API : 
    if (is_callable(array($admin,"checkPolicy"))) {
Alan Garcia's avatar
Alan Garcia committed
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")) {
Alan Garcia's avatar
Alan Garcia committed
384
      $encrypted_password = _md5cr($pass,strrev(microtime(true)));
Alan Garcia's avatar
Alan Garcia committed
385
      $db->query("INSERT INTO ftpusers (name,password, encrypted_password,homedir,uid) VALUES ('".$full_login."', '', '$encrypted_password', '$absolute', '$cuid')");
386
387
      return true;
    } else {
388
      $err->raise("ftp",_("Your FTP account quota is over. You cannot create more ftp accounts"));
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
      return false;
    }
  }

  /* ----------------------------------------------------------------- */
  /** Retourne TRUE si $dir possde 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);
    $lo=$mem->user["login"];
    $l=substr($lo,0,1);
    if (substr($dir,0,1)=="/") $dir=substr($dir,1);
404
    $db->query("SELECT id FROM ftpusers WHERE homedir='".getuserpath()."/$dir';");
405
406
407
408
409
410
411
412
413
    if ($db->num_rows()) {
      $db->next_record();
      return $db->f("id");
    } else {
      return false;
    }
  }

  /* ----------------------------------------------------------------- */
414
  /** Fonction appelle par domains quand un domaine est supprim pour le membre
415
416
417
418
419
   * @param string $dom Domaine  dtruire.
   * @access private
   */
  function alternc_del_domain($dom) {
    global $db,$err,$cuid;
420
421
    $err->log("ftp","alternc_del_domain",$dom);
    $db->query("DELETE FROM ftpusers WHERE uid='$cuid' AND ( name LIKE '$dom\_%' OR name LIKE '$dom') ");
422
423
424
425
426
427
428
429
430
431
    return true;
  }

  /* ----------------------------------------------------------------- */
  /** Fonction appelle par membres quand un membre est effac.
   * @param integer $uid Numro de membre effac.
   * @access private
   */
  function alternc_del_member() {
    global $db,$err,$cuid;
432
    $err->log("ftp","alternc_del_member");
433
434
435
436
437
438
439
440
441
442
443
    $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
   */
444
  function hook_quota_get() {
445
    global $db,$err,$cuid;
446
447
448
449
450
451
452
    $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;
453
454
455
456
457
458
459
460
461
  }


  /* ----------------------------------------------------------------- */
  /**
   * Exporte toutes les informations ftp du compte AlternC
   * @access private
   * EXPERIMENTAL 'sid' function ;) 
   */
462
  function alternc_export_conf() {
463
464
465
    global $db,$err;
    $err->log("ftp","export");
    $f=$this->get_list();
466
    $str="  <ftp>";
467
    foreach ($f as $d=>$v) {
468
469
470
      $str.="   <login>".($v["login"])."</login>\n";
      $str.="   <password>".($v["encrypted_password"])."</password>\n";
      $str.="   <directory>".($v["dir"])."<directory>\n";
471
    }
472
    $str.=" </ftp>\n";
473
474
    return $str;
  }
475
476
477
478
479
480
481
482
483
484
485
486
487


  /* ----------------------------------------------------------------- */
  /** 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),
		 );
  }  
488
489
490
  
} /* Class m_ftp */