require_once '../../common/ucpheader.php';
require_once '../../common/user.php';
verifyGroup('Moderators');
$_SESSION['mod_apps_token'] = uniqid(mt_rand(), true);
//Generate token for moderator action
$mysqlConn = connectToDatabase();
$pendingApps = getArrayFromSQLQuery($mysqlConn, 'SELECT app.guid, app.name, appver.number AS version, user.nick AS publisher FROM apps app
														LEFT JOIN appversions appver ON appver.versionId = app.version
														LEFT JOIN users user ON user.userId = app.publisher
														WHERE app.publishstate = 0 OR app.publishstate = 4
														ORDER BY version ASC LIMIT 50');
$mysqlConn->close();
echo 'Pending apps/updates (showing only oldest 50):<br />';
$md5Token = md5($_SESSION['mod_apps_token']);
foreach ($pendingApps as $app) {
    echo '<br />' . '<a href="appview.php?guid=' . $app['guid'] . '&token=' . $md5Token . '">' . $app['guid'] . '</a> (name: ' . escapeHTMLChars($app['name']) . ', version: ' . escapeHTMLChars($app['version']) . ', publisher: ' . escapeHTMLChars($app['publisher']) . ')';
}
?>
<br />
<br />
<br />
<form action="appview.php" method="get">
Query app by GUID:
<br />
<input type="text" name="guid" size="50">
<input type="hidden" name="token" value="<?php 
echo $md5Token;
?>
">
<input type="submit" value="Query">
</form>
    /**
     * Send a notification to a specific group
     *
     * @param string $groupName The name of the group to send the notification to
     * @param string $summary Short notification summary (should be up to 10 words)
     * @param string $body Full notification text
     * @param string $url URL to related page
     */
    public function createGroupNotification($groupName, $summary, $body, $url = null)
    {
        //Insert notification entry into database
        executePreparedSQLQuery($this->mysqlConn, 'INSERT INTO notifications (groupId, summary, body, url)
														VALUES ((SELECT groupId FROM groups WHERE name = ?), ?, ?, ?)', 'ssss', [$groupName, escapeHTMLChars($summary), escapeHTMLChars($body), $url]);
    }
<?php

$title = 'Mod CP';
require_once '../../common/ucpheader.php';
require_once '../../common/user.php';
verifyGroup('Moderators');
sendResponseCodeAndExitIfTrue(!isset($_POST['guid'], $_POST['publishstate'], $_POST['failpublishmessage'], $_POST['token']), 400);
if (isset($_SESSION['mod_appview_token' . $_POST['guid']])) {
    $appViewToken = $_SESSION['mod_appview_token' . $_POST['guid']];
}
sendResponseCodeAndExitIfTrue(!isset($appViewToken) || md5($appViewToken) !== $_POST['token'] || !is_numeric($_POST['publishstate']) || $_POST['publishstate'] < 0 || $_POST['publishstate'] > 5, 422);
$appGuid = $_POST['guid'];
$appPublishState = $_POST['publishstate'];
$appFailPublishMessage = $_POST['publishstate'] == 2 || $_POST['publishstate'] == 5 ? escapeHTMLChars($_POST['failpublishmessage']) : '';
$mysqlConn = connectToDatabase();
if ($appPublishState == 1) {
    executePreparedSQLQuery($mysqlConn, 'UPDATE apps SET version = (SELECT versionId FROM appversions WHERE appGuid = ? ORDER BY versionId DESC LIMIT 1),
												publishstate = ?, failpublishmessage = ?
												WHERE guid = ? LIMIT 1', 'siss', [$appGuid, $appPublishState, $appFailPublishMessage, $appGuid]);
    //Update latest version and publish state in database
} else {
    executePreparedSQLQuery($mysqlConn, 'UPDATE apps SET publishstate = ?, failpublishmessage = ?
												WHERE guid = ? LIMIT 1', 'iss', [$appPublishState, $appFailPublishMessage, $appGuid]);
    //Update publish state in database
}
if (isset($_POST['sendnotification']) && $_POST['sendnotification'] === 'yes') {
    $currentApp = getArrayFromSQLQuery($mysqlConn, 'SELECT name, publisher FROM apps WHERE guid = ?', 's', [$appGuid])[0];
    $notificationUserId = $currentApp['publisher'];
    //Generate notification summary
    $notificationSummary = '"' . $currentApp['name'] . '" has been';
    switch ($appPublishState) {
    if (!empty($app['webicon'])) {
        echo $app['webicon'];
    } else {
        if (!empty($app['largeIcon'])) {
            echo $app['largeIcon'];
        } else {
            echo '/img/no_icon.png';
        }
    }
    ?>
"/>
				<div class="app-content app-vertical-center-outer pull-left" style="padding:0 10px;background:#f3f3f3;width:100%;">
					<div class="pull-left">
						<h4 class="app-vertical-center-inner">
							<span itemprop="name" style="float:left;overflow:hidden"> <div class="app-name"><?php 
    echo escapeHTMLChars($app['name']);
    ?>
<div class="dimmer"/></div></span><br/>
							<a href="/user/<?php 
    echo $app['publisher'];
    ?>
" style="color:black"><span itemprop="publisher" itemscope itemtype="http://schema.org/Organization" style="width:100%;padding:2px;font-size:14px"><?php 
    echo $app['publisher'];
    ?>
</span></a>
						</h4>
					</div>
				</div>
				<div class="app-content app-vertical-center-outer pull-right btn-toolbar" style="background:#f3f3f3;width:100%;padding:15px 10px">
					<div class="app-vertical-center-inner" style="text-align: center;">
						<div><span class="glyphicon glyphicon-download"></span> <?php 
printAndExitIfTrue(!preg_match('`^[a-zA-Z0-9_]{1,}$`', $_POST['user']), 'Invalid username.');
printAndExitIfTrue(mb_strlen($_POST['user']) < 3, 'Username is too short.');
printAndExitIfTrue(mb_strlen($_POST['user']) > 24, 'Username is too long.');
//Check passwords
printAndExitIfTrue($_POST['pass'] !== $_POST['pass2'], 'Passwords don\'t match.');
printAndExitIfTrue(mb_strlen($_POST['pass']) < 8, 'Password is too short.');
//Check e-mail
printAndExitIfTrue(!filter_var($_POST['email'], FILTER_VALIDATE_EMAIL) || !checkdnsrr(substr($_POST['email'], strpos($_POST['email'], '@') + 1), 'MX'), 'Invalid email address.');
printAndExitIfTrue(mb_strlen($_POST['email']) > 255, 'E-mail is too long.');
//Check captcha
$reCaptcha = new ReCaptcha(getConfigValue('apikey_recaptcha_secret'));
$resp = $reCaptcha->verifyResponse($_SERVER["REMOTE_ADDR"], $_POST["g-recaptcha-response"]);
printAndExitIfTrue($resp == null || !$resp->success, 'Invalid or no captcha response.');
$tryRegisterName = escapeHTMLChars($_POST['user']);
$tryRegisterPass = $_POST['pass'];
$tryRegisterEmail = escapeHTMLChars($_POST['email']);
$hashedTryRegisterPass = crypt($tryRegisterPass, '$2y$07$' . uniqid(mt_rand(), true));
$mysqlConn = connectToDatabase();
//Check if there are any users with the same nick or email
$matchingUsers = getArrayFromSQLQuery($mysqlConn, 'SELECT userId FROM users WHERE LOWER(nick) = LOWER(?) OR LOWER(email) = LOWER(?) LIMIT 1', 'ss', [$tryRegisterName, $tryRegisterEmail]);
printAndExitIfTrue(count($matchingUsers) != 0, 'User with this name and/or email already exists.');
//Insert user into database
$stmt = executePreparedSQLQuery($mysqlConn, 'INSERT INTO users (nick, password, email, token)
											VALUES (?, ?, ?, ?)', 'ssss', [$tryRegisterName, $hashedTryRegisterPass, $tryRegisterEmail, sha1($registerToken)], true);
$userId = $stmt->insert_id;
$stmt->close();
//Insert user group connection
executePreparedSQLQuery($mysqlConn, 'INSERT INTO groupconnections (userId, groupId)
											VALUES (?, 1)', 'i', [$userId]);
$mysqlConn->close();
print 'Register complete.';
                break;
            case 2:
                if (!empty($app['failpublishmessage'])) {
                    echo '<button class="btn btn-danger disabled"><span class="glyphicon glyphicon-ban-circle"></span> ' . escapeHTMLChars($app['failpublishmessage']) . '</button>';
                } else {
                    echo '<button class="btn btn-danger disabled"><span class="glyphicon glyphicon-ban-circle"></span> Rejected</button>';
                }
                break;
            case 3:
                echo '<button class="btn btn-danger disabled"><span class="glyphicon glyphicon-eye-close"></span> Hidden</button>';
                break;
            case 4:
                echo '<button class="btn btn-info disabled"><span class="glyphicon glyphicon-ok"></span> Published (version ' . $app['version_new'] . ' pending approval)</button>';
                break;
            case 5:
                echo '<button class="btn btn-warning disabled"><span class="glyphicon glyphicon-info-sign"></span> Published (version ' . $app['version_new'] . ': ' . escapeHTMLChars($app['failpublishmessage']) . ')</button>';
                break;
        }
        ?>

						</div>
						<div class="pull-right" style="margin-left: 5px;">
							<button class="btn btn-default disabled"><span class="glyphicon glyphicon-download"></span> <?php 
        echo $app['downloads'];
        ?>
 downloads</button>
						</div>
					</div>
				</div>
			</div>
		</div>
 throwExceptionIfTrue(mb_strlen($_POST['description']) > 300, 'Description is too long.');
 //Check file upload errors
 foreach ($_FILES as $file) {
     throwExceptionIfTrue($file['error'] === 1 || $file['error'] === 2, $file['name'] . ' exceeds the file size limit.');
     throwExceptionIfTrue($file['error'] === 3, $file['name'] . ' wasn\'t fully uploaded.');
     throwExceptionIfTrue($file['error'] > 4, $file['name'] . ' encountered an internal error upon upload: ' . $file['error']);
 }
 //Check captcha
 $reCaptcha = new ReCaptcha(getConfigValue('apikey_recaptcha_secret'));
 $resp = $reCaptcha->verifyResponse($_SERVER["REMOTE_ADDR"], $_POST["g-recaptcha-response"]);
 throwExceptionIfTrue($resp == null || !$resp->success, 'Invalid or no captcha response.');
 $appName = escapeHTMLChars($_POST['name']);
 $appVersion = escapeHTMLChars($_POST['version']);
 $appCategory = $_POST['category'];
 $appSubCategory = $subCategorySelected ? $_POST['subcategory'] : null;
 $appDescription = escapeHTMLChars(str_replace(['\\r\\n', '\\r', '\\n'], ' ', $_POST['description']));
 $app3dsxPath = $_FILES['3dsx']['tmp_name'];
 $appSmdhPath = $_FILES['smdh']['tmp_name'];
 $isDeveloper = clientPartOfGroup('Developers');
 $updatingApp = isset($_SESSION['user_app_version' . $guid]);
 //Check which optional files were uploaded
 $uploadingAppData = isset($_FILES['appdata']) && !deletingFile('appdata') && is_uploaded_file($_FILES['appdata']['tmp_name']);
 if ($uploadingAppData) {
     $appDataPath = $_FILES['appdata']['tmp_name'];
 }
 $uploadingWebIcon = isset($_FILES['webicon']) && !deletingFile('webicon') && is_uploaded_file($_FILES['webicon']['tmp_name']);
 if ($uploadingWebIcon) {
     $webIconPath = $_FILES['webicon']['tmp_name'];
     //Verify that image is JPEG/PNG
     $imageMIME = getimagesize($webIconPath)['mime'];
     throwExceptionIfTrue(!($imageMIME && ($imageMIME === 'image/jpeg' || $imageMIME === 'image/png')), 'Invalid hi-res icon file type. It must be in JPEG or PNG format.');
//Get app with requested GUID
printAndExitIfTrue(count($matchingApps) != 1, 'Invalid app GUID.');
//Check if there is one app matching attempted GUID
$currentApp = $matchingApps[0];
$screenshots = explode(',', $currentApp['screenshots']);
//Print all app attributes
foreach ($currentApp as $attributeName => $attributeValue) {
    if ($attributeName == 'screenshots') {
        for ($i = 0; $i < count($screenshots); $i++) {
            echo $attributeName . ' (' . ($i + 1) . '): <a href="' . $screenshots[$i] . '">' . $screenshots[$i] . '</a><br />';
        }
    } else {
        if ($attributeName == '3dsx' || $attributeName == 'smdh' || $attributeName == 'appdata' || $attributeName == 'largeIcon') {
            echo $attributeName . ': <a href="' . $attributeValue . '">' . $attributeValue . '</a><br />';
        } else {
            $safeValue = escapeHTMLChars($attributeValue);
            echo $attributeName . ': ' . $safeValue . '<br />';
        }
    }
}
//Print icon
echo '<img src="' . $currentApp['largeIcon'] . '" /><br />';
for ($i = 0; $i < count($screenshots); $i++) {
    echo '<img src="' . $screenshots[$i] . '" /> ';
}
?>
<br />
<br />

<form action="appset.php" method="post">
Set publish state:
        printAndExitIfTrue(count($matchingApps) != 1, 'Invalid app GUID.');
        //Check if there is one app matching attempted GUID/user combination
        $appToEdit = $matchingApps[0];
        $_SESSION['publish_app_guid' . $guidId] = $appToEdit['guid'];
        $_SESSION['user_app_version' . $appToEdit['guid']] = $appToEdit['version'];
    } else {
        $_SESSION['publish_app_guid' . $guidId] = generateGUID();
    }
    if (!isset($_SESSION['publish_token' . $_SESSION['publish_app_guid' . $guidId]])) {
        $_SESSION['publish_token' . $_SESSION['publish_app_guid' . $guidId]] = uniqid(mt_rand(), true);
    }
    $editing = isset($appToEdit);
    ?>
		<h1 class="animated bounceInDown text-center"><?php 
    if (isset($appToEdit)) {
        echo 'Updating ' . escapeHTMLChars($appToEdit['name']);
    } else {
        echo 'Add a new application';
    }
    ?>
</h1>
		<br />
		
		<?php 
    if (isset($errorMessage)) {
        ?>
		<div class="animated shake alert alert-danger">
			<a class="close" href="#" data-dismiss="alert">&times;</a>
			<strong>Error!</strong> <?php 
        echo $errorMessage;
        ?>
								<?php 
echo escapeHTMLChars($app['name']);
?>
							</span>
							<span class="app-version" itemprop="softwareVersion">
								<?php 
echo escapeHTMLChars($app['version']);
?>
							</span>
							<br/>
							<a href="/user/<?php 
echo $app['publisher'];
?>
" style="color:black"><span class="app-publisher" itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
								<?php 
echo escapeHTMLChars($app['publisher']);
?>
							</span></a>
						</h4>
					</div>
				</div>
				<div class="app-vertical-center-outer pull-right btn-toolbar">
					<div class="app-vertical-center-inner">
						<button class="btn btn-default disabled" style="display: inline-block">3DS</button>
						<button class="btn btn-default disabled"><span class="glyphicon glyphicon-download"></span> <?php 
echo $app['downloads'];
?>
 downloads </button>
					</div>
				</div>
			</div>