diff --git a/THIRDPARTY b/THIRDPARTY
index c55f1ff8..64d673bf 100644
--- a/THIRDPARTY
+++ b/THIRDPARTY
@@ -25,4 +25,10 @@ https://github.com/aFarkas/html5shiv
These icons are public domain, and as such are free for any use (attribution appreciated but not required).
Note that these flags are named using the ISO3166-1 alpha-2 country codes where appropriate. A list of codes can be found at http://en.wikipedia.org/wiki/ISO_3166-1_alpha-2
If you find these icons useful, please donate via paypal to mjames@gmail.com (or click the donate button available at http://www.famfamfam.com/lab/icons/silk)
-Contact: mjames@gmail.com
\ No newline at end of file
+Contact: mjames@gmail.com
+
+* password_compat
+https://github.com/ircmaxell/password_compat/
+Copyright Anthony Ferrara
+Released under the MIT license
+http://opensource.org/licenses/MIT
\ No newline at end of file
diff --git a/external/api_users.php b/external/api_users.php
index 3d7c5783..fccc8054 100644
--- a/external/api_users.php
+++ b/external/api_users.php
@@ -89,8 +89,9 @@
$pdo=new PDO("mysql:host=".$config['dbHost'].";dbname=".$config['dbName'],$config['dbUser'],$config['dbPwd'],array(PDO::MYSQL_ATTR_INIT_COMMAND=>"SET NAMES utf8"));
$pdo->setAttribute(PDO::ATTR_ERRMODE,PDO::ERRMODE_EXCEPTION);
- // JSON array
- $json=array();
+ // Define default values so we always have a proper return
+ $total = 0;
+ $json = array();
// User export
if ($list == 'user') {
@@ -148,20 +149,21 @@
} else if ($config['sourceType']=='teklab') {
// Get amount of users that are new or received an update
- // The Query needs to be altered to your database. This is just an example!
$sql="SELECT COUNT(`id`) AS `amount` FROM `{$config['tblPrefix']}_members`
WHERE `rank`=1";
$query=$pdo->prepare($sql);
- $query->execute(array($lastID,$updateTime));
+ $query->execute();
$total=$query->fetchColumn();
+ // users
$sql = "SELECT * FROM `{$config['tblPrefix']}_members`
WHERE `rank`=1
LIMIT $start,$chunkSize";
$query=$pdo->prepare($sql);
- $query->execute(array($lastID,$updateTime));
+ $query->execute();
foreach ($query->fetchAll(PDO::FETCH_ASSOC) as $row) {
- // Easy-Wi stores the salutation with numbers
+
+ // Teklab has also 3 for company which Easy-WI currently does not maintain
if ($row['title'] == 0) {
$salutation = 1;
} else if ($row['title']==1) {
@@ -170,6 +172,7 @@
$salutation = null;
}
+ // Easy-WI uses ISO code for storing countries
if ($row['country'] == 1) {
$country = 'de';
} else if ($row['country'] == 2) {
@@ -182,8 +185,9 @@
$country = null;
}
+ // Street and streetnumber are stored in the same column Easy-WI has individual columns
$exploded = explode(" ", $row['street']);
- if (count($exploded) > 2) {
+ if (count($exploded) > 1) {
$streetNumber = $exploded[count($exploded) - 1];
unset($exploded[count($exploded) - 1]);
$streetName = implode(' ', $exploded);
@@ -191,8 +195,7 @@
$streetName = null;
$streetNumber = null;
}
-
- // the keys needs to be adjusted to your table layout and query!
+
$json[]=array(
'externalID' => $row['id'],
'salutation' => $salutation,
@@ -215,29 +218,114 @@
);
}
}
- // Echo the JSON reply with
- echo json_encode(array('total' => $total,'entries' => $json));
- } else if ($list == 'substitutes' and $config['sourceType']=='teklab') {
- die;
-
- } else if ($list == 'dedicated' and $config['sourceType']=='teklab') {
- die;
+
+/**
+ } else if ($list == 'gameroots') {
+ if ($config['sourceType']=='teklab') {
+
+ // Get amount of users that are new or received an update
+ $sql="SELECT COUNT(`id`) AS `amount` FROM `{$config['tblPrefix']}_rootserver`
+ WHERE `active`=1
+ AND `games`=1";
+ $query=$pdo->prepare($sql);
+ $query->execute();
+ $total=$query->fetchColumn();
+
+ // users
+ $sql = "SELECT * FROM `{$config['tblPrefix']}_rootserver`
+ WHERE `active`=1
+ AND `games`=1
+ LIMIT $start,$chunkSize";
+ $query=$pdo->prepare($sql);
+ $query->execute();
+ foreach ($query->fetchAll(PDO::FETCH_ASSOC) as $row) {
+ $json[]=array(
+ 'externalID' => $row['id'],
+ 'userID' => $row['memberid'],
+ 'description' => $row['name'],
+ 'serverIP' => $row['serverip'],
+ 'sshPort' => $row['sshport'],
+ 'ftpPort' => $row['ftpport'],
+ 'cpuCores' => $row['cpucores'],
+ 'maxRam' => $row['ram']
+ );
+ }
- } else if ($list == 'gameserver' and $config['sourceType']=='teklab') {
- die;
+ }
+
+ } else if ($list == 'gameimages') {
+ if ($config['sourceType']=='teklab') {
- } else if ($list == 'voice' and $config['sourceType']=='teklab') {
- die;
+ }
+
+ } else if ($list == 'gameserver') {
+ if ($config['sourceType']=='teklab') {
- } else if ($list == 'node' and $config['sourceType']=='teklab') {
- die;
+ }
+/**
+ } else if ($list == 'addons') {
+
+ if ($config['sourceType']=='teklab') {
+
+ // Get amount of users that are new or received an update
+ $sql="SELECT COUNT(`id`) AS `amount` FROM `{$config['tblPrefix']}_games_addons`";
+ $query=$pdo->prepare($sql);
+ $query->execute();
+ $total=$query->fetchColumn();
+
+ // users
+ $sql = "SELECT * FROM `{$config['tblPrefix']}_games_addons`
+ LIMIT $start,$chunkSize";
+ $query=$pdo->prepare($sql);
+ $query->execute();
+ foreach ($query->fetchAll(PDO::FETCH_ASSOC) as $row) {
+ $json[]=array(
+ 'externalID' => $row['id'],
+ 'name' => $row['addonname'],
+ 'description' => $row['text'],
+ 'shortName' => $row['sname']
+ );
+ }
- } else if ($list == 'virt' and $config['sourceType']=='teklab') {
- die;
+ }
+**/
+ } else if ($list == 'voice') {
+ if ($config['sourceType']=='teklab') {
+ }
+
+ // Substitutes at last so we can get access permissions as well
+ } else if ($list == 'substitutes') {
+ if ($config['sourceType']=='teklab') {
+
+ // Get amount of users that are new or received an update
+ $sql="SELECT COUNT(`id`) AS `amount` FROM `{$config['tblPrefix']}_subusers`";
+ $query=$pdo->prepare($sql);
+ $query->execute();
+ $total=$query->fetchColumn();
+
+ // users
+ $sql = "SELECT * FROM `{$config['tblPrefix']}_subusers`
+ LIMIT $start,$chunkSize";
+ $query=$pdo->prepare($sql);
+ $query->execute();
+ foreach ($query->fetchAll(PDO::FETCH_ASSOC) as $row) {
+ $json[]=array(
+ 'externalID' => $row['id'],
+ 'belongsToExternalID' => $row['memberid'],
+ 'loginName' => $row['user'],
+ 'firstName' => null,
+ 'lastName' => null,
+ 'password' => $row['password']
+ );
+ }
+ }
}
- // Catch database error and display
+ // Echo the JSON reply with
+ echo json_encode(array('total' => $total,'entries' => $json));
+
+ // Catch database error and display
} catch(PDOException $error) {
echo json_encode(array('error' => $error->getMessage()));
}
diff --git a/web/login.php b/web/login.php
index 0848b808..978c478c 100644
--- a/web/login.php
+++ b/web/login.php
@@ -50,6 +50,7 @@
include(EASYWIDIR . '/stuff/vorlage.php');
include(EASYWIDIR . '/stuff/class_validator.php');
+include(EASYWIDIR . '/third_party/password_compat/password.php');
include(EASYWIDIR . '/stuff/functions.php');
include(EASYWIDIR . '/stuff/settings.php');
include(EASYWIDIR . '/stuff/keyphrasefile.php');
@@ -150,19 +151,17 @@
} else if ($ui->password('password1', 255, 'post') and $ui->password('password2', 255, 'post') and $ui->w('token', 32, 'get')) {
- if ($ui->password('password1', 255, 'post')==$ui->password('password2', 255, 'post')) {
+ if ($ui->password('password1', 255, 'post') == $ui->password('password2', 255, 'post')) {
$query = $sql->prepare("SELECT `id`,`cname` FROM `userdata` WHERE `token`=? LIMIT 1");
$query->execute(array($ui->w('token',32, 'get')));
foreach ($query->fetchAll(PDO::FETCH_ASSOC) as $row) {
$username = $row['cname'];
- $salt = md5(mt_rand() . date('Y-m-d H:i:s:u'));
- $password = createHash($username, $ui->password('password1', 255, 'post'), $salt, $aeskey);
$text = $sprache->passwordreseted;
- $query2 = $sql->prepare("UPDATE `userdata` SET `token`='',`security`=?,`salt`=? WHERE `id`=? LIMIT 1");
- $query2->execute(array($password, $salt, $row['id']));
+ $query2 = $sql->prepare("UPDATE `userdata` SET `token`='',`security`=? WHERE `id`=? LIMIT 1");
+ $query2->execute(array(password_hash($ui->password('password1', 255, 'post'), PASSWORD_DEFAULT), $row['id']));
}
} else if ($ui->password('password1', 255, 'post') != $ui->password('password2', 255, 'post')) {
@@ -223,36 +222,15 @@
$id = $row['id'];
$active = $row['active'];
$mail = $row['mail'];
- $salt = $row['salt'];
$externalID = $row['externalID'];
- $security = $row['security'];
$resellerid = $row['resellerid'];
$accounttype = $row['accounttype'];
- $userpassNew = createHash($username, $password, $salt, $aeskey);
+ $passwordCorrect = passwordCheck($password, $row['security'], $row['cname'], $row['salt'], $aeskey);
- if (isset($security) and $security != $userpassNew) {
-
- $userpassOld = passwordhash($username, $password);
-
- // some systems do not care about security at all.
- // In case we imported users from such insecure implementations we need to migrate to something safe
- $md5Import = md5($password);
-
- if ($userpassOld == $security or $md5Import == $security) {
-
- $salt = md5(mt_rand() . date('Y-m-d H:i:s:u'));
- $userpass = ($userpassOld == $security) ? $userpassOld : $security;
-
- $query = $sql->prepare("UPDATE `userdata` SET `security`=?,`salt`=? WHERE `id`=? LIMIT 1");
- $query->execute(array(createHash($username, $password, $salt, $aeskey), $salt, $id));
-
- } else {
- $userpass = $userpassNew;
- }
-
- } else {
- $userpass = $userpassNew;
+ if ($passwordCorrect !== true and $passwordCorrect !== false) {
+ $query = $sql->prepare("UPDATE `userdata` SET `security`=? WHERE `id`=? LIMIT 1");
+ $query->execute(array($passwordCorrect, $id));
}
}
@@ -267,14 +245,18 @@
$id = $row['userID'];
$username = $row['loginName'];
$active = $row['active'];
- $salt = $row['salt'];
- $security = $row['passwordHashed'];
$resellerid = $row['resellerID'];
- $userpass = createHash($username, $password, $salt, $aeskey);
+
+ $passwordCorrect = passwordCheck($password, $row['passwordHashed'], $row['loginName'], $row['salt'], $aeskey);
+
+ if ($passwordCorrect !== true and $passwordCorrect !== false) {
+ $query = $sql->prepare("UPDATE `userdata_substitutes` SET `passwordHashed`=? WHERE `sID`=? LIMIT 1");
+ $query->execute(array($passwordCorrect, $sID));
+ }
}
}
- if (isset($active) and $active == 'Y' and $security != $userpass) {
+ if (!isset($sID) and isset($active) and $active == 'Y' and isset($passwordCorrect) and $passwordCorrect === false) {
$authLookupID = ($resellerid == $id) ? 0 : $resellerid;
@@ -316,11 +298,10 @@
if ($xmlReply and isset($xmlReply->success) and $xmlReply->success == 1 and $xmlReply->user == $username) {
- $externalOK = 1;
- $salt = md5(mt_rand() . date('Y-m-d H:i:s:u'));
+ $passwordCorrect = true;
- $query = $sql->prepare("UPDATE `userdata` SET `security`=?,`salt`=? WHERE `id`=? LIMIT 1");
- $query->execute(array(createHash($username, $password, $salt, $aeskey), $salt, $id));
+ $query = $sql->prepare("UPDATE `userdata` SET `security`=? WHERE `id`=? LIMIT 1");
+ $query->execute(array(password_hash($password, PASSWORD_DEFAULT), $id));
} else if ($xmlReply and isset($xmlReply->error)) {
$externalAuthError = $xmlReply->error;
@@ -331,7 +312,7 @@
}
}
- if (isset($active) and $active == 'Y' and ($security == $userpass or (isset($externalOK) and $externalOK == 1))) {
+ if (isset($active) and $active == 'Y' and isset($passwordCorrect) and $passwordCorrect) {
session_unset();
session_destroy();
@@ -433,7 +414,7 @@
}
}
- } else if (!isset($security) or $security != $userpass) {
+ } else if (!isset($passwordCorrect) or $passwordCorrect === false) {
$halfhour = date('Y-m-d H:i:s', strtotime('+30 minutes'));
diff --git a/web/stuff/api_users.php b/web/stuff/api_users.php
index ec3a6ce8..4105d5cb 100644
--- a/web/stuff/api_users.php
+++ b/web/stuff/api_users.php
@@ -38,6 +38,7 @@
*/
include(EASYWIDIR . '/stuff/keyphrasefile.php');
+include(EASYWIDIR . '/third_party/password_compat/password.php');
$username = '';
$externalID = '';
@@ -170,11 +171,14 @@
}
if (!isset($success) and isset($localID) and isset($insert) and $insert == true) {
if (!isset($data['password']) or in_array($data['password'],$bad)) $password=passwordgenerate(10);
- $passwordhash=createHash($username,$password,$salt,$aeskey);
+
$query = $sql->prepare("UPDATE `userdata` SET `cname`=?,`security`=? WHERE `id`=? LIMIT 1");
- $query->execute(array($username,$passwordhash,$localID));
+ $query->execute(array($username, password_hash($password, PASSWORD_DEFAULT), $localID));
+
$query = $sql->prepare("INSERT INTO `userdata_groups` (`userID`,`groupID`,`resellerID`) VALUES (?,?,?)");
- foreach ($userGroupIDs as $groupID) $query->execute(array($localID,$groupID,$resellerID));
+ foreach ($userGroupIDs as $groupID) {
+ $query->execute(array($localID, $groupID, $resellerID));
+ }
} else if (!isset($success)) {
$success['false'][] = 'Could not write user to database';
}
@@ -200,10 +204,7 @@
$what = array();
if (isset($data['password']) and !in_array($data['password'],$bad)) {
$password = $data['password'];
- $salt=md5(mt_rand().date('Y-m-d H:i:s:u'));
- $security=createHash($name,$data['password'],$salt,$aeskey);
- $what['security'] = $security;
- $what['salt'] = $salt;
+ $what['security'] = password_hash($password, PASSWORD_DEFAULT);
}
if (isset($data['email']) and ismail($data['email'])) {
$what['mail'] = $data['email'];
diff --git a/web/stuff/functions.php b/web/stuff/functions.php
index fbe5d6ad..0c60d8c3 100644
--- a/web/stuff/functions.php
+++ b/web/stuff/functions.php
@@ -89,7 +89,25 @@ function createHash ($name, $pwd, $saltOne, $saltTwo = 'ZPZw$[pkJF!;SHdl', $iter
return false;
}
-
+
+ function passwordCheck ($password, $storedHash, $username = '', $salt = '', $aeskey = '') {
+
+ // Easy-WI uses the PHP hash API introduced with version 5.5.
+
+ // Return true in case the password is ok
+ if (password_verify($password, $storedHash)) {
+ return true;
+
+ // Password is correctly but stored in an old or insecure format. We need to hash it with a secure implementation.
+ // Insecure implementations like md5 or sha1 are imported from other systems with the cloud.php job.
+ } else if (createHash($username, $password, $salt, $aeskey) == $storedHash or md5($password) == $storedHash or sha1($password) == $storedHash or passwordhash($username, $password) == $storedHash) {
+ return password_hash($password, PASSWORD_DEFAULT);
+ }
+
+ // Password Is Not Correct
+ return false;
+ }
+
function szrp ($value) {
$szrm = array('ä' => 'ae','ö' => 'oe','ü' => 'ue','Ä' => 'Ae','Ö' => 'Oe','Ü' => 'Ue','ß' => 'ss','á' => 'a','à' => 'a','Á' => 'A','À' => 'A','é' => 'e','è' => 'e','É' => 'E','È' => 'E','ó' => 'o','ò' => 'o','Ó' => 'O','Ò' => 'O','ú' => 'u','ù' => 'u','Ú' => 'U','Ù' => 'U');
return strtolower(preg_replace('/[^a-zA-Z0-9]{1}/', '-', strtr($value, $szrm)));
diff --git a/web/stuff/global_userdata.php b/web/stuff/global_userdata.php
index 44269fb0..212d3388 100644
--- a/web/stuff/global_userdata.php
+++ b/web/stuff/global_userdata.php
@@ -36,6 +36,7 @@
*/
include(EASYWIDIR . '/stuff/keyphrasefile.php');
+include(EASYWIDIR . '/third_party/password_compat/password.php');
if ($ui->st('w', 'get') == 'se') {
if ((!isset($user_id) or $main!=1) or (isset($user_id) and !$pa['usersettings'])) {
@@ -88,10 +89,8 @@
$query = $sql->prepare("SELECT `cname` FROM `userdata` WHERE `id`=? AND `resellerid`=? LIMIT 1");
$query->execute(array($lookUpID,$reseller_id));
$cname = $query->fetchColumn();
- $salt = md5(mt_rand().date('Y-m-d H:i:s:u'));
- $security = createHash($cname,$ui->password('pass2', 255, 'post'),$salt,$aeskey);
- $query = $sql->prepare("UPDATE `userdata` SET `updateTime`=NOW(),`security`=?,`salt`=? WHERE `id`=? AND `resellerid`=? LIMIT 1");
- $query->execute(array($security,$salt,$lookUpID,$reseller_id));
+ $query = $sql->prepare("UPDATE `userdata` SET `updateTime`=NOW(),`security`=? WHERE `id`=? AND `resellerid`=? LIMIT 1");
+ $query->execute(array(password_hash($password, PASSWORD_DEFAULT) ,$lookUpID,$reseller_id));
if ($query->rowCount()>0) {
$template_file = $spracheResponse->table_add;
$loguseraction="%psw% %user% $cname";
diff --git a/web/stuff/page_register.php b/web/stuff/page_register.php
index 82d1345b..ba137e99 100644
--- a/web/stuff/page_register.php
+++ b/web/stuff/page_register.php
@@ -1,4 +1,5 @@
.
*/
+include(EASYWIDIR . '/third_party/password_compat/password.php');
+
if (!isset($page_include) or (isset($user_id)) or isset($admin_id) or isset($reseller_id)) {
if (isset($page_data->canurl)) header('Location: '.$page_data->canurl);
else header('Location: index.php');
@@ -159,11 +162,8 @@
$query->execute(array($mail,$activeHash,$salutation = $ui->id('salutation',1, 'post'),$ui->st('country', 'post'),$name,$vname,$bday,$ui->phone('phone',50, 'post'),$ui->phone('fax',50, 'post'),$ui->phone('handy',50, 'post'),$ui->names('city',50, 'post'),$ui->id('cityn',6, 'post'),$ui->names('street',50, 'post'),$ui->w('streetn',6, 'post')));
$userID = $sql->lastInsertId();
- // generate hash
- $hash=createHash($rSA['prefix2'].$userID,$ui->password('password',100, 'post'),$userSalt,$aeskey);
-
$query = $sql->prepare("UPDATE `userdata` SET `cname`=?,`security`=?,`salt`=? WHERE `id`=? LIMIT 1");
- $query->execute(array($rSA['prefix2'].$userID,$hash,$userSalt,$userID));
+ $query->execute(array($rSA['prefix2'].$userID, password_hash($ui->password('password', 100, 'post'), PASSWORD_DEFAULT), $userID));
// Setup default Group
$query = $sql->prepare("SELECT `id` FROM `usergroups` WHERE `grouptype`='u' AND `active`='Y' AND `defaultgroup`='Y' AND `resellerid`=0 LIMIT 1");
diff --git a/web/stuff/user.php b/web/stuff/user.php
index e0481ae1..8d917c09 100644
--- a/web/stuff/user.php
+++ b/web/stuff/user.php
@@ -1,4 +1,5 @@
.
*/
+
+include(EASYWIDIR . '/stuff/keyphrasefile.php');
+include(EASYWIDIR . '/third_party/password_compat/password.php');
+
if ((!isset($admin_id) or $main!=1) or (isset($admin_id) and !$pa['user'] and !$pa['user_users'])) {
header('Location: admin.php');
die();
}
-include(EASYWIDIR . '/stuff/keyphrasefile.php');
$sprache = getlanguagefile('user',$user_language,$reseller_id);
$rsprache = getlanguagefile('reseller',$user_language,$reseller_id);
$loguserid = $admin_id;
@@ -281,17 +285,14 @@ function CopyAdminTable ($tablename,$id,$reseller_id,$limit,$sql,$where='') {
$resellersid = $reseller_id;
}
- $salt = md5(mt_rand() . date('Y-m-d H:i:s:u'));
- $security2 = createHash($cnamenew, $password, $salt, $aeskey);
-
- $query = $sql->prepare("UPDATE `userdata` SET `cname`=?,`security`=?,`salt`=?,`resellerid`=? WHERE `id`=? LIMIT 1");
+ $query = $sql->prepare("UPDATE `userdata` SET `cname`=?,`security`=?,`resellerid`=? WHERE `id`=? LIMIT 1");
if ($user_accounttype == 'a') {
- $query->execute(array($cnamenew, $security2, $salt, $id, $id));
+ $query->execute(array($cnamenew, password_hash($password, PASSWORD_DEFAULT), $id, $id));
} else if ($user_accounttype == 'r' and $admin_id == $reseller_id) {
- $query->execute(array($cnamenew, $security2, $salt, $reseller_id, $id));
+ $query->execute(array($cnamenew, password_hash($password, PASSWORD_DEFAULT), $reseller_id, $id));
} else if ($user_accounttype == 'r') {
- $query->execute(array($cnamenew, $security2, $salt, $admin_id, $id));
+ $query->execute(array($cnamenew, password_hash($password, PASSWORD_DEFAULT), $admin_id, $id));
}
sendmail('emailuseradd',$id,$cnamenew,$password);
@@ -631,10 +632,8 @@ function CopyAdminTable ($tablename,$id,$reseller_id,$limit,$sql,$where='') {
} else {
if ($reseller_id != 0 and $admin_id != $reseller_id) $reseller_id = $admin_id;
$password = $ui->password('password',20, 'post');
- $salt=md5(mt_rand().date('Y-m-d H:i:s:u'));
- $security=createHash($cname,$password,$salt,$aeskey);
- $query=($reseller_id == 0) ? $sql->prepare("UPDATE `userdata` SET `updateTime`=NOW(),`security`=?,`salt`=? WHERE id=? AND (`resellerid`=? OR `id`=`resellerid`) LIMIT 1") : $sql->prepare("UPDATE `userdata` SET `updateTime`=NOW(),`security`=?,`salt`=? WHERE id=? AND `resellerid`=? LIMIT 1");
- $query->execute(array($security,$salt,$id,$reseller_id));
+ $query=($reseller_id == 0) ? $sql->prepare("UPDATE `userdata` SET `updateTime`=NOW(),`security`=? WHERE id=? AND (`resellerid`=? OR `id`=`resellerid`) LIMIT 1") : $sql->prepare("UPDATE `userdata` SET `updateTime`=NOW(),`security`=?,`salt`=? WHERE id=? AND `resellerid`=? LIMIT 1");
+ $query->execute(array(password_hash($password, PASSWORD_DEFAULT), $id, $reseller_id));
$template_file = $spracheResponse->table_add ."
";
$loguseraction="%psw% %user% $cname";
$insertlog->execute();
diff --git a/web/stuff/userpanel_substitutes.php b/web/stuff/userpanel_substitutes.php
index e99b9377..f51c147f 100644
--- a/web/stuff/userpanel_substitutes.php
+++ b/web/stuff/userpanel_substitutes.php
@@ -39,13 +39,14 @@
* Programm erhalten haben. Wenn nicht, siehe