about summary refs log tree commit diff stats
path: root/dwm.html
Commit message (Expand)AuthorAgeFilesLines
* fixed dmenu link (thx to deifl)arg@10ksloc.org2006-08-041-1/+1
* added dmenu to dwm.htmlarg@10ksloc.org2006-08-041-0/+4
* updated dwm.htmlarg@10ksloc.org2006-08-021-3/+3
* small fixes to dwm.htmlarg@10ksloc.org2006-08-011-6/+6
* updated htmlarg@10ksloc.org2006-07-211-1/+1
* prepared 0.4 0.4arg@10ksloc.org2006-07-201-1/+1
* yet another html patcharg@10ksloc.org2006-07-201-5/+5
* updated htmlarg@10ksloc.org2006-07-201-5/+5
* some changes in the html page 0.3arg@10ksloc.org2006-07-191-2/+2
* changed occurrences of wmii.de into 10kloc.org in dwm.html, because 10kloc.or...Anselm R. Garbe2006-07-181-8/+8
* added new stuffAnselm R. Garbe2006-07-171-9/+6
* updated htmlAnselm R. Garbe2006-07-171-9/+9
* patched dwmAnselm R. Garbe2006-07-171-28/+44
* updated html 0.2Anselm R. Garbe2006-07-171-1/+1
* ordered variables in structs and source files alphabeticallyAnselm R. Garbe2006-07-171-4/+3
* changing XFlush into XSyncAnselm R. Garbe2006-07-151-3/+3
* prep 0.1 0.1Anselm R. Garbe2006-07-141-1/+3
* implemented dwm reading status text from stdin Anselm R. Garbe2006-07-141-2/+2
* made barclick to select the specific tagAnselm R. Garbe2006-07-141-2/+14
* several fixesAnselm R. Garbe2006-07-131-3/+3
* added philosophy sectionAnselm R. Garbe2006-07-131-0/+11
* added xlock command (I need it regularly)Anselm R. Garbe2006-07-131-3/+3
* fixed grammar bug reported by John-GaltAnselm R. Garbe2006-07-131-1/+1
* last change to dwm.htmlAnselm R. Garbe2006-07-131-43/+47
* renamed/changedAnselm R. Garbe2006-07-131-1/+1
* added logo+descriptionAnselm R. Garbe2006-07-131-0/+77
a> 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335
/*
 * capabilities.c
 *
 * Copyright (C) 2012, 2013 James Booth <boothj5@gmail.com>
 *
 * This file is part of Profanity.
 *
 * Profanity 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 3 of the License, or
 * (at your option) any later version.
 *
 * Profanity 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 Profanity.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

#include "config.h"

#ifdef HAVE_GIT_VERSION
#include "gitversion.h"
#endif

#include <stdlib.h>
#include <string.h>

#include <glib.h>
#include <openssl/evp.h>
#include <strophe.h>

#include "common.h"
#include "xmpp/xmpp.h"
#include "xmpp/stanza.h"

static GHashTable *capabilities;

static void _caps_destroy(Capabilities *caps);

void
caps_init(void)
{
    capabilities = g_hash_table_new_full(g_str_hash, g_str_equal, g_free,
        (GDestroyNotify)_caps_destroy);
}

void
caps_add(const char * const caps_str, const char * const category,
    const char * const type, const char * const name,
    const char * const software, const char * const software_version,
    const char * const os, const char * const os_version,
    GSList *features)
{
    Capabilities *new_caps = malloc(sizeof(struct capabilities_t));

    if (category != NULL) {
        new_caps->category = strdup(category);
    } else {
        new_caps->category = NULL;
    }
    if (type != NULL) {
        new_caps->type = strdup(type);
    } else {
        new_caps->type = NULL;
    }
    if (name != NULL) {
        new_caps->name = strdup(name);
    } else {
        new_caps->name = NULL;
    }
    if (software != NULL) {
        new_caps->software = strdup(software);
    } else {
        new_caps->software = NULL;
    }
    if (software_version != NULL) {
        new_caps->software_version = strdup(software_version);
    } else {
        new_caps->software_version = NULL;
    }
    if (os != NULL) {
        new_caps->os = strdup(os);
    } else {
        new_caps->os = NULL;
    }
    if (os_version != NULL) {
        new_caps->os_version = strdup(os_version);
    } else {
        new_caps->os_version = NULL;
    }
    if (features != NULL) {
        new_caps->features = features;
    } else {
        new_caps->features = NULL;
    }

    g_hash_table_insert(capabilities, strdup(caps_str), new_caps);
}

gboolean
caps_contains(const char * const caps_str)
{
    return (g_hash_table_lookup(capabilities, caps_str) != NULL);
}

static Capabilities *
_caps_get(const char * const caps_str)
{
    return g_hash_table_lookup(capabilities, caps_str);
}

char *
caps_create_sha1_str(xmpp_stanza_t * const query)
{
    char *category = NULL;
    char *type = NULL;
    char *lang = NULL;
    char *name = NULL;
    char *feature_str = NULL;
    GSList *identities = NULL;
    GSList *features = NULL;
    GSList *form_names = NULL;
    DataForm *form = NULL;
    FormField *field = NULL;
    GHashTable *forms = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, (GDestroyNotify)stanza_destroy_form);

    GString *s = g_string_new("");

    xmpp_stanza_t *child = xmpp_stanza_get_children(query);
    while (child != NULL) {
        if (g_strcmp0(xmpp_stanza_get_name(child), STANZA_NAME_IDENTITY) == 0) {
            category = xmpp_stanza_get_attribute(child, "category");
            type = xmpp_stanza_get_attribute(child, "type");
            lang = xmpp_stanza_get_attribute(child, "xml:lang");
            name = xmpp_stanza_get_attribute(child, "name");

            GString *identity_str = g_string_new(category);
            g_string_append(identity_str, "/");
            if (type != NULL) {
                g_string_append(identity_str, type);
            }
            g_string_append(identity_str, "/");
            if (lang != NULL) {
                g_string_append(identity_str, lang);
            }
            g_string_append(identity_str, "/");
            if (name != NULL) {
                g_string_append(identity_str, name);
            }
            g_string_append(identity_str, "<");
            identities = g_slist_insert_sorted(identities, g_strdup(identity_str->str), (GCompareFunc)strcmp);
            g_string_free(identity_str, TRUE);
        } else if (g_strcmp0(xmpp_stanza_get_name(child), STANZA_NAME_FEATURE) == 0) {
            feature_str = xmpp_stanza_get_attribute(child, "var");
            features = g_slist_insert_sorted(features, g_strdup(feature_str), (GCompareFunc)strcmp);
        } else if (g_strcmp0(xmpp_stanza_get_name(child), STANZA_NAME_X) == 0) {
            if (strcmp(xmpp_stanza_get_ns(child), STANZA_NS_DATA) == 0) {
                form = stanza_create_form(child);
                form_names = g_slist_insert_sorted(form_names, g_strdup(form->form_type), (GCompareFunc)strcmp);
                g_hash_table_insert(forms, g_strdup(form->form_type), form);
            }
        }
        child = xmpp_stanza_get_next(child);
    }

    GSList *curr = identities;
    while (curr != NULL) {
        g_string_append(s, curr->data);
        curr = g_slist_next(curr);
    }

    curr = features;
    while (curr != NULL) {
        g_string_append(s, curr->data);
        g_string_append(s, "<");
        curr = g_slist_next(curr);
    }

    curr = form_names;
    while (curr != NULL) {
        form = g_hash_table_lookup(forms, curr->data);
        g_string_append(s, form->form_type);
        g_string_append(s, "<");

        GSList *curr_field = form->fields;
        while (curr_field != NULL) {
            field = curr_field->data;
            g_string_append(s, field->var);
            g_string_append(s, "<");
            GSList *curr_value = field->values;
            while (curr_value != NULL) {
                g_string_append(s, curr_value->data);
                g_string_append(s, "<");
                curr_value = g_slist_next(curr_value);
            }
            curr_field = g_slist_next(curr_field);
        }

        curr = g_slist_next(curr);
    }

    EVP_MD_CTX mdctx;
    const EVP_MD *md;

    unsigned char md_value[EVP_MAX_MD_SIZE];
    unsigned int md_len;
    OpenSSL_add_all_digests();
    md = EVP_get_digestbyname("SHA1");
    EVP_MD_CTX_init(&mdctx);
    EVP_DigestInit_ex(&mdctx, md, NULL);
    EVP_DigestUpdate(&mdctx, s->str, strlen(s->str));
    EVP_DigestFinal_ex(&mdctx, md_value, &md_len);
    EVP_MD_CTX_cleanup(&mdctx);

    char *result = g_base64_encode(md_value, md_len);

    g_string_free(s, TRUE);
    g_slist_free_full(identities, g_free);
    g_slist_free_full(features, g_free);
    g_slist_free_full(form_names, g_free);
    g_hash_table_destroy(forms);

    return result;
}

xmpp_stanza_t *
caps_create_query_response_stanza(xmpp_ctx_t * const ctx)
{
    xmpp_stanza_t *query = xmpp_stanza_new(ctx);
    xmpp_stanza_set_name(query, STANZA_NAME_QUERY);
    xmpp_stanza_set_ns(query, XMPP_NS_DISCO_INFO);

    xmpp_stanza_t *identity = xmpp_stanza_new(ctx);
    xmpp_stanza_set_name(identity, "identity");
    xmpp_stanza_set_attribute(identity, "category", "client");
    xmpp_stanza_set_attribute(identity, "type", "console");

    GString *name_str = g_string_new("Profanity ");
    g_string_append(name_str, PACKAGE_VERSION);
    if (strcmp(PACKAGE_STATUS, "development") == 0) {
#ifdef HAVE_GIT_VERSION
        g_string_append(name_str, "dev.");
        g_string_append(name_str, PROF_GIT_BRANCH);
        g_string_append(name_str, ".");
        g_string_append(name_str, PROF_GIT_REVISION);
#else
        g_string_append(name_str, "dev");
#endif
    }
    xmpp_stanza_set_attribute(identity, "name", name_str->str);

    xmpp_stanza_t *feature_caps = xmpp_stanza_new(ctx);
    xmpp_stanza_set_name(feature_caps, STANZA_NAME_FEATURE);
    xmpp_stanza_set_attribute(feature_caps, STANZA_ATTR_VAR, STANZA_NS_CAPS);

    xmpp_stanza_t *feature_discoinfo = xmpp_stanza_new(ctx);
    xmpp_stanza_set_name(feature_discoinfo, STANZA_NAME_FEATURE);
    xmpp_stanza_set_attribute(feature_discoinfo, STANZA_ATTR_VAR, XMPP_NS_DISCO_INFO);

    xmpp_stanza_t *feature_discoitems = xmpp_stanza_new(ctx);
    xmpp_stanza_set_name(feature_discoitems, STANZA_NAME_FEATURE);
    xmpp_stanza_set_attribute(feature_discoitems, STANZA_ATTR_VAR, XMPP_NS_DISCO_ITEMS);

    xmpp_stanza_t *feature_muc = xmpp_stanza_new(ctx);
    xmpp_stanza_set_name(feature_muc, STANZA_NAME_FEATURE);
    xmpp_stanza_set_attribute(feature_muc, STANZA_ATTR_VAR, STANZA_NS_MUC);

    xmpp_stanza_t *feature_version = xmpp_stanza_new(ctx);
    xmpp_stanza_set_name(feature_version, STANZA_NAME_FEATURE);
    xmpp_stanza_set_attribute(feature_version, STANZA_ATTR_VAR, STANZA_NS_VERSION);

    xmpp_stanza_t *feature_chatstates = xmpp_stanza_new(ctx);
    xmpp_stanza_set_name(feature_chatstates, STANZA_NAME_FEATURE);
    xmpp_stanza_set_attribute(feature_chatstates, STANZA_ATTR_VAR, STANZA_NS_CHATSTATES);

    xmpp_stanza_t *feature_ping = xmpp_stanza_new(ctx);
    xmpp_stanza_set_name(feature_ping, STANZA_NAME_FEATURE);
    xmpp_stanza_set_attribute(feature_ping, STANZA_ATTR_VAR, STANZA_NS_PING);

    xmpp_stanza_add_child(query, identity);

    xmpp_stanza_add_child(query, feature_caps);
    xmpp_stanza_add_child(query, feature_chatstates);
    xmpp_stanza_add_child(query, feature_discoinfo);
    xmpp_stanza_add_child(query, feature_discoitems);
    xmpp_stanza_add_child(query, feature_muc);
    xmpp_stanza_add_child(query, feature_version);
    xmpp_stanza_add_child(query, feature_ping);

    xmpp_stanza_release(feature_ping);
    xmpp_stanza_release(feature_version);
    xmpp_stanza_release(feature_muc);
    xmpp_stanza_release(feature_discoitems);
    xmpp_stanza_release(feature_discoinfo);
    xmpp_stanza_release(feature_chatstates);
    xmpp_stanza_release(feature_caps);
    xmpp_stanza_release(identity);

    return query;
}

static void
_caps_close(void)
{
    g_hash_table_destroy(capabilities);
}

static void
_caps_destroy(Capabilities *caps)
{
    if (caps != NULL) {
        free(caps->category);
        free(caps->type);
        free(caps->name);
        free(caps->software);
        free(caps->software_version);
        free(caps->os);
        free(caps->os_version);
        if (caps->features != NULL) {
            g_slist_free_full(caps->features, free);
        }
        free(caps);
    }
}

void
capabilities_init_module(void)
{
    caps_get = _caps_get;
    caps_close = _caps_close;
}