diaspora/diaspora

External use: empty value for cookie "remember_user_token"

Closed this issue · 1 comments

Hi.

I would like to do a plugin for SPIP (a quite popular CMS in France) to send messages on diaspora*. My target is currently instances on version 0.7. I first tried to do a small PHP (v7.4) script that sends a single message.

I tried to reproduce what seemed to be done by my web browser and by a plugin for WordPress (an other CMS written in PHP), but I failed. Cookie "remember_user_token" has an empty value. In consequence, I am not logged and can not continue. Do you know why cookie "remember_user_token" can have an empty value? or better: what I have badly or not done in the following script? Thanks.

<?php
/*
 * Copying and distribution of this file, with or without modification,
 * are permitted in any medium without royalty provided this notice is
 * preserved.  This file is offered as-is, without any warranty.
 * Names of contributors must not be used to endorse or promote products
 * derived from this file without specific prior written permission.
 */


function array_get_or_default_value
    (array $an_array,
     $key,
     $default_value)
{
    return
        isset($an_array[$key])
        ? $an_array[$key]
        : $default_value;
}

function load_html_to_dom_document
    (DOMDocument $document,
     string $html)
    : void
{
    libxml_use_internal_errors(true);
    $document->loadHTML($html);
    libxml_use_internal_errors(false);
}

function get_xpath_querier_from_html_string
    (string $html)
    : DOMXPath
{
    $document = new DOMDocument();
    load_html_to_dom_document
        ($document,
         $html);
    return
        new DOMXPath
        ($document);
}


function get_curl_handler()
{
    $curl_handler = curl_init();
    // TODO disable SSL/TLS only if it failed due to that
    curl_setopt
        ($curl_handler,
         CURLOPT_SSL_VERIFYHOST,
         0);
    curl_setopt
        ($curl_handler,
         CURLOPT_SSL_VERIFYPEER,
         0);
    return $curl_handler;
}

function print_curl_error
    (string $context,
     int    $error_number,
     string $error_message)
    : void
{
    fprintf
        (STDERR,
         "%s: curl error #%u:\n%s\n",
         $context,
         $error_number,
         $error_message);
}

function get_cookies
    (string $server_output)
    : array
{
    preg_match_all(
        '/^Set-Cookie:\s*([^\r\n]*)/mi',
        $server_output,
        $matches);
    $cookies = array();
    foreach($matches[1] as $match) {
        list($name, $value) = explode('=', $match, 2);
        $cookies[$name] = $value;
    }
    return $cookies;
}


function get_csrf_token
    (string $pod_url)
    : string
{
    $curl_handler = get_curl_handler();
    curl_setopt(
        $curl_handler,
        CURLOPT_URL,
        rtrim($pod_url, "/") . "/users/sign_in");
    curl_setopt
        ($curl_handler,
         CURLOPT_RETURNTRANSFER,
         true);
    curl_setopt
        ($curl_handler,
         CURLOPT_HEADER,
         true);
    $server_output
        =
        curl_exec
        ($curl_handler);
    if(!$server_output)
    {
        $error_number
            =
            curl_errno
            ($curl_handler);
        $error_message
            =
            curl_error
            ($curl_handler);
    }
    curl_close($curl_handler);
    
    if(isset($error_number))
    {
        print_curl_error
            ("get CSRF token",
             $error_number,
             $error_message);
        return '';
    }
    
    $cookies
        =
        get_cookies
        ($server_output);
    return
        array_get_or_default_value
        ($cookies,
         'X-CSRF-Token',
         '');
}

function get_authenticity_token
    (string $pod_url)
    : string
{
    $curl_handler = get_curl_handler();
    curl_setopt(
        $curl_handler,
        CURLOPT_URL,
        rtrim($pod_url, "/") . "/users/sign_in");
    curl_setopt
        ($curl_handler,
         CURLOPT_HTTPGET,
         true);
    curl_setopt
        ($curl_handler,
         CURLOPT_RETURNTRANSFER,
         true);
    $server_output
        =
        curl_exec
        ($curl_handler);
    if(!$server_output)
    {
        $error_number
            =
            curl_errno
            ($curl_handler);
        $error_message
            =
            curl_error
            ($curl_handler);
    }
    curl_close($curl_handler);
    
    if(isset($error_number))
    {
        print_curl_error
            ("get authenticity token",
             $error_number,
             $error_message);
        return '';
    }
    
    $xpath_querier
        =
        get_xpath_querier_from_html_string
        ($server_output);
    $XPATH_QUERY = "/html/body//form//input[@name='authenticity_token']/@value";
    $xpath_results = $xpath_querier->query($XPATH_QUERY);
    $nb_xpath_results
        =
        count
        ($xpath_results);
    if($nb_xpath_results == 0)
    {
        fwrite
            (STDERR,
             "Fail to find authenticity token in DOM document!\n");
        return '';
    }
    if($nb_xpath_results > 1)
    {
        fwrite
            (STDERR,
             "Find more than 1 authenticity token in DOM document!\n");
        return '';
    }
    $xpath_result
        =
        $xpath_results
        [0];
    return
        $xpath_result
        ->textContent;
}

