Author: Pekka Riikonen <priikone@silcnet.org> Copyright (C) 2004 Pekka Riikonen 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; version 2 of the License. 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. #include "silcincludes.h" /***************************** Key Agreement *********************************/ silcpurple_buddy_keyagr(PurpleBlistNode *node, gpointer data); silcpurple_buddy_keyagr_do(PurpleConnection *gc, const char *name, silcpurple_buddy_keyagr_resolved(SilcClient client, SilcClientConnection conn, SilcClientEntry *clients, SilcUInt32 clients_count, PurpleConnection *gc = client->application; SilcPurpleResolve r = context; g_snprintf(tmp, sizeof(tmp), _("User %s is not present in the network"), r->nick); purple_notify_error(gc, _("Key Agreement"), _("Cannot perform the key agreement"), tmp); silcpurple_buddy_keyagr_do(gc, r->nick, FALSE); silcpurple_buddy_keyagr_cb(SilcClient client, SilcClientConnection conn, SilcClientEntry client_entry, SilcKeyAgreementStatus status, PurpleConnection *gc = client->application; SilcPurple sg = gc->proto_data; SilcPurpleKeyAgr a = context; case SILC_KEY_AGREEMENT_OK: PurpleConversation *convo; /* Set the private key for this client */ silc_client_del_private_message_key(client, conn, client_entry); silc_client_add_private_message_key_ske(client, conn, client_entry, NULL, NULL, key, a->responder); silc_ske_free_key_material(key); convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, client_entry->nickname, sg->account); /* we don't have windows in the core anymore...but we may want to * provide some method for asking the UI to show the window purple_conv_window_show(purple_conversation_get_window(convo)); convo = purple_conversation_new(PURPLE_CONV_TYPE_IM, sg->account, g_snprintf(tmp, sizeof(tmp), "%s [private key]", client_entry->nickname); purple_conversation_set_title(convo, tmp); case SILC_KEY_AGREEMENT_ERROR: purple_notify_error(gc, _("Key Agreement"), _("Error occurred during key agreement"), NULL); case SILC_KEY_AGREEMENT_FAILURE: purple_notify_error(gc, _("Key Agreement"), _("Key Agreement failed"), NULL); case SILC_KEY_AGREEMENT_TIMEOUT: purple_notify_error(gc, _("Key Agreement"), _("Timeout during key agreement"), NULL); case SILC_KEY_AGREEMENT_ABORTED: purple_notify_error(gc, _("Key Agreement"), _("Key agreement was aborted"), NULL); case SILC_KEY_AGREEMENT_ALREADY_STARTED: purple_notify_error(gc, _("Key Agreement"), _("Key agreement is already started"), NULL); case SILC_KEY_AGREEMENT_SELF_DENIED: purple_notify_error(gc, _("Key Agreement"), _("Key agreement cannot be started with yourself"), silcpurple_buddy_keyagr_do(PurpleConnection *gc, const char *name, SilcPurple sg = gc->proto_data; SilcClientEntry *clients; SilcUInt32 clients_count; char *local_ip = NULL, *remote_ip = NULL; if (!silc_parse_userfqdn(name, &nickname, NULL)) clients = silc_client_get_clients_local(sg->client, sg->conn, nickname, name, /* Resolve unknown user */ SilcPurpleResolve r = silc_calloc(1, sizeof(*r)); r->nick = g_strdup(name); silc_client_get_clients(sg->client, sg->conn, nickname, NULL, silcpurple_buddy_keyagr_resolved, r); /* Resolve the local IP from the outgoing socket connection. We resolve it to check whether we have a private range IP address or public IP address. If we have public then we will assume that we are not behind NAT and will provide automatically the point of connection to the agreement. If we have private range address we assume that we are behind NAT and we let the responder provide the point of connection. The algorithm also checks the remote IP address of server connection. If it is private range address and we have private range address we assume that we are chatting in LAN and will provide the point of Naturally this algorithm does not always get things right. */ if (silc_net_check_local_by_sock(sg->conn->sock->sock, NULL, &local_ip)) { /* Check if the IP is private */ if (!force_local && silcpurple_ip_is_private(local_ip)) { /* Local IP is private, resolve the remote server IP to see whether we are talking to Internet or just on LAN. */ if (silc_net_check_host_by_sock(sg->conn->sock->sock, NULL, if (silcpurple_ip_is_private(remote_ip)) /* We assume we are in LAN. Let's provide local_ip = silc_net_localip(); a = silc_calloc(1, sizeof(*a)); /* Send the key agreement request */ silc_client_send_key_agreement(sg->client, sg->conn, clients[0], local ? local_ip : NULL, NULL, 0, 60, silcpurple_buddy_keyagr_cb, a); SilcClientConnection conn; silcpurple_buddy_keyagr_request_cb(SilcPurpleKeyAgrAsk a, gint id) SilcClientEntry client_entry; /* Get the client entry. */ client_entry = silc_client_get_client_by_id(a->client, a->conn, purple_notify_error(a->client->application, _("Key Agreement"), _("The remote user is not present in the network any more"), /* If the hostname was provided by the requestor perform the key agreement now. Otherwise, we will send him a request to connect to us. */ ai = silc_calloc(1, sizeof(*ai)); silc_client_perform_key_agreement(a->client, a->conn, client_entry, silcpurple_buddy_keyagr_cb, ai); /* Send request. Force us as the point of connection since requestor did not provide the point of connection. */ silcpurple_buddy_keyagr_do(a->client->application, client_entry->nickname, TRUE); void silcpurple_buddy_keyagr_request(SilcClient client, SilcClientConnection conn, SilcClientEntry client_entry, const char *hostname, SilcUInt16 port) char tmp[128], tmp2[128]; PurpleConnection *gc = client->application; g_snprintf(tmp, sizeof(tmp), _("Key agreement request received from %s. Would you like to " "perform the key agreement?"), client_entry->nickname); g_snprintf(tmp2, sizeof(tmp2), _("The remote user is waiting key agreement on:\n" "Remote host: %s\nRemote port: %d"), hostname, port); a = silc_calloc(1, sizeof(*a)); a->client_id = *client_entry->id; a->hostname = strdup(hostname); purple_request_action(client->application, _("Key Agreement Request"), tmp, hostname ? tmp2 : NULL, 1, gc->account, client_entry->nickname, NULL, a, 2, _("Yes"), G_CALLBACK(silcpurple_buddy_keyagr_request_cb), _("No"), G_CALLBACK(silcpurple_buddy_keyagr_request_cb)); silcpurple_buddy_keyagr(PurpleBlistNode *node, gpointer data) buddy = (PurpleBuddy *)node; silcpurple_buddy_keyagr_do(buddy->account->gc, buddy->name, FALSE); /**************************** Static IM Key **********************************/ silcpurple_buddy_resetkey(PurpleBlistNode *node, gpointer data) SilcClientEntry *clients; SilcUInt32 clients_count; g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY(node)); b = (PurpleBuddy *) node; gc = purple_account_get_connection(b->account); if (!silc_parse_userfqdn(b->name, &nickname, NULL)) clients = silc_client_get_clients_local(sg->client, sg->conn, clients[0]->prv_resp = FALSE; silc_client_del_private_message_key(sg->client, sg->conn, SilcClientConnection conn; silcpurple_buddy_privkey(PurpleConnection *gc, const char *name); silcpurple_buddy_privkey_cb(SilcPurplePrivkey p, const char *passphrase) SilcClientEntry client_entry; if (!passphrase || !(*passphrase)) { /* Get the client entry. */ client_entry = silc_client_get_client_by_id(p->client, p->conn, purple_notify_error(p->client->application, _("IM With Password"), _("The remote user is not present in the network any more"), /* Set the private message key */ silc_client_del_private_message_key(p->client, p->conn, silc_client_add_private_message_key(p->client, p->conn, client_entry, NULL, NULL, (unsigned char *)passphrase, strlen(passphrase), FALSE, if (!client_entry->prv_resp) silc_client_send_private_message_key_request(p->client, silcpurple_buddy_privkey_resolved(SilcClient client, SilcClientConnection conn, SilcClientEntry *clients, SilcUInt32 clients_count, g_snprintf(tmp, sizeof(tmp), _("User %s is not present in the network"), purple_notify_error(client->application, _("IM With Password"), _("Cannot set IM key"), tmp); silcpurple_buddy_privkey(client->application, context); silcpurple_buddy_privkey(PurpleConnection *gc, const char *name) SilcPurple sg = gc->proto_data; SilcClientEntry *clients; SilcUInt32 clients_count; if (!silc_parse_userfqdn(name, &nickname, NULL)) clients = silc_client_get_clients_local(sg->client, sg->conn, silc_client_get_clients(sg->client, sg->conn, nickname, NULL, silcpurple_buddy_privkey_resolved, p = silc_calloc(1, sizeof(*p)); p->client_id = *clients[0]->id; purple_request_input(gc, _("IM With Password"), NULL, _("Set IM Password"), NULL, FALSE, TRUE, NULL, _("OK"), G_CALLBACK(silcpurple_buddy_privkey_cb), _("Cancel"), G_CALLBACK(silcpurple_buddy_privkey_cb), gc->account, NULL, NULL, p); silcpurple_buddy_privkey_menu(PurpleBlistNode *node, gpointer data) g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY(node)); buddy = (PurpleBuddy *) node; gc = purple_account_get_connection(buddy->account); silcpurple_buddy_privkey(gc, buddy->name); /**************************** Get Public Key *********************************/ SilcClientConnection conn; } *SilcPurpleBuddyGetkey; silcpurple_buddy_getkey(PurpleConnection *gc, const char *name); silcpurple_buddy_getkey_cb(SilcPurpleBuddyGetkey g, SilcClientCommandReplyContext cmd) SilcClientEntry client_entry; /* Get the client entry. */ client_entry = silc_client_get_client_by_id(g->client, g->conn, purple_notify_error(g->client->application, _("Get Public Key"), _("The remote user is not present in the network any more"), if (!client_entry->public_key) { /* Now verify the public key */ pk = silc_pkcs_public_key_encode(client_entry->public_key, &pk_len); silcpurple_verify_public_key(g->client, g->conn, client_entry->nickname, pk, pk_len, SILC_SKE_PK_TYPE_SILC, silcpurple_buddy_getkey_resolved(SilcClient client, SilcClientConnection conn, SilcClientEntry *clients, SilcUInt32 clients_count, g_snprintf(tmp, sizeof(tmp), _("User %s is not present in the network"), purple_notify_error(client->application, _("Get Public Key"), _("Cannot fetch the public key"), tmp); silcpurple_buddy_getkey(client->application, context); silcpurple_buddy_getkey(PurpleConnection *gc, const char *name) SilcPurple sg = gc->proto_data; SilcClient client = sg->client; SilcClientConnection conn = sg->conn; SilcClientEntry *clients; SilcUInt32 clients_count; if (!silc_parse_userfqdn(name, &nickname, NULL)) clients = silc_client_get_clients_local(client, conn, nickname, name, silc_client_get_clients(client, conn, nickname, NULL, silcpurple_buddy_getkey_resolved, g = silc_calloc(1, sizeof(*g)); g->client_id = *clients[0]->id; silc_client_command_call(client, conn, NULL, "GETKEY", clients[0]->nickname, NULL); silc_client_command_pending(conn, SILC_COMMAND_GETKEY, (SilcCommandCb)silcpurple_buddy_getkey_cb, g); silcpurple_buddy_getkey_menu(PurpleBlistNode *node, gpointer data) g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY(node)); buddy = (PurpleBuddy *) node; gc = purple_account_get_connection(buddy->account); silcpurple_buddy_getkey(gc, buddy->name); silcpurple_buddy_showkey(PurpleBlistNode *node, gpointer data) SilcPublicKey public_key; g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY(node)); b = (PurpleBuddy *) node; gc = purple_account_get_connection(b->account); pkfile = purple_blist_node_get_string(node, "public-key"); if (!silc_pkcs_load_public_key(pkfile, &public_key, SILC_PKCS_FILE_PEM) && !silc_pkcs_load_public_key(pkfile, &public_key, SILC_PKCS_FILE_BIN)) { _("Could not load public key"), NULL); silcpurple_show_public_key(sg, b->name, public_key, NULL, NULL); silc_pkcs_public_key_free(public_key); /**************************** Buddy routines *********************************/ /* The buddies are implemented by using the WHOIS and WATCH commands that can be used to search users by their public key. Since nicknames aren't unique in SILC we cannot trust the buddy list using their nickname. We associate public keys to buddies and use those to search and watch The problem is that Purple does not return PurpleBuddy contexts to the callbacks but the buddy names. Naturally, this is not going to work with SILC. But, for now, we have to do what we can... */ SilcClientConnection conn; unsigned char *offline_pk; SilcUInt32 offline_pk_len; unsigned int offline : 1; unsigned int pubkey_search : 1; silcpurple_add_buddy_ask_pk_cb(SilcPurpleBuddyRes r, gint id); silcpurple_add_buddy_resolved(SilcClient client, SilcClientConnection conn, SilcClientEntry *clients, SilcUInt32 clients_count, void silcpurple_get_info(PurpleConnection *gc, const char *who) SilcPurple sg = gc->proto_data; SilcClient client = sg->client; SilcClientConnection conn = sg->conn; SilcClientEntry client_entry; const char *filename, *nick = who; if (strlen(who) > 1 && who[0] == '@') if (strlen(who) > 1 && who[0] == '*') if (strlen(who) > 2 && who[0] == '*' && who[1] == '@') b = purple_find_buddy(gc->account, nick); /* See if we have this buddy's public key. If we do use that to search the details. */ filename = purple_blist_node_get_string((PurpleBlistNode *)b, "public-key"); /* Call WHOIS. The user info is displayed in the WHOIS silc_client_command_call(client, conn, NULL, "WHOIS", "-details", "-pubkey", filename, NULL); g_snprintf(tmp, sizeof(tmp), _("User %s is not present in the network"), b->name); purple_notify_error(gc, _("User Information"), _("Cannot get user information"), tmp); client_entry = silc_client_get_client_by_id(client, conn, b->proto_data); /* Call WHOIS. The user info is displayed in the WHOIS silc_client_command_call(client, conn, NULL, "WHOIS", client_entry->nickname, "-details", NULL); /* Call WHOIS just with nickname. */ silc_client_command_call(client, conn, NULL, "WHOIS", nick, NULL); silcpurple_add_buddy_pk_no(SilcPurpleBuddyRes r) g_snprintf(tmp, sizeof(tmp), _("The %s buddy is not trusted"), purple_notify_error(r->client->application, _("Add Buddy"), tmp, _("You cannot receive buddy notifications until you " "import his/her public key. You can use the Get Public Key " "command to get the public key.")); purple_prpl_got_user_status(purple_buddy_get_account(r->b), purple_buddy_get_name(r->b), SILCPURPLE_STATUS_ID_OFFLINE, NULL); silcpurple_add_buddy_save(bool success, void *context) SilcPurpleBuddyRes r = context; SilcClient client = r->client; SilcClientEntry client_entry; SilcAttributePayload attr; SilcAttributeObjMime message, extension; #ifdef SILC_ATTRIBUTE_USER_ICON SilcAttributeObjMime usericon; SilcAttributeObjPk serverpk, usersign, serversign; gboolean usign_success = TRUE, ssign_success = TRUE; char filename[512], filename2[512], *fingerprint = NULL, *tmp; /* The user did not trust the public key. */ silcpurple_add_buddy_pk_no(r); /* User is offline. Associate the imported public key with fingerprint = silc_hash_fingerprint(NULL, r->offline_pk, for (i = 0; i < strlen(fingerprint); i++) if (fingerprint[i] == ' ') g_snprintf(filename, sizeof(filename) - 1, "%s" G_DIR_SEPARATOR_S "clientkeys" G_DIR_SEPARATOR_S "clientkey_%s.pub", silcpurple_silcdir(), fingerprint); purple_blist_node_set_string((PurpleBlistNode *)b, "public-key", filename); purple_prpl_got_user_status(purple_buddy_get_account(r->b), purple_buddy_get_name(r->b), SILCPURPLE_STATUS_ID_OFFLINE, NULL); silc_free(r->offline_pk); /* Get the client entry. */ client_entry = silc_client_get_client_by_id(r->client, r->conn, memset(&vcard, 0, sizeof(vcard)); memset(&message, 0, sizeof(message)); memset(&extension, 0, sizeof(extension)); #ifdef SILC_ATTRIBUTE_USER_ICON memset(&usericon, 0, sizeof(usericon)); memset(&serverpk, 0, sizeof(serverpk)); memset(&usersign, 0, sizeof(usersign)); memset(&serversign, 0, sizeof(serversign)); /* Now that we have the public key and we trust it now we save the attributes of the buddy and update its status. */ if (client_entry->attrs) { silc_dlist_start(client_entry->attrs); while ((attr = silc_dlist_get(client_entry->attrs)) attribute = silc_attribute_get_attribute(attr); case SILC_ATTRIBUTE_USER_INFO: if (!silc_attribute_get_object(attr, (void *)&vcard, case SILC_ATTRIBUTE_STATUS_MESSAGE: if (!silc_attribute_get_object(attr, (void *)&message, case SILC_ATTRIBUTE_EXTENSION: if (!silc_attribute_get_object(attr, (void *)&extension, #ifdef SILC_ATTRIBUTE_USER_ICON case SILC_ATTRIBUTE_USER_ICON: if (!silc_attribute_get_object(attr, (void *)&usericon, case SILC_ATTRIBUTE_SERVER_PUBLIC_KEY: if (!silc_attribute_get_object(attr, (void *)&serverpk, case SILC_ATTRIBUTE_USER_DIGITAL_SIGNATURE: if (!silc_attribute_get_object(attr, (void *)&usersign, case SILC_ATTRIBUTE_SERVER_DIGITAL_SIGNATURE: if (!silc_attribute_get_object(attr, (void *)&serversign, /* Verify the attribute signatures */ silc_pkcs_alloc((unsigned char*)"rsa", &pkcs); verifyd = silc_attribute_get_verify_data(client_entry->attrs, if (verifyd && silc_pkcs_public_key_set(pkcs, client_entry->public_key)){ if (!silc_pkcs_verify_with_hash(pkcs, client->sha1hash, if (serversign.data && !strcmp(serverpk.type, "silc-rsa")) { SilcPublicKey public_key; if (silc_pkcs_public_key_decode(serverpk.data, serverpk.data_len, silc_pkcs_alloc((unsigned char *)"rsa", &pkcs); verifyd = silc_attribute_get_verify_data(client_entry->attrs, if (verifyd && silc_pkcs_public_key_set(pkcs, public_key)) { if (!silc_pkcs_verify_with_hash(pkcs, client->sha1hash, silc_pkcs_public_key_free(public_key); fingerprint = silc_fingerprint(client_entry->fingerprint, client_entry->fingerprint_len); for (i = 0; i < strlen(fingerprint); i++) if (fingerprint[i] == ' ') if (usign_success || ssign_success) { memset(filename2, 0, sizeof(filename2)); tmp = fingerprint + strlen(fingerprint) - 9; g_snprintf(filename, sizeof(filename) - 1, "%s" G_DIR_SEPARATOR_S "friends" G_DIR_SEPARATOR_S "%s", silcpurple_silcdir(), tmp); /* Create dir if it doesn't exist */ if ((g_stat(filename, &st)) == -1) { if (pw->pw_uid == geteuid()) { int ret = g_mkdir(filename, 0755); g_snprintf(filename2, sizeof(filename2) - 1, "%s" G_DIR_SEPARATOR_S "vcard", filename); tmp = (char *)silc_vcard_encode(&vcard, &len); silc_file_writefile(filename2, tmp, len); /* Save status message */ memset(filename2, 0, sizeof(filename2)); g_snprintf(filename2, sizeof(filename2) - 1, "%s" G_DIR_SEPARATOR_S "status_message.mime", silc_file_writefile(filename2, (char *)message.mime, /* Save extension data */ memset(filename2, 0, sizeof(filename2)); g_snprintf(filename2, sizeof(filename2) - 1, "%s" G_DIR_SEPARATOR_S "extension.mime", silc_file_writefile(filename2, (char *)extension.mime, #ifdef SILC_ATTRIBUTE_USER_ICON SilcMime m = silc_mime_decode(usericon.mime, const char *type = silc_mime_get_field(m, "Content-Type"); if (!strcmp(type, "image/jpeg") || !strcmp(type, "image/gif") || !strcmp(type, "image/bmp") || !strcmp(type, "image/png")) { const unsigned char *data; data = silc_mime_get_data(m, &data_len); /* TODO: Check if SILC gives us something to use as the checksum instead */ purple_buddy_icons_set_for_user(purple_buddy_get_account(r->b), purple_buddy_get_name(r->b), g_memdup(data, data_len), data_len, NULL); /* Save the public key path to buddy properties, as it is used to identify the buddy in the network (and not the nickname). */ memset(filename, 0, sizeof(filename)); g_snprintf(filename, sizeof(filename) - 1, "%s" G_DIR_SEPARATOR_S "clientkeys" G_DIR_SEPARATOR_S "clientkey_%s.pub", silcpurple_silcdir(), fingerprint); purple_blist_node_set_string((PurpleBlistNode *)b, "public-key", filename); /* Update online status */ purple_prpl_got_user_status(purple_buddy_get_account(r->b), purple_buddy_get_name(r->b), SILCPURPLE_STATUS_ID_AVAILABLE, NULL); /* Finally, start watching this user so we receive its status changes from the server */ g_snprintf(filename2, sizeof(filename2) - 1, "+%s", filename); silc_client_command_call(r->client, r->conn, NULL, "WATCH", "-pubkey", silcpurple_add_buddy_ask_import(void *user_data, const char *name) SilcPurpleBuddyRes r = (SilcPurpleBuddyRes)user_data; SilcPublicKey public_key; /* Load the public key */ if (!silc_pkcs_load_public_key(name, &public_key, SILC_PKCS_FILE_PEM) && !silc_pkcs_load_public_key(name, &public_key, SILC_PKCS_FILE_BIN)) { silcpurple_add_buddy_ask_pk_cb(r, 0); purple_notify_error(r->client->application, _("Add Buddy"), _("Could not load public key"), NULL); /* Now verify the public key */ r->offline_pk = silc_pkcs_public_key_encode(public_key, &r->offline_pk_len); silcpurple_verify_public_key(r->client, r->conn, r->b->name, r->offline_pk, r->offline_pk_len, silcpurple_add_buddy_save, r); silcpurple_add_buddy_ask_pk_cancel(void *user_data, const char *name) SilcPurpleBuddyRes r = (SilcPurpleBuddyRes)user_data; /* The user did not import public key. The buddy is unusable. */ silcpurple_add_buddy_pk_no(r); silcpurple_add_buddy_ask_pk_cb(SilcPurpleBuddyRes r, gint id) /* The user did not import public key. The buddy is unusable. */ silcpurple_add_buddy_pk_no(r); /* Open file selector to select the public key. */ purple_request_file(r->client->application, _("Open..."), NULL, FALSE, G_CALLBACK(silcpurple_add_buddy_ask_import), G_CALLBACK(silcpurple_add_buddy_ask_pk_cancel), purple_buddy_get_account(r->b), purple_buddy_get_name(r->b), NULL, r); silcpurple_add_buddy_ask_pk(SilcPurpleBuddyRes r) g_snprintf(tmp, sizeof(tmp), _("The %s buddy is not present in the network"), purple_request_action(r->client->application, _("Add Buddy"), tmp, _("To add the buddy you must import his/her public key. " "Press Import to import a public key."), 0, purple_buddy_get_account(r->b), purple_buddy_get_name(r->b), NULL, r, 2, _("Cancel"), G_CALLBACK(silcpurple_add_buddy_ask_pk_cb), _("_Import..."), G_CALLBACK(silcpurple_add_buddy_ask_pk_cb)); silcpurple_add_buddy_getkey_cb(SilcPurpleBuddyRes r, SilcClientCommandReplyContext cmd) SilcClientEntry client_entry; /* Get the client entry. */ client_entry = silc_client_get_client_by_id(r->client, r->conn, if (!client_entry || !client_entry->public_key) { /* The buddy is offline/nonexistent. We will require user to associate a public key with the buddy or the buddy silcpurple_add_buddy_ask_pk(r); /* Now verify the public key */ pk = silc_pkcs_public_key_encode(client_entry->public_key, &pk_len); silcpurple_verify_public_key(r->client, r->conn, client_entry->nickname, pk, pk_len, SILC_SKE_PK_TYPE_SILC, silcpurple_add_buddy_save, r); silcpurple_add_buddy_select_cb(SilcPurpleBuddyRes r, PurpleRequestFields *fields) SilcClientEntry client_entry; f = purple_request_fields_get_field(fields, "list"); list = purple_request_field_list_get_selected(f); /* The user did not select any user. */ silcpurple_add_buddy_pk_no(r); client_entry = purple_request_field_list_get_data(f, list->data); silcpurple_add_buddy_resolved(r->client, r->conn, &client_entry, 1, r); silcpurple_add_buddy_select_cancel(SilcPurpleBuddyRes r, PurpleRequestFields *fields) /* The user did not select any user. */ silcpurple_add_buddy_pk_no(r); silcpurple_add_buddy_select(SilcPurpleBuddyRes r, SilcClientEntry *clients, SilcUInt32 clients_count) PurpleRequestFields *fields; PurpleRequestFieldGroup *g; char tmp[512], tmp2[128]; fields = purple_request_fields_new(); g = purple_request_field_group_new(NULL); f = purple_request_field_list_new("list", NULL); purple_request_field_group_add_field(g, f); purple_request_field_list_set_multi_select(f, FALSE); purple_request_fields_add_group(fields, g); for (i = 0; i < clients_count; i++) { if (clients[i]->fingerprint) { fingerprint = silc_fingerprint(clients[i]->fingerprint, clients[i]->fingerprint_len); g_snprintf(tmp2, sizeof(tmp2), "\n%s", fingerprint); g_snprintf(tmp, sizeof(tmp), "%s - %s (%s@%s)%s", clients[i]->realname, clients[i]->nickname, clients[i]->username, clients[i]->hostname ? clients[i]->hostname : "", fingerprint ? tmp2 : ""); purple_request_field_list_add_icon(f, tmp, NULL, clients[i]); purple_request_fields(r->client->application, _("Add Buddy"), _("Select correct user"), ? _("More than one user was found with the same public key. Select " "the correct user from the list to add to the buddy list.") : _("More than one user was found with the same name. Select " "the correct user from the list to add to the buddy list."), _("OK"), G_CALLBACK(silcpurple_add_buddy_select_cb), _("Cancel"), G_CALLBACK(silcpurple_add_buddy_select_cancel), purple_buddy_get_account(r->b), purple_buddy_get_name(r->b), NULL, r); silcpurple_add_buddy_resolved(SilcClient client, SilcClientConnection conn, SilcClientEntry *clients, SilcUInt32 clients_count, SilcPurpleBuddyRes r = context; SilcAttributePayload pub; SilcAttributeObjPk userpk; filename = purple_blist_node_get_string((PurpleBlistNode *)b, "public-key"); /* If the buddy is offline/nonexistent, we will require user to associate a public key with the buddy or the buddy /* If the user has already associated a public key, try loading it * before prompting the user to load it again */ silcpurple_add_buddy_ask_import(r, filename); silcpurple_add_buddy_ask_pk(r); /* If more than one client was found with nickname, we need to verify from user which one is the correct. */ if (clients_count > 1 && !r->pubkey_search) { silcpurple_add_buddy_select(r, clients, clients_count); /* If we searched using public keys and more than one entry was found the same person is logged on multiple times. */ if (clients_count > 1 && r->pubkey_search && b->name) { /* Find the entry that closest matches to the for (i = 0; i < clients_count; i++) { if (!g_ascii_strncasecmp(b->name, clients[i]->nickname, /* Verify from user which one is correct */ silcpurple_add_buddy_select(r, clients, clients_count); /* The client was found. Now get its public key and verify that before adding the buddy. */ memset(&userpk, 0, sizeof(userpk)); b->proto_data = silc_memdup(clients[0]->id, sizeof(*clients[0]->id)); r->client_id = *clients[0]->id; /* Get the public key from attributes, if not present then resolve it with GETKEY unless we have it cached already. */ if (clients[0]->attrs && !clients[0]->public_key) { pub = silcpurple_get_attr(clients[0]->attrs, SILC_ATTRIBUTE_USER_PUBLIC_KEY); if (!pub || !silc_attribute_get_object(pub, (void *)&userpk, /* Get public key with GETKEY */ silc_client_command_call(client, conn, NULL, "GETKEY", clients[0]->nickname, NULL); silc_client_command_pending(conn, SILC_COMMAND_GETKEY, (SilcCommandCb)silcpurple_add_buddy_getkey_cb, if (!silc_pkcs_public_key_decode(userpk.data, userpk.data_len, &clients[0]->public_key)) } else if (filename && !clients[0]->public_key) { if (!silc_pkcs_load_public_key(filename, &clients[0]->public_key, !silc_pkcs_load_public_key(filename, &clients[0]->public_key, /* Get public key with GETKEY */ silc_client_command_call(client, conn, NULL, "GETKEY", clients[0]->nickname, NULL); silc_client_command_pending(conn, SILC_COMMAND_GETKEY, (SilcCommandCb)silcpurple_add_buddy_getkey_cb, } else if (!clients[0]->public_key) { /* Get public key with GETKEY */ silc_client_command_call(client, conn, NULL, "GETKEY", clients[0]->nickname, NULL); silc_client_command_pending(conn, SILC_COMMAND_GETKEY, (SilcCommandCb)silcpurple_add_buddy_getkey_cb, /* We have the public key, verify it. */ pk = silc_pkcs_public_key_encode(clients[0]->public_key, &pk_len); silcpurple_verify_public_key(client, conn, clients[0]->nickname, pk, pk_len, SILC_SKE_PK_TYPE_SILC, silcpurple_add_buddy_save, r); silcpurple_add_buddy_i(PurpleConnection *gc, PurpleBuddy *b, gboolean init) SilcPurple sg = gc->proto_data; SilcClient client = sg->client; SilcClientConnection conn = sg->conn; const char *filename, *name = b->name; r = silc_calloc(1, sizeof(*r)); /* See if we have this buddy's public key. If we do use that to search the details. */ filename = purple_blist_node_get_string((PurpleBlistNode *)b, "public-key"); SilcPublicKey public_key; SilcAttributeObjPk userpk; if (!silc_pkcs_load_public_key(filename, &public_key, !silc_pkcs_load_public_key(filename, &public_key, /* Get all attributes, and use the public key to search user */ attrs = silc_client_attributes_request(SILC_ATTRIBUTE_USER_INFO, SILC_ATTRIBUTE_STATUS_MOOD, SILC_ATTRIBUTE_STATUS_FREETEXT, SILC_ATTRIBUTE_STATUS_MESSAGE, SILC_ATTRIBUTE_PREFERRED_LANGUAGE, SILC_ATTRIBUTE_PREFERRED_CONTACT, SILC_ATTRIBUTE_GEOLOCATION, #ifdef SILC_ATTRIBUTE_USER_ICON SILC_ATTRIBUTE_USER_ICON, SILC_ATTRIBUTE_DEVICE_INFO, 0); userpk.type = "silc-rsa"; userpk.data = silc_pkcs_public_key_encode(public_key, &userpk.data_len); attrs = silc_attribute_payload_encode(attrs, SILC_ATTRIBUTE_USER_PUBLIC_KEY, SILC_ATTRIBUTE_FLAG_VALID, &userpk, sizeof(userpk)); silc_pkcs_public_key_free(public_key); attrs = silc_client_attributes_request(0); silc_client_get_clients_whois(client, conn, name, NULL, attrs, silcpurple_add_buddy_resolved, r); void silcpurple_add_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *group) /* Don't add if the buddy is already on the list. * SILC doesn't have groups, so we don't need to do anything if (purple_buddy_get_protocol_data(buddy) == NULL) silcpurple_add_buddy_i(gc, buddy, FALSE); void silcpurple_send_buddylist(PurpleConnection *gc) PurpleBlistNode *gnode, *cnode, *bnode; account = purple_connection_get_account(gc); if ((blist = purple_get_blist()) != NULL) for (gnode = blist->root; gnode != NULL; gnode = gnode->next) if (!PURPLE_BLIST_NODE_IS_GROUP(gnode)) for (cnode = gnode->child; cnode != NULL; cnode = cnode->next) if (!PURPLE_BLIST_NODE_IS_CONTACT(cnode)) for (bnode = cnode->child; bnode != NULL; bnode = bnode->next) if (!PURPLE_BLIST_NODE_IS_BUDDY(bnode)) buddy = (PurpleBuddy *)bnode; if (purple_buddy_get_account(buddy) == account) silcpurple_add_buddy_i(gc, buddy, TRUE); void silcpurple_remove_buddy(PurpleConnection *gc, PurpleBuddy *buddy, silc_free(buddy->proto_data); void silcpurple_idle_set(PurpleConnection *gc, int idle) SilcClientConnection conn; SilcAttributeObjService service; server = purple_account_get_string(sg->account, "server", port = purple_account_get_int(sg->account, "port", 706), memset(&service, 0, sizeof(service)); silc_client_attribute_del(client, conn, SILC_ATTRIBUTE_SERVICE, NULL); g_snprintf(service.address, sizeof(service.address), "%s", server); silc_client_attribute_add(client, conn, SILC_ATTRIBUTE_SERVICE, &service, sizeof(service)); char *silcpurple_status_text(PurpleBuddy *b) SilcPurple sg = b->account->gc->proto_data; SilcClient client = sg->client; SilcClientConnection conn = sg->conn; SilcClientID *client_id = b->proto_data; SilcClientEntry client_entry; SilcAttributePayload attr; SilcAttributeMood mood = 0; /* Get the client entry. */ client_entry = silc_client_get_client_by_id(client, conn, client_id); /* If user is online, we show the mood status, if available. If user is offline or away that status is indicated. */ if (client_entry->mode & SILC_UMODE_DETACHED) return g_strdup(_("Detached")); if (client_entry->mode & SILC_UMODE_GONE) return g_strdup(_("Away")); if (client_entry->mode & SILC_UMODE_INDISPOSED) return g_strdup(_("Indisposed")); if (client_entry->mode & SILC_UMODE_BUSY) return g_strdup(_("Busy")); if (client_entry->mode & SILC_UMODE_PAGE) return g_strdup(_("Wake Me Up")); if (client_entry->mode & SILC_UMODE_HYPER) return g_strdup(_("Hyper Active")); if (client_entry->mode & SILC_UMODE_ROBOT) return g_strdup(_("Robot")); attr = silcpurple_get_attr(client_entry->attrs, SILC_ATTRIBUTE_STATUS_MOOD); if (attr && silc_attribute_get_object(attr, &mood, sizeof(mood))) { /* The mood is a bit mask, so we could show multiple moods, but let's show only one for now. */ if (mood & SILC_ATTRIBUTE_MOOD_HAPPY) return g_strdup(_("Happy")); if (mood & SILC_ATTRIBUTE_MOOD_SAD) return g_strdup(_("Sad")); if (mood & SILC_ATTRIBUTE_MOOD_ANGRY) return g_strdup(_("Angry")); if (mood & SILC_ATTRIBUTE_MOOD_JEALOUS) return g_strdup(_("Jealous")); if (mood & SILC_ATTRIBUTE_MOOD_ASHAMED) return g_strdup(_("Ashamed")); if (mood & SILC_ATTRIBUTE_MOOD_INVINCIBLE) return g_strdup(_("Invincible")); if (mood & SILC_ATTRIBUTE_MOOD_INLOVE) return g_strdup(_("In Love")); if (mood & SILC_ATTRIBUTE_MOOD_SLEEPY) return g_strdup(_("Sleepy")); if (mood & SILC_ATTRIBUTE_MOOD_BORED) return g_strdup(_("Bored")); if (mood & SILC_ATTRIBUTE_MOOD_EXCITED) return g_strdup(_("Excited")); if (mood & SILC_ATTRIBUTE_MOOD_ANXIOUS) return g_strdup(_("Anxious")); void silcpurple_tooltip_text(PurpleBuddy *b, PurpleNotifyUserInfo *user_info, gboolean full) SilcPurple sg = b->account->gc->proto_data; SilcClient client = sg->client; SilcClientConnection conn = sg->conn; SilcClientID *client_id = b->proto_data; SilcClientEntry client_entry; char *moodstr, *statusstr, *contactstr, *langstr, *devicestr, *tzstr, *geostr; /* Get the client entry. */ client_entry = silc_client_get_client_by_id(client, conn, client_id); if (client_entry->nickname) purple_notify_user_info_add_pair(user_info, _("Nickname"), if (client_entry->username && client_entry->hostname) { g_snprintf(tmp, sizeof(tmp), "%s@%s", client_entry->username, client_entry->hostname); purple_notify_user_info_add_pair(user_info, _("Username"), tmp); if (client_entry->mode) { memset(tmp, 0, sizeof(tmp)); silcpurple_get_umode_string(client_entry->mode, tmp, sizeof(tmp) - strlen(tmp)); purple_notify_user_info_add_pair(user_info, _("User Modes"), tmp); silcpurple_parse_attrs(client_entry->attrs, &moodstr, &statusstr, &contactstr, &langstr, &devicestr, &tzstr, &geostr); purple_notify_user_info_add_pair(user_info, _("Message"), statusstr); purple_notify_user_info_add_pair(user_info, _("Mood"), moodstr); purple_notify_user_info_add_pair(user_info, _("Preferred Contact"), contactstr); purple_notify_user_info_add_pair(user_info, _("Preferred Language"), langstr); purple_notify_user_info_add_pair(user_info, _("Device"), devicestr); purple_notify_user_info_add_pair(user_info, _("Timezone"), tzstr); purple_notify_user_info_add_pair(user_info, _("Geolocation"), geostr); silcpurple_buddy_kill(PurpleBlistNode *node, gpointer data) g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY(node)); b = (PurpleBuddy *) node; gc = purple_account_get_connection(b->account); silc_client_command_call(sg->client, sg->conn, NULL, "KILL", b->name, "Killed by operator", NULL); SilcClientEntry client_entry; silcpurple_buddy_wb(PurpleBlistNode *node, gpointer data) SilcPurpleBuddyWb wb = data; silcpurple_wb_init(wb->sg, wb->client_entry); GList *silcpurple_buddy_menu(PurpleBuddy *buddy) PurpleConnection *gc = purple_account_get_connection(buddy->account); SilcPurple sg = gc->proto_data; SilcClientConnection conn = sg->conn; const char *pkfile = NULL; SilcClientEntry client_entry = NULL; pkfile = purple_blist_node_get_string((PurpleBlistNode *) buddy, "public-key"); client_entry = silc_client_get_client_by_id(sg->client, if (client_entry && client_entry->send_key) { act = purple_menu_action_new(_("Reset IM Key"), PURPLE_CALLBACK(silcpurple_buddy_resetkey), m = g_list_append(m, act); act = purple_menu_action_new(_("IM with Key Exchange"), PURPLE_CALLBACK(silcpurple_buddy_keyagr), m = g_list_append(m, act); act = purple_menu_action_new(_("IM with Password"), PURPLE_CALLBACK(silcpurple_buddy_privkey_menu), m = g_list_append(m, act); act = purple_menu_action_new(_("Show Public Key"), PURPLE_CALLBACK(silcpurple_buddy_showkey), m = g_list_append(m, act); act = purple_menu_action_new(_("Get Public Key..."), PURPLE_CALLBACK(silcpurple_buddy_getkey_menu), m = g_list_append(m, act); if (conn && conn->local_entry->mode & SILC_UMODE_ROUTER_OPERATOR) { act = purple_menu_action_new(_("Kill User"), PURPLE_CALLBACK(silcpurple_buddy_kill), m = g_list_append(m, act); wb = silc_calloc(1, sizeof(*wb)); wb->client_entry = client_entry; act = purple_menu_action_new(_("Draw On Whiteboard"), PURPLE_CALLBACK(silcpurple_buddy_wb), m = g_list_append(m, act); #ifdef SILC_ATTRIBUTE_USER_ICON void silcpurple_buddy_set_icon(PurpleConnection *gc, PurpleStoredImage *img) SilcPurple sg = gc->proto_data; SilcClient client = sg->client; SilcClientConnection conn = sg->conn; SilcAttributeObjMime obj; silc_client_attribute_del(client, conn, SILC_ATTRIBUTE_USER_ICON, NULL); mime = silc_mime_alloc(); t = purple_imgstore_get_extension(img); if (!t || !strcmp(t, "icon")) { g_snprintf(type, sizeof(type), "image/%s", t); silc_mime_add_field(mime, "Content-Type", type); silc_mime_add_data(mime, purple_imgstore_get_data(img), purple_imgstore_get_size(img)); obj.mime = icon = silc_mime_encode(mime, &obj.mime_len); silc_client_attribute_add(client, conn, SILC_ATTRIBUTE_USER_ICON, &obj, sizeof(obj));