void silc_attribute_payload_list_free(SilcDList list) { SilcAttributePayload entry; silc_dlist_start(list); while ((entry = silc_dlist_get(list)) != SILC_LIST_END) { silc_attribute_payload_free(entry); silc_dlist_del(list, entry); } silc_dlist_uninit(list); }
void silc_client_ftp_free_sessions(SilcClient client) { SilcClientFtpSession session; if (!client->internal->ftp_sessions) return; silc_dlist_start(client->internal->ftp_sessions); while ((session = silc_dlist_get(client->internal->ftp_sessions))) silc_client_ftp_session_free(session); silc_dlist_del(client->internal->ftp_sessions, session); }
void silc_mime_partial_free(SilcDList partials) { SilcBuffer buf; if (!partials) return; silc_dlist_start(partials); while ((buf = silc_dlist_get(partials)) != SILC_LIST_END) silc_buffer_free(buf); silc_dlist_uninit(partials); }
void silc_client_ftp_session_free_client(SilcClient client, SilcClientEntry client_entry) { SilcClientFtpSession session; if (!client->internal->ftp_sessions) return; /* Get the session */ silc_dlist_start(client->internal->ftp_sessions); while ((session = silc_dlist_get(client->internal->ftp_sessions))) if (session->client_entry == client_entry) silc_client_ftp_session_free(session); }
SilcAttributePayload silcpurple_get_attr(SilcDList attrs, SilcAttribute attribute) { SilcAttributePayload attr = NULL; if (!attrs) return NULL; silc_dlist_start(attrs); while ((attr = silc_dlist_get(attrs)) != SILC_LIST_END) if (attribute == silc_attribute_get_attribute(attr)) break; return attr; }
SilcClientFileError silc_client_file_close(SilcClient client, SilcClientConnection conn, SilcUInt32 session_id) { SilcClientFtpSession session; if (!client || !conn) return SILC_CLIENT_FILE_ERROR; SILC_LOG_DEBUG(("Closing file transer session %d", session_id)); /* Get the session */ silc_dlist_start(client->internal->ftp_sessions); while ((session = silc_dlist_get(client->internal->ftp_sessions)) != SILC_LIST_END) { if (session->session_id == session_id) break; } if (session == SILC_LIST_END) { SILC_LOG_DEBUG(("Unknown session ID: %d\n", session_id)); return SILC_CLIENT_FILE_UNKNOWN_SESSION; } if (session->monitor) { (*session->monitor)(session->client, session->conn, SILC_CLIENT_FILE_MONITOR_CLOSED, SILC_CLIENT_FILE_OK, 0, 0, session->client_entry, session->session_id, session->filepath, session->monitor_context); /* No more callbacks to application */ session->monitor = NULL; } silc_schedule_task_del_by_context(client->schedule, session); session->closed = TRUE; /* Destroy via timeout */ silc_schedule_task_add_timeout(conn->internal->schedule, silc_client_file_close_final, session, 0, 1); return SILC_CLIENT_FILE_OK; }
unsigned char *silc_attribute_get_verify_data(SilcDList attrs, SilcBool server_verification, SilcUInt32 *data_len) { SilcAttributePayload attr; SilcBufferStruct buffer; unsigned char *data = NULL; SilcUInt32 len = 0; silc_dlist_start(attrs); while ((attr = silc_dlist_get(attrs)) != SILC_LIST_END) { switch (attr->attribute) { case SILC_ATTRIBUTE_SERVER_DIGITAL_SIGNATURE: /* Server signature is never part of the verification data */ break; case SILC_ATTRIBUTE_USER_DIGITAL_SIGNATURE: /* For user signature verification this is not part of the data */ if (!server_verification) break; /* Fallback, for server signature verification, user digital signature is part of verification data. */ default: /* All other data is part of the verification data */ data = silc_realloc(data, sizeof(*data) * (4 + attr->data_len + len)); if (!data) return NULL; silc_buffer_set(&buffer, data + len, 4 + attr->data_len); silc_buffer_format(&buffer, SILC_STR_UI_CHAR(attr->attribute), SILC_STR_UI_CHAR(attr->flags), SILC_STR_UI_SHORT(attr->data_len), SILC_STR_UI_XNSTRING(attr->data, attr->data_len), SILC_STR_END); len += 4 + attr->data_len; break; } } if (data_len) *data_len = len; return data; }
void silc_mime_free(SilcMime mime) { SilcMime m; if (mime->fields) silc_hash_table_free(mime->fields); if (mime->multiparts) { silc_dlist_start(mime->multiparts); while ((m = silc_dlist_get(mime->multiparts)) != SILC_LIST_END) silc_mime_free(m); silc_dlist_uninit(mime->multiparts); } silc_free(mime->boundary); silc_free(mime->multitype); silc_free(mime->data); silc_free(mime); }
SilcBool silc_client_del_channel_private_key(SilcClient client, SilcClientConnection conn, SilcChannelEntry channel, SilcChannelPrivateKey key) { SilcChannelPrivateKey entry; if (!client || !conn || !channel) return FALSE; if (!channel->internal.private_keys) return FALSE; silc_dlist_start(channel->internal.private_keys); while ((entry = silc_dlist_get(channel->internal.private_keys))) { if (entry != key) continue; if (channel->internal.curr_key == entry) { channel->internal.curr_key = NULL; channel->cipher = silc_cipher_get_name(channel->internal.send_key); channel->hmac = silc_hmac_get_name(channel->internal.hmac); } silc_dlist_del(channel->internal.private_keys, entry); silc_free(entry->name); silc_cipher_free(entry->send_key); silc_cipher_free(entry->receive_key); silc_hmac_free(entry->hmac); silc_free(entry); if (silc_dlist_count(channel->internal.private_keys) == 0) { silc_dlist_uninit(channel->internal.private_keys); channel->internal.private_keys = NULL; } return TRUE; } return FALSE; }
int command_silc_msg(void *data, struct t_gui_buffer *buffer, int argc, char **argv, char **argv_eol) { char *nickname; char *msg; struct t_gui_buffer *query_buffer; struct SilcClientContext *clientCtx; SilcClientEntry client_entry; SilcDList list; SilcPluginServerList server = find_server_for_buffer(buffer); if (argc < 3) { weechat_printf(buffer, "you need to specify a recipient"); return WEECHAT_RC_ERROR; } nickname = argv[2]; list = silc_client_get_clients_local(silc_plugin->client, server->connection, nickname, FALSE); if (list == NULL) { weechat_printf(buffer, "no such nick: %s", nickname); return WEECHAT_RC_OK; } silc_dlist_start(list); client_entry = silc_dlist_get(list); silc_client_list_free(silc_plugin->client, server->connection, list); clientCtx = client_entry->context; if (clientCtx == NULL) { clientCtx = malloc(sizeof(struct SilcClientContext)); query_buffer = weechat_buffer_new(client_entry->nickname, &silc_plugin_query_input, clientCtx, NULL, NULL); clientCtx->query_buffer = query_buffer; clientCtx->client_entry = client_entry; clientCtx->connection = server->connection; } if (argc > 3) { msg = argv_eol[3]; silc_plugin_query_input(clientCtx, clientCtx->query_buffer, msg); } return WEECHAT_RC_OK; }
SilcDList silc_client_list_channel_private_keys(SilcClient client, SilcClientConnection conn, SilcChannelEntry channel) { SilcChannelPrivateKey entry; SilcDList list; if (!client || !conn || !channel) return FALSE; if (!channel->internal.private_keys) return NULL; list = silc_dlist_init(); if (!list) return NULL; silc_dlist_start(channel->internal.private_keys); while ((entry = silc_dlist_get(channel->internal.private_keys))) silc_dlist_add(list, entry); return list; }
static char silc_server_command_reply_whois_save(SilcServerCommandReplyContext cmd) { SilcServer server = cmd->server; unsigned char *id_data, *umodes; char *nickname, *username, *realname, *tmp; unsigned char *fingerprint; SilcID id; SilcClientEntry client; char global = FALSE; char nick[128 + 1], servername[256 + 1], uname[128 + 1]; SilcUInt32 mode = 0, len, len2, id_len, flen; const char *hostname, *ip; silc_socket_stream_get_info(silc_packet_stream_get_stream(cmd->sock), NULL, &hostname, &ip, NULL); id_data = silc_argument_get_arg_type(cmd->args, 2, &id_len); nickname = silc_argument_get_arg_type(cmd->args, 3, &len); username = silc_argument_get_arg_type(cmd->args, 4, &len); realname = silc_argument_get_arg_type(cmd->args, 5, &len); if (!id_data || !nickname || !username || !realname) return FALSE; tmp = silc_argument_get_arg_type(cmd->args, 7, &len); if (tmp) SILC_GET32_MSB(mode, tmp); if (!silc_id_payload_parse_id(id_data, id_len, &id)) return FALSE; fingerprint = silc_argument_get_arg_type(cmd->args, 9, &flen); /* Check if we have this client cached already. */ client = silc_idlist_find_client_by_id(server->local_list, SILC_ID_GET_ID(id), FALSE, NULL); if (!client) { client = silc_idlist_find_client_by_id(server->global_list, SILC_ID_GET_ID(id), FALSE, NULL); global = TRUE; } if (!client) { /* If router did not find such Client ID in its lists then this must be bogus client or some router in the net is buggy. */ if (server->server_type != SILC_SERVER) return FALSE; /* Take hostname out of nick string if it includes it. */ silc_parse_userfqdn(nickname, nick, sizeof(nick), servername, sizeof(servername)); /* We don't have that client anywhere, add it. The client is added to global list since server didn't have it in the lists so it must be global. This will check for valid nickname and username strings. */ client = silc_idlist_add_client(server->global_list, strdup(nick), username, strdup(realname), silc_id_dup(SILC_ID_GET_ID(id), SILC_ID_CLIENT), silc_packet_get_context(cmd->sock), NULL); if (!client) { SILC_LOG_ERROR(("Could not add new client to the ID Cache")); return FALSE; } client->data.status |= (SILC_IDLIST_STATUS_REGISTERED | SILC_IDLIST_STATUS_RESOLVED); client->data.status &= ~SILC_IDLIST_STATUS_RESOLVING; client->mode = mode; client->servername = servername[0] ? strdup(servername) : NULL; SILC_LOG_DEBUG(("stat.clients %d->%d", server->stat.clients, server->stat.clients + 1)); server->stat.clients++; } else { /* We have the client already, update the data */ SILC_LOG_DEBUG(("Updating client data")); /* Check nickname */ silc_parse_userfqdn(nickname, nick, sizeof(nick), servername, sizeof(servername)); nickname = silc_identifier_check(nick, strlen(nick), SILC_STRING_UTF8, 128, NULL); if (!nickname) { SILC_LOG_ERROR(("Malformed nickname '%s' received in WHOIS reply " "from %s", hostname ? hostname : "", nick)); return FALSE; } /* Check username */ silc_parse_userfqdn(username, uname, sizeof(uname), NULL, 0); if (!silc_identifier_verify(uname, strlen(uname), SILC_STRING_UTF8, 128)) { SILC_LOG_ERROR(("Malformed username '%s' received in WHOIS reply " "from %s", hostname ? hostname : "", tmp)); return FALSE; } /* Update entry */ silc_idcache_update_by_context(global ? server->global_list->clients : server->local_list->clients, client, NULL, nickname, TRUE); silc_free(client->nickname); silc_free(client->username); silc_free(client->userinfo); silc_free(client->servername); client->nickname = strdup(nick); client->username = strdup(username); client->userinfo = strdup(realname); client->servername = servername[0] ? strdup(servername) : NULL; client->mode = mode; client->data.status |= SILC_IDLIST_STATUS_RESOLVED; client->data.status &= ~SILC_IDLIST_STATUS_RESOLVING; } /* Save channel list if it was sent to us */ if (server->server_type == SILC_SERVER) { tmp = silc_argument_get_arg_type(cmd->args, 6, &len); umodes = silc_argument_get_arg_type(cmd->args, 10, &len2); if (tmp && umodes) { SilcBufferStruct channels_buf, umodes_buf; silc_buffer_set(&channels_buf, tmp, len); silc_buffer_set(&umodes_buf, umodes, len2); silc_server_save_user_channels(server, cmd->sock, client, &channels_buf, &umodes_buf); } else { silc_server_save_user_channels(server, cmd->sock, client, NULL, NULL); } } if (fingerprint && flen == sizeof(client->data.fingerprint)) memcpy(client->data.fingerprint, fingerprint, flen); /* Take Requested Attributes if set. */ tmp = silc_argument_get_arg_type(cmd->args, 11, &len); if (tmp) { silc_free(client->attrs); client->attrs = silc_memdup(tmp, len); client->attrs_len = len; /* Try to take public key from attributes if present and we don't have the key already. Do this only on normal server. Routers do GETKEY for all clients anyway. */ if (server->server_type != SILC_ROUTER && !client->data.public_key) { SilcAttributePayload attr; SilcAttributeObjPk pk; unsigned char f[SILC_HASH_MAXLEN]; SilcDList attrs = silc_attribute_payload_parse(tmp, len); SILC_LOG_DEBUG(("Take client public key from attributes")); if (attrs) { silc_dlist_start(attrs); while ((attr = silc_dlist_get(attrs)) != SILC_LIST_END) { if (silc_attribute_get_attribute(attr) == SILC_ATTRIBUTE_USER_PUBLIC_KEY) { if (!silc_attribute_get_object(attr, &pk, sizeof(pk))) continue; /* Take only SILC public keys */ if (strcmp(pk.type, "silc-rsa")) { silc_free(pk.type); silc_free(pk.data); continue; } /* Verify that the server provided fingerprint matches the key */ silc_hash_make(server->sha1hash, pk.data, pk.data_len, f); if (memcmp(f, client->data.fingerprint, sizeof(f))) { silc_free(pk.type); silc_free(pk.data); continue; } /* Save the public key. */ if (!silc_pkcs_public_key_alloc(SILC_PKCS_SILC, pk.data, pk.data_len, &client->data.public_key)) { silc_free(pk.type); silc_free(pk.data); continue; } SILC_LOG_DEBUG(("Saved client public key from attributes")); /* Add client's public key to repository */ if (!silc_server_get_public_key_by_client(server, client, NULL)) silc_skr_add_public_key_simple(server->repository, client->data.public_key, SILC_SKR_USAGE_IDENTIFICATION, client, NULL); silc_free(pk.type); silc_free(pk.data); break; } } silc_attribute_payload_list_free(attrs); } } } return TRUE; }
SilcClientFileError silc_client_file_receive(SilcClient client, SilcClientConnection conn, SilcClientConnectionParams *params, SilcPublicKey public_key, SilcPrivateKey private_key, SilcClientFileMonitor monitor, void *monitor_context, const char *path, SilcUInt32 session_id, SilcClientFileAskName ask_name, void *ask_name_context) { SilcClientFtpSession session; SilcBuffer keyagr; if (!client || !conn) return SILC_CLIENT_FILE_ERROR; SILC_LOG_DEBUG(("Start, Session ID: %d", session_id)); /* Get the session */ silc_dlist_start(client->internal->ftp_sessions); while ((session = silc_dlist_get(client->internal->ftp_sessions)) != SILC_LIST_END) { if (session->session_id == session_id) { break; } } if (session == SILC_LIST_END) { SILC_LOG_DEBUG(("Unknown session ID: %d\n", session_id)); return SILC_CLIENT_FILE_UNKNOWN_SESSION; } /* See if we have this session running already */ if (session->sftp || session->listener) { SILC_LOG_DEBUG(("Session already started")); return SILC_CLIENT_FILE_ALREADY_STARTED; } session->monitor = monitor; session->monitor_context = monitor_context; session->ask_name = ask_name; session->ask_name_context = ask_name_context; session->path = path ? strdup(path) : NULL; /* If the hostname and port already exists then the remote client did provide the connection point to us and we won't create listener, but create the connection ourselves. */ if (session->hostname && session->port) { SILC_LOG_DEBUG(("Connecting to remote client")); /* Connect to the remote client. Performs key exchange automatically. */ session->op = silc_client_connect_to_client(client, params, public_key, private_key, session->hostname, session->port, silc_client_ftp_connect_completion, session); if (!session->op) { silc_free(session); return SILC_CLIENT_FILE_ERROR; } } else { /* Add the listener for the key agreement */ SILC_LOG_DEBUG(("Creating listener for file transfer")); if (!params || (!params->local_ip && !params->bind_ip)) { session->client->internal->ops->say(session->client, session->conn, SILC_CLIENT_MESSAGE_ERROR, "Cannot create listener for file " "transfer; IP address and/or port " "not provided"); silc_free(session); return SILC_CLIENT_FILE_ERROR; } session->listener = silc_client_listener_add(client, conn->internal->schedule, params, public_key, private_key, silc_client_ftp_connect_completion, session); if (!session->listener) { client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_ERROR, "Cannot create listener for file transfer: " "%s", strerror(errno)); silc_free(session); return SILC_CLIENT_FILE_NO_MEMORY; } session->hostname = (params->bind_ip ? strdup(params->bind_ip) : strdup(params->local_ip)); session->port = silc_client_listener_get_local_port(session->listener); /* Send the key agreement inside FTP packet */ SILC_LOG_DEBUG(("Sending key agreement for file transfer")); keyagr = silc_key_agreement_payload_encode(session->hostname, 0, session->port); if (!keyagr) { silc_client_listener_free(session->listener); silc_free(session); return SILC_CLIENT_FILE_NO_MEMORY; } silc_packet_send_va_ext(conn->stream, SILC_PACKET_FTP, 0, 0, NULL, SILC_ID_CLIENT, &session->client_entry->id, NULL, NULL, SILC_STR_UI_CHAR(1), SILC_STR_DATA(silc_buffer_data(keyagr), silc_buffer_len(keyagr)), SILC_STR_END); silc_buffer_free(keyagr); /* Add session request timeout */ if (params && params->timeout_secs) silc_schedule_task_add_timeout(client->schedule, silc_client_ftp_timeout, session, params->timeout_secs, 0); } return SILC_CLIENT_FILE_OK; }
SilcClientFileError silc_client_file_send(SilcClient client, SilcClientConnection conn, SilcClientEntry client_entry, SilcClientConnectionParams *params, SilcPublicKey public_key, SilcPrivateKey private_key, SilcClientFileMonitor monitor, void *monitor_context, const char *filepath, SilcUInt32 *session_id) { SilcClientFtpSession session; SilcBuffer keyagr; char *filename, *path; int fd; SILC_LOG_DEBUG(("File send request (file: %s)", filepath)); if (!client || !client_entry || !filepath || !params || !public_key || !private_key) return SILC_CLIENT_FILE_ERROR; /* Check for existing session for `filepath'. */ silc_dlist_start(client->internal->ftp_sessions); while ((session = silc_dlist_get(client->internal->ftp_sessions))) { if (session->filepath && !strcmp(session->filepath, filepath) && session->client_entry == client_entry) return SILC_CLIENT_FILE_ALREADY_STARTED; } /* See whether the file exists and can be opened */ fd = silc_file_open(filepath, O_RDONLY); if (fd < 0) return SILC_CLIENT_FILE_NO_SUCH_FILE; silc_file_close(fd); /* Add new session */ session = silc_calloc(1, sizeof(*session)); if (!session) return SILC_CLIENT_FILE_ERROR; session->session_id = ++client->internal->next_session_id; session->client = client; session->server_conn = conn; session->initiator = TRUE; session->client_entry = silc_client_ref_client(client, conn, client_entry); session->monitor = monitor; session->monitor_context = monitor_context; session->filepath = strdup(filepath); session->params = *params; session->public_key = public_key; session->private_key = private_key; if (silc_asprintf(&path, "file://%s", filepath) < 0) { silc_free(session); return SILC_CLIENT_FILE_NO_MEMORY; } /* Allocate memory filesystem and put the file to it */ if (strrchr(path, '/')) filename = strrchr(path, '/') + 1; else filename = (char *)path; session->fs = silc_sftp_fs_memory_alloc(SILC_SFTP_FS_PERM_READ | SILC_SFTP_FS_PERM_EXEC); silc_sftp_fs_memory_add_file(session->fs, NULL, SILC_SFTP_FS_PERM_READ, filename, path); session->filesize = silc_file_size(filepath); /* If local IP is provided, create listener for incoming key exchange */ if (params->local_ip || params->bind_ip) { session->listener = silc_client_listener_add(client, conn->internal->schedule, params, public_key, private_key, silc_client_ftp_connect_completion, session); if (!session->listener) { client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_ERROR, "Cannot create listener for file transfer: " "%s", strerror(errno)); silc_free(session); return SILC_CLIENT_FILE_NO_MEMORY; } session->hostname = (params->bind_ip ? strdup(params->bind_ip) : strdup(params->local_ip)); session->port = silc_client_listener_get_local_port(session->listener); } SILC_LOG_DEBUG(("Sending key agreement for file transfer")); /* Send the key agreement inside FTP packet */ keyagr = silc_key_agreement_payload_encode(session->hostname, 0, session->port); if (!keyagr) { if (session->listener) silc_client_listener_free(session->listener); silc_free(session); return SILC_CLIENT_FILE_NO_MEMORY; } silc_packet_send_va_ext(conn->stream, SILC_PACKET_FTP, 0, 0, NULL, SILC_ID_CLIENT, &client_entry->id, NULL, NULL, SILC_STR_UI_CHAR(1), SILC_STR_DATA(silc_buffer_data(keyagr), silc_buffer_len(keyagr)), SILC_STR_END); silc_buffer_free(keyagr); silc_free(path); silc_dlist_add(client->internal->ftp_sessions, session); if (session_id) *session_id = session->session_id; /* Add session request timeout */ if (params && params->timeout_secs) silc_schedule_task_add_timeout(client->schedule, silc_client_ftp_timeout, session, params->timeout_secs, 0); return SILC_CLIENT_FILE_OK; }
SilcBool silc_client_channel_save_public_keys(SilcChannelEntry channel, unsigned char *chpk_list, SilcUInt32 chpk_list_len, SilcBool remove_all) { SilcArgumentDecodedList a, b; SilcDList chpks; SilcBool found; if (remove_all) { /* Remove all channel public keys */ if (!channel->channel_pubkeys) return FALSE; silc_dlist_start(channel->channel_pubkeys); while ((b = silc_dlist_get(channel->channel_pubkeys))) silc_dlist_del(channel->channel_pubkeys, b); silc_dlist_uninit(channel->channel_pubkeys); channel->channel_pubkeys = NULL; return TRUE; } /* Parse channel public key list and add or remove public keys */ chpks = silc_argument_list_parse_decoded(chpk_list, chpk_list_len, SILC_ARGUMENT_PUBLIC_KEY); if (!chpks) return FALSE; if (!channel->channel_pubkeys) { channel->channel_pubkeys = silc_dlist_init(); if (!channel->channel_pubkeys) { silc_argument_list_free(chpks, SILC_ARGUMENT_PUBLIC_KEY); return FALSE; } } silc_dlist_start(chpks); while ((a = silc_dlist_get(chpks))) { found = FALSE; silc_dlist_start(channel->channel_pubkeys); while ((b = silc_dlist_get(channel->channel_pubkeys))) { if (silc_pkcs_public_key_compare(a->argument, b->argument)) { found = TRUE; break; } } if ((a->arg_type == 0x00 || a->arg_type == 0x03) && !found) { silc_dlist_add(channel->channel_pubkeys, a); silc_dlist_del(chpks, a); } else if (a->arg_type == 0x01 && found) { silc_dlist_del(channel->channel_pubkeys, b); } } silc_argument_list_free(chpks, SILC_ARGUMENT_PUBLIC_KEY); return TRUE; }
SilcDList silcpurple_image_message(const char *msg, SilcMessageFlags *mflags) { SilcMime mime = NULL, p; SilcDList list, parts = NULL; const char *start, *end, *last; GData *attribs; char *type; gboolean images = FALSE; last = msg; while (last && *last && purple_markup_find_tag("img", last, &start, &end, &attribs)) { PurpleStoredImage *image = NULL; const char *id; /* Check if there is text before image */ if (start - last) { char *text, *tmp; p = silc_mime_alloc(); /* Add content type */ silc_mime_add_field(p, "Content-Type", "text/plain; charset=utf-8"); tmp = g_strndup(last, start - last); text = purple_unescape_html(tmp); g_free(tmp); /* Add text */ silc_mime_add_data(p, (const unsigned char *)text, strlen(text)); g_free(text); if (!parts) parts = silc_dlist_init(); silc_dlist_add(parts, p); } id = g_datalist_get_data(&attribs, "id"); if (id && (image = purple_imgstore_find_by_id(atoi(id)))) { unsigned long imglen = purple_imgstore_get_size(image); gconstpointer img = purple_imgstore_get_data(image); p = silc_mime_alloc(); /* Add content type */ type = silcpurple_file2mime(purple_imgstore_get_filename(image)); if (!type) { g_datalist_clear(&attribs); last = end + 1; continue; } silc_mime_add_field(p, "Content-Type", type); g_free(type); /* Add content transfer encoding */ silc_mime_add_field(p, "Content-Transfer-Encoding", "binary"); /* Add image data */ silc_mime_add_data(p, img, imglen); if (!parts) parts = silc_dlist_init(); silc_dlist_add(parts, p); images = TRUE; } g_datalist_clear(&attribs); /* Continue after tag */ last = end + 1; } /* Check for text after the image(s) */ if (images && last && *last) { char *tmp = purple_unescape_html(last); p = silc_mime_alloc(); /* Add content type */ silc_mime_add_field(p, "Content-Type", "text/plain; charset=utf-8"); /* Add text */ silc_mime_add_data(p, (const unsigned char *)tmp, strlen(tmp)); g_free(tmp); if (!parts) parts = silc_dlist_init(); silc_dlist_add(parts, p); } /* If there weren't any images, don't return anything. */ if (!images) { if (parts) silc_dlist_uninit(parts); return NULL; } if (silc_dlist_count(parts) > 1) { /* Multipart MIME message */ char b[32]; mime = silc_mime_alloc(); silc_mime_add_field(mime, "MIME-Version", "1.0"); g_snprintf(b, sizeof(b), "b%4X%4X", (unsigned int)time(NULL), silc_dlist_count(parts)); silc_mime_set_multipart(mime, "mixed", b); silc_dlist_start(parts); while ((p = silc_dlist_get(parts)) != SILC_LIST_END) silc_mime_add_multipart(mime, p); } else { /* Simple MIME message */ silc_dlist_start(parts); mime = silc_dlist_get(parts); silc_mime_add_field(mime, "MIME-Version", "1.0"); } *mflags &= ~SILC_MESSAGE_FLAG_UTF8; *mflags |= SILC_MESSAGE_FLAG_DATA; /* Encode message. Fragment if it is too large */ list = silc_mime_encode_partial(mime, 0xfc00); silc_dlist_uninit(parts); /* Added multiparts gets freed here */ silc_mime_free(mime); return list; }
SilcBool silc_client_send_channel_message(SilcClient client, SilcClientConnection conn, SilcChannelEntry channel, SilcChannelPrivateKey key, SilcMessageFlags flags, SilcHash hash, unsigned char *data, SilcUInt32 data_len) { SilcChannelUser chu; SilcBuffer buffer; SilcCipher cipher; SilcHmac hmac; SilcBool ret; SilcID sid, rid; SILC_LOG_DEBUG(("Sending channel message")); if (silc_unlikely(!client || !conn || !channel)) return FALSE; if (silc_unlikely(flags & SILC_MESSAGE_FLAG_SIGNED && !hash)) return FALSE; if (silc_unlikely(conn->internal->disconnected)) return FALSE; chu = silc_client_on_channel(channel, conn->local_entry); if (silc_unlikely(!chu)) { client->internal->ops->say(conn->client, conn, SILC_CLIENT_MESSAGE_AUDIT, "Cannot talk to channel: not joined"); return FALSE; } /* Check if it is allowed to send messages to this channel by us. */ if (silc_unlikely(channel->mode & SILC_CHANNEL_MODE_SILENCE_USERS && !chu->mode)) return FALSE; if (silc_unlikely(channel->mode & SILC_CHANNEL_MODE_SILENCE_OPERS && chu->mode & SILC_CHANNEL_UMODE_CHANOP && !(chu->mode & SILC_CHANNEL_UMODE_CHANFO))) return FALSE; if (silc_unlikely(chu->mode & SILC_CHANNEL_UMODE_QUIET)) return FALSE; /* Take the key to be used */ if (channel->internal.private_keys) { if (key) { /* Use key application specified */ cipher = key->send_key; hmac = key->hmac; } else if (channel->mode & SILC_CHANNEL_MODE_PRIVKEY && channel->internal.curr_key) { /* Use current private key */ cipher = channel->internal.curr_key->send_key; hmac = channel->internal.curr_key->hmac; } else if (channel->mode & SILC_CHANNEL_MODE_PRIVKEY && !channel->internal.curr_key && channel->internal.private_keys) { /* Use just some private key since we don't know what to use and private keys are set. */ silc_dlist_start(channel->internal.private_keys); key = silc_dlist_get(channel->internal.private_keys); cipher = key->send_key; hmac = key->hmac; /* Use this key as current private key */ channel->internal.curr_key = key; } else { /* Use normal channel key generated by the server */ cipher = channel->internal.send_key; hmac = channel->internal.hmac; } } else { /* Use normal channel key generated by the server */ cipher = channel->internal.send_key; hmac = channel->internal.hmac; } if (silc_unlikely(!cipher || !hmac)) { SILC_LOG_ERROR(("No cipher and HMAC for channel")); return FALSE; } /* Encode the message payload. This also encrypts the message payload. */ sid.type = SILC_ID_CLIENT; sid.u.client_id = chu->client->id; rid.type = SILC_ID_CHANNEL; rid.u.channel_id = chu->channel->id; buffer = silc_message_payload_encode(flags, data, data_len, TRUE, FALSE, cipher, hmac, client->rng, NULL, conn->private_key, hash, &sid, &rid, NULL); if (silc_unlikely(!buffer)) { SILC_LOG_ERROR(("Error encoding channel message")); return FALSE; } /* Send the channel message */ ret = silc_packet_send_ext(conn->stream, SILC_PACKET_CHANNEL_MESSAGE, 0, 0, NULL, SILC_ID_CHANNEL, &channel->id, silc_buffer_datalen(buffer), NULL, NULL); silc_buffer_free(buffer); return ret; }
SilcBuffer silc_client_attributes_process(SilcClient client, SilcClientConnection conn, SilcDList attrs) { SilcBuffer buffer = NULL; SilcAttrForeach f; SilcAttribute attribute; SilcAttributePayload attr; SilcAttributeObjPk pk; unsigned char sign[2048 + 1]; SilcUInt32 sign_len; SILC_LOG_DEBUG(("Process Requested Attributes")); /* If nothing is set by application assume that we don't want to use attributes, ignore the request. */ if (!conn->internal->attrs) { SILC_LOG_DEBUG(("User has not set any attributes")); return NULL; } /* Always put our public key. */ pk.type = "silc-rsa"; pk.data = silc_pkcs_public_key_encode(conn->public_key, &pk.data_len); buffer = silc_attribute_payload_encode(buffer, SILC_ATTRIBUTE_USER_PUBLIC_KEY, pk.data ? SILC_ATTRIBUTE_FLAG_VALID : SILC_ATTRIBUTE_FLAG_INVALID, &pk, sizeof(pk)); silc_free(pk.data); /* Go through all requested attributes */ f.buffer = buffer; silc_dlist_start(attrs); while ((attr = silc_dlist_get(attrs)) != SILC_LIST_END) { /* Put all attributes of this type */ attribute = silc_attribute_get_attribute(attr); /* Ignore signature since we will compute it later */ if (attribute == SILC_ATTRIBUTE_USER_DIGITAL_SIGNATURE) continue; silc_hash_table_find_foreach(conn->internal->attrs, SILC_32_TO_PTR(attribute), silc_client_attributes_process_foreach, &f); } buffer = f.buffer; /* Finally compute the digital signature of all the data we provided. */ if (silc_pkcs_sign(conn->private_key, silc_buffer_data(buffer), silc_buffer_len(buffer), sign, sizeof(sign), &sign_len, TRUE, conn->internal->sha1hash)) { pk.type = NULL; pk.data = sign; pk.data_len = sign_len; buffer = silc_attribute_payload_encode(buffer, SILC_ATTRIBUTE_USER_DIGITAL_SIGNATURE, SILC_ATTRIBUTE_FLAG_VALID, &pk, sizeof(pk)); } return buffer; }
unsigned char *silc_mime_encode(SilcMime mime, SilcUInt32 *encoded_len) { SilcMime part; SilcHashTableList htl; SilcBufferStruct buf; SilcBuffer buffer; char *field, *value, tmp[1024], tmp2[4]; unsigned char *ret; int i; SILC_LOG_DEBUG(("Encoding MIME message")); if (!mime) return NULL; memset(&buf, 0, sizeof(buf)); /* Encode the headers. Order doesn't matter */ i = 0; silc_hash_table_list(mime->fields, &htl); while (silc_hash_table_get(&htl, (void *)&field, (void *)&value)) { memset(tmp, 0, sizeof(tmp)); SILC_LOG_DEBUG(("Header %s: %s", field, value)); silc_snprintf(tmp, sizeof(tmp) - 1, "%s: %s\r\n", field, value); silc_buffer_strformat(&buf, tmp, SILC_STRFMT_END); i++; } silc_hash_table_list_reset(&htl); if (i) silc_buffer_strformat(&buf, "\r\n", SILC_STRFMT_END); /* Assemble the whole buffer */ buffer = silc_buffer_alloc_size(mime->data_len + silc_buffer_len(&buf)); if (!buffer) return NULL; /* Add headers */ if (silc_buffer_len(&buf)) { silc_buffer_put(buffer, buf.head, silc_buffer_len(&buf)); silc_buffer_pull(buffer, silc_buffer_len(&buf)); silc_buffer_purge(&buf); } /* Add data */ if (mime->data) { SILC_LOG_DEBUG(("Data len %d", mime->data_len)); silc_buffer_put(buffer, mime->data, mime->data_len); } /* Add multiparts */ if (mime->multiparts) { SILC_LOG_DEBUG(("Encoding multiparts")); silc_dlist_start(mime->multiparts); i = 0; while ((part = silc_dlist_get(mime->multiparts)) != SILC_LIST_END) { unsigned char *pd; SilcUInt32 pd_len; /* Recursive encoding */ pd = silc_mime_encode(part, &pd_len); if (!pd) return NULL; memset(tmp, 0, sizeof(tmp)); memset(tmp2, 0, sizeof(tmp2)); /* If fields are not present, add extra CRLF */ if (!silc_hash_table_count(part->fields)) silc_snprintf(tmp2, sizeof(tmp2) - 1, "\r\n"); silc_snprintf(tmp, sizeof(tmp) - 1, "%s--%s\r\n%s", i != 0 ? "\r\n" : "", mime->boundary, tmp2); i = 1; buffer = silc_buffer_realloc(buffer, silc_buffer_truelen(buffer) + pd_len + strlen(tmp)); if (!buffer) return NULL; silc_buffer_put_tail(buffer, tmp, strlen(tmp)); silc_buffer_pull_tail(buffer, strlen(tmp)); silc_buffer_put_tail(buffer, pd, pd_len); silc_buffer_pull_tail(buffer, pd_len); silc_free(pd); } memset(tmp, 0, sizeof(tmp)); silc_snprintf(tmp, sizeof(tmp) - 1, "\r\n--%s--\r\n", mime->boundary); buffer = silc_buffer_realloc(buffer, silc_buffer_truelen(buffer) + strlen(tmp)); if (!buffer) return NULL; silc_buffer_put_tail(buffer, tmp, strlen(tmp)); silc_buffer_pull_tail(buffer, strlen(tmp)); } ret = silc_buffer_steal(buffer, encoded_len); silc_buffer_free(buffer); return ret; }
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); }