Exemple #1
0
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;
}
Exemple #2
0
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);
}