Protocol-OTR
view release on metacpan or search on metacpan
/* 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);
/* 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 )