aboutsummaryrefslogblamecommitdiffstats
path: root/matrix-api.c
blob: 34021af17d4bf9303ca16f4e4082575607b3c340 (plain) (tree)



















                                                                                 
             

                   




                                









                               



















































                                                                               











                                                                    



                            















                                                                          
                                                        








                                                        


                                                                              


                                                    
                                                     
                                                      
 



                                                              























                                                                            





                                                                         
                                                           


                                   
 
                 

                                       








                                                                      



                                                                          
                                                                         
                                                
                             


                      
/**
 * Interface to the matrix client/server API
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
 */

#include "matrix-api.h"

/* std lib */
#include <string.h>

/* json-glib */
#include <json-glib/json-glib.h>

/* libpurple */
#include <debug.h>

#include "libmatrix.h"

typedef struct {
    MatrixAccount *account;
    MatrixApiCallback callback;
    gpointer user_data;
} MatrixApiRequestData;


/**
 * Parse the body of an API response
 *
 * @param ret_data       pointer to the response data
 * @param parser         returns a pointer to the JsonParser, or NULL if there
 *                       is no body. Use g_object_unref() to release it.
 * @param error_message  returns a pointer to an error message on error. Do not
 *                       free this.
 * @return a pointer to the start of the body, or NULL if there is no body
 */
static const gchar *matrix_api_parse_body(const gchar *ret_data,
                                          JsonParser **parser,
                                          const gchar **error_message)
{
    const gchar *body_pointer;
    GError *err = NULL;

    g_assert(parser != NULL);
    g_assert(error_message != NULL);
    
    *parser = NULL;
    *error_message = NULL;

    /* find the start of the body */
    body_pointer = strstr(ret_data, "\r\n\r\n");

    if(body_pointer == NULL) {
        /* no separator */
        return NULL;
    }

    body_pointer += 4;
    if(*body_pointer == '\0') {
        /* empty body */
        return NULL;
    }
    
    /* we have a body - parse it as JSON */
    *parser = json_parser_new();
    if(!json_parser_load_from_data(*parser, body_pointer, -1, &err)) {
        purple_debug_info("matrixprpl",
                          "unable to parse JSON: %s\n",
                          err->message);
        *error_message = _("Error parsing response");
        g_error_free(err);
        return body_pointer;
    }

    return body_pointer;
}

/**
 * The callback we give to purple_util_fetch_url_request - does some
 * initial processing of the response
 */
static void matrix_api_complete(PurpleUtilFetchUrlData *url_data,
                                gpointer user_data,
                                const gchar *ret_data,
                                gsize ret_len,
                                const gchar *error_message)
{
    MatrixApiRequestData *data = (MatrixApiRequestData *)user_data;
    int response_code = -1;
    gchar *response_message = NULL;
    JsonParser *parser;
    JsonNode *root;
    const gchar *body_start;
    
    if (!error_message) {
        /* parse the response line */
        gchar *response_line;
        gchar **splits;
        gchar *ptr;
        
        ptr = strchr(ret_data, '\r');
        response_line = g_strndup(ret_data,
                                  ptr == NULL ? ret_len : ptr - ret_data);
        splits = g_strsplit(response_line, " ", 3);

        if(splits[0] == NULL || splits[1] == NULL || splits[2] == NULL) {
            /* invalid response line */
            purple_debug_info("matrixprpl",
                              "unable to parse response line %s\n",
                              response_line);
            error_message = _("Error parsing response");
        } else {
            response_code = strtol(splits[1], NULL, 10);
            response_message = g_strdup(splits[2]);
        }
        g_free(response_line);
        g_strfreev(splits);
    }

    if (!error_message) {
        body_start = matrix_api_parse_body(ret_data, &parser, &error_message);
        if(parser)
            root = json_parser_get_root(parser);
    }

    (data->callback)(data->account, data->user_data,
                     response_code, response_message,
                     body_start, root, error_message);

    /* free the JSON parser, and all of the node structures */
    if(parser)
        g_object_unref(parser);
    
    g_free(data);
    g_free(response_message);
}

/**
 * Start an HTTP call to the API
 *
 * @param max_len maximum number of bytes to return from the request. -1 for
 *                default (512K).
 */
static PurpleUtilFetchUrlData *matrix_api_start(const gchar *url,
                                                MatrixAccount *account,
                                                MatrixApiCallback callback,
                                                gpointer user_data,
                                                gssize max_len)
{
    MatrixApiRequestData *data;

    data = g_new0(MatrixApiRequestData, 1);
    data->account = account;
    data->callback = callback;
    data->user_data = user_data;

    /* TODO: implement the per-account proxy settings */
    return purple_util_fetch_url_request_len(url, TRUE, NULL, TRUE, NULL,
                                             TRUE, max_len,
                                             matrix_api_complete, data);
}


PurpleUtilFetchUrlData *matrix_sync(MatrixAccount *account,
        const gchar *since,
        MatrixApiCallback callback,
        gpointer user_data)
{
    GString *url;
    PurpleUtilFetchUrlData *fetch_data;
    
    url = g_string_new("");
    g_string_append_printf(url,
            "https://%s/_matrix/client/v2_alpha/sync?access_token=%s",
            account->homeserver, account->access_token);

    if(since != NULL)
        g_string_append_printf(url, "&timeout=30000&since=%s", since);

    purple_debug_info("matrixprpl", "request %s\n", url->str);

    /* XXX: stream the response, so that we don't need to allocate so much
     * memory? But it's JSON
     */
    fetch_data = matrix_api_start(url->str, account, callback, user_data,
                                  10*1024*1024);
    g_string_free(url, TRUE);
    
    return fetch_data;
}