Protocol-OTR

 view release on metacpan or  search on metacpan

OTR.xs  view on Meta::CPAN

            /* privkeys_file */
            fs = fopen(ctx->privkeys_file,"w+b");
            if ( ! fs ) croak("Could not open %s for updating: %s", ctx->privkeys_file, Strerror(errno));
            err = otrl_privkey_generate_FILEp(ctx->userstate, fs, accountname, protocol);
            if (fs) fclose(fs);

            if (err) {
                croak("Cannot generate: %s", gcry_strerror(err));
            }
        }

        Newx(fingerprint, OTRL_PRIVKEY_FPRINT_HUMAN_LEN, char);

        fingerprint = otrl_privkey_fingerprint(ctx->userstate, fingerprint, accountname, protocol);

        RETVAL = newSVpvn(fingerprint, OTRL_PRIVKEY_FPRINT_HUMAN_LEN - 1);

        Safefree(fingerprint);
    }
    OUTPUT:
        RETVAL

void
DESTROY(self)
    Protocol::OTR self
    CODE:
    {
        if ( self->userstate ) {
            otrl_userstate_free(self->userstate);
            self->userstate = NULL;
        }
        Safefree(self->privkeys_file);
        Safefree(self->contacts_file);
        Safefree(self->instance_tags_file);

        Safefree(self);
    }


MODULE = Protocol::OTR        PACKAGE = Protocol::OTR::Account