function log_in_raw
    (string $pod_url,
     string $authenticity_token,
     string $username,
     string $password)
    : string
{
    $curl_handler = get_curl_handler();
    curl_setopt(
        $curl_handler,
        CURLOPT_URL,
        rtrim($pod_url, "/") . "/users/sign_in");
    curl_setopt
        ($curl_handler,
         CURLOPT_POST,
         true);
    $form_parameters = [
        'authenticity_token' => $authenticity_token,
        'user' => compact('username', 'password'),
        'commit' => 'Sign',
    ];
    curl_setopt
        ($curl_handler,
         CURLOPT_POSTFIELDS, 
         http_build_query($form_parameters));
    curl_setopt
        ($curl_handler,
         CURLOPT_RETURNTRANSFER,
         true);
    curl_setopt
        ($curl_handler,
         CURLOPT_HEADER,
         true);
    $server_output
        =
        curl_exec
        ($curl_handler);
    if(!$server_output)
    {
        $error_number
            =
            curl_errno
            ($curl_handler);
        $error_message
            =
            curl_error
            ($curl_handler);
    }
    curl_close($curl_handler);
    
    if(isset($error_number))
    {
        print_curl_error
            ("get CSRF token",
             $error_number,
             $error_message);
        return '';
    }
    
    $cookies
        =
        get_cookies
        ($server_output);
    print_r($cookies); // TODO remove
    return
        array_get_or_default_value
        ($cookies,
         'remember_user_token',
         '');;
}

function log_in_full
    (string $pod_url,
     string $username,
     string $password)
    : string
{
    $authenticity_token
        =
        get_authenticity_token
        ($pod_url);
    if(!$authenticity_token)
    {
        fwrite
            (STDERR,
             "Fail to get authenticity token!\n");
        return '';
    }
    printf("%s\n", $authenticity_token); // TODO remove
    return
        log_in_raw
        ($pod_url,
         $authenticity_token,
         $username,
         $password);
}

function post_message
    (string $pod_url,
     string $message)
    : bool
{
    assert($pod_url != "");
    assert(str_starts_with($pod_url, "http"));
    
    $curl_handler = get_curl_handler();
    curl_setopt(
        $curl_handler,
        CURLOPT_URL,
        rtrim($pod_url, "/") . "/status_messages");
    curl_setopt
        ($curl_handler,
         CURLOPT_POST,
         true);
    $json_request = [
        'aspect_ids' => 'public',
        'status_message' => [
            'text' => $message,
        ],
    ];
    curl_setopt
        ($curl_handler,
         CURLOPT_POSTFIELDS, 
         http_build_query(array('json' => $json_request)));
    curl_setopt
        ($curl_handler,
         CURLOPT_RETURNTRANSFER,
         true);
    
    $server_output
        =
        curl_exec
        ($curl_handler);
    if(!$server_output)
    {
        $error_number
            =
            curl_errno
            ($curl_handler);
        $error_message
            =
            curl_error
            ($curl_handler);
    }
    curl_close($curl_handler);
    
    if(isset($error_number))
    {
        print_curl_error
            ("post message",
             $error_number,
             $error_message);
        return false;
    }
    
    echo "Result: " . $server_output; // TODO
    return true;
}

$nb_cmd_args = count($argv);
if($nb_cmd_args != 5)
{
    fprintf
        (STDERR,
         "%u command-line argument(s), but 4 excepted.\n",
         $nb_cmd_args - 1);
    exit(1);
}
$pod_url  = $argv[1];
$username = $argv[2];
$password = $argv[3];
$message  = $argv[4];

/* TODO useful?
$csrf_token
    =
    get_csrf_token
    ($pod_url);
if(!$csrf_token)
{
    fwrite
        (STDERR,
         "Fail to get CSRF token!\n");
}
*/

$login_token
    =
    log_in_full
    ($pod_url,
     $username,
     $password);
if(!$login_token)
{
    fwrite
        (STDERR,
         "Fail to log in!\n");
    exit(2);
}

if(!post_message
   ($pod_url,
    $message))
{
    fwrite
        (STDERR,
         "Fail to post message!\n");
    exit(3);
}
fwrite
    (STDOUT,
     "The message has been successfully sent!");

Our GitHub issues are not a discussion forum or support tracker, please use our Discourse for that.

As a general note, please don't try to reverse-engineer the diaspora* auth layer and the frontend methods. You won't get any support from us for that, because we know it'll break every release for unexpected reasons.

The current develop sources do support a proper API, and that version will be released as a stable version relatively soon(tm) - so I strongly suggest you built your app against the API. Our Discourse has a dedicated section for API suggestions and help.