/* ------------------ * called before display of received messages * ------------------ */ static gboolean receiving_im_msg_cb(PurpleAccount *account, char **sender, char **buffer, PurpleConversation *conv, PurpleMessageFlags *flags, void *data) { char sys_msg_buffer[1000]; // check if the user with the jid=conv->name has signed his presence char* bare_jid = get_bare_jid(*sender); // set default message sprintf(sys_msg_buffer,"Encryption disabled"); // get encryption key struct list_item* item = g_hash_table_lookup(list_fingerprints,bare_jid); if (item != NULL) { if (item->mode_sec == TRUE) sprintf(sys_msg_buffer,"Encryption enabled"); // display a basic message, only if mode changed if (item->mode_sec != item->mode_sec_old) purple_conversation_write(conv,"",sys_msg_buffer,PURPLE_MESSAGE_SYSTEM | PURPLE_MESSAGE_NO_LOG,time(NULL)); item->mode_sec_old = item->mode_sec; } free(bare_jid); return FALSE; }
/* ------------------ * called before message is sent * ------------------ */ void sending_im_msg_cb(PurpleAccount *account, const char *receiver, char **message) { PurpleConversation *gconv = NULL; // search for conversation gconv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, receiver, account); if(gconv) { // check if the user with the jid=conv->name has signed his presence char* bare_jid = get_bare_jid(gconv->name); // get stored info about user struct list_item* item = g_hash_table_lookup(list_fingerprints,bare_jid); if (item != NULL) { // if we are in private mode if (item->mode_sec == TRUE) { // try to get key if (is_key_available(item->fpr,FALSE,FALSE,NULL) == FALSE) { // we do not have key of receiver // -> cancel message sending free (*message); *message = NULL; // tell user of this purple_conversation_write(gconv,"","The key of the receiver is not available, please ask the receiver for the key before trying to encrypt messages.",PURPLE_MESSAGE_SYSTEM | PURPLE_MESSAGE_NO_LOG,time(NULL)); } } } free(bare_jid); } }
/* ------------------ * conversation extended menu * ------------------ */ void conversation_extended_menu_cb(PurpleConversation *conv, GList **list) { char buffer[1000]; PurpleMenuAction *action = NULL; // check if the user with the jid=conv->name has signed his presence char* bare_jid = get_bare_jid(conv->name); // get stored info about user struct list_item* item = g_hash_table_lookup(list_fingerprints,bare_jid); if (item != NULL) { // on display encryption menu item, if user sent signed presence action = purple_menu_action_new("Toggle OPENPGP encryption", PURPLE_CALLBACK(menu_action_toggle_cb),NULL,NULL); *list = g_list_append(*list, action); sprintf(buffer,"Send own public key to '%s'",bare_jid); action = purple_menu_action_new(buffer, PURPLE_CALLBACK(menu_action_sendkey_cb),NULL,NULL); *list = g_list_append(*list, action); sprintf(buffer,"Try to retrieve key of '%s' from server",bare_jid); action = purple_menu_action_new(buffer, PURPLE_CALLBACK(menu_action_retrievekey_cb),NULL,NULL); *list = g_list_append(*list, action); } free(bare_jid); }
/* ------------------ * try to retrieve key from server * ------------------ */ static void menu_action_retrievekey_cb(PurpleConversation *conv, void* data) { char sys_msg_buffer[1000]; // check if the user with the jid=conv->name has signed his presence char* bare_jid = get_bare_jid(conv->name); // get stored info about user struct list_item* item = g_hash_table_lookup(list_fingerprints,bare_jid); if (item != NULL) { char* userid = NULL; if (is_key_available(item->fpr,FALSE,TRUE,&userid) == FALSE) { sprintf(sys_msg_buffer,"Did not find key with ID '%s' on keyservers.",item->fpr); purple_conversation_write(conv,"",sys_msg_buffer,PURPLE_MESSAGE_SYSTEM | PURPLE_MESSAGE_NO_LOG,time(NULL)); }else { // found key -> enable mode_enc sprintf(sys_msg_buffer,"Found key with ID '%s'/'%s' for '%s' on keyservers.",item->fpr,userid,bare_jid); purple_conversation_write(conv,"",sys_msg_buffer,PURPLE_MESSAGE_SYSTEM | PURPLE_MESSAGE_NO_LOG,time(NULL)); purple_conversation_write(conv,"","Encryption enabled",PURPLE_MESSAGE_SYSTEM | PURPLE_MESSAGE_NO_LOG,time(NULL)); item->mode_sec = TRUE; } if (userid != NULL) free(userid); } free(bare_jid); }
/* ------------------ * called on new conversations * ------------------ */ void conversation_created_cb(PurpleConversation *conv, char* data) { char sys_msg_buffer[1000]; if (purple_conversation_get_type(conv) != PURPLE_CONV_TYPE_IM) return; purple_debug_info(PLUGIN_ID, "conversation name: %s\n",conv->name); // check if the user with the jid=conv->name has signed his presence char* bare_jid = get_bare_jid(conv->name); // get stored info about user struct list_item* item = g_hash_table_lookup(list_fingerprints,bare_jid); if (item == NULL) { sprintf(sys_msg_buffer,"No encryption support in client of '%s'",bare_jid); }else { sprintf(sys_msg_buffer,"Client of user %s supports encryption",bare_jid); } // display a basic message purple_conversation_write(conv,"",sys_msg_buffer,PURPLE_MESSAGE_SYSTEM | PURPLE_MESSAGE_NO_LOG,time(NULL)); if (item != NULL) { char* userid = NULL; // check if we have key locally if (is_key_available(item->fpr,FALSE,FALSE,&userid) == FALSE) { if (userid != NULL) free(userid); userid = NULL; sprintf(sys_msg_buffer,"User has key with ID '%s', but we do not have it locally, try Options->\"Try to retrieve key of '%s' from server\"",item->fpr,bare_jid); purple_conversation_write(conv,"",sys_msg_buffer,PURPLE_MESSAGE_SYSTEM | PURPLE_MESSAGE_NO_LOG,time(NULL)); }else { // key is already available locally -> enable mode_enc sprintf(sys_msg_buffer,"'%s' uses key with id '%s'/'%s'",bare_jid,userid,item->fpr); purple_conversation_write(conv,"",sys_msg_buffer,PURPLE_MESSAGE_SYSTEM | PURPLE_MESSAGE_NO_LOG,time(NULL)); item->mode_sec = TRUE; } if (userid != NULL) free(userid); userid = NULL; // if we have the key now, move to secure mode if (item->mode_sec == TRUE) sprintf(sys_msg_buffer,"Encryption enabled"); else sprintf(sys_msg_buffer,"Encryption disabled"); }else sprintf(sys_msg_buffer,"Encryption disabled"); // display message about received message purple_conversation_write(conv,"",sys_msg_buffer,PURPLE_MESSAGE_SYSTEM | PURPLE_MESSAGE_NO_LOG,time(NULL)); free(bare_jid); }
/* ------------------ * conversation menu action, that toggles mode_sec * ------------------ */ static void menu_action_toggle_cb(PurpleConversation *conv, void* data) { // check if the user with the jid=conv->name has signed his presence char* bare_jid = get_bare_jid(conv->name); // get stored info about user struct list_item* item = g_hash_table_lookup(list_fingerprints,bare_jid); if (item != NULL) { item->mode_sec = !(item->mode_sec); item->mode_sec_old = item->mode_sec; // tell user, that we toggled mode purple_conversation_write(conv,"",item->mode_sec?"Encryption enabled":"Encryption disabled",PURPLE_MESSAGE_SYSTEM | PURPLE_MESSAGE_NO_LOG,time(NULL)); } free(bare_jid); }
/* ------------------ * called on received presence * ------------------ */ static gboolean jabber_presence_received(PurpleConnection *pc, const char *type, const char *from, xmlnode *presence) { const xmlnode* parent_node = presence; xmlnode* x_node = NULL; // check if presence has special "x" childnode x_node = xmlnode_get_child_with_namespace(parent_node,"x",NS_SIGNED); if (x_node != NULL) { // user supports openpgp encryption purple_debug_info(PLUGIN_ID, "user %s supports openpgp encryption!\n",from); char* x_node_data = xmlnode_get_data(x_node); if (x_node_data != NULL) { // try to verify char* fpr = verify(x_node_data); if (fpr != NULL) { char* bare_jid = get_bare_jid(from); purple_debug_info(PLUGIN_ID, "user %s has fingerprint %s\n",bare_jid,fpr); // add key to list struct list_item *item = malloc(sizeof(struct list_item)); item->fpr = fpr; g_hash_table_replace(list_fingerprints,bare_jid,item); }else { purple_debug_error(PLUGIN_ID, "could not verify presence of user %s\n",from); } }else { purple_debug_info(PLUGIN_ID, "user %s sent empty signed presence\n",from); } } /* We don't want the plugin to stop processing */ return FALSE; }
static void xmpp_stream_process_presence(XmppStream *stream, xmlnode *root) { xmlnode *node; XmppBuddy *buddy; gchar *value; gchar *full_jid; gchar *bare_jid; gchar *show; gchar *status; gchar *photo; if (!xmlnode_has_prop(root, "from")) { hybrid_debug_error("xmpp", "invalid presence."); return; } full_jid = xmlnode_prop(root, "from"); bare_jid = get_bare_jid(full_jid); if (xmlnode_has_prop(root, "type")) { value = xmlnode_prop(root, "type"); if (g_strcmp0(value, "unavailable") == 0) { if (!(buddy = xmpp_buddy_find(stream->account, bare_jid))) { goto presence_over; } xmpp_buddy_set_show(buddy, full_jid, value); g_free(value); goto presence_over; } else if (g_strcmp0(value, "subscribed") == 0) { hybrid_message_box_show(HYBRID_MESSAGE_INFO, "(<b>%s</b>) has accepted your request.", bare_jid); g_free(value); goto presence_over; } else if (g_strcmp0(value, "subscribe") == 0) { hybrid_buddy_request_window_create(stream->account->account, full_jid, NULL); g_free(value); goto presence_over; } g_free(value); } if (!(buddy = xmpp_buddy_find(stream->account, bare_jid))) { goto presence_over; } /* * If the presence message doesn't have a <show> label, * then it means the current status of the buddy is 'avaiable'. */ if ((node = xmlnode_find(root, "show"))) { show = xmlnode_content(node); xmpp_buddy_set_show(buddy, full_jid, show); g_free(show); } else { xmpp_buddy_set_show(buddy, full_jid, "avaiable"); } if ((node = xmlnode_find(root, "status"))) { status = xmlnode_content(node); xmpp_buddy_set_status(buddy, full_jid, status); g_free(status); } /* * Check whether it has a photo label, then we can * determine whether to fetch the buddy's photo. */ if ((node = xmlnode_find(root, "photo"))) { photo = xmlnode_content(node); if (g_strcmp0(photo, buddy->photo) != 0) { xmpp_buddy_set_photo(buddy, photo); xmpp_buddy_get_info(stream, buddy->jid, (trans_callback)buddy_get_info_cb, buddy); } g_free(photo); } presence_over: g_free(full_jid); g_free(bare_jid); }
static void xmpp_stream_process_message(XmppStream *stream, xmlnode *root) { gchar *value; gchar *bare_jid; xmlnode *node; HybridAccount *account; g_return_if_fail(stream != NULL); g_return_if_fail(root != NULL); account = stream->account->account; if (!xmlnode_has_prop(root, "type")) { hybrid_debug_error("xmpp", "invalid message received without a type property."); return; } value = xmlnode_prop(root, "type"); if (g_strcmp0(value, "chat") != 0) { hybrid_debug_error("xmpp", "unsupported message type."); g_free(value); return; } g_free(value); if (!xmlnode_has_prop(root, "from")) { hybrid_debug_error("xmpp", "invalid message without a from property."); return; } value = xmlnode_prop(root, "from"); bare_jid = get_bare_jid(value); g_free(value); if ((node = xmlnode_find(root, "composing"))) { hybrid_conv_got_inputing(account, bare_jid, FALSE); } if ((node = xmlnode_find(root, "active"))) { hybrid_conv_clear_inputing(account, bare_jid); } if ((node = xmlnode_find(root, "paused"))) { hybrid_conv_stop_inputing(account, bare_jid); } if ((node = xmlnode_find(root, "body"))) { value = xmlnode_content(node); hybrid_conv_got_message(account, bare_jid, value, time(NULL)); g_free(value); return; } }
/* ------------------ * called on every sent packet * ------------------ */ void jabber_send_signal_cb(PurpleConnection *pc, xmlnode **packet, gpointer unused) { if (NULL == packet) return; g_return_if_fail(PURPLE_CONNECTION_IS_VALID(pc)); // if we are sending a presence stanza, add new child node // so others know we support openpgp if (g_str_equal((*packet)->name, "presence")) { const char* status_str = NULL; xmlnode* status_node; // check if user selected a main key const char* fpr = purple_prefs_get_string(PREF_MY_KEY); if (fpr == NULL) fpr = ""; if (strcmp(fpr,"") != 0) {// user did select a key // get status message from packet status_node = xmlnode_get_child(*packet,"status"); if (status_node != NULL) { status_str = xmlnode_get_data(status_node); } // sign status message if (status_str == NULL) status_str = ""; purple_debug_info(PLUGIN_ID, "signing status '%s' with key %s\n",status_str,fpr); char* sig_str = sign(status_str,fpr); if (sig_str == NULL) { purple_debug_error(PLUGIN_ID,"sign failed\n"); return; } // create special "x" childnode purple_debug_info(PLUGIN_ID, "sending presence with signature\n"); xmlnode *x_node = xmlnode_new_child(*packet,"x"); xmlnode_set_namespace(x_node, NS_SIGNED); xmlnode_insert_data(x_node, sig_str,-1); }else { purple_debug_info(PLUGIN_ID, "no key selecteded!\n"); } }else if (g_str_equal((*packet)->name, "message")) { const char* to = xmlnode_get_attrib(*packet,"to"); xmlnode* body_node = xmlnode_get_child(*packet,"body"); if (body_node != NULL && to != NULL) { // get message char* message = g_strdup(xmlnode_get_data(body_node)); char* enc_str = NULL; char* bare_jid = get_bare_jid(to); // get encryption key struct list_item *item = g_hash_table_lookup(list_fingerprints,bare_jid); if (item == NULL) { purple_debug_info(PLUGIN_ID, "there is no key for encrypting message to %s\n",bare_jid); return; } // do not encrypt if mode_sec is disabled if (item->mode_sec == FALSE) return; char* fpr_to = item->fpr; purple_debug_info(PLUGIN_ID, "found key for encryption to user %s: %s\n",bare_jid,fpr_to); free(bare_jid); // encrypt message enc_str = encrypt(message,fpr_to); if (enc_str != NULL) { // remove message from body xmlnode_clear_data(body_node); xmlnode_insert_data(body_node,"[ERROR: This message is encrypted, and you are unable to decrypt it.]",-1); // add special "x" childnode for encrypted text purple_debug_info(PLUGIN_ID, "sending encrypted message\n"); xmlnode *x_node = xmlnode_new_child(*packet,"x"); xmlnode_set_namespace(x_node, NS_ENC); xmlnode_insert_data(x_node, enc_str,-1); }else { purple_debug_error(PLUGIN_ID, "could not encrypt message\n"); } }else { // ignore this type of messages //purple_debug_warning(PLUGIN_ID, "empty message or empty 'to'\n"); } } }
/* ------------------ * called on received message * ------------------ */ static gboolean jabber_message_received(PurpleConnection *pc, const char *type, const char *id, const char *from, const char *to, xmlnode *message) { const xmlnode* parent_node = message; xmlnode* x_node = NULL; xmlnode* body_node = NULL; if (parent_node == NULL) return FALSE; // check if message is a key body_node = xmlnode_get_child(parent_node,"body"); if (body_node != NULL) { char* data = xmlnode_get_data(body_node); if (data != NULL) { char* header = "-----BEGIN PGP PUBLIC KEY BLOCK-----"; if (strncmp(data,header,strlen(header)) == 0) { // if we received a ascii armored key // try to import it //purple_conversation_write(conv,"","received key",PURPLE_MESSAGE_SYSTEM | PURPLE_MESSAGE_NO_LOG,time(NULL)); if (import_key(data) == TRUE) { xmlnode_clear_data(body_node); xmlnode_insert_data(body_node,"key import ok",-1); } else { xmlnode_clear_data(body_node); xmlnode_insert_data(body_node,"key import failed",-1); } } } } // check if the user with the jid=from has signed his presence char* bare_jid = get_bare_jid(from); // get stored info about user struct list_item* item = g_hash_table_lookup(list_fingerprints,bare_jid); if (item == NULL) { //TODO: maybe create item in list? }else { // set default value to "not encrypted mode" item->mode_sec = FALSE; } free(bare_jid); // check if message has special "x" child node => encrypted message x_node = xmlnode_get_child_with_namespace(parent_node,"x",NS_ENC); if (x_node != NULL) { purple_debug_info(PLUGIN_ID, "user %s sent us an encrypted message\n",from); // get data of "x" node char* cipher_str = xmlnode_get_data(x_node); if (cipher_str != NULL) { // try to decrypt char* plain_str = decrypt(cipher_str); if (plain_str != NULL) { purple_debug_info(PLUGIN_ID, "decrypted message: %s\n",plain_str); // find body node xmlnode *body_node = xmlnode_get_child(parent_node,"body"); if (body_node != NULL) { // clear body node data if it is found xmlnode_clear_data(body_node); }else { // add body node if it is not found body_node = xmlnode_new_child(message,"body"); } // set "body" content node to decrypted string //xmlnode_insert_data(body_node,"Encrypted message: ",-1); xmlnode_insert_data(body_node,plain_str,-1); // only set to encrypted mode, if we know other users key fingerprint if (item != NULL) { // all went well, we received an encrypted message item->mode_sec = TRUE; } }else { purple_debug_error(PLUGIN_ID, "could not decrypt message!\n"); } }else { purple_debug_error(PLUGIN_ID, "xml token had no data!\n"); } } /* We don't want the plugin to stop processing */ return FALSE; }