void
_contact(account, username, sv_fprint, verified)
    SV * account
    char * username
    SV * sv_fprint
    SV * verified
    PROTOTYPE: $$;$$
    PREINIT:
        OTRctx *ctx;
        unsigned char fingerprint[20] = {0};
        int with_fingerprint = 0;
        int is_verified = 0;

        ConnContext *context;
        Fingerprint *fprint;
        int context_created = 0;
        int fprint_added = 0;
        char * account_name;
        char * account_protocol;
    PPCODE:
    {
        ctx = ACCOUNT2CTX(account);

        if ( SvOK(sv_fprint) ) {
            STRLEN flen;
            unsigned char *fingerprint_hex = (unsigned char*)SvPV(sv_fprint, flen);

            if (
                ( /* human readable string */
                    flen == 44
                    && fingerprint_hex[8] == ' '
                    && fingerprint_hex[17] == ' '
                    && fingerprint_hex[26] == ' '
                    && fingerprint_hex[35] == ' '
                )
                ||
                ( /* hex encoded string */
                    flen == 40
                    &&
                    strchr((char*)fingerprint_hex, ' ') == NULL
                )
            ) {
                int j, i;
                for(j=0, i=0; j<=20; i+=2) {
                    fingerprint[j++] = (ctoh(fingerprint_hex[i]) << 4) + (ctoh(fingerprint_hex[i+1]));
                    if (flen == 44 && j % 4 == 0 ) { /* skip spaces */
                        i++;
                    }
                }

                with_fingerprint = 1;
            }
            else {
                croak("Fingerprint has invalid format: %s", fingerprint_hex);
            }

            if ( SvOK(verified) && SvTRUE(verified) ) {
                is_verified = 1;
            }
        }

        account_name = SvPV_nolen(HASHGET(account, "name", 4)); 
        account_protocol = SvPV_nolen(HASHGET(account, "protocol", 8)); 

        context = otrl_context_find(
            ctx->userstate, username, account_name, account_protocol,
            OTRL_INSTAG_BEST, 1, &context_created, NULL, NULL );

        if ( context ) {
            if ( context_created && ! with_fingerprint ) {
                //croak("New contact requires fingerprint");
            }

            if ( with_fingerprint ) {
                fprint = otrl_context_find_fingerprint(context, fingerprint, 1, &fprint_added);
                otrl_context_set_trust(fprint, is_verified ? "verified" : NULL);
            }

            if ( context_created || fprint_added ) {
                write_fingerprints(ctx);

OTR.xs  view on Meta::CPAN


            /* Found matching account */
            if ( ! strEQ(context->accountname, account_name) ) continue;
            if ( ! strEQ(context->protocol, account_protocol) ) continue;
            if ( ! strEQ(context->username, contact_name) ) continue;

            /* Don't bother with the first (fingerprintless) entry. */
            for (fingerprint = context->fingerprint_root.next; fingerprint; fingerprint = fingerprint->next) {
                ConnContext *context_iter;
                TrustLevel best_level = TRUST_NOT_PRIVATE;
                int used = 0;
                HV *fprint = (HV*)sv_2mortal((SV*)newHV());

                for (context_iter = context->m_context;
                    context_iter && context_iter->m_context == context->m_context;
                    context_iter = context_iter->next) {

                    TrustLevel this_level = TRUST_NOT_PRIVATE;

                    if (context_iter->active_fingerprint == fingerprint) {
                        this_level = otrp_plugin_context_to_trust(context_iter);
                        used = 1;

                        if (this_level == TRUST_PRIVATE) {
                            best_level = TRUST_PRIVATE;
                        } else if (this_level == TRUST_UNVERIFIED
                            && best_level != TRUST_PRIVATE) {
                            best_level = TRUST_UNVERIFIED;
                        } else if (this_level == TRUST_FINISHED
                            && best_level == TRUST_NOT_PRIVATE) {
                            best_level = TRUST_FINISHED;
                        }
                    }
                }

                (void)hv_store(fprint, "fingerprint", 11, newSVpvn( (char*)fingerprint->fingerprint, 20), 0);

                (void)hv_store(fprint, "is_verified", 11, otrl_context_is_fingerprint_trusted(fingerprint) ? &PL_sv_yes : &PL_sv_no , 0);

                (void)hv_store(fprint, "status", 6, newSVpv( used ? TrustStates[best_level] : "Unused", 0 ), 0);

                av_push(list, newRV((SV*)fprint));
            }
        }
        RETVAL = newRV_inc((SV*) list);
    }
    OUTPUT:
        RETVAL

void
_active_fingerprint(contact)
    SV * contact
    PROTOTYPE: \%
    PREINIT:
        ConnContext *context;
        Fingerprint *fingerprint;
        OTRctx *ctx;
        char *contact_name;
        char *account_name;
        char *account_protocol;
    PPCODE:
    {
        HV *account = (HV*)HASHGET(contact, "act", 3);

        ctx = ACCOUNT2CTX(account);

        contact_name = SvPV_nolen(HASHGET(contact, "name", 4));
        account_name = SvPV_nolen(HASHGET(account, "name", 4));
        account_protocol = SvPV_nolen(HASHGET(account, "protocol", 8));

        context = otrl_context_find(
            ctx->userstate, contact_name, account_name, account_protocol,
            OTRL_INSTAG_MASTER, 0, NULL, NULL, NULL );

        if (context) {
            /* Don't bother with the first (fingerprintless) entry. */
            for (fingerprint = context->fingerprint_root.next; fingerprint; fingerprint = fingerprint->next) {
                ConnContext *context_iter;

                for (context_iter = context->m_context;
                    context_iter && context_iter->m_context == context->m_context;
                    context_iter = context_iter->next) {

                    if (context_iter->active_fingerprint == fingerprint) {
                        HV *fprint = (HV*)sv_2mortal((SV*)newHV());
                        TrustLevel this_level = otrp_plugin_context_to_trust(context_iter);

                        (void)hv_store(fprint, "fingerprint", 11, newSVpvn( (char *)fingerprint->fingerprint, 20), 0);

                        (void)hv_store(fprint, "is_verified", 11, otrl_context_is_fingerprint_trusted(fingerprint) ? &PL_sv_yes : &PL_sv_no , 0);

                        (void)hv_store(fprint, "status", 6, newSVpv( TrustStates[this_level], 0 ), 0);

                        XPUSHs(newRV_inc((SV*) fprint));

                        XSRETURN(1);
                    }
                }
            }
        }
        XSRETURN_UNDEF;
    }


MODULE = Protocol::OTR        PACKAGE = Protocol::OTR::Fingerprint

void
hash(fprint)
    SV *fprint
    PROTOTYPE: $
    PREINIT:
        char *hex_fingerprint;
        unsigned char *fprint_bytes;
    PPCODE:
    {
        fprint_bytes = (unsigned char*)SvPVbyte_nolen(HASHGET(fprint, "fingerprint", 11));

        Newx(hex_fingerprint, OTRL_PRIVKEY_FPRINT_HUMAN_LEN, char);

        otrl_privkey_hash_to_human(hex_fingerprint, fprint_bytes);

        XPUSHs( sv_2mortal( newSVpvn( hex_fingerprint, OTRL_PRIVKEY_FPRINT_HUMAN_LEN - 1)));

        Safefree(hex_fingerprint);

        XSRETURN(1);
    }

void
set_verified(fprint, verified)
    SV *fprint
    SV * verified
    PROTOTYPE: $$
    PREINIT:
        OTRctx * ctx;
        ConnContext * context;
        Fingerprint * fingerprint;
        int is_verified;
        char *contact_name;
        char *account_name;
        char *account_protocol;
    PPCODE:
    {
        HV *contact = (HV*)HASHGET(fprint, "cnt", 3);
        HV *account = (HV*)HASHGET(contact, "act", 3);
        ctx = ACCOUNT2CTX(account);

        is_verified = SvOK(verified) && SvTRUE(verified) ? 1 : 0;

        contact_name = SvPV_nolen(HASHGET(contact, "name", 4));
        account_name = SvPV_nolen(HASHGET(account, "name", 4));
        account_protocol = SvPV_nolen(HASHGET(account, "protocol", 8));

        context = otrl_context_find(ctx->userstate, contact_name, account_name, account_protocol,
            OTRL_INSTAG_MASTER, 0, NULL, NULL, NULL);

        if ( context ) {
            unsigned char *fprint_bytes = (unsigned char*)SvPV_nolen(HASHGET(fprint, "fingerprint", 11));
            fingerprint = otrl_context_find_fingerprint(context, fprint_bytes, 0, NULL);

            if ( fingerprint ) {
                otrl_context_set_trust(fingerprint, verified ? "verified" : "");
                (void)hv_store((HV*)SvRV(fprint), "is_verified", 11, is_verified ? &PL_sv_yes : &PL_sv_no , 0);

                write_fingerprints(ctx);

                is_verified ? XSRETURN_YES : XSRETURN_NO;
            }
        }

        XSRETURN_UNDEF;
    }


MODULE = Protocol::OTR        PACKAGE = Protocol::OTR::Channel

void
init(channel)
    SV * channel
    ALIAS:
        refresh = 1
    PROTOTYPE: $
    PPCODE:
    {
        send_default_query_msg(channel);

        XSRETURN_YES;
    }

void
create_symkey(channel, use, use_for)
    SV * channel
    unsigned int use
    SV * use_for
    PROTOTYPE: $$$
    PREINIT:
        ConnContext *context;
        int context_created = 0;
        OTRctx * ctx;
        char *contact_name;
        char * account_name;
        char * account_protocol;
    PPCODE:
    {
        HV * contact = (HV*)CHANNEL2CONTACT(channel);
        HV * account = (HV*)CONTACT2ACCOUNT(contact);

        ctx = CHANNEL2CTX(channel);

        contact_name = SvPV_nolen(HASHGET(contact, "name", 4));
        account_name = SvPV_nolen(HASHGET(account, "name", 4)); 
        account_protocol = SvPV_nolen(HASHGET(account, "protocol", 8)); 

        context = otrl_context_find(
            ctx->userstate, contact_name, account_name, account_protocol,
            SvUV(HASHGET(channel, "selected_instag", 15)), 0, &context_created, NULL, NULL );

        if ( context && ! context_created && context->msgstate == OTRL_MSGSTATE_ENCRYPTED ) {
            unsigned char *symkey;
            unsigned char *usedata;
            STRLEN usedatalen;
            gcry_error_t err;

            usedata = (unsigned char*)SvPVbyte(use_for, usedatalen);

            Newx(symkey, OTRL_EXTRAKEY_BYTES, unsigned char);

            err = otrl_message_symkey(ctx->userstate,
                &callbacks,
                channel /* opdata */,
                context,
                use,
                usedata,
                (size_t)usedatalen,
                symkey);

            XPUSHs( sv_2mortal( newSVpvn( (char*)symkey, OTRL_EXTRAKEY_BYTES )));

            Safefree(symkey);
        
            XSRETURN( 1 );
        }

        XSRETURN_UNDEF;
    }

void
finish(channel)
    SV * channel
    PROTOTYPE: $
    PREINIT:
        OTRctx * ctx;
        char *contact_name;
        char *account_name;
        char *account_protocol;
    PPCODE:
    {
        HV * contact = (HV*)CHANNEL2CONTACT(channel);
        HV * account = (HV*)CONTACT2ACCOUNT(contact);
        ctx = ACCOUNT2CTX(account);

        contact_name = SvPV_nolen(HASHGET(contact, "name", 4));
        account_name = SvPV_nolen(HASHGET(account, "name", 4));
        account_protocol = SvPV_nolen(HASHGET(account, "protocol", 8));

        (void)hv_store((HV*)SvRV(channel), "gone_secure", 11, &PL_sv_no, 0);

        otrl_message_disconnect_all_instances(
            ctx->userstate,
            &callbacks,
            channel,
	        account_name,
            account_protocol,
            contact_name
        );

        XSRETURN_YES;
    }


void
write(channel, plain_text)
    SV * channel
    char * plain_text
    PROTOTYPE: $$
    PREINIT:
        OTRctx * ctx;
        char *contact_name;
        char *account_name;
        char *account_protocol;
    PPCODE:
    {
        ConnContext *context;
        int context_created = 0;
        gcry_error_t err;
        char *newmessage = NULL;
        unsigned int selected_instag;
        HV * contact = (HV*)CHANNEL2CONTACT(channel);
        HV * account = (HV*)CONTACT2ACCOUNT(contact);
        ctx = ACCOUNT2CTX(account);

        contact_name = SvPV_nolen(HASHGET(contact, "name", 4));
        account_name = SvPV_nolen(HASHGET(account, "name", 4));
        account_protocol = SvPV_nolen(HASHGET(account, "protocol", 8));

        selected_instag = SvUV(HASHGET(channel, "selected_instag", 15));

        context = otrl_context_find(
            ctx->userstate, contact_name, account_name, account_protocol,
            selected_instag, 0, &context_created, NULL, NULL );

        if ( context && context->msgstate == OTRL_MSGSTATE_FINISHED ) {

            handle_msg_event_cb(
                (void*) channel,
                OTRL_MSGEVENT_CONNECTION_ENDED,
                context,
                NULL,
                err
            );
            XSRETURN_NO;
        }

        err = otrl_message_sending(ctx->userstate,
            &callbacks,
            channel /* opdata */,
            account_name, account_protocol, contact_name,
            selected_instag,
            plain_text,
            NULL /* tlvs */,
            &newmessage,
            OTRL_FRAGMENT_SEND_ALL,
            &context,
            NULL /* add_app_info */,
            NULL /* add_app_info_data */);

        if ( err ) {
            otrl_message_free(newmessage);
            croak("Cannot send: %s", gcry_strerror(err));
        }

        otrl_message_free(newmessage);

        XSRETURN_YES;
    }

void
ping(channel)
    SV * channel
    PROTOTYPE: $
    PPCODE:
    {
        OTRctx * ctx = CHANNEL2CTX(channel);

        otrl_message_poll(ctx->userstate, &callbacks, channel);
        XSRETURN_YES;
    }

void
smp_verify(channel, answer, ...)
    SV * channel
    SV * answer
    PROTOTYPE: $$;$
    PPCODE:
    {
        ConnContext *context;
        int context_created = 0;
        char * question = NULL;
        STRLEN slen;
        unsigned char * secret;
        OTRctx * ctx;
        char *contact_name;
        char * account_name;
        char * account_protocol;

        HV * contact = (HV*)CHANNEL2CONTACT(channel);
        HV * account = (HV*)CONTACT2ACCOUNT(contact);

        ctx = CHANNEL2CTX(channel);

        contact_name = SvPV_nolen(HASHGET(contact, "name", 4));
        account_name = SvPV_nolen(HASHGET(account, "name", 4));
        account_protocol = SvPV_nolen(HASHGET(account, "protocol", 8));

        context = otrl_context_find(
            ctx->userstate, contact_name, account_name, account_protocol,
            SvUV(HASHGET(channel, "selected_instag", 15)), 0, &context_created, NULL, NULL );

        if ( ! context ) XSRETURN_NO;

        secret = (unsigned char*)SvPVbyte(answer, slen);

        if ( items == 3 && SvOK(ST(2)) ) {
            STRLEN qlen;
            question = SvPVbyte(ST(2), qlen);
        
            otrl_message_initiate_smp_q(
                ctx->userstate,
                &callbacks,
                channel,
                context,
                question,
                secret,
                slen
            );
        } else {
            otrl_message_initiate_smp(
                ctx->userstate,
                &callbacks,
                channel,
                context,
                secret,
                slen
            );
        }

        XSRETURN_YES;
    }

void
smp_respond(channel, response)
    SV * channel
    unsigned char *response
    PROTOTYPE: $$
    PPCODE:
    {
        ConnContext *context;
        int context_created = 0;
        OTRctx * ctx;
        char *contact_name;
        char * account_name;
        char * account_protocol;

        HV * contact = (HV*)CHANNEL2CONTACT(channel);
        HV * account = (HV*)CONTACT2ACCOUNT(contact);

        ctx = CHANNEL2CTX(channel);

        contact_name = SvPV_nolen(HASHGET(contact, "name", 4));
        account_name = SvPV_nolen(HASHGET(account, "name", 4));
        account_protocol = SvPV_nolen(HASHGET(account, "protocol", 8));

        context = otrl_context_find(
            ctx->userstate, contact_name, account_name, account_protocol,
            SvUV(HASHGET(channel, "selected_instag", 15)), 0, &context_created, NULL, NULL );


        if ( ! context ) XSRETURN_NO;

        otrl_message_respond_smp(ctx->userstate, &callbacks, channel,
            context, response, strlen((char*)response));

        XSRETURN_YES;
    }


void
smp_abort(channel)
    SV * channel
    PROTOTYPE: $
    PPCODE:
    {
        ConnContext *context;
        int context_created = 0;
        OTRctx * ctx;
        char *contact_name;
        char * account_name;
        char * account_protocol;

        HV * contact = (HV*)CHANNEL2CONTACT(channel);
        HV * account = (HV*)CONTACT2ACCOUNT(contact);

        ctx = CHANNEL2CTX(channel);

        contact_name = SvPV_nolen(HASHGET(contact, "name", 4));
        account_name = SvPV_nolen(HASHGET(account, "name", 4)); 
        account_protocol = SvPV_nolen(HASHGET(account, "protocol", 8)); 

        context = otrl_context_find(
            ctx->userstate, contact_name, account_name, account_protocol,
            SvUV(HASHGET(channel, "selected_instag", 15)), 0, &context_created, NULL, NULL );


        if ( ! context ) XSRETURN_NO;

        otrl_message_abort_smp(
            ctx->userstate,
            &callbacks,
            channel,
            context
        );

        XSRETURN_YES;
    }


void
read(channel, input)
    SV * channel
    char * input
    PROTOTYPE: $$
    PPCODE:
    {
        int res;
        char *newmessage = NULL;
        OtrlTLV *tlvs = NULL;
        OtrlTLV *tlv = NULL;
        OtrlMessageType msgtype;
        ConnContext *context;
        HV * contact = (HV*)CHANNEL2CONTACT(channel);
        HV * account = (HV*)CONTACT2ACCOUNT(contact);
        OTRctx *ctx = ACCOUNT2CTX(account);
        char *contact_name;
        char *account_name;
        char *account_protocol;

        if ( ! input ) {
            XSRETURN_NO;
        }

        contact_name = SvPV_nolen(HASHGET(contact, "name", 4));
        account_name = SvPV_nolen(HASHGET(account, "name", 4));
        account_protocol = SvPV_nolen(HASHGET(account, "protocol", 8));

        msgtype = otrl_proto_message_type(input);

        res = otrl_message_receiving(ctx->userstate,
            &callbacks,
            channel /* opdata */,
            account_name, account_protocol,
            contact_name,
            input,
            &newmessage,
            &tlvs,
            &context,
            NULL /* add_app_info */,
            NULL /* add_app_info_data */);

        if ( context ) {
            if (newmessage) {

                ENTER;
                SAVETMPS;

                PUSHMARK(SP);
                XPUSHs( channel );
                XPUSHs( sv_2mortal( newSVpvn( "on_read", 7 )));
                XPUSHs( sv_2mortal( newSVpv( newmessage, 0 )));
                PUTBACK;

                otrl_message_free(newmessage);

                call_method( "_ev", G_DISCARD | G_VOID );

                FREETMPS;
                LEAVE;

            } else {
                otrl_message_free(newmessage);
            }
        }

        tlv = otrl_tlv_find(tlvs, OTRL_TLV_DISCONNECTED);
        if (tlv) {
            if ( hv_exists((HV*)SvRV(channel), "on_gone_insecure", 16) ) {

                ENTER;
                SAVETMPS;

                PUSHMARK(SP);
                XPUSHs( channel );
                XPUSHs( sv_2mortal( newSVpvn( "on_gone_insecure", 16 )));
                PUTBACK;

                call_method( "_ev", G_DISCARD | G_VOID );

                FREETMPS;
                LEAVE;

                (void)hv_store((HV*)SvRV(channel), "gone_secure", 11, &PL_sv_no, 0);
            }
        }

        otrl_tlv_free(tlvs);

        if ( res == 1) {
            int gone_secure = SvTRUE(HASHGET(channel, "gone_secure", 11));

            /* gone_secure */
            if ( gone_secure
                    &&
                    ( /* our init */
                        msgtype == OTRL_MSGTYPE_REVEALSIG
                        ||
                     /* their init */
                        msgtype == OTRL_MSGTYPE_SIGNATURE
                    )
                    && context->msgstate == OTRL_MSGSTATE_ENCRYPTED
                    && context->auth.authstate == OTRL_AUTHSTATE_NONE
            ) {
                gone_secure_cb(
                    (void*) channel,
                    context
                );
            }
            XSRETURN_NO;
        }

        XSRETURN_YES;
    }

void
sessions(channel)
    SV * channel
    PROTOTYPE: $
    PREINIT:
        HV * sessions;
        HE * entry;
        I32 count;
    PPCODE:
    {
        sessions = (HV*)SvRV(HASHGET(channel, "known_sessions", 14));
        count = hv_iterinit(sessions);

        EXTEND(SP, count);

        while ( (entry = hv_iternext(sessions)) ) {
            I32 klen;
            char *key = hv_iterkey( entry, &klen );

            XPUSHs(sv_2mortal(newSVpvn(key, klen)));
        }
    }

void
current_session(channel)
    SV * channel
    PROTOTYPE: $
    PREINIT:
        ConnContext *context;
        OTRctx * ctx;
        char *contact_name;
        char * account_name;
        char * account_protocol;
    PPCODE:
    {
        HV * contact = (HV*)CHANNEL2CONTACT(channel);
        HV * account = (HV*)CONTACT2ACCOUNT(contact);

        ctx = CHANNEL2CTX(channel);

        contact_name = SvPV_nolen(HASHGET(contact, "name", 4));
        account_name = SvPV_nolen(HASHGET(account, "name", 4)); 
        account_protocol = SvPV_nolen(HASHGET(account, "protocol", 8)); 

        context = otrl_context_find(
            ctx->userstate, contact_name, account_name, account_protocol,
            SvUV(HASHGET(channel, "selected_instag", 15)), 0, NULL, NULL, NULL );

        if ( context ) {
            XPUSHs( sv_2mortal( newSVuv(context->their_instance) ) );

            XSRETURN(1);
        }

        XSRETURN_UNDEF;
    }


void
select_session(channel, session)
    SV * channel
    SV * session
    PROTOTYPE: $$
    PREINIT:
        otrl_instag_t instag;
        ConnContext *context;
        OTRctx * ctx;
        char *contact_name;
        char *account_name;
        char *account_protocol;
    PPCODE:
    {
        HV * contact = (HV*)CHANNEL2CONTACT(channel);
        HV * account = (HV*)CONTACT2ACCOUNT(contact);
        ctx = ACCOUNT2CTX(account);

        instag = SvUV(session);

        contact_name = SvPV_nolen(HASHGET(contact, "name", 4));
        account_name = SvPV_nolen(HASHGET(account, "name", 4));
        account_protocol = SvPV_nolen(HASHGET(account, "protocol", 8));

        context = otrl_context_find(
            ctx->userstate, contact_name, account_name, account_protocol,
            instag, 0, NULL, NULL, NULL );

        if ( context ) {
            (void)hv_store((HV*)SvRV(channel), "selected_instag", 6, newSVuv(instag), 0);

            send_default_query_msg(channel);

            XSRETURN_YES;
        }

        XSRETURN_NO;
    }

void
status(channel)
    SV * channel
    PROTOTYPE: $ 
    PREINIT:
        ConnContext *context;
        Fingerprint *fingerprint;
        OTRctx *ctx;
        char *contact_name;
        char *account_name;
        char *account_protocol;
    PPCODE:
    {
        HV * contact = (HV*)CHANNEL2CONTACT(channel);
        HV * account = (HV*)CONTACT2ACCOUNT(contact);
        ctx = ACCOUNT2CTX(account);

        contact_name = SvPV_nolen(HASHGET(contact, "name", 4));
        account_name = SvPV_nolen(HASHGET(account, "name", 4));
        account_protocol = SvPV_nolen(HASHGET(account, "protocol", 8));

        context = otrl_context_find(
            ctx->userstate, contact_name, account_name, account_protocol,
            SvUV(HASHGET(channel, "selected_instag", 15)), 0, NULL, NULL, NULL );

        if (context) {
            TrustLevel this_level = otrp_plugin_context_to_trust(context);

            XPUSHs( newSVpv( TrustStates[this_level], 0 ) );

            XSRETURN(1);
        }
        XSRETURN_UNDEF;
    }




( run in 1.713 second using v1.01-cache-2.11-cpan-5511b514fd6 )