<?php
/**
 * api: php
 * title: Session startup
 * description: Avoids session startup until actual login occured
 * license: MITL
 * version: 0.4
 *
 * Start $_SESSION only if there's already a session cookie present.
 * (Prevent needless cookies and tracking ids for not logged-in users.)
 *
 * The only handler that initiates any login process is `page_login.php`
 *
 * Now primarily using IndieAuth, albeit OpenID plugin remains active.
 # And $_SESSION["openid"] will be kept throughout the codebase anywho.
 *
 */


#error_reporting(E_ALL);set_error_handler("var_dump");

// Kill off CloudFlare cookie when Do-Not-Track header present
### WILL BECOME REDUNDANT IN MAY 2021 ###
if ($_SERVER->has("HTTP_DNT") and $_SERVER->boolean["HTTP_DNT"]) {
    header("Set-Cookie: __cfduid= ; path=/; domain=.freshcode.club; HttpOnly");
}

define("INDIEAUTH_API", "https://indieauth.com/auth");
define("INDIEAUTH_CLIENT_ID", "https://freshcode.club/");
define("INDIEAUTH_REDIRECT", "https://freshcode.club/login");




// Check for pre-existant cookie before defaulting to initiate session store
if ($_COOKIE->has("USER") or $_REQUEST->has("set_password")) {
    session_fresh();
}
// If there's none, make sure there's an array at least
if (!isset($_SESSION)) {
   $_SESSION = ["fromempty1"=>1];
}
// Populate stub array with empty defaults.
isset($_SESSION["openid"]) or $_SESSION["openid"] = "" and $_SESSION["reset_3"] = "3";
isset($_SESSION["name"]) or $_SESSION["name"] = "";
isset($_SESSION["csrf"]) or $_SESSION["csrf"] = [];
isset($_SESSION["password"]) or $_SESSION["password"] = "";



// verify incoming OpenID request
if ($_GET->has("openid_mode") and empty($_SESSION["openid"])) {

    try {
        $openid = new LightOpenID(HTTP_HOST);
        $openid->verify_peer = false;
        if ($openid->mode) {
            if ($openid->validate()) {
                $_COOKIE->no("USER") and session_fresh();
                $_SESSION["openid"] = $openid->identity;
                $_SESSION["name"] = $openid->getAttributes()["namePerson/friendly"];
            }
        }
    }
    catch (ErrorException $e) {
        die("OpenID verify exception (possibly endpoint / SSL error)");
    }

}
elseif ($_REQUEST->has("set_password")) {
    $_SESSION["password"] = $_REQUEST->ascii->nocontrol->trim["set_password"];
}
elseif ($_REQUEST->has("start_indieauth")) {
    initiate_indieauth($_POST->uri["login_url"]);
}
elseif ($_REQUEST->has("code","me")) {
    $indieauth_login = verify_indieauth();
}


#-- IndieAuth
function initiate_indieauth($url="") {
    $_SESSION["indie/state"] = $_state = md5("ia:".rand());
    die(header(
         "Location: " . INDIEAUTH_API . "?me=" . urlencode($url) .
         "&client_id=" . INDIEAUTH_CLIENT_ID . "&redirect_uri=" . INDIEAUTH_REDIRECT .
         "&state=" . urlencode($_state)
    ));
}
#-- if &code= parameter received
function verify_indieauth() {
    # "https://freshcode.club/login?code=...&me=http://userurl..."
    $fields = [
        "code" => $_REQUEST->raw["code"],
        "client_id"  => INDIEAUTH_CLIENT_ID,
        "redirect_uri" => INDIEAUTH_REDIRECT,
    ];
    $json = curl(INDIEAUTH_API)->httpheader(array("Accept: application/json"))->post(1)->postfields(
        http_build_query($fields)
    )->exec();
    if ($json) {
        $d = json_decode($json, True) or parse_str($json, $d);
        if (!empty($d["me"])) {
            session_fresh();
            return $_SESSION["openid"] = $d["me"];
        }
    }
}

#session_write_close();


// Prevent some session tampering
function session_fresh() {

    // Initiate with current session identifier
    if ($_COOKIE->has("USER")) {
        session_id($_COOKIE->id["USER"]);
    }
    session_name("USER");
    session_set_cookie_params(7*24*3600, "/", HTTP_HOST, false, true);
#    session_cache_limiter('private');
    session_cache_expire(7*24*60);
    session_start();

    // Security by obscurity: lock client against User-Agent
    $useragent = $_SERVER->text->length…30["HTTP_USER_AGENT"];
    // Security by obscurity: IP subnet lock (or just major route for IPv6)
    $subnet = $_SERVER->ip->length…6["REMOTE_ADDR"];
    // Server-side timeout (7 days)
    $expire = time() + 7 * 24 * 3600;

    // New ID for mismatches
    if (empty($_SESSION["state/client"]) or $_SESSION["state/client"] != $useragent
    or  empty($_SESSION["state/subnet"]) or $_SESSION["state/subnet"] != $subnet
    or  empty($_SESSION["state/expire"]) or $_SESSION["state/expire"] < time()
    ) {
        $dat = json_encode($_SESSION);
# CloudFlare vs PHP 5.6.4
/*
        #session_destroy();
        #session_regenerate_id(true);
        #session_start();
*/
//        $_SESSION["destroyed2"] = "{$_COOKIE->json_encode['USER']} / $dat / $useragent / $subnet / " . time();
    }
    // and Repopulate status fields
    $_SESSION["state/client"] = $useragent;
    $_SESSION["state/subnet"] = $subnet;
    $_SESSION["state/expire"] = $expire;
}


