X-Git-Url: http://git.silcnet.org/gitweb/?a=blobdiff_plain;f=apps%2Firssi%2Fsrc%2Fsilc%2Fcore%2Fsilc-servers.c;h=e16fb02cdc056fef1116a982f49259c3bf0d7483;hb=52e57c880aba9c5e89f59d962eb9af75670b76e0;hp=fdcbd431f52320990d0057aec74901ece9f0baea;hpb=275e2f50c1cbe4a0eec582cf490ef485049541af;p=silc.git diff --git a/apps/irssi/src/silc/core/silc-servers.c b/apps/irssi/src/silc/core/silc-servers.c index fdcbd431..e16fb02c 100644 --- a/apps/irssi/src/silc/core/silc-servers.c +++ b/apps/irssi/src/silc/core/silc-servers.c @@ -1,14 +1,14 @@ /* silc-server.c : irssi - Copyright (C) 2000 - 2001 Timo Sirainen - Pekka Riikonen + Copyright (C) 2000 - 2007 Timo Sirainen + Pekka Riikonen This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. - + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -33,14 +33,19 @@ #include "settings.h" #include "servers-setup.h" +#include "channels-setup.h" +#include "client_ops.h" #include "silc-servers.h" #include "silc-channels.h" #include "silc-queries.h" #include "silc-nicklist.h" +#include "silc-cmdqueue.h" #include "window-item-def.h" #include "fe-common/core/printtext.h" +#include "fe-common/core/fe-channels.h" +#include "fe-common/core/keyboard.h" #include "fe-common/silc/module-formats.h" #include "silc-commands.h" @@ -48,24 +53,27 @@ void silc_servers_reconnect_init(void); void silc_servers_reconnect_deinit(void); -static void silc_send_channel(SILC_SERVER_REC *server, - char *channel, char *msg) +int silc_send_channel(SILC_SERVER_REC *server, + char *channel, char *msg, + SilcMessageFlags flags) { SILC_CHANNEL_REC *rec; - + rec = silc_channel_find(server, channel); if (rec == NULL || rec->entry == NULL) { - cmd_return_error(CMDERR_NOT_JOINED); - return; + cmd_return_error_value(CMDERR_NOT_JOINED, FALSE); } - silc_client_send_channel_message(silc_client, server->conn, rec->entry, - NULL, 0, msg, strlen(msg), TRUE); + return silc_client_send_channel_message(silc_client, server->conn, + rec->entry, NULL, flags, sha1hash, + msg, strlen(msg)); } typedef struct { char *nick; char *msg; + int len; + SilcMessageFlags flags; SILC_SERVER_REC *server; } PRIVMSG_REC; @@ -74,96 +82,138 @@ typedef struct { static void silc_send_msg_clients(SilcClient client, SilcClientConnection conn, - SilcClientEntry *clients, - SilcUInt32 clients_count, + SilcStatus status, + SilcDList clients, void *context) { PRIVMSG_REC *rec = context; SILC_SERVER_REC *server = rec->server; SilcClientEntry target; - char *nickname = NULL; - if (!clients_count) { - printtext(NULL, NULL, MSGLEVEL_CLIENTERROR, + if (!clients) { + printtext(NULL, NULL, MSGLEVEL_CLIENTERROR, "%s: There is no such client", rec->nick); - } else { - if (clients_count > 1) { - silc_parse_userfqdn(rec->nick, &nickname, NULL); - - /* Find the correct one. The rec->nick might be a formatted nick - so this will find the correct one. */ - clients = silc_client_get_clients_local(silc_client, server->conn, - nickname, rec->nick, - &clients_count); - if (!clients) { - printtext(NULL, NULL, MSGLEVEL_CLIENTERROR, - "%s: There is no such client", rec->nick); - silc_free(nickname); - goto out; - } - silc_free(nickname); - } + goto out; + } - target = clients[0]; + /* Find the correct one. The rec->nick might be a formatted nick + so this will find the correct one. */ + target = silc_dlist_get(clients); + clients = silc_client_get_clients_local(silc_client, server->conn, + rec->nick, FALSE); + if (!clients) { + if (strchr(rec->nick, '@') && target->server) + printtext(NULL, NULL, MSGLEVEL_CLIENTERROR, + "%s: There is no such client (did you mean %s@%s?)", rec->nick, + target->nickname, target->server); + else + printtext(NULL, NULL, MSGLEVEL_CLIENTERROR, + "%s: There is no such client (did you mean %s?)", rec->nick, + target->nickname); + goto out; + } - /* Still check for exact math for nickname, this compares the - real (formatted) nickname and the nick (maybe formatted) that - use gave. This is to assure that `nick' does not match - `nick@host'. */ - if (strcasecmp(rec->nick, clients[0]->nickname)) { - printtext(NULL, NULL, MSGLEVEL_CLIENTERROR, - "%s: There is no such client", rec->nick); - goto out; - } + /* Send the private message */ + silc_dlist_start(clients); + target = silc_dlist_get(clients); + silc_client_send_private_message(client, conn, target, rec->flags, sha1hash, + rec->msg, rec->len); - /* Send the private message */ - silc_client_send_private_message(client, conn, target, 0, - rec->msg, strlen(rec->msg), - TRUE); - } - out: + silc_client_list_free(silc_client, server->conn, clients); g_free(rec->nick); g_free(rec->msg); g_free(rec); } -static void silc_send_msg(SILC_SERVER_REC *server, char *nick, char *msg) +int silc_send_msg(SILC_SERVER_REC *server, char *nick, char *msg, + int msg_len, SilcMessageFlags flags) { PRIVMSG_REC *rec; - SilcClientEntry *clients; - SilcUInt32 clients_count; - char *nickname = NULL; - - if (!silc_parse_userfqdn(nick, &nickname, NULL)) { - printformat_module("fe-common/silc", server, NULL, - MSGLEVEL_CRAP, SILCTXT_BAD_NICK, nick); - return; - } + SilcDList clients; + SilcClientEntry target; + int ret; /* Find client entry */ - clients = silc_client_get_clients_local(silc_client, server->conn, - nickname, nick, &clients_count); + clients = silc_client_get_clients_local(silc_client, server->conn, nick, + FALSE); if (!clients) { + char *nickname = NULL; + rec = g_new0(PRIVMSG_REC, 1); rec->nick = g_strdup(nick); rec->msg = g_strdup(msg); rec->server = server; + rec->flags = flags; + rec->len = msg_len; + + silc_client_nickname_parse(silc_client, server->conn, nick, &nickname); + if (!nickname) + nickname = strdup(nick); /* Could not find client with that nick, resolve it from server. */ - silc_client_get_clients(silc_client, server->conn, - nickname, NULL, silc_send_msg_clients, rec); + silc_client_get_clients_whois(silc_client, server->conn, nickname, + NULL, NULL, silc_send_msg_clients, rec); silc_free(nickname); - return; + return TRUE; } /* Send the private message directly */ - silc_free(nickname); - silc_client_send_private_message(silc_client, server->conn, - clients[0], 0, msg, strlen(msg), TRUE); + target = silc_dlist_get(clients); + ret = silc_client_send_private_message(silc_client, server->conn, + target, flags, sha1hash, + msg, msg_len); + + silc_client_list_free(silc_client, server->conn, clients); + + return ret; } -static int isnickflag_func(char flag) +void silc_send_mime(SILC_SERVER_REC *server, int channel, const char *to, + const char *data, int sign) +{ + char *unescaped_data; + SilcUInt32 unescaped_data_len; + int target_type; + + if (!(IS_SILC_SERVER(server)) || (data == NULL) || (to == NULL)) + return; + + if (channel) { + target_type = SEND_TARGET_CHANNEL; + } else { + target_type = server_ischannel(SERVER(server), to) ? + SEND_TARGET_CHANNEL : SEND_TARGET_NICK; + } + + unescaped_data = silc_unescape_data(data, &unescaped_data_len); + + if (target_type == SEND_TARGET_CHANNEL) { + SILC_CHANNEL_REC *rec; + + rec = silc_channel_find(server, to); + if (rec == NULL || rec->entry == NULL) { + cmd_return_error(CMDERR_NOT_JOINED); + } + + silc_client_send_channel_message(silc_client, server->conn, rec->entry, + NULL, SILC_MESSAGE_FLAG_DATA | + (sign ? SILC_MESSAGE_FLAG_SIGNED : 0), + sha1hash, unescaped_data, + unescaped_data_len); + } else { + silc_send_msg(server, (char *)to, unescaped_data, unescaped_data_len, + SILC_MESSAGE_FLAG_DATA | + (sign ? SILC_MESSAGE_FLAG_SIGNED : 0)); + + } + + signal_stop(); + + silc_free(unescaped_data); +} + +static int isnickflag_func(SERVER_REC *server, char flag) { return flag == '@' || flag == '+'; } @@ -173,7 +223,7 @@ static int ischannel_func(SERVER_REC *server, const char *data) return FALSE; } -const char *get_nick_flags(void) +const char *get_nick_flags(SERVER_REC *server) { return "@\0\0"; } @@ -181,48 +231,214 @@ const char *get_nick_flags(void) static void send_message(SILC_SERVER_REC *server, char *target, char *msg, int target_type) { + char *message = NULL, *t = NULL; + int len; + SilcBool sign; + g_return_if_fail(server != NULL); g_return_if_fail(target != NULL); g_return_if_fail(msg != NULL); - if (target_type == SEND_TARGET_CHANNEL) - silc_send_channel(server, target, msg); - else - silc_send_msg(server, target, msg); + if (!silc_term_utf8()) { + len = silc_utf8_encoded_len(msg, strlen(msg), SILC_STRING_LOCALE); + message = silc_calloc(len + 1, sizeof(*message)); + g_return_if_fail(message != NULL); + silc_utf8_encode(msg, strlen(msg), SILC_STRING_LOCALE, message, len); + } + + if (target_type == SEND_TARGET_CHANNEL) { + sign = settings_get_bool("sign_channel_messages"); + silc_send_channel(server, target, message ? message : msg, + SILC_MESSAGE_FLAG_UTF8 | + (sign ? SILC_MESSAGE_FLAG_SIGNED : 0)); + } else { + sign = settings_get_bool("sign_private_messages"); + if (!silc_term_utf8()) { + len = silc_utf8_encoded_len(target, strlen(target), SILC_STRING_LOCALE); + t = silc_calloc(len + 1, sizeof(*t)); + g_return_if_fail(t != NULL); + silc_utf8_encode(target, strlen(target), SILC_STRING_LOCALE, t, len); + } + + silc_send_msg(server, t ? t : target, message ? message : msg, + message ? strlen(message) : strlen(msg), + SILC_MESSAGE_FLAG_UTF8 | + (sign ? SILC_MESSAGE_FLAG_SIGNED : 0)); + } + + silc_free(message); + silc_free(t); } -static void sig_connected(SILC_SERVER_REC *server) +/* Connection callback */ + +static void silc_connect_cb(SilcClient client, + SilcClientConnection conn, + SilcClientConnectionStatus status, + SilcStatus error, + const char *message, + void *context) { - SilcClientConnection conn; - SilcClientConnectionParams params; - char file[256]; - int fd; + SILC_SERVER_REC *server = context; + FtpSession ftp; + char *file; - if (!IS_SILC_SERVER(server)) - return; + SILC_LOG_DEBUG(("Connection callback %p, status %d, error %d, message %s", + conn, status, error, message ? message : "N/A")); - /* Try to read detached session data and use it if found. */ - memset(¶ms, 0, sizeof(params)); - memset(file, 0, sizeof(file)); - snprintf(file, sizeof(file) - 1, "%s/session.%s.%d", get_irssi_dir(), - server->connrec->address, server->connrec->port); - params.detach_data = silc_file_readfile(file, ¶ms.detach_data_len); + server->op = NULL; - /* Add connection to the client library */ - conn = silc_client_add_connection(silc_client, ¶ms, - server->connrec->address, - server->connrec->port, - server); - server->conn = conn; + switch (status) { + case SILC_CLIENT_CONN_SUCCESS: + if (server->disconnected) { + silc_client_close_connection(client, conn); + return; + } - silc_free(params.detach_data); - unlink(file); + /* We have successfully connected to server */ + + /* Enable queueing until we have our requested nick */ + if (((opt_nickname && + !silc_utf8_strcasecmp(opt_nickname, + conn->local_entry->nickname)) || + (settings_get_str("nick") && + !silc_utf8_strcasecmp(settings_get_str("nick"), + conn->local_entry->nickname))) && + silc_utf8_strcasecmp(conn->local_entry->nickname, + conn->local_entry->username)) + silc_queue_enable(conn); + + /* Put default attributes */ + silc_query_attributes_default(silc_client, conn); + + server->connected = TRUE; + server->conn = conn; + server->conn->context = server; + signal_emit("event connected", 1, server); + break; - fd = g_io_channel_unix_get_fd(net_sendbuffer_handle(server->handle)); + case SILC_CLIENT_CONN_SUCCESS_RESUME: + if (server->disconnected) { + silc_client_close_connection(client, conn); + return; + } + + /* We have successfully resumed old detached session */ + server->connected = TRUE; + server->conn = conn; + server->conn->context = server; + signal_emit("event connected", 1, server); + + /* Put default attributes */ + silc_query_attributes_default(silc_client, conn); + + /* Remove the detach data now */ + file = silc_get_session_filename(server); + unlink(file); + silc_free(file); + break; + + case SILC_CLIENT_CONN_DISCONNECTED: + /* Server disconnected */ + if (server->conn && server->conn->local_entry) { + nicklist_rename_unique(SERVER(server), + server->conn->local_entry, server->nick, + server->conn->local_entry, + silc_client->username); + silc_change_nick(server, silc_client->username); + } - /* Start key exchange with the server */ - silc_client_start_key_exchange(silc_client, conn, fd); + if (message) + silc_say(client, conn, SILC_CLIENT_MESSAGE_AUDIT, + "Server closed connection: %s (%d) %s", + silc_get_status_message(error), error, + message ? message : ""); + /* Close FTP sessions */ + silc_dlist_start(server->ftp_sessions); + while ((ftp = silc_dlist_get(server->ftp_sessions))) + silc_client_file_close(client, conn, ftp->session_id); + silc_dlist_uninit(server->ftp_sessions); + + if (server->conn) + server->conn->context = NULL; + server->conn = NULL; + server->connection_lost = TRUE; + if (!server->disconnected) + server_disconnect(SERVER(server)); + server_unref(SERVER(server)); + break; + + default: + file = silc_get_session_filename(server); + if (silc_file_size(file) > 0) + printformat_module("fe-common/silc", server, NULL, + MSGLEVEL_CRAP, SILCTXT_REATTACH_FAILED, file); + silc_free(file); + + server->connection_lost = TRUE; + server->conn = NULL; + if (server->conn) + server->conn->context = NULL; + if (!server->disconnected) + server_disconnect(SERVER(server)); + server_unref(SERVER(server)); + break; + } +} + +/* Called after TCP stream has been created */ + +static void sig_connected_stream_created(SilcSocketStreamStatus status, + SilcStream stream, void *context) +{ + SILC_SERVER_REC *server = context; + SilcClientConnectionParams params; + char *file; + + server->tcp_op = NULL; + if (!stream) { + server->connection_lost = TRUE; + server_disconnect(SERVER(server)); + return; + } + + if (server->disconnected) { + silc_stream_destroy(stream); + return; + } + + /* Set connection parameters */ + memset(¶ms, 0, sizeof(params)); + params.nickname = (opt_nickname ? (char *)opt_nickname : + (char *)settings_get_str("nick")); + params.timeout_secs = settings_get_int("key_exchange_timeout_secs"); + params.rekey_secs = settings_get_int("key_exchange_rekey_secs"); + params.pfs = settings_get_bool("key_exchange_rekey_pfs"); + + /* Try to read detached session data and use it if found. */ + file = silc_get_session_filename(server); + params.detach_data = silc_file_readfile(file, ¶ms.detach_data_len); + if (params.detach_data) + params.detach_data[params.detach_data_len] = 0; + if (params.detach_data) + printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP, + SILCTXT_REATTACH, server->tag); + silc_free(file); + + /* Start key exchange */ + server->op = silc_client_key_exchange(silc_client, ¶ms, + irssi_pubkey, irssi_privkey, + stream, SILC_CONN_SERVER, + silc_connect_cb, server); + if (!server->op) { + server->connection_lost = TRUE; + server_disconnect(SERVER(server)); + silc_stream_destroy(stream); + return; + } + + server_ref(SERVER(server)); server->ftp_sessions = silc_dlist_init(); server->isnickflag = isnickflag_func; server->ischannel = ischannel_func; @@ -230,54 +446,81 @@ static void sig_connected(SILC_SERVER_REC *server) server->send_message = (void *) send_message; } -static void sig_disconnected(SILC_SERVER_REC *server) +static void sig_connected(SILC_SERVER_REC *server) { + int fd; + if (!IS_SILC_SERVER(server)) return; - silc_dlist_uninit(server->ftp_sessions); + /* Wrap the socket to TCP stream */ + fd = g_io_channel_unix_get_fd(net_sendbuffer_handle(server->handle)); + server->tcp_op = + silc_socket_tcp_stream_create(fd, TRUE, FALSE, + silc_client->schedule, + sig_connected_stream_created, server); +} - if (server->conn && server->conn->sock != NULL) { +static void sig_disconnected(SILC_SERVER_REC *server) +{ + if (!IS_SILC_SERVER(server)) + return; + + if (server->conn) { + /* Close connection */ silc_client_close_connection(silc_client, server->conn); - - /* SILC closes the handle */ + } else if (server->op) { + /* Abort on going connecting (key exchange) */ + silc_async_abort(server->op, NULL, NULL); + server->op = NULL; + } else if (server->tcp_op) { + /* Abort on going TCP stream creation */ + silc_async_abort(server->tcp_op, NULL, NULL); + server->tcp_op = NULL; + } + + /* SILC closes the handle */ + if (server->handle) { g_io_channel_unref(net_sendbuffer_handle(server->handle)); net_sendbuffer_destroy(server->handle, FALSE); server->handle = NULL; } } -SILC_SERVER_REC *silc_server_connect(SILC_SERVER_CONNECT_REC *conn) +SERVER_REC *silc_server_init_connect(SERVER_CONNECT_REC *conn) { SILC_SERVER_REC *server; g_return_val_if_fail(IS_SILC_SERVER_CONNECT(conn), NULL); - if (conn->address == NULL || *conn->address == '\0') + if (conn->address == NULL || *conn->address == '\0') return NULL; if (conn->nick == NULL || *conn->nick == '\0') { - printtext(NULL, NULL, MSGLEVEL_CLIENTERROR, - "Cannot connect: nickname is not set"); + silc_say_error("Cannot connect: nickname is not set"); return NULL; } server = g_new0(SILC_SERVER_REC, 1); server->chat_type = SILC_PROTOCOL; - server->connrec = conn; - if (server->connrec->port <= 0) + server->connrec = (SILC_SERVER_CONNECT_REC *)conn; + server_connect_ref(conn); + + if (server->connrec->port <= 0) server->connrec->port = 706; - server_connect_ref(SERVER_CONNECT(conn)); + server_connect_init((SERVER_REC *)server); + return (SERVER_REC *)server; +} - if (!server_start_connect((SERVER_REC *) server)) { - server_connect_unref(SERVER_CONNECT(conn)); +void silc_server_connect(SERVER_REC *server) +{ + if (!server_start_connect(server)) { + server_connect_unref(server->connrec); g_free(server); - return NULL; + return; } - - return server; } -/* Return a string of all channels in server in server->channels_join() +/* Return a string of all channels in server in server->channels_join() format */ char *silc_server_get_channels(SILC_SERVER_REC *server) @@ -291,8 +534,14 @@ char *silc_server_get_channels(SILC_SERVER_REC *server) chans = g_string_new(NULL); for (tmp = server->channels; tmp != NULL; tmp = tmp->next) { CHANNEL_REC *channel = tmp->data; - - g_string_sprintfa(chans, "%s,", channel->name); + CHANNEL_SETUP_REC *schannel; + + if ((schannel = channel_setup_find(channel->name, server->connrec->chatnet)) && + schannel->password) + g_string_sprintfa(chans, "%s %s,", channel->name, + schannel->password); + else + g_string_sprintfa(chans, "%s,", channel->name); } if (chans->len > 0) @@ -300,7 +549,7 @@ char *silc_server_get_channels(SILC_SERVER_REC *server) ret = chans->str; g_string_free(chans, FALSE); - + return ret; } @@ -309,79 +558,54 @@ char *silc_server_get_channels(SILC_SERVER_REC *server) /* SYNTAX: BAN [+|-[[@[![@hostname>]]]]] */ /* SYNTAX: CMODE +|- [{ }] */ -/* SYNTAX: CUMODE +|- [@] [-pubkey|] */ +/* SYNTAX: CUMODE +|- [@] */ /* SYNTAX: GETKEY */ /* SYNTAX: INVITE [[@hostname>] */ /* SYNTAX: INVITE [+|-[[@[![@hostname>]]]]] */ /* SYNTAX: KEY MSG set|unset|list|agreement|negotiate [] */ /* SYNTAX: KEY CHANNEL set|unset|list|change [] */ /* SYNTAX: KICK [@] [] */ -/* SYNTAX: KILL [@] [] */ +/* SYNTAX: KILL [@] [] [-pubkey] */ /* SYNTAX: OPER [-pubkey] */ /* SYNTAX: SILCOPER [-pubkey] */ /* SYNTAX: TOPIC [] */ /* SYNTAX: UMODE +|- */ -/* SYNTAX: WHOIS [@] [] */ +/* SYNTAX: WHOIS [[@]] [-details] [-pubkey ] [] */ /* SYNTAX: WHOWAS [@] [] */ /* SYNTAX: CLOSE [] */ -/* SYNTAX: SHUTDOWN */ /* SYNTAX: MOTD [] */ /* SYNTAX: LIST [] */ /* SYNTAX: ME */ -/* SYNTAX: ACTION */ +/* SYNTAX: ACTION [-sign] [-channel] */ /* SYNTAX: AWAY [] */ /* SYNTAX: INFO [] */ /* SYNTAX: NICK */ -/* SYNTAX: NOTICE */ +/* SYNTAX: NOTICE [-sign] [-channel] */ /* SYNTAX: PART [] */ /* SYNTAX: PING */ -/* SYNTAX: SCONNECT [] */ /* SYNTAX: USERS */ -/* SYNTAX: FILE SEND [ []] */ -/* SYNTAX: FILE RECEIVE [] */ +/* SYNTAX: FILE SEND [ []] [-no-listener]*/ +/* SYNTAX: FILE ACCEPT [] */ /* SYNTAX: FILE CLOSE [] */ /* SYNTAX: FILE */ -/* SYNTAX: JOIN [] [-cipher ] [-hmac ] [-founder <-pubkey|passwd>] */ +/* SYNTAX: JOIN [] [-cipher ] [-hmac ] [-founder] [-auth [ []]]*/ /* SYNTAX: DETACH */ +/* SYNTAX: WATCH [<-add | -del> ] [-pubkey +|-] */ +/* SYNTAX: STATS */ +/* SYNTAX: ATTR [<-del>