Alternc  3.2
Alternc logiel libre pour l'hébergement
 All Data Structures Namespaces Files Functions Variables Pages
m_admin.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: Administrate members and rights.
23  ----------------------------------------------------------------------
24 */
25 
26 /* ----------------------------------------------------------------- */
27 
28 /**
29 * Manage the AlternC's account administration (create/edit/delete)
30 */
31 class m_admin {
32 
33 
34  /* ----------------------------------------------------------------- */
35  /** $enabled tells if the logged user is super-admin or not
36  */
37  var $enabled=0;
38 
39  /* ----------------------------------------------------------------- */
40  /** List of the controls made for each TLD
41  *
42  * $tldmode is used by the administration panel, while choosing
43  * the authorized TLDs. It's an array of strings explaining the current state of the TLD.
44  */
45  public $tldmode=array();
46 
47  var $archive='';
48 
49 
50  /* ----------------------------------------------------------------- */
51  /** Constructor
52  */
53  function m_admin() {
54  global $db,$cuid;
55  $db->query("SELECT su FROM membres WHERE uid='$cuid';");
56  $db->next_record();
57  $this->enabled=$db->f("su");
58 
59  $this->tldmode=array(
60  0 => _("This TLD is forbidden"),
61  1 => _("primary DNS is checked in WHOIS db"),
62  2 => _("primary & secondary DNS are checked in WHOIS db"),
63  3 => _("Domain must exist, but don't do any DNS check"),
64  4 => _("Domain can be installed, no check at all"),
65  5 => _("Domain can be installed, force NO DNS hosting"),
66  );
67  $this->archive=variable_get('archive_del_data','','If folder specified html folder of deleted user is archived, else it is deleted. ');
68  }
69 
70  function hook_menu() {
71  global $mem, $cuid, $debug_alternc, $L_INOTIFY_UPDATE_DOMAIN;
72  if (!$mem->checkRight()) return false;
73 
74  $obj = array(
75  'title' => _("Administration"),
76  'ico' => 'images/admin.png',
77  'link' => 'toggle',
78  'class' => 'adminmenu',
79  'pos' => 10,
80  'links' =>
81  array(
82  array(
83  'txt' => _("Manage AlternC accounts"),
84  'url' => 'adm_list.php',
85  'class' => 'adminmenu'
86  ),
87  array(
88  'txt' => _("User Quotas"),
89  'url' => 'quotas_users.php?mode=4',
90  'class' => 'adminmenu'
91  ),
92  )
93  ) ;
94 
95  if ($cuid == 2000) {
96  $obj['links'][] =
97  array(
98  'txt' => _("Admin Control Panel"),
99  'url' => 'adm_panel.php',
100  'class' => 'adminmenu'
101  );
102  $obj['links'][] =
103  array(
104  'txt' => _("PhpMyAdmin"),
105  'url' => '/alternc-sql/',
106  'class' => 'adminmenu',
107  'target' => '_blank',
108  );
109  $obj['links'][] =
110  array(
111  'txt' => ($debug_alternc->status)?_("Switch debug Off"):_("Switch debug On"),
112  'url' => "alternc_debugme.php?enable=".($debug_alternc->status?"0":"1"),
113  'class' => 'adminmenu'
114  );
115  if (empty($L_INOTIFY_UPDATE_DOMAIN) || file_exists("$L_INOTIFY_UPDATE_DOMAIN") ) {
116  $obj['links'][] =
117  array(
118  'txt' => _("Applying..."),
119  'url' => 'javascript:alert(\''._("Domain changes are already applying").'\');',
120  'class' => 'adminmenu',
121  );
122  } else {
123  $obj['links'][] =
124  array(
125  'txt' => _("Apply changes"),
126  'url' => 'adm_update_domains.php',
127  'class' => 'adminmenu',
128  'onclick' => 'return confirm("'.addslashes(_("Server configuration changes are applied every 5 minutes. Do you want to do it right now?")).'");',
129  );
130 
131  } // L_INOTIFY_UPDATE_DOMAIN
132 
133  } // cuid == 2000
134 
135 
136  return $obj;
137  }
138 
139  function stop_if_jobs_locked() {
140  if ( file_exists(ALTERNC_LOCK_JOBS)) {
141  echo "There is a file ".ALTERNC_LOCK_JOBS."\n";
142  echo "So no jobs are allowed\n";
143  echo "Did you launch alternc.install ?\n";
144  die();
145  }
146  }
147 
148  # return the uid of an alternc account
149  function get_uid_by_login($login) {
150  global $db;
151  $db->query("SELECT uid FROM membres WHERE login='$login';");
152  if (! $db->next_record()) {
153  return null;
154  }
155  return $db->f('uid');
156  }
157 
158 
159  /* ----------------------------------------------------------------- */
160  /** Returns the known information about a hosted account
161  *
162  * Returns all what we know about an account (contents of the tables
163  * <code>membres</code> et <code>local</code>)
164  * Ckecks if the account is super-admin
165  * @param integer $uid a unique integer identifying the account
166  * @return an associative array containing all the fields of the
167  * table <code>membres</code> and <code>local</code> of the corresponding account.
168  * Returns FALSE if an error occurs.
169  */
170  function get($uid,$recheck=false) {
171  global $err,$db,$lst_users_properties;
172  // $err->log("admin","get",$uid);
173  if (!$this->enabled) {
174  $err->raise("admin",_("-- Only administrators can access this page! --"));
175  return false;
176  }
177 
178  if (!isset($lst_users_properties) || empty($lst_users_properties) || !is_array($lst_users_properties) || $recheck ) {
179  $lst_users_properties=array();
180  $db->query("
181  SELECT
182  m.uid as muid,
183  l.*,
184  m.*,
185  parent.login as parentlogin,
186  dbs.name as db_server_name,
187  m.renewed + INTERVAL m.duration MONTH as expiry,
188  CASE
189  WHEN m.duration IS NULL THEN 0
190  WHEN m.renewed + INTERVAL m.duration MONTH <= NOW() THEN 3
191  WHEN m.renewed <= NOW() THEN 2
192  ELSE 1 END 'status'
193 
194  FROM membres as m
195  LEFT JOIN membres as parent ON (parent.uid = m.creator)
196  LEFT JOIN db_servers as dbs ON (m.db_server_id = dbs.id)
197  LEFT JOIN local as l ON (m.uid = l.uid) ;");
198  while ($db->next_record()) {
199  $lst_users_properties[$db->f('muid')]=$db->Record;
200  }
201  }
202 
203  if ( !isset($lst_users_properties[$uid]) ) {
204  if ( !$recheck ) {
205  // don't exist, but is not a forced check. Do a forced check
206  return $this->get($uid, true);
207  }
208  $err->raise("admin",_("Account not found"));
209  return false;
210  }
211 
212  return $lst_users_properties[$uid];
213  }
214 
215 
216  /* ----------------------------------------------------------------- */
217  /** Returns the known information about a specific hosted account
218  * Similar to get_list() but for creators/resellers.
219  */
220  function get_creator($uid) {
221  global $err,$db;
222  // $err->log("admin","get",$uid);
223  if (!$this->enabled) {
224  $err->raise("admin",_("-- Only administrators can access this page! --"));
225  return false;
226  }
227 
228  $db->query("SELECT m.*, parent.login as parentlogin FROM membres as m LEFT JOIN membres as parent ON (parent.uid = m.creator) WHERE m.uid='$uid';");
229 
230  if ($db->num_rows()) {
231  $db->next_record();
232  $c=$db->Record;
233  } else {
234  $err->raise("admin",_("Account not found"));
235  return false;
236  }
237 
238  $db->query("SELECT * FROM local WHERE uid='$uid';");
239  if ($db->num_rows()) {
240  $db->next_record();
241  reset($db->Record);
242  while (list($key,$val)=each($db->Record)) {
243  $c[$key]=$val;
244  }
245  }
246 
247  $db->query("SELECT count(*) as nbcreated FROM membres WHERE creator='$uid';");
248  if ($db->num_rows()) {
249  $db->next_record();
250  reset($db->Record);
251  while (list($key,$val)=each($db->Record)) {
252  $c[$key]=$val;
253  }
254  }
255 
256  return $c;
257  }
258 
259 
260  /* ----------------------------------------------------------------- */
261  /** @return TRUE if there's only ONE admin account
262  * @return boolean TRUE if there is only one admin account
263  * (allow the program to prevent the destruction of the last admin account)
264  */
265  function onesu() {
266  global $db;
267  $db->query("SELECT COUNT(*) AS cnt FROM membres WHERE su=1");
268  $db->next_record();
269  return ($db->f("cnt")==1);
270  }
271 
272 
273  /* ----------------------------------------------------------------- */
274  /** Returns the list of the hosted accounts
275  *
276  * Returns all what we know about ALL the accounts (contents of the tables
277  * <code>membres</code> et <code>local</code>)
278  * Check for super-admin accounts
279  * @param
280  * @return an associative array containing all the fields of the
281  * table <code>membres</code> and <code>local</code> of all the accounts.
282  * Returns FALSE if an error occurs.
283  */
284  function get_list($all=0,$creator=0,$pattern=FALSE,$pattern_type=FALSE) {
285  global $err,$mem,$cuid;
286  $err->log("admin","get_list");
287  if (!$this->enabled) {
288  $err->raise("admin",_("-- Only administrators can access this page! --"));
289  return false;
290  }
291  $db=new DB_System();
292 
293 
294  if ($pattern) {
295 
296  if ($pattern_type === 'domaine') {
297 
298  $request = 'SELECT compte AS uid FROM domaines WHERE 1';
299 
300  if ($pattern && preg_match('/[.a-zA-Z0-9]+/', $pattern))
301  $request .= sprintf(' AND domaine LIKE "%%%s%%"', $pattern);
302 
303  if ($creator)
304  $request .= sprintf(' AND compte in (select uid from membres where creator = "%s" ) ', $creator);
305 
306  if ($mem->user['uid']!=2000 && !$all)
307  $request .= sprintf(' AND compte in (select uid from membres where creator = "%s") ', $cuid);
308 
309 
310  $request .= ' GROUP BY uid';
311 
312  } elseif ($pattern_type === 'login') {
313 
314  $request = 'SELECT uid FROM membres WHERE 1';
315 
316  if ($pattern && preg_match('/[a-zA-Z0-9]+/', $pattern))
317  $request .= sprintf(' AND login LIKE "%%%s%%"', $pattern);
318 
319  if ($creator)
320  $request .= sprintf(' AND creator = "%s"', $creator);
321 
322  if ($mem->user['uid']!=2000 && !$all)
323  $request .= sprintf(' AND creator = "%s"', $cuid);
324 
325  $request .= ' ORDER BY login;';
326 
327  } else {
328 
329  $err->raise("admin", _("Invalid pattern type provided. Are you even performing a legitimate action?"));
330  return FALSE;
331 
332  }
333 
334  } else {
335 
336  if ($creator)
337  {
338  // Limit listing to a specific reseller
339  $request = "SELECT uid FROM membres WHERE creator='".$creator."' ORDER BY login;";
340  } elseif ($mem->user['uid']==2000 || $all) {
341  $request = "SELECT uid FROM membres ORDER BY login;";
342  } else {
343  $request = "SELECT uid FROM membres WHERE creator='".$cuid."' ORDER BY login;";
344  }
345  }
346 
347  $db->query($request);
348 
349  if ($db->num_rows()) {
350  while ($db->next_record()) {
351  $c[$db->f("uid")]=$this->get($db->f("uid"));
352  }
353  return $c;
354  } else {
355  return false;
356  }
357  }
358 
359 
360  /* ----------------------------------------------------------------- */
361  /** Send an email to all AlternC's accounts
362  * @param $subject string Subject of the email to send
363  * @param $message string Message to send
364  * @param $from string expeditor of that email.
365  * @return true if the mail has been successfully sent.
366  */
367  function mailallmembers($subject,$message,$from) {
368  global $err,$mem,$cuid,$db;
369  $err->log("admin","mailallmembers");
370  if (!$this->enabled) {
371  $err->raise("admin",_("-- Only administrators can access this page! --"));
372  return false;
373  }
374  $subject=trim($subject);
375  $message=trim($message);
376  $from=trim($from);
377 
378  if (empty($subject) || empty($message) || empty($from) ){
379  $err->raise("admin",_("Subject, message and sender are mandatory"));
380  return false;
381  }
382 
383  if (checkmail($from) != 0) {
384  $err->raise("admin",_("Sender is syntaxically incorrect"));
385  return false;
386  }
387 
388  @set_time_limit(1200);
389  $db->query("SELECT DISTINCT mail FROM membres WHERE mail!='';");
390  while ($db->next_record()) {
391  // Can't do BCC due to postfix limitation
392  // FIXME: use phpmailer, far better for mass-mailing than sendmail (reply-to issue among others)
393  mail($db->f('mail'), $subject, $message, null, "-f$from");
394  }
395  return true;
396  }
397 
398 
399  /* ----------------------------------------------------------------- */
400  /** Returns an array with the known information about resellers (uid, login, number of accounts)
401  * Does not include account 2000 in the list.
402  * May only be called by the admin account (2000)
403  * If there are no reseller accounts, returns an empty array.
404  */
405  function get_creator_list() {
406  global $err,$mem,$cuid;
407 
408  $creators = array();
409 
410  $err->log("admin","get_reseller_list");
411  if (!$this->enabled || $cuid!=2000) {
412  $err->raise("admin",_("-- Only administrators can access this page! --"));
413  return false;
414  }
415 
416  $db=new DB_System();
417  $db->query("SELECT DISTINCT creator FROM membres WHERE creator <> 0 ORDER BY creator ASC;");
418  if ($db->num_rows()) {
419  while ($db->next_record()) {
420  $creators[] = $this->get_creator($db->f("creator"));
421  }
422  }
423  $creators2 = array();
424  foreach ($creators as $cc ) {
425  $creators2[$cc['uid']] = $cc;
426  }
427  return $creators2;
428  }
429 
430  /* ----------------------------------------------------------------- */
431  /** Check if I am the creator of the member $uid
432  * @param integer $uid a unique integer identifying the account
433  * @return boolean TRUE if I am the creator of that account. FALSE else.
434  */
435  function checkcreator($uid) {
436  global $err,$mem,$db,$cuid;
437  if ($cuid==2000) {
438  return true;
439  }
440  $db->query("SELECT creator FROM membres WHERE uid='$uid';");
441  $db->next_record();
442  if ($db->Record["creator"]!=$cuid) {
443  $err->raise("admin",_("-- Only administrators can access this page! --"));
444  return false;
445  }
446  return true;
447  }
448 
449  // When the admin want to delegate a subdomain to an account
450  function add_shared_domain($u, $domain_name) {
451  global $db,$err,$dom,$mem,$cuid;
452  $err->log("admin","add_shared_domain",$u."/".$domain_name);
453 
454  if (! $mem->checkright() ) {
455  $err->raise("admin",_("-- Only administrators can do that! --"));
456  return false;
457  }
458 
459  // Check if this domain exist on this admin account
460  if (! in_array($domain_name, $dom->enum_domains())) {
461  $err->raise("admin",_("You don't seem to be allowed to delegate this domain"));
462  $err->log("admin","add_shared_domain","domain not allowed");
463  return false;
464  }
465 
466  // Clean the domain_name
467  $domain_name=preg_replace("/^\.\.*/", "", $domain_name);
468 
469  $mem->su($u);
470  $dom->lock();
471  // option : 1=hébergement dns, 1=noerase, empeche de modifier, 1=force
472  $dom->add_domain($mem->user['login'].".".$domain_name,1,1,1);
473  $dom->unlock();
474  $mem->unsu();
475  return true;
476  }
477 
478  /* ----------------------------------------------------------------- */
479  /** Creates a new hosted account
480  *
481  * Creates a new hosted account (in the tables <code>membres</code>
482  * and <code>local</code>). Prevents any manipulation of the account if
483  * the account $mid is not super-admin.
484  *
485  * @param $login string Login name like [a-z][a-z0-9]*
486  * @param $pass string Password (max. 64 characters)
487  * @param $nom string Name of the account owner
488  * @param $prenom string First name of the account owner
489  * @param $mail string Email address of the account owner, useful to get
490  * one's lost password
491  * @pararm $type string Account type for quotas
492  * @return boolean Returns FALSE if an error occurs, TRUE if not.
493  */
494  function add_mem($login, $pass, $nom, $prenom, $mail, $canpass=1, $type='default', $duration=0, $notes = "", $force=0, $create_dom=false, $db_server_id) {
495  global $err,$quota,$classes,$cuid,$mem,$L_MYSQL_DATABASE,$L_MYSQL_LOGIN,$hooks,$action;
496  $err->log("admin","add_mem",$login."/".$mail);
497  if (!$this->enabled) {
498  $err->raise("admin",_("-- Only administrators can access this page! --"));
499  return false;
500  }
501  if (empty($db_server_id)) {
502  $err->raise("admin",_("Missing db_server field"));
503  return false;
504  }
505  if (($login=="")||($pass=="")) {
506  $err->raise("admin",_("All fields are mandatory"));
507  return false;
508  }
509  if (!$force) {
510  if ($mail=="") {
511  $err->raise("admin",_("All fields are mandatory"));
512  return false;
513  }
514  if (checkmail($mail)!=0){
515  $err->raise("admin",_("Please enter a valid email address"));
516  return false;
517  }
518  }
519  $login=strtolower($login);
520  if (!preg_match("#^[a-z0-9]+$#",$login)) { //$
521  $err->raise("admin", _("Login can only contains characters a-z and 0-9"));
522  return false;
523  }
524  if (strlen($login) > 14) {
525  // Not an arbitrary value : MySQL user names can be up to 16 characters long
526  // If we want to allow people to create a few mysql_user (and we want to!)
527  // we have to limit the login lenght
528  $err->raise("admin",_("The login is too long (14 chars max)"));
529  return false;
530  }
531  // Some login are not allowed...
532  if ($login==$L_MYSQL_DATABASE || $login==$L_MYSQL_LOGIN || $login=="mysql" || $login=="root") {
533  $err->raise("admin",_("Login can only contains characters a-z, 0-9 and -"));
534  return false;
535  }
536  $pass=_md5cr($pass);
537  $db=new DB_System();
538  $notes = mysql_real_escape_string($notes);
539  // Already exist?
540  $db->query("SELECT count(*) AS cnt FROM membres WHERE login='$login';");
541  $db->next_record();
542  if (!$db->f("cnt")) {
543  $db->query("SELECT max(m.uid)+1 as nextid FROM membres m");
544  if (!$db->next_record()) {
545  $uid=2000;
546  } else {
547  $uid=$db->Record["nextid"];
548  if ($uid<=2000) $uid=2000;
549  }
550  $db->query("INSERT INTO membres (uid,login,pass,mail,creator,canpass,type,created,notes,db_server_id) VALUES ('$uid','$login','$pass','$mail','$cuid','$canpass', '$type', NOW(), '$notes', '$db_server_id');");
551  $db->query("INSERT INTO local(uid,nom,prenom) VALUES('$uid','$nom','$prenom');");
552  $this->renew_update($uid, $duration);
553  #exec("sudo /usr/lib/alternc/mem_add ".$login." ".$uid);
554  $action->create_dir(getuserpath("$login"));
555  $action->fix_user($uid);
556 
557  // Triggering hooks
558  $mem->su($uid);
559  // TODO: old hook method FIXME: when unused remove this
560  /*
561  foreach($classes as $c) {
562  if (method_exists($GLOBALS[$c],"alternc_add_member")) {
563  $GLOBALS[$c]->alternc_add_member();
564  }
565  }
566  */
567  $hooks->invoke("alternc_add_member");
568  // New hook way
569  $hooks->invoke("hook_admin_add_member", array(), array('quota')); // First !!! The quota !!! Etherway, we can't be sure to be able to create all
570  $hooks->invoke("hook_admin_add_member");
571  $mem->unsu();
572 
573  if (!empty($create_dom)) {
574  $this->add_shared_domain($uid, $create_dom);
575  }
576 
577  return $uid;
578  } else {
579  $err->raise("admin",_("This login already exists"));
580  return false;
581  }
582  }
583 
584  /* ----------------------------------------------------------------- */
585  /** AlternC's standard function called when a user is created
586  * This sends an email if configured through the interface.
587  */
589  global $err, $cuid, $L_FQDN, $L_HOSTING;
590  $dest = variable_get('new_email', '0', 'An email will be sent to this address when new accounts are created if set.', array(array('desc'=>'Enabled','type'=>'boolean')));
591  if (!$dest) {
592  return false;
593  }
594  $db=new DB_System();
595  if (!$db->query("SELECT m.*, parent.login as parentlogin FROM membres m LEFT JOIN membres parent ON parent.uid=m.creator WHERE m.uid='$cuid'")) {
596  $err->raise("admin",sprintf(_("query failed: %s "), $db->Error));
597  return false;
598  }
599  if ($db->next_record()) {
600  // TODO: put that string into gettext !
601  $mail = <<<EOF
602 A new AlternC account was created on %fqdn by %creator.
603 
604 Account details
605 ---------------
606 
607 login: %login (%uid)
608 email: %mail
609 createor: %creator (%cuid)
610 can change password: %canpass
611 type: %type
612 notes: %notes
613 EOF;
614  $mail = strtr($mail, array('%fqdn' => $L_FQDN,
615  '%creator' => $db->Record['parentlogin'],
616  '%uid' => $db->Record['uid'],
617  '%login' => $db->Record['login'],
618  '%mail' => $db->Record['mail'],
619  '%cuid' => $db->Record['creator'],
620  '%canpass' => $db->Record['canpass'],
621  '%type' => $db->Record['type'],
622  '%notes' => $db->Record['notes']));
623  $subject=sprintf(_("New account %s from %s on %s"), $db->Record['login'], $db->Record['parentlogin'], $L_HOSTING);
624  if (mail($dest,$subject,$mail,"From: postmaster@$L_FQDN")) {
625  //sprintf(_("Email successfully sent to %s"), $dest);
626  return true;
627  } else {
628  $err->raise("admin",sprintf(_("Cannot send email to %s"), $dest));
629  return false;
630  }
631  } else {
632  $err->raise("admin",sprintf(_("Query failed: %s"), $db->Error));
633  return false;
634  }
635  }
636 
637 
638  /* ----------------------------------------------------------------- */
639  /** Edit an account
640  *
641  * Change an account (in the tables <code>membres</code>
642  * and <code>local</code>). Prevents any manipulation of the account if
643  * the account $mid is not super-admin.
644  *
645  * @param $uid integer the uid number of the account we want to modify
646  * @param login string new login name like [a-z][a-z0-9]*
647  * @param $pass string new password (max. 64 characters)
648  * @param $nom string new name of the account owner
649  * @param $prenom string new first name of the account owner
650  * @param $mail string new email address of the account owner
651  * @param $enabled integer (value: 0 or 1) activates or desactivates the
652  * @param $type string new type of account
653  * access to the virtual desktop of this account.
654  * @return boolean Returns FALSE if an error occurs, TRUE if not.
655  */
656  function update_mem($uid, $mail, $nom, $prenom, $pass, $enabled, $canpass, $type='default', $duration=0, $notes = "",$reset_quotas=false) {
657  global $err,$db;
658  global $cuid, $quota;
659 
660  $notes=addslashes($notes);
661 
662  $err->log("admin","update_mem",$uid);
663  if (!$this->enabled) {
664  $err->raise("admin",_("-- Only administrators can access this page! --"));
665  return false;
666  }
667  $db=new DB_System();
668  if ($pass) {
669  $pass=_md5cr($pass);
670  $ssq=" ,pass='$pass' ";
671  } else {
672  $ssq="";
673  }
674 
675  $old_mem = $this->get($uid);
676 
677  if (($db->query("UPDATE local SET nom='$nom', prenom='$prenom' WHERE uid='$uid';"))
678  &&($db->query("UPDATE membres SET mail='$mail', canpass='$canpass', enabled='$enabled', `type`='$type', notes='$notes' $ssq WHERE uid='$uid';"))){
679  if($reset_quotas == "on" || $type != $old_mem['type'] ) {
680  $quota->addquotas();
681  $quota->synchronise_user_profile();
682  }
683  $this->renew_update($uid, $duration);
684  return true;
685  }
686  else {
687  $err->raise("admin",_("Account not found"));
688  return false;
689  }
690  }
691 
692 
693  /* ----------------------------------------------------------------- */
694  /** Lock an account
695  * Lock an account and prevent the user to access its account.
696  * @param $uid integer the uid number of the account we want to lock
697  * @return boolean Returns FALSE if an error occurs, TRUE if not.
698  */
699  function lock_mem($uid) {
700  global $err,$db;
701  $err->log("admin","lock_mem",$uid);
702  if (!$this->enabled) {
703  $err->raise("admin",_("-- Only administrators can access this page! --"));
704  return false;
705  }
706  $db=new DB_System();
707  if ($db->query("UPDATE membres SET enabled='0' WHERE uid='$uid';")) {
708  return true;
709  }
710  else {
711  $err->raise("admin",_("Account not found"));
712  return false;
713  }
714  }
715 
716 
717  /* ----------------------------------------------------------------- */
718  /** UnLock an account
719  * UnLock an account and prevent the user to access its account.
720  * @param $uid integer the uid number of the account we want to unlock
721  * @return boolean Returns FALSE if an error occurs, TRUE if not.
722  */
723  function unlock_mem($uid) {
724  global $err,$db;
725  $err->log("admin","unlock_mem",$uid);
726  if (!$this->enabled) {
727  $err->raise("admin",_("-- Only administrators can access this page! --"));
728  return false;
729  }
730  $db=new DB_System();
731  if ($db->query("UPDATE membres SET enabled='1' WHERE uid='$uid';")) {
732  return true;
733  }
734  else {
735  $err->raise("admin",_("Account not found"));
736  return false;
737  }
738  }
739 
740 
741  /* ----------------------------------------------------------------- */
742  /** Deletes an account
743  * Deletes the specified account. Prevents any manipulation of the account if
744  * the account $mid is not super-admin.
745  * @param $uid integer the uid number of the account we want to delete
746  * @return boolean Returns FALSE if an error occurs, TRUE if not.
747  */
748  function del_mem($uid) {
749  global $err,$quota,$classes,$cuid,$mem,$dom,$hooks,$action;
750  $err->log("admin","del_mem",$uid);
751 
752  if (!$this->enabled) {
753  $err->raise("admin",_("-- Only administrators can access this page! --"));
754  return false;
755  }
756  $db=new DB_System();
757  $tt=$this->get($uid);
758 
759  $mem->su($uid);
760  // This script may take a long time on big accounts, let's give us some time ... Fixes 1132
761  @set_time_limit(0);
762  // WE MUST call m_dom before all others because of conflicts ...
763  $dom->hook_admin_del_member();
764 
765  # New way of deleting or backup delted user html folders using action class
766  $path=getuserpath($tt['login']);
767  $action->archive($path);
768 
769  $hooks->invoke("alternc_del_member");
770  $hooks->invoke("hook_admin_del_member");
771 
772  if (($db->query("DELETE FROM membres WHERE uid='$uid';")) &&
773  ($db->query("DELETE FROM local WHERE uid='$uid';"))) {
774  $mem->unsu();
775  // If this user was (one day) an administrator one, he may have a list of his own accounts. Let's associate those accounts to nobody as a creator.
776  $db->query("UPDATE membres SET creator=2000 WHERE creator='$uid';");
777  return true;
778  } else {
779  $err->raise("admin",_("Account not found"));
780  $mem->unsu();
781  return false;
782  }
783  }
784 
785 
786  /* ----------------------------------------------------------------- */
787  /** Renew an account
788  * Renew an account for its duration
789  * @param $uid integer the uid number of the account we want to renew
790  * @param $periods integer the number of periods we renew for
791  * @return boolean Returns FALSE if an error occurs, TRUE if not.
792  */
793  function renew_mem($uid, $periods=1) {
794  global $err,$db;
795 
796  $periods = intval($periods);
797  if($periods == 0)
798  return false;
799 
800  $query = "UPDATE membres SET renewed = renewed + INTERVAL (duration * $periods) MONTH WHERE uid=${uid};";
801  if ($db->query($query)) {
802  return true;
803  } else {
804  $err->raise("admin",_("Account not found"));
805  return false;
806  }
807  }
808 
809 
810  /* ----------------------------------------------------------------- */
811  /** Update the duration information for an account
812  * @param $uid integer the uid number of the account we want to update
813  * @param $duration integer the new duration, in months, of the account
814  * @return boolean Returns FALSE if an error occurs, TRUE if not.
815  */
816  function renew_update($uid, $duration) {
817  global $err,$db;
818 
819  if($duration == 0) {
820  if($db->query("UPDATE membres SET duration = NULL, renewed = NULL WHERE uid=$uid;"))
821  return true;
822  } else {
823  if($db->query("UPDATE membres SET duration = $duration WHERE uid=$uid") &&
824  $db->query("UPDATE membres SET renewed = NOW() WHERE uid=$uid and renewed is null;"))
825  return true;
826  }
827 
828  $err->raise("admin",_("Account not found"));
829  return false;
830  }
831 
832 
833  /* ----------------------------------------------------------------- */
834  /** Get the expiry date for an account
835  * @param $uid integer The uid number of the account
836  * @return string The expiry date, a string as printed by MySQL
837  */
838  function renew_get_expiry($uid) {
839  $jj=$this->get($uid);
840  if ( isset($jj) && isset($jj['expiry']) && ! empty($jj['expiry']) ) {
841  return $jj['expiry'];
842  }
843  return '';
844  }
845 
846 
847  /* ----------------------------------------------------------------- */
848  /** Get the expiry status for an account
849  * @param $uid integer The uid number of the account
850  * @return integer The expiry status:
851  * 0: account does not expire
852  * 1: expires in more than duration,
853  * 2: expires within the duration
854  * 3: has expired past the duration
855  */
856  function renew_get_status($uid) {
857  $jj=$this->get($uid);
858 
859  if ( isset($jj) && isset($jj['status']) && ! empty($jj['status']) ) {
860  return $jj['status'];
861  }
862 
863  return 0;
864  }
865 
866 
867  /* ----------------------------------------------------------------- */
868  /** Get the expired/about to expire accounts.
869  * @return resource The recordset of the corresponding accounts
870  */
872  global $db;
873 
874  if(!$db->query("SELECT *, m.renewed + INTERVAL duration MONTH 'expiry'," .
875  " CASE WHEN m.duration IS NULL THEN 0" .
876  " WHEN m.renewed + INTERVAL m.duration MONTH <= NOW() THEN 3" .
877  " WHEN m.renewed <= NOW() THEN 2" .
878  " ELSE 1 END 'status' FROM membres m, local l" .
879  " WHERE m.uid = l.uid" .
880  " HAVING status=2 or status=3 ORDER BY status DESC, expiry;"))
881  return false;
882  else {
883  $res=array();
884  while($db->next_record())
885  $res[] = $db->Record;
886  return $res;
887  }
888  }
889 
890 
891  /* ----------------------------------------------------------------- */
892  /** Turns a common account into a super-admin account
893  * @param $uid integer the uid number of the common account we want to turn into a
894  * super-admin account.
895  * @return Returns FALSE if an error occurs, TRUE if not.
896  */
897  function normal2su($uid) {
898  global $err,$db;
899  $db->query("SELECT su FROM membres WHERE uid='$uid';");
900  if (!$db->next_record()) {
901  $err->raise("admin",_("Account not found"));
902  return false;
903  }
904  if ($db->Record["su"]!=0) {
905  $err->raise("admin",_("This account is ALREADY an administrator account"));
906  return false;
907  }
908  $db->query("UPDATE membres SET su=1 WHERE uid='$uid';");
909  return true;
910  }
911 
912 
913  /* ----------------------------------------------------------------- */
914  /** Turns a super-admin account into a common account
915  * @param $uid integer the uid number of the super-admin account we want to turn into a
916  * common account.
917  * @return boolean Returns FALSE if an error occurs, TRUE if not.
918  */
919  function su2normal($uid) {
920  global $err,$db;
921  $db->query("SELECT su FROM membres WHERE uid='$uid';");
922  if (!$db->next_record()) {
923  $err->raise("admin",_("Account not found"));
924  return false;
925  }
926  if ($db->Record["su"]!=1) {
927  $err->raise("admin",_("This account is NOT an administrator account!"));
928  return false;
929  }
930  $db->query("UPDATE membres SET su=0 WHERE uid='$uid';");
931  return true;
932  }
933 
934 
935  /* ----------------------------------------------------------------- */
936  /** List of the authorized TLDs
937  * Returns the list of the authorized TLDs and also the way they are
938  * authorized. A TLD is the last members (or the last two) of a
939  * domain. For example, "com", "org" etc... AlternC keeps a table
940  * containing the list of the TLDs authorized to be installed on the
941  * server with the instructions to validate the installation of a
942  * domain for each TLD (if necessary).
943  * @return array An associative array like $r["tld"], $r["mode"] where tld
944  * is the tld and mode is the authorized mode.
945  */
946  function listtld() {
947  global $db;
948  $db->query("SELECT tld,mode FROM tld ORDER BY tld;");
949  while ($db->next_record()) {
950  $c[]=$db->Record;
951  }
952  return $c;
953  }
954 
955 
956  /* ----------------------------------------------------------------- */
957  /** List the hosted domains on this server
958  * Return the list of hosted domains on this server, (an array of associative arrays)
959  * @param boolean $alsocheck Returns also errstr and errno telling the domains dig checks
960  * @param boolean $forcecheck Force the check of dig domain even if a cache exists.
961  * @return array $r[$i] / [domaine][member][noerase][gesdns][gesmx]
962  */
963  function dom_list($alsocheck=false,$forcecheck=false) {
964  global $db;
965  $cachefile="/tmp/alternc_dig_check_cache";
966  $cachetime=3600; // The dns cache file can be up to 1H old
967  if ($alsocheck) {
968  if (!$forcecheck && file_exists($cachefile) && filemtime($cachefile)+$cachetime>time()) {
969  $checked=unserialize(file_get_contents($cachefile));
970  } else {
971  // TODO : do the check here (cf checkdom.php) and store it in $checked
972  $checked=$this->checkalldom();
973  file_put_contents($cachefile,serialize($checked));
974  }
975  }
976  $db->query("SELECT m.uid,m.login,d.domaine,d.gesdns,d.gesmx,d.noerase FROM domaines d LEFT JOIN membres m ON m.uid=d.compte ORDER BY domaine;");
977  while ($db->next_record()) {
978  $tmp=$db->Record;
979  if ($alsocheck) {
980  $tmp["errstr"]=$checked[$tmp["domaine"]]["errstr"];
981  $tmp["errno"]=$checked[$tmp["domaine"]]["errno"];
982  }
983  $c[]=$tmp;
984  }
985  return $c;
986  }
987 
988 
989  /* ----------------------------------------------------------------- */
990  /** Check all the domains for their NS MX and IPs
991  */
992  function checkalldom() {
993  global $db,$L_NS1,$L_NS2,$L_MX,$L_PUBLIC_IP;
994  $checked=array();
995  $r=$db->query("SELECT * FROM domaines ORDER BY domaine;");
996  $dl=array();
997  while ($db->next_record()) {
998  $dl[$db->Record["domaine"]]=$db->Record;
999  }
1000  sort($dl);
1001  foreach($dl as $c) {
1002  // For each domain check its type:
1003  $errno=0;
1004  $errstr="";
1005  $dontexist=false;
1006  // Check the domain.
1007  if ($c["gesdns"]==1) {
1008  // Check the NS pointing to us
1009  $out=array();
1010  exec("dig +short NS ".escapeshellarg($c["domaine"]),$out);
1011  if (count($out)==0) {
1012  $dontexist=true;
1013  } else {
1014  if (!in_array($L_NS1.".",$out) || !in_array($L_NS2.".",$out)) {
1015  $errno=1; $errstr.="NS for this domain are not $L_NS1 and $L_NS2 BUT ".implode(",",$out)."\n";
1016  }
1017  }
1018  }
1019  if ($c["gesmx"]==1 && !$dontexist) {
1020  $out=array();
1021  exec("dig +short MX ".escapeshellarg($c["domaine"]),$out);
1022  $out2=array();
1023  foreach($out as $o) {
1024  list($t,$out2[])=explode(" ",$o);
1025  }
1026  if (!in_array($L_MX.".",$out2)) {
1027  $errno=1; $errstr.="MX is not $L_MX BUT ".implode(",",$out2)."\n";
1028  }
1029  }
1030  if (!$dontexist) {
1031  // We list all subdomains and check they are pointing to us.
1032  $db->query("SELECT * FROM sub_domaines WHERE domaine='".addslashes($c["domaine"])."' ORDER BY sub;");
1033  while ($db->next_record()) {
1034  $d=$db->Record;
1035  if ($d["type"]==0) {
1036  // Check the IP:
1037  $out=array();
1038  exec("dig +short A ".escapeshellarg($d["sub"].(($d["sub"]!="")?".":"").$c["domaine"]),$out);
1039  if (!in_array($L_PUBLIC_IP,$out)) {
1040  $errstr.="subdomain '".$d["sub"]."' don't point to $L_PUBLIC_IP but to ".implode(",",$out)."\n";
1041  $errno=1;
1042  }
1043  }
1044  }
1045  }
1046  if ($dontexist) {
1047  $errno=2;
1048  $errstr="Domain don't exist anymore !";
1049  }
1050  if ($errno==0) $errstr="OK";
1051  $checked[$c["domaine"]]=array("errno"=>$errno, "errstr"=>$errstr);
1052  }
1053  return $checked;
1054  }
1055 
1056 
1057  /* ----------------------------------------------------------------- */
1058  /** Lock / Unlock a domain
1059  * Lock (or unlock) a domain, so that the member will be (not be) able to delete it
1060  * from its account
1061  * @param $dom string Domain name to lock / unlock
1062  * @return boolean TRUE if the domain has been locked/unlocked or FALSE if it does not exist.
1063  */
1064  function dom_lock($domain) {
1065  global $db,$err;
1066  $db->query("SELECT compte FROM domaines WHERE domaine='$domain';");
1067  if (!$db->next_record()) {
1068  $err->raise("dom",_("Domain '%s' not found."),$domain);
1069  return false;
1070  }
1071  $db->query("UPDATE domaines SET noerase=1-noerase WHERE domaine='$domain';");
1072  return true;
1073  }
1074 
1075 
1076  /* ----------------------------------------------------------------- */
1077  /** Add a new TLD to the list of the authorized TLDs
1078  *
1079  * @param $tld string top-level domain to add (org, com...)
1080  * @param $mode integer number of the authorized mode (0 to 5)
1081  * @return boolean TRUE if the tld has been successfully added, FALSE if not.
1082  */
1083  function gettld($tld) {
1084  global $db,$err;
1085  $db->query("SELECT mode FROM tld WHERE tld='$tld';");
1086  if (!$db->next_record()) {
1087  $err->raise("admin",_("This TLD does not exist"));
1088  return false;
1089  }
1090  return $db->Record["mode"];
1091  }
1092 
1093 
1094  /* ----------------------------------------------------------------- */
1095  /** Prints the list of the actually authorized TLDs
1096  * @param $current integer Value to select in the list
1097  */
1098  function selecttldmode($current=false) {
1099  for($i=0;$i<count($this->tldmode);$i++) {
1100  echo "<option value=\"$i\"";
1101  if ($current==$i) echo " selected=\"selected\"";
1102  echo ">"._($this->tldmode[$i])."</option>\n";
1103  }
1104  }
1105 
1106 
1107  /* ----------------------------------------------------------------- */
1108  /** Deletes the specified tld in the list of the authorized TLDs
1109  * <b>Note</b> : This function does not delete the domains depending
1110  * on this TLD
1111  * @param $tld string The TLD you want to delete
1112  * @return boolean returns true if the TLD has been deleted, or
1113  * false if an error occured.
1114  */
1115  function deltld($tld) {
1116  global $db,$err;
1117  $db->query("SELECT tld FROM tld WHERE tld='$tld';");
1118  if (!$db->next_record()) {
1119  $err->raise("admin",_("This TLD does not exist"));
1120  return false;
1121  }
1122  $db->query("DELETE FROM tld WHERE tld='$tld';");
1123  return true;
1124  }
1125 
1126 
1127  /* ----------------------------------------------------------------- */
1128  /** Add a TLD to the list of the authorized TLDs during the installation
1129  * @param $tld string TLD we want to authorize
1130  * @param $mode integer Controls to make on this TLD.
1131  * <b>Note: </b> If you check in the whois, be sure that
1132  * <code>m_domains</code> knows how to name the whois of the specified
1133  * domain !
1134  * @return boolean TRUE if the TLD has been successfully
1135  * added. FALSE if not.
1136  */
1137  function addtld($tld,$mode) {
1138  global $db,$err;
1139  if (!$tld) {
1140  $err->raise("admin",_("The TLD name is mandatory"));
1141  return false;
1142  }
1143  $tld=trim($tld);
1144 
1145  $db->query("SELECT tld FROM tld WHERE tld='$tld';");
1146  if ($db->next_record()) {
1147  $err->raise("admin",_("This TLD already exist"));
1148  return false;
1149  }
1150  if (substr($tld,0,1)==".") $tld=substr($tld,1);
1151  $mode=intval($mode);
1152  if ($mode==0) $mode="0";
1153  $db->query("INSERT INTO tld (tld,mode) VALUES ('$tld','$mode');");
1154  return true;
1155  }
1156 
1157 
1158  /* ----------------------------------------------------------------- */
1159  /** Modify a TLD of the list of the authorized TLDs
1160  * @param $tld string TLD we want to modify
1161  * @param $mode integer Controls to make on this TLD.
1162  * @return boolean TRUE if the TLD has been successfully
1163  * modified. FALSE if not.
1164  */
1165  function edittld($tld,$mode) {
1166  global $db,$err;
1167  $db->query("SELECT tld FROM tld WHERE tld='$tld';");
1168  if (!$db->next_record()) {
1169  $err->raise("admin",_("This TLD does not exist"));
1170  return false;
1171  }
1172  $mode=intval($mode);
1173  if ($mode==0) $mode="0";
1174  $db->query("UPDATE tld SET mode='$mode' WHERE tld='$tld';");
1175  return true;
1176  }
1177 
1178 
1179  /* ----------------------------------------------------------------- */
1180  /** Get the login name of the main administrator account
1181  * @return string the login name of admin, like 'root' for older alterncs
1182  */
1183  function getadmin() {
1184  global $db;
1185  $db->query("SELECT login FROM membres WHERE uid = '2000';");
1186  $db->next_record();
1187  return $db->f("login");
1188  }
1189 
1190 
1191  /* ----------------------------------------------------------------- */
1192  /** List the password policies currently installed in the policy table
1193  * @return array an indexed array of associative array from the MySQL "policy" table
1194  */
1196  global $db,$classes,$hooks;
1197  $tmp1=array();
1198  $tmp2=array();
1199  $tmp3=array();
1200  $policies=array();
1201  $db->query("SELECT * FROM policy;");
1202  while ($db->next_record()) {
1203  $tmp1[$db->Record["name"]]=$db->Record;
1204  }
1205 /* * /
1206  foreach($classes as $c) {
1207  if (method_exists($GLOBALS[$c],"alternc_password_policy")) {
1208  $res=$GLOBALS[$c]->alternc_password_policy(); // returns an array
1209  foreach($res as $k=>$v) {
1210  $tmp2[$k]=$v;
1211  }
1212  }
1213  }
1214 /* */
1215  $tmp3=$hooks->invoke("alternc_password_policy");
1216  foreach ($tmp3 as $v) {
1217  foreach ($v as $l=>$m) {
1218  $tmp2[$l]=$m;
1219  }
1220  }
1221  foreach($tmp2 as $k=>$v) {
1222  if (!isset($tmp1[$k])) {
1223  // Default policy :
1224  $db->query("INSERT INTO policy SET name='".addslashes($k)."', minsize=0, maxsize=64, classcount=0, allowlogin=0;");
1225  $tmp1[$k]=array(
1226  "minsize"=>0, "maxsize"=>64, "classcount"=>0, "allowlogin"=>0
1227  );
1228  }
1229  $policies[$k]=$tmp1[$k];
1230  $policies[$k]["description"]=_($v);
1231  unset($tmp1[$k]);
1232  }
1233  foreach ($tmp1 as $k=>$v) {
1234  // Delete disabled modules :
1235  $db->query("DELETE FROM policy WHERE name='".addslashes($k)."';");
1236  }
1237  return $policies;
1238  }
1239 
1240 
1241  /* ----------------------------------------------------------------- */
1242  /** Change a password policy for one kind of password
1243  *
1244  * @param $policy string Name of the policy to edit
1245  * @param $minsize integer Minimum Password size
1246  * @param $maxsize integer Maximum Password size
1247  * @param $classcount integer How many class of characters must this password have
1248  * @param $allowlogin boolean Do we allow the password to be like the login ?
1249  * @return boolean TRUE if the policy has been edited, or FALSE if an error occured.
1250  */
1251  function editPolicy($policy,$minsize,$maxsize,$classcount,$allowlogin) {
1252  global $db;
1253  $minsize=intval($minsize);
1254  $maxsize=intval($maxsize);
1255  $classcount=intval($classcount);
1256  $allowlogin=intval($allowlogin);
1257 
1258  $db->query("SELECT * FROM policy WHERE name='".addslashes($policy)."';");
1259  if (!$db->next_record()) {
1260  return false; // Policy not found
1261  }
1262  if ($minsize<0 || $minsize>64 || $maxsize<0 || $maxsize>64 || $maxsize<$minsize || $classcount<0 || $classcount>4) {
1263  return false; // Incorrect policy ...
1264  }
1265  $allowlogin=($allowlogin)?1:0;
1266  $db->query("UPDATE policy SET minsize=$minsize, maxsize=$maxsize, classcount=$classcount, allowlogin=$allowlogin WHERE name='".addslashes($policy)."';");
1267  return true;
1268  }
1269 
1270 
1271  /* ----------------------------------------------------------------- */
1272  /** Check a password and a login for a specific policy
1273  * @param $policy string Name of the policy to check for
1274  * @param $login The login that will be set
1275  * @param $password The password we have to check
1276  * @return boolean TRUE if the password if OK for this login and this policy, FALSE if it is not.
1277  */
1278  function checkPolicy($policy,$login,$password) {
1279  global $db,$err;
1280 
1281  if (empty($login)) {
1282  $err->raise("admin",_("Please enter a login"));
1283  return false;
1284  }
1285  if (empty($password)) {
1286  $err->raise("admin",_("Please enter a password"));
1287  return false;
1288  }
1289 
1290  $pol=$this->listPasswordPolicies();
1291  if (!$pol[$policy]) {
1292  $err->raise("admin",_("-- Program error -- The requested password policy does not exist!"));
1293  return false;
1294  }
1295  $pol=$pol[$policy];
1296  // Ok, now let's check it :
1297  $plen=strlen($password);
1298 
1299  if ($plen<$pol["minsize"]) {
1300  $err->raise("admin",_("The password length is too short according to the password policy"));
1301  return false;
1302  }
1303 
1304  if ($plen>$pol["maxsize"]) {
1305  $err->raise("admin",_("The password is too long according to the password policy"));
1306  return false;
1307  }
1308 
1309  if (!$pol["allowlogin"]) {
1310  // We do misc check on password versus login :
1311  $logins=explode("@",$login);
1312  $logins[]=$login;
1313  foreach($logins as $l) {
1314  if (strpos($password,$l)!==false) {
1315  $err->raise("admin",_("The password policy prevents you to use your login name inside your password"));
1316  return false;
1317  }
1318  }
1319  }
1320 
1321  if ($pol["classcount"]>0) {
1322  $cls=array(0,0,0,0,0);
1323  for($i=0;$i<strlen($password);$i++) {
1324  $p=substr($password,$i,1);
1325  if (strpos("abcdefghijklmnopqrstuvwxyz",$p)!==false) {
1326  $cls[0]=1;
1327  } elseif (strpos("ABCDEFGHIJKLMNOPQRSTUVWXYZ",$p)!==false) {
1328  $cls[1]=1;
1329  } elseif (strpos("0123456789",$p)!==false) {
1330  $cls[2]=1;
1331  } elseif (strpos('!"#$%&\'()*+,-./:;<=>?@[\\]^_`',$p)!==false) {
1332  $cls[3]=1;
1333  } else {
1334  $cls[4]=1;
1335  }
1336  } // foreach
1337  $clc=array_sum($cls);
1338  if ($clc<$pol["classcount"]) {
1339  $err->raise("admin",_("Your password contains not enough different classes of character, between low-case, up-case, figures and special characters."));
1340  return false;
1341  }
1342  }
1343  return true; // congratulations !
1344  }
1345 
1346 
1347  /* ----------------------------------------------------------------- */
1348  /** hook function called by AlternC-upnp to know which open
1349  * tcp or udp ports this class requires or suggests
1350  * @return array a key => value list of port protocol name mandatory values
1351  * @access private
1352  */
1353  function hook_upnp_list() {
1354  return array(
1355  "http" => array("port" => 80, "protocol" => "tcp", "mandatory" => 1),
1356  "https" => array("port" => 443, "protocol" => "tcp", "mandatory" => 0),
1357  "ssh" => array("port" => 22, "protocol" => "tcp", "mandatory" => 0),
1358  );
1359  }
1360 
1361 
1362 } /* Classe ADMIN */
1363