Beispiel #1
0
SilcBool silc_mime_is_partial(SilcMime mime)
{
    const char *type = silc_mime_get_field(mime, "Content-Type");
    if (!type)
        return FALSE;

    if (!strstr(type, "message/partial"))
        return FALSE;

    return TRUE;
}
Beispiel #2
0
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;
}
Beispiel #3
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;
}
Beispiel #4
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);
}