SilcBool silc_attribute_get_object(SilcAttributePayload payload, void *object, SilcUInt32 object_size) { SilcUInt16 len; SilcBool ret = FALSE; if (!object || payload->flags & SILC_ATTRIBUTE_FLAG_INVALID) return FALSE; switch (payload->attribute) { case SILC_ATTRIBUTE_USER_INFO: { SilcVCard vcard = object; if (object_size != sizeof(*vcard)) break; if (!silc_vcard_decode(payload->data, payload->data_len, vcard)) break; ret = TRUE; } break; case SILC_ATTRIBUTE_SERVICE: { SilcAttributeObjService *service = object; SilcBufferStruct buf; SilcUInt16 addr_len, signon_len; char *addr, *signon; int res; if (object_size != sizeof(*service)) break; if (payload->data_len < 13) break; silc_buffer_set(&buf, payload->data, payload->data_len); res = silc_buffer_unformat(&buf, SILC_STR_UI_INT(&service->port), SILC_STR_UI16_NSTRING(&addr, &addr_len), SILC_STR_UI_CHAR(&service->status), SILC_STR_UI16_NSTRING(&signon, &signon_len), SILC_STR_UI_INT(&service->idle), SILC_STR_END); if (res == -1) break; memset(service->address, 0, sizeof(service->address)); memset(service->signon, 0, sizeof(service->signon)); memcpy(service->address, addr, (addr_len < sizeof(service->address) - 1 ? addr_len : sizeof(service->address) - 1)); memcpy(service->signon, signon, (signon_len < sizeof(service->signon) - 1 ? signon_len : sizeof(service->signon) - 1)); ret = TRUE; } break; case SILC_ATTRIBUTE_STATUS_MOOD: case SILC_ATTRIBUTE_PREFERRED_CONTACT: { SilcUInt32 *mask = (SilcUInt32 *)object; if (object_size != sizeof(SilcUInt32)) break; if (payload->data_len < 4) break; SILC_GET32_MSB(*mask, payload->data); ret = TRUE; } break; case SILC_ATTRIBUTE_STATUS_FREETEXT: case SILC_ATTRIBUTE_PREFERRED_LANGUAGE: case SILC_ATTRIBUTE_TIMEZONE: { char *string = object; if (payload->data_len < 2) break; SILC_GET16_MSB(len, payload->data); if (payload->data_len < 2 + len) break; if (object_size < len) break; memcpy(string, payload->data + 2, len); ret = TRUE; } break; case SILC_ATTRIBUTE_STATUS_MESSAGE: case SILC_ATTRIBUTE_EXTENSION: case SILC_ATTRIBUTE_USER_ICON: { SilcMime mime = object; if (object_size != sizeof(*mime)) break; if (!silc_mime_decode(mime, payload->data, payload->data_len)) break; ret = TRUE; } break; case SILC_ATTRIBUTE_GEOLOCATION: { SilcAttributeObjGeo *geo = object; SilcBufferStruct buffer; int res; if (object_size != sizeof(*geo)) break; silc_buffer_set(&buffer, (unsigned char *)payload->data, payload->data_len); res = silc_buffer_unformat(&buffer, SILC_STR_UI16_STRING_ALLOC(&geo->longitude), SILC_STR_UI16_STRING_ALLOC(&geo->latitude), SILC_STR_UI16_STRING_ALLOC(&geo->altitude), SILC_STR_UI16_STRING_ALLOC(&geo->accuracy), SILC_STR_END); if (res == -1) break; ret = TRUE; } break; case SILC_ATTRIBUTE_DEVICE_INFO: { SilcAttributeObjDevice *dev = object; SilcBufferStruct buffer; SilcUInt32 type; int res; if (object_size != sizeof(*dev)) break; silc_buffer_set(&buffer, (unsigned char *)payload->data, payload->data_len); res = silc_buffer_unformat(&buffer, SILC_STR_UI_INT(&type), SILC_STR_UI16_STRING_ALLOC(&dev->manufacturer), SILC_STR_UI16_STRING_ALLOC(&dev->version), SILC_STR_UI16_STRING_ALLOC(&dev->model), SILC_STR_UI16_STRING_ALLOC(&dev->language), SILC_STR_END); if (res == -1) break; dev->type = type; ret = TRUE; } break; case SILC_ATTRIBUTE_PHONE_NUMBER: { SilcAttributeObjPN *pn = object; SilcBufferStruct buffer; SilcUInt32 pn_format; int res; if (object_size != sizeof(*pn)) break; silc_buffer_set(&buffer, (unsigned char *)payload->data, payload->data_len); res = silc_buffer_unformat(&buffer, SILC_STR_UI_INT(&pn_format), SILC_STR_UI16_STRING_ALLOC(&pn->number), SILC_STR_END); if (res == -1) break; pn->format = pn_format; ret = TRUE; } break; case SILC_ATTRIBUTE_USER_PUBLIC_KEY: case SILC_ATTRIBUTE_SERVER_PUBLIC_KEY: { SilcAttributeObjPk *pk = object; SilcBufferStruct buffer; int res; if (object_size != sizeof(*pk)) break; silc_buffer_set(&buffer, (unsigned char *)payload->data, payload->data_len); res = silc_buffer_unformat(&buffer, SILC_STR_UI16_NSTRING_ALLOC(&pk->type, &len), SILC_STR_END); if (res == -1 || len > silc_buffer_len(&buffer) - 2) break; pk->data = silc_memdup(payload->data + 2 + len, payload->data_len - 2 - len); pk->data_len = payload->data_len - 2 - len; ret = TRUE; } break; case SILC_ATTRIBUTE_USER_DIGITAL_SIGNATURE: case SILC_ATTRIBUTE_SERVER_DIGITAL_SIGNATURE: { SilcAttributeObjPk *pk = object; if (object_size != sizeof(*pk)) break; pk->type = NULL; pk->data = silc_memdup(payload->data, payload->data_len); pk->data_len = payload->data_len; ret = TRUE; } break; default: break; } return ret; }
SilcMime silc_mime_decode(SilcMime mime, const unsigned char *data, SilcUInt32 data_len) { SilcMime m = NULL; int i, k; char *tmp, *field, *value, *line; SILC_LOG_DEBUG(("Parsing MIME message")); if (!data) return NULL; if (!mime) { mime = silc_mime_alloc(); if (!mime) return NULL; m = mime; } /* Parse the fields */ line = tmp = (char *)data; for (i = 0; i < data_len; i++) { /* Get field line */ if (data_len - i >= 2 && tmp[i] == '\r' && tmp[i + 1] == '\n') { /* Get field */ field = strchr(line, ':'); if (!field) goto err; field = silc_memdup(line, field - line); if (!field) goto err; /* Get value. Remove whitespaces too. */ value = strchr(line, ':'); if ((tmp + i) - value < 2) goto err; value++; for (k = 0; k < (tmp + i) - value; k++) { if (value[k] == '\r') goto err; if (value[k] != ' ' && value[k] != '\t') break; } value += k; if ((tmp + i) - value < 1) goto err; value = silc_memdup(value, (tmp + i) - value); if (!value) goto err; SILC_LOG_DEBUG(("Header '%s' '%s'", field, value)); /* Add field and value */ silc_mime_add_field(mime, field, value); silc_free(field); silc_free(value); /* Mark start of next line */ line = (tmp + i) + 2; i += 2; /* Break if this is last header */ if (data_len - i >= 2 && tmp[i] == '\r' && tmp[i + 1] == '\n') { i += 2; break; } } } /* Parse multiparts if present */ field = (char *)silc_mime_get_field(mime, "Content-Type"); if (field && strstr(field, "multipart")) { char b[1024]; SilcMime p; unsigned int len; mime->multiparts = silc_dlist_init(); if (!mime->multiparts) goto err; /* Get multipart type */ value = strchr(field, '/'); if (!value) goto err; value++; if (strchr(field, '"')) value++; if (!strchr(field, ';')) goto err; memset(b, 0, sizeof(b)); len = (unsigned int)(strchr(field, ';') - value); if (len > sizeof(b) - 1) goto err; strncpy(b, value, len); if (strchr(b, '"')) *strchr(b, '"') = '\0'; mime->multitype = silc_memdup(b, strlen(b)); /* Get boundary */ value = strrchr(field, '='); if (value && strlen(value) > 1) { value++; SILC_LOG_DEBUG(("Boundary '%s'", value)); memset(b, 0, sizeof(b)); line = strdup(value); if (strrchr(line, '"')) { *strrchr(line, '"') = '\0'; silc_snprintf(b, sizeof(b) - 1, "--%s", line + 1); mime->boundary = strdup(line + 1); } else { silc_snprintf(b, sizeof(b) - 1, "--%s", line); mime->boundary = strdup(line); } silc_free(line); for (i = i; i < data_len; i++) { /* Get boundary data */ if (data_len - i >= strlen(b) && tmp[i] == '-' && tmp[i + 1] == '-') { if (memcmp(tmp + i, b, strlen(b))) continue; i += strlen(b); if (data_len - i >= 4 && tmp[i ] == '\r' && tmp[i + 1] == '\n' && tmp[i + 2] == '\r' && tmp[i + 3] == '\n') i += 4; else if (data_len - i >= 2 && tmp[i] == '\r' && tmp[i + 1] == '\n') i += 2; else if (data_len - i >= 2 && tmp[i] == '-' && tmp[i + 1] == '-') break; line = tmp + i; /* Find end of boundary */ for (k = i; k < data_len; k++) if (data_len - k >= strlen(b) && tmp[k] == '-' && tmp[k + 1] == '-') if (!memcmp(tmp + k, b, strlen(b))) break; if (k >= data_len) goto err; /* Remove preceding CRLF */ k -= 2; /* Parse the part */ p = silc_mime_decode(NULL, line, k - i); if (!p) goto err; silc_dlist_add(mime->multiparts, p); i += (k - i); } } } } else { /* Get data area. If we are at the end and we have fields present there is no data area present, but, if fields are not present we only have data area. */ if (i >= data_len && !silc_hash_table_count(mime->fields)) i = 0; SILC_LOG_DEBUG(("Data len %d", data_len - i)); if (data_len - i) silc_mime_add_data(mime, tmp + i, data_len - i); } return mime; err: if (m) silc_mime_free(m); return NULL; }
SilcMime silc_mime_assemble(SilcMimeAssembler assembler, SilcMime partial) { char *type, *id = NULL, *tmp; SilcHashTable f; SilcMime p, complete; int i, number, total = -1; const unsigned char *data; SilcUInt32 data_len; SilcBuffer compbuf = NULL; SILC_LOG_DEBUG(("Assembling MIME fragments")); if (!assembler || !partial) goto err; type = (char *)silc_mime_get_field(partial, "Content-Type"); if (!type) goto err; /* Get ID */ tmp = strstr(type, "id="); if (!tmp) goto err; if (strlen(tmp) <= 4) goto err; tmp += 3; if (*tmp == '"') tmp++; id = strdup(tmp); if (strchr(id, ';')) *strchr(id, ';') = '\0'; if (strrchr(id, '"')) *strrchr(id, '"') = '\0'; SILC_LOG_DEBUG(("Fragment ID %s", id)); /* Get fragment number */ tmp = strstr(type, "number="); if (!tmp) goto err; tmp = strchr(tmp, '='); if (strlen(tmp) < 2) goto err; tmp++; if (strchr(tmp, ';')) { tmp = strdup(tmp); *strchr(tmp, ';') = '\0'; number = atoi(tmp); silc_free(tmp); } else { number = atoi(tmp); } SILC_LOG_DEBUG(("Fragment number %d", number)); /* Find fragments with this ID. */ if (!silc_hash_table_find(assembler->fragments, (void *)id, NULL, (void *)&f)) { /* This is new fragment to new message. Add to hash table and return. */ f = silc_hash_table_alloc(0, silc_hash_uint, NULL, NULL, NULL, silc_mime_assemble_dest, NULL, TRUE); if (!f) goto err; silc_hash_table_add(f, SILC_32_TO_PTR(number), partial); silc_hash_table_add(assembler->fragments, id, f); return NULL; } /* Try to get total number */ tmp = strstr(type, "total="); if (tmp) { tmp = strchr(tmp, '='); if (strlen(tmp) < 2) goto err; tmp++; if (strchr(tmp, ';')) { tmp = strdup(tmp); *strchr(tmp, ';') = '\0'; total = atoi(tmp); silc_free(tmp); } else { total = atoi(tmp); } SILC_LOG_DEBUG(("Fragment total %d", total)); } /* If more fragments to come, add to hash table */ if (number != total) { silc_hash_table_add(f, SILC_32_TO_PTR(number), partial); return NULL; } silc_hash_table_add(f, SILC_32_TO_PTR(number), partial); /* Verify that we really have all the fragments */ if (silc_hash_table_count(f) < total) return NULL; /* Assemble the complete MIME message now. We get them in order from the hash table. */ for (i = 1; i <= total; i++) { if (!silc_hash_table_find(f, SILC_32_TO_PTR(i), NULL, (void *)&p)) goto err; /* The fragment is in the data portion of the partial message */ data = silc_mime_get_data(p, &data_len); if (!data) goto err; /* Assemble */ if (!compbuf) { compbuf = silc_buffer_alloc_size(data_len); if (!compbuf) goto err; silc_buffer_put(compbuf, data, data_len); } else { compbuf = silc_buffer_realloc(compbuf, silc_buffer_truelen(compbuf) + data_len); if (!compbuf) goto err; silc_buffer_put_tail(compbuf, data, data_len); silc_buffer_pull_tail(compbuf, data_len); } } /* Now parse the complete MIME message and deliver it */ complete = silc_mime_decode(NULL, (const unsigned char *)compbuf->head, silc_buffer_truelen(compbuf)); if (!complete) goto err; /* Delete the hash table entry. Destructors will free memory */ silc_hash_table_del(assembler->fragments, (void *)id); silc_free(id); silc_buffer_free(compbuf); return complete; err: silc_free(id); if (compbuf) silc_buffer_free(compbuf); silc_mime_free(partial); 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); }