static unsigned char * silc_attribute_payload_encode_int(SilcAttribute attribute, SilcAttributeFlags flags, void *object, SilcUInt32 object_size, SilcUInt32 *ret_len) { SilcBuffer tmpbuf = NULL; unsigned char tmp[4], *str = NULL, *ret; SilcUInt32 len; /* Encode according to attribute type */ if (flags & SILC_ATTRIBUTE_FLAG_VALID) { if (!object && !object_size) return NULL; switch (attribute) { case SILC_ATTRIBUTE_USER_INFO: { SilcVCard vcard = object; if (object_size != sizeof(*vcard)) return NULL; str = silc_vcard_encode(vcard, &object_size); if (!str) return NULL; object = str; } break; case SILC_ATTRIBUTE_SERVICE: { SilcAttributeObjService *service = object; SilcUInt32 len2; if (object_size != sizeof(*service)) return NULL; len = strlen(service->address); len2 = strlen(service->signon); tmpbuf = silc_buffer_alloc_size(13 + len + len2); if (!tmpbuf) return NULL; silc_buffer_format(tmpbuf, SILC_STR_UI_INT(service->port), SILC_STR_UI_SHORT(len), SILC_STR_UI_XNSTRING(service->address, len), SILC_STR_UI_CHAR(service->status), SILC_STR_UI_SHORT(len2), SILC_STR_UI_XNSTRING(service->signon, len2), SILC_STR_UI_INT(service->idle), SILC_STR_END); object = tmpbuf->data; object_size = silc_buffer_len(tmpbuf); } break; case SILC_ATTRIBUTE_STATUS_MOOD: case SILC_ATTRIBUTE_PREFERRED_CONTACT: { SilcUInt32 mask = SILC_PTR_TO_32(object); if (object_size != sizeof(SilcUInt32)) return NULL; SILC_PUT32_MSB(mask, tmp); object = tmp; object_size = sizeof(SilcUInt32); } break; case SILC_ATTRIBUTE_STATUS_FREETEXT: case SILC_ATTRIBUTE_PREFERRED_LANGUAGE: case SILC_ATTRIBUTE_TIMEZONE: { unsigned char *string = object; str = silc_malloc(2 + object_size); if (!str) return NULL; SILC_PUT16_MSB(object_size, str); memcpy(str + 2, string, object_size); object = str; object_size += 2; } break; case SILC_ATTRIBUTE_STATUS_MESSAGE: case SILC_ATTRIBUTE_EXTENSION: case SILC_ATTRIBUTE_USER_ICON: { SilcMime mime = object; if (object_size != sizeof(*mime)) return NULL; str = silc_mime_encode(mime, &object_size); if (!str) return NULL; object = str; } break; case SILC_ATTRIBUTE_GEOLOCATION: { SilcAttributeObjGeo *geo = object; SilcUInt32 len1, len2, len3, len4; if (object_size != sizeof(*geo)) return NULL; len1 = (geo->longitude ? strlen(geo->longitude) : 0); len2 = (geo->latitude ? strlen(geo->latitude) : 0); len3 = (geo->altitude ? strlen(geo->altitude) : 0); len4 = (geo->accuracy ? strlen(geo->accuracy) : 0); if (len1 + len2 + len3 + len4 == 0) return NULL; len = len1 + len2 + len3 + len4; tmpbuf = silc_buffer_alloc_size(8 + len); if (!tmpbuf) return NULL; silc_buffer_format(tmpbuf, SILC_STR_UI_SHORT(len1), SILC_STR_UI16_STRING(len1 ? geo->longitude : ""), SILC_STR_UI_SHORT(len2), SILC_STR_UI16_STRING(len2 ? geo->latitude : ""), SILC_STR_UI_SHORT(len3), SILC_STR_UI16_STRING(len3 ? geo->altitude : ""), SILC_STR_UI_SHORT(len4), SILC_STR_UI16_STRING(len4 ? geo->accuracy : ""), SILC_STR_END); object = tmpbuf->data; object_size = silc_buffer_len(tmpbuf); } break; case SILC_ATTRIBUTE_DEVICE_INFO: { SilcAttributeObjDevice *dev = object; SilcUInt32 len1, len2, len3, len4; if (object_size != sizeof(*dev)) return NULL; len1 = (dev->manufacturer ? strlen(dev->manufacturer) : 0); len2 = (dev->version ? strlen(dev->version) : 0); len3 = (dev->model ? strlen(dev->model) : 0); len4 = (dev->language ? strlen(dev->language) : 0); if (len1 + len2 + len3 + len4 == 0) return NULL; len = len1 + len2 + len3 + len4; tmpbuf = silc_buffer_alloc_size(4 + 8 + len); if (!tmpbuf) return NULL; silc_buffer_format(tmpbuf, SILC_STR_UI_INT(dev->type), SILC_STR_UI_SHORT(len1), SILC_STR_UI16_STRING(len1 ? dev->manufacturer : ""), SILC_STR_UI_SHORT(len2), SILC_STR_UI16_STRING(len2 ? dev->version : ""), SILC_STR_UI_SHORT(len3), SILC_STR_UI16_STRING(len3 ? dev->model : ""), SILC_STR_UI_SHORT(len4), SILC_STR_UI16_STRING(len4 ? dev->language : ""), SILC_STR_END); object = tmpbuf->data; object_size = silc_buffer_len(tmpbuf); } break; case SILC_ATTRIBUTE_PHONE_NUMBER: { SilcAttributeObjPN *pn = object; if (object_size != sizeof(*pn)) return NULL; if (!pn->number || strlen(pn->number) < 5) return NULL; tmpbuf = silc_buffer_alloc(0); if (!tmpbuf) return NULL; if (silc_buffer_format(tmpbuf, SILC_STR_UI_INT(pn->format), SILC_STR_UI_SHORT(strlen(pn->number)), SILC_STR_UI16_STRING(pn->number), SILC_STR_END) < 0) return NULL; object = tmpbuf->data; object_size = silc_buffer_len(tmpbuf); } break; case SILC_ATTRIBUTE_USER_PUBLIC_KEY: case SILC_ATTRIBUTE_SERVER_PUBLIC_KEY: { SilcAttributeObjPk *pk = object; if (object_size != sizeof(*pk)) return NULL; len = (pk->type ? strlen(pk->type) : 0); tmpbuf = silc_buffer_alloc_size(2 + len + pk->data_len); if (!tmpbuf) return NULL; silc_buffer_format(tmpbuf, SILC_STR_UI_SHORT(len), SILC_STR_UI16_STRING(pk->type), SILC_STR_UI_XNSTRING(pk->data, pk->data_len), SILC_STR_END); object = tmpbuf->data; object_size = silc_buffer_len(tmpbuf); } break; case SILC_ATTRIBUTE_USER_DIGITAL_SIGNATURE: case SILC_ATTRIBUTE_SERVER_DIGITAL_SIGNATURE: { SilcAttributeObjPk *pk = object; if (object_size != sizeof(*pk)) return NULL; object = pk->data; object_size = pk->data_len; } break; default: return NULL; break; } ret = silc_memdup(object, object_size); if (tmpbuf) silc_buffer_free(tmpbuf); silc_free(str); if (ret_len) *ret_len = object_size; return ret; } return NULL; }
static void silcpurple_add_buddy_save(bool success, void *context) { SilcPurpleBuddyRes r = context; PurpleBuddy *b = r->b; SilcClient client = r->client; SilcClientEntry client_entry; SilcAttributePayload attr; SilcAttribute attribute; SilcVCardStruct vcard; SilcAttributeObjMime message, extension; #ifdef SILC_ATTRIBUTE_USER_ICON SilcAttributeObjMime usericon; #endif SilcAttributeObjPk serverpk, usersign, serversign; gboolean usign_success = TRUE, ssign_success = TRUE; char filename[512], filename2[512], *fingerprint = NULL, *tmp; SilcUInt32 len; int i; if (!success) { /* The user did not trust the public key. */ silcpurple_add_buddy_pk_no(r); silc_free(r); return; } if (r->offline) { /* User is offline. Associate the imported public key with this user. */ fingerprint = silc_hash_fingerprint(NULL, r->offline_pk, r->offline_pk_len); for (i = 0; i < strlen(fingerprint); i++) if (fingerprint[i] == ' ') 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(fingerprint); silc_free(r->offline_pk); silc_free(r); return; } /* Get the client entry. */ client_entry = silc_client_get_client_by_id(r->client, r->conn, &r->client_id); if (!client_entry) { silc_free(r); return; } 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)); #endif 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)) != SILC_LIST_END) { attribute = silc_attribute_get_attribute(attr); switch (attribute) { case SILC_ATTRIBUTE_USER_INFO: if (!silc_attribute_get_object(attr, (void *)&vcard, sizeof(vcard))) continue; break; case SILC_ATTRIBUTE_STATUS_MESSAGE: if (!silc_attribute_get_object(attr, (void *)&message, sizeof(message))) continue; break; case SILC_ATTRIBUTE_EXTENSION: if (!silc_attribute_get_object(attr, (void *)&extension, sizeof(extension))) continue; break; #ifdef SILC_ATTRIBUTE_USER_ICON case SILC_ATTRIBUTE_USER_ICON: if (!silc_attribute_get_object(attr, (void *)&usericon, sizeof(usericon))) continue; break; #endif case SILC_ATTRIBUTE_SERVER_PUBLIC_KEY: if (serverpk.type) continue; if (!silc_attribute_get_object(attr, (void *)&serverpk, sizeof(serverpk))) continue; break; case SILC_ATTRIBUTE_USER_DIGITAL_SIGNATURE: if (usersign.data) continue; if (!silc_attribute_get_object(attr, (void *)&usersign, sizeof(usersign))) continue; break; case SILC_ATTRIBUTE_SERVER_DIGITAL_SIGNATURE: if (serversign.data) continue; if (!silc_attribute_get_object(attr, (void *)&serversign, sizeof(serversign))) continue; break; default: break; } } } /* Verify the attribute signatures */ if (usersign.data) { SilcPKCS pkcs; unsigned char *verifyd; SilcUInt32 verify_len; silc_pkcs_alloc((unsigned char*)"rsa", &pkcs); verifyd = silc_attribute_get_verify_data(client_entry->attrs, FALSE, &verify_len); if (verifyd && silc_pkcs_public_key_set(pkcs, client_entry->public_key)){ if (!silc_pkcs_verify_with_hash(pkcs, client->sha1hash, usersign.data, usersign.data_len, verifyd, verify_len)) usign_success = FALSE; } silc_free(verifyd); } if (serversign.data && purple_strequal(serverpk.type, "silc-rsa")) { SilcPublicKey public_key; SilcPKCS pkcs; unsigned char *verifyd; SilcUInt32 verify_len; if (silc_pkcs_public_key_decode(serverpk.data, serverpk.data_len, &public_key)) { silc_pkcs_alloc((unsigned char *)"rsa", &pkcs); verifyd = silc_attribute_get_verify_data(client_entry->attrs, TRUE, &verify_len); if (verifyd && silc_pkcs_public_key_set(pkcs, public_key)) { if (!silc_pkcs_verify_with_hash(pkcs, client->sha1hash, serversign.data, serversign.data_len, verifyd, verify_len)) ssign_success = FALSE; } silc_pkcs_public_key_free(public_key); silc_free(verifyd); } } fingerprint = silc_fingerprint(client_entry->fingerprint, client_entry->fingerprint_len); for (i = 0; i < strlen(fingerprint); i++) if (fingerprint[i] == ' ') fingerprint[i] = '_'; if (usign_success || ssign_success) { struct passwd *pw; struct stat st; memset(filename2, 0, sizeof(filename2)); /* Filename for dir */ 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); pw = getpwuid(getuid()); if (!pw) return; /* Create dir if it doesn't exist */ if ((g_stat(filename, &st)) == -1) { if (errno == ENOENT) { if (pw->pw_uid == geteuid()) { int ret = g_mkdir(filename, 0755); if (ret < 0) return; } } } /* Save VCard */ g_snprintf(filename2, sizeof(filename2) - 1, "%s" G_DIR_SEPARATOR_S "vcard", filename); if (vcard.full_name) { tmp = (char *)silc_vcard_encode(&vcard, &len); silc_file_writefile(filename2, tmp, len); silc_free(tmp); } /* Save status message */ if (message.mime) { memset(filename2, 0, sizeof(filename2)); g_snprintf(filename2, sizeof(filename2) - 1, "%s" G_DIR_SEPARATOR_S "status_message.mime", filename); silc_file_writefile(filename2, (char *)message.mime, message.mime_len); } /* Save extension data */ if (extension.mime) { memset(filename2, 0, sizeof(filename2)); g_snprintf(filename2, sizeof(filename2) - 1, "%s" G_DIR_SEPARATOR_S "extension.mime", filename); silc_file_writefile(filename2, (char *)extension.mime, extension.mime_len); } #ifdef SILC_ATTRIBUTE_USER_ICON /* Save user icon */ if (usericon.mime) { SilcMime m = silc_mime_decode(usericon.mime, usericon.mime_len); if (m) { const char *type = silc_mime_get_field(m, "Content-Type"); if (purple_strequal(type, "image/jpeg") || purple_strequal(type, "image/gif") || purple_strequal(type, "image/bmp") || purple_strequal(type, "image/png")) { const unsigned char *data; SilcUInt32 data_len; data = silc_mime_get_data(m, &data_len); if (data) { /* 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); } } silc_mime_free(m); } } #endif } /* 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", filename2, NULL); silc_free(fingerprint); silc_free(r); }