X-Git-Url: http://git.silcnet.org/gitweb/?p=silc.git;a=blobdiff_plain;f=apps%2Firssi%2Fsrc%2Fsilc%2Fcore%2Fclient_ops.c;h=b55b0d31a7c859a9aa96d21fb73d0cdce191311c;hp=0b95e988bd309111cf570e1716e00fbc279113c9;hb=382d15d447b7a95390decfa783836ae4fe255b3d;hpb=f3c997c2d8327717f13a868ce4a1a9548ef08ce4 diff --git a/apps/irssi/src/silc/core/client_ops.c b/apps/irssi/src/silc/core/client_ops.c index 0b95e988..b55b0d31 100644 --- a/apps/irssi/src/silc/core/client_ops.c +++ b/apps/irssi/src/silc/core/client_ops.c @@ -33,19 +33,99 @@ #include "signals.h" #include "levels.h" #include "settings.h" +#include "ignore.h" #include "fe-common/core/printtext.h" #include "fe-common/core/fe-channels.h" #include "fe-common/core/keyboard.h" +#include "fe-common/core/window-items.h" #include "fe-common/silc/module-formats.h" +#include "core.h" + static void silc_verify_public_key_internal(SilcClient client, SilcClientConnection conn, - SilcSocketType conn_type, unsigned char *pk, - uint32 pk_len, SilcSKEPKType pk_type, + const char *name, SilcSocketType conn_type, + unsigned char *pk, SilcUInt32 pk_len, + SilcSKEPKType pk_type, SilcVerifyPublicKey completion, void *context); +static void silc_get_umode_string(SilcUInt32 mode, char *buf, + SilcUInt32 buf_size) +{ + if ((mode & SILC_UMODE_SERVER_OPERATOR) || + (mode & SILC_UMODE_ROUTER_OPERATOR)) { + strcat(buf, (mode & SILC_UMODE_SERVER_OPERATOR) ? + "[server operator]" : + (mode & SILC_UMODE_ROUTER_OPERATOR) ? + "[SILC operator]" : "[unknown mode]"); + } + if (mode & SILC_UMODE_GONE) + strcat(buf, " [away]"); + if (mode & SILC_UMODE_INDISPOSED) + strcat(buf, " [indisposed]"); + if (mode & SILC_UMODE_BUSY) + strcat(buf, " [busy]"); + if (mode & SILC_UMODE_PAGE) + strcat(buf, " [page to reach]"); + if (mode & SILC_UMODE_HYPER) + strcat(buf, " [hyper active]"); + if (mode & SILC_UMODE_ROBOT) + strcat(buf, " [robot]"); + if (mode & SILC_UMODE_ANONYMOUS) + strcat(buf, " [anonymous]"); + if (mode & SILC_UMODE_BLOCK_PRIVMSG) + strcat(buf, " [blocks private messages]"); + if (mode & SILC_UMODE_DETACHED) + strcat(buf, " [detached]"); + if (mode & SILC_UMODE_REJECT_WATCHING) + strcat(buf, " [rejects watching]"); + if (mode & SILC_UMODE_BLOCK_INVITE) + strcat(buf, " [blocks invites]"); +} + +/* print "nick appears as" message to every channel of a server */ +static void +silc_print_nick_change_channel(SILC_SERVER_REC *server, const char *channel, + const char *newnick, const char *oldnick, + const char *address) +{ + if (ignore_check(SERVER(server), oldnick, address, + channel, newnick, MSGLEVEL_NICKS)) + return; + + printformat_module("fe-common/silc", server, channel, MSGLEVEL_NICKS, + SILCTXT_CHANNEL_APPEARS, + oldnick, newnick, channel, address); +} + +static void +silc_print_nick_change(SILC_SERVER_REC *server, const char *newnick, + const char *oldnick, const char *address) +{ + GSList *tmp, *windows; + + /* Print to each channel/query where the nick is. + Don't print more than once to the same window. */ + windows = NULL; + + for (tmp = server->channels; tmp != NULL; tmp = tmp->next) { + CHANNEL_REC *channel = tmp->data; + WINDOW_REC *window = window_item_window((WI_ITEM_REC *) channel); + + if (nicklist_find(channel, newnick) == NULL || + g_slist_find(windows, window) != NULL) + continue; + + windows = g_slist_append(windows, window); + silc_print_nick_change_channel(server, channel->visible_name, + newnick, oldnick, address); + } + + g_slist_free(windows); +} + void silc_say(SilcClient client, SilcClientConnection conn, - char *msg, ...) + SilcClientMessageType type, char *msg, ...) { SILC_SERVER_REC *server; va_list va; @@ -73,52 +153,306 @@ void silc_say_error(char *msg, ...) va_end(va); } +/* try to verify a message using locally stored public key data */ +int verify_message_signature(SilcClientEntry sender, + SilcMessageSignedPayload sig, + SilcMessagePayload message) +{ + SilcPublicKey pk; + char file[256], filename[256]; + char *fingerprint, *fingerprint2; + unsigned char *pk_data; + SilcUInt32 pk_datalen; + struct stat st; + int ret = SILC_MSG_SIGNED_VERIFIED, i; + + if (sig == NULL) + return SILC_MSG_SIGNED_UNKNOWN; + + /* get public key from the signature payload and compare it with the + one stored in the client entry */ + pk = silc_message_signed_get_public_key(sig, &pk_data, &pk_datalen); + + if (pk != NULL) { + fingerprint = silc_hash_fingerprint(NULL, pk_data, pk_datalen); + + if (sender->fingerprint) { + fingerprint2 = silc_fingerprint(sender->fingerprint, + sender->fingerprint_len); + if (strcmp(fingerprint, fingerprint2)) { + /* since the public key differs from the senders public key, the + verification _failed_ */ + silc_pkcs_public_key_free(pk); + silc_free(fingerprint); + ret = SILC_MSG_SIGNED_UNKNOWN; + } + silc_free(fingerprint2); + } + } else if (sender->fingerprint) + fingerprint = silc_fingerprint(sender->fingerprint, + sender->fingerprint_len); + else + /* no idea, who or what signed that message ... */ + return SILC_MSG_SIGNED_UNKNOWN; + + /* search our local client key cache */ + for (i = 0; i < strlen(fingerprint); i++) + if (fingerprint[i] == ' ') + fingerprint[i] = '_'; + + snprintf(file, sizeof(file) - 1, "clientkey_%s.pub", fingerprint); + snprintf(filename, sizeof(filename) - 1, "%s/clientkeys/%s", + get_irssi_dir(), file); + silc_free(fingerprint); + + if (stat(filename, &st) < 0) + /* we don't have the public key cached ... use the one from the sig */ + ret = SILC_MSG_SIGNED_UNKNOWN; + else { + SilcPublicKey cached_pk=NULL; + + /* try to load the file */ + if (!silc_pkcs_load_public_key(filename, &cached_pk, SILC_PKCS_FILE_PEM) && + !silc_pkcs_load_public_key(filename, &cached_pk, + SILC_PKCS_FILE_BIN)) { + printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, + SILCTXT_PUBKEY_COULD_NOT_LOAD, "client"); + if (pk == NULL) + return SILC_MSG_SIGNED_UNKNOWN; + else + ret = SILC_MSG_SIGNED_UNKNOWN; + } + + if (cached_pk) { + if (pk) + silc_pkcs_public_key_free(pk); + pk = cached_pk; + } + } + + /* the public key is now in pk, our "level of trust" in ret */ + if ((pk) && silc_message_signed_verify(sig, message, pk, + silc_client->sha1hash)!= SILC_AUTH_OK) + ret = SILC_MSG_SIGNED_FAILED; + + if (pk) + silc_pkcs_public_key_free(pk); + + return ret; +} + /* Message for a channel. The `sender' is the nickname of the sender received in the packet. The `channel_name' is the name of the channel. */ void silc_channel_message(SilcClient client, SilcClientConnection conn, SilcClientEntry sender, SilcChannelEntry channel, - SilcMessageFlags flags, char *msg) + SilcMessagePayload payload, + SilcMessageFlags flags, const unsigned char *message, + SilcUInt32 message_len) { SILC_SERVER_REC *server; SILC_NICK_REC *nick; SILC_CHANNEL_REC *chanrec; + int verified = 0; + SILC_LOG_DEBUG(("Start")); + + if (!message) + return; + server = conn == NULL ? NULL : conn->context; chanrec = silc_channel_find_entry(server, channel); if (!chanrec) return; nick = silc_nicklist_find(chanrec, sender); + if (!nick) { + /* We didn't find client but it clearly exists, add it. */ + SilcChannelUser chu = silc_client_on_channel(channel, sender); + if (chu) + nick = silc_nicklist_insert(chanrec, chu, FALSE); + } + + /* If the messages is digitally signed, verify it, if possible. */ + if (flags & SILC_MESSAGE_FLAG_SIGNED) { + if (!settings_get_bool("ignore_message_signatures")) { + SilcMessageSignedPayload sig = silc_message_get_signature(payload); + verified = verify_message_signature(sender, sig, payload); + } else { + flags &= ~SILC_MESSAGE_FLAG_SIGNED; + } + } + + if (flags & SILC_MESSAGE_FLAG_DATA) { + /* MIME object received, try to display it as well as we can */ + char type[128], enc[128]; + unsigned char *data; + SilcUInt32 data_len; + + memset(type, 0, sizeof(type)); + memset(enc, 0, sizeof(enc)); + if (!silc_mime_parse(message, message_len, NULL, 0, type, sizeof(type) - 1, + enc, sizeof(enc) - 1, &data, &data_len)) + return; + + /* Then figure out what we can display */ + if (strstr(type, "text/") && !strstr(type, "text/t140") && + !strstr(type, "text/vnd")) { + /* It is something textual, display it */ + message = (const unsigned char *)data; + } else { + printformat_module("fe-common/silc", server, channel->channel_name, + MSGLEVEL_CRAP, SILCTXT_MESSAGE_DATA, + nick == NULL ? "[]" : nick->nick, type); + message = NULL; + } + } + + if (!message) + return; + /* FIXME: replace those printformat calls with signals and add signature + information to them (if present) */ if (flags & SILC_MESSAGE_FLAG_ACTION) printformat_module("fe-common/silc", server, channel->channel_name, MSGLEVEL_ACTIONS, SILCTXT_CHANNEL_ACTION, - nick == NULL ? "[]" : nick->nick, msg); + nick == NULL ? "[]" : nick->nick, message); else if (flags & SILC_MESSAGE_FLAG_NOTICE) printformat_module("fe-common/silc", server, channel->channel_name, MSGLEVEL_NOTICES, SILCTXT_CHANNEL_NOTICE, - nick == NULL ? "[]" : nick->nick, msg); - else - signal_emit("message public", 6, server, msg, - nick == NULL ? "[]" : nick->nick, - nick == NULL ? "" : nick->host == NULL ? "" : nick->host, - chanrec->name, nick); + nick == NULL ? "[]" : nick->nick, message); + else { + if (flags & SILC_MESSAGE_FLAG_UTF8 && !silc_term_utf8()) { + char tmp[256], *cp, *dm = NULL; + + memset(tmp, 0, sizeof(tmp)); + cp = tmp; + if (message_len > sizeof(tmp) - 1) { + dm = silc_calloc(message_len + 1, sizeof(*dm)); + cp = dm; + } + + silc_utf8_decode(message, message_len, SILC_STRING_LANGUAGE, + cp, message_len); + if (flags & SILC_MESSAGE_FLAG_SIGNED) + signal_emit("message signed_public", 6, server, cp, + nick == NULL ? "[]" : nick->nick, + nick == NULL ? "" : nick->host == NULL ? "" : nick->host, + chanrec->name, verified); + else + signal_emit("message public", 6, server, cp, + nick == NULL ? "[]" : nick->nick, + nick == NULL ? "" : nick->host == NULL ? "" : nick->host, + chanrec->name, nick); + silc_free(dm); + return; + } + + if (flags & SILC_MESSAGE_FLAG_SIGNED) + signal_emit("message signed_public", 6, server, message, + nick == NULL ? "[]" : nick->nick, + nick == NULL ? "" : nick->host == NULL ? "" : nick->host, + chanrec->name, verified); + else + signal_emit("message public", 6, server, message, + nick == NULL ? "[]" : nick->nick, + nick == NULL ? "" : nick->host == NULL ? "" : nick->host, + chanrec->name, nick); + } } /* Private message to the client. The `sender' is the nickname of the sender received in the packet. */ void silc_private_message(SilcClient client, SilcClientConnection conn, - SilcClientEntry sender, SilcMessageFlags flags, - char *msg) + SilcClientEntry sender, SilcMessagePayload payload, + SilcMessageFlags flags, + const unsigned char *message, + SilcUInt32 message_len) { SILC_SERVER_REC *server; + char userhost[256]; + int verified = 0; + SILC_LOG_DEBUG(("Start")); + server = conn == NULL ? NULL : conn->context; - signal_emit("message private", 4, server, msg, - sender->nickname ? sender->nickname : "[]", - sender->username ? sender->username : NULL); + memset(userhost, 0, sizeof(userhost)); + if (sender->username) + snprintf(userhost, sizeof(userhost) - 1, "%s@%s", + sender->username, sender->hostname); + + /* If the messages is digitally signed, verify it, if possible. */ + if (flags & SILC_MESSAGE_FLAG_SIGNED) { + if (!settings_get_bool("ignore_message_signatures")) { + SilcMessageSignedPayload sig = silc_message_get_signature(payload); + verified = verify_message_signature(sender, sig, payload); + } else { + flags &= ~SILC_MESSAGE_FLAG_SIGNED; + } + } + + if (flags & SILC_MESSAGE_FLAG_DATA) { + /* MIME object received, try to display it as well as we can */ + char type[128], enc[128]; + unsigned char *data; + SilcUInt32 data_len; + + memset(type, 0, sizeof(type)); + memset(enc, 0, sizeof(enc)); + if (!silc_mime_parse(message, message_len, NULL, 0, type, sizeof(type) - 1, + enc, sizeof(enc) - 1, &data, &data_len)) + return; + + /* Then figure out what we can display */ + if (strstr(type, "text/") && !strstr(type, "text/t140") && + !strstr(type, "text/vnd")) { + /* It is something textual, display it */ + message = (const unsigned char *)data; + } else { + printformat_module("fe-common/silc", server, NULL, + MSGLEVEL_CRAP, SILCTXT_MESSAGE_DATA, + sender->nickname ? sender->nickname : "[]", + type); + message = NULL; + } + } + + if (!message) + return; + + if (flags & SILC_MESSAGE_FLAG_UTF8 && !silc_term_utf8()) { + char tmp[256], *cp, *dm = NULL; + + memset(tmp, 0, sizeof(tmp)); + cp = tmp; + if (message_len > sizeof(tmp) - 1) { + dm = silc_calloc(message_len + 1, sizeof(*dm)); + cp = dm; + } + + silc_utf8_decode(message, message_len, SILC_STRING_LANGUAGE, + cp, message_len); + if (flags & SILC_MESSAGE_FLAG_SIGNED) + signal_emit("message signed_private", 5, server, cp, + sender->nickname ? sender->nickname : "[]", + sender->username ? userhost : NULL, verified); + else + signal_emit("message private", 4, server, cp, + sender->nickname ? sender->nickname : "[]", + sender->username ? userhost : NULL); + silc_free(dm); + return; + } + + if (flags & SILC_MESSAGE_FLAG_SIGNED) + signal_emit("message signed_private", 5, server, message, + sender->nickname ? sender->nickname : "[]", + sender->username ? userhost : NULL, verified); + else + signal_emit("message private", 4, server, message, + sender->nickname ? sender->nickname : "[]", + sender->username ? userhost : NULL); } /* Notify message to the client. The notify arguments are sent in the @@ -129,52 +463,573 @@ void silc_private_message(SilcClient client, SilcClientConnection conn, for channel the channel entry is sent to application (even if server does not send it). */ -typedef struct { - int type; - const char *name; -} NOTIFY_REC; - -#define MAX_NOTIFY (sizeof(notifies)/sizeof(notifies[0])) -static NOTIFY_REC notifies[] = { - { SILC_NOTIFY_TYPE_NONE, NULL }, - { SILC_NOTIFY_TYPE_INVITE, "invite" }, - { SILC_NOTIFY_TYPE_JOIN, "join" }, - { SILC_NOTIFY_TYPE_LEAVE, "leave" }, - { SILC_NOTIFY_TYPE_SIGNOFF, "signoff" }, - { SILC_NOTIFY_TYPE_TOPIC_SET, "topic" }, - { SILC_NOTIFY_TYPE_NICK_CHANGE, "nick" }, - { SILC_NOTIFY_TYPE_CMODE_CHANGE, "cmode" }, - { SILC_NOTIFY_TYPE_CUMODE_CHANGE, "cumode" }, - { SILC_NOTIFY_TYPE_MOTD, "motd" }, - { SILC_NOTIFY_TYPE_CHANNEL_CHANGE, "channel_change" }, - { SILC_NOTIFY_TYPE_SERVER_SIGNOFF, "server_signoff" }, - { SILC_NOTIFY_TYPE_KICKED, "kick" }, - { SILC_NOTIFY_TYPE_KILLED, "kill" }, - { SILC_NOTIFY_TYPE_UMODE_CHANGE, "umode" }, - { SILC_NOTIFY_TYPE_BAN, "ban" }, -}; - void silc_notify(SilcClient client, SilcClientConnection conn, SilcNotifyType type, ...) { - SILC_SERVER_REC *server; va_list va; - - server = conn == NULL ? NULL : conn->context; + SILC_SERVER_REC *server; + SILC_CHANNEL_REC *chanrec; + SILC_NICK_REC *nickrec; + SilcClientEntry client_entry, client_entry2; + SilcChannelEntry channel, channel2; + SilcServerEntry server_entry; + SilcIdType idtype; + void *entry; + SilcUInt32 mode; + char buf[512]; + char *name, *tmp; + GSList *list1, *list_tmp; + + SILC_LOG_DEBUG(("Start")); + va_start(va, type); + + server = conn == NULL ? NULL : conn->context; - if (type == SILC_NOTIFY_TYPE_NONE) { + switch(type) { + case SILC_NOTIFY_TYPE_NONE: /* Some generic notice from server */ printtext(server, NULL, MSGLEVEL_CRAP, "%s", (char *)va_arg(va, char *)); - } else if (type < MAX_NOTIFY) { - /* Send signal about the notify event */ - char signal[50]; - g_snprintf(signal, sizeof(signal), "silc event %s", notifies[type].name); - signal_emit(signal, 2, server, va); - } else { + break; + + case SILC_NOTIFY_TYPE_INVITE: + /* + * Invited or modified invite list. + */ + + SILC_LOG_DEBUG(("Notify: INVITE")); + + channel = va_arg(va, SilcChannelEntry); + name = va_arg(va, char *); + client_entry = va_arg(va, SilcClientEntry); + + memset(buf, 0, sizeof(buf)); + snprintf(buf, sizeof(buf) - 1, "%s@%s", + client_entry->username, client_entry->hostname); + signal_emit("message invite", 4, server, channel ? channel->channel_name : + name, client_entry->nickname, buf); + break; + + case SILC_NOTIFY_TYPE_JOIN: + /* + * Joined channel. + */ + + SILC_LOG_DEBUG(("Notify: JOIN")); + + client_entry = va_arg(va, SilcClientEntry); + channel = va_arg(va, SilcChannelEntry); + + if (client_entry == server->conn->local_entry) { + /* You joined to channel */ + chanrec = silc_channel_find(server, channel->channel_name); + if (chanrec != NULL && !chanrec->joined) + chanrec->entry = channel; + } else { + chanrec = silc_channel_find_entry(server, channel); + if (chanrec != NULL) { + SilcChannelUser chu = silc_client_on_channel(channel, client_entry); + if (chu) + nickrec = silc_nicklist_insert(chanrec, chu, TRUE); + } + } + + memset(buf, 0, sizeof(buf)); + if (client_entry->username) + snprintf(buf, sizeof(buf) - 1, "%s@%s", + client_entry->username, client_entry->hostname); + signal_emit("message join", 4, server, channel->channel_name, + client_entry->nickname, + client_entry->username == NULL ? "" : buf); + break; + + case SILC_NOTIFY_TYPE_LEAVE: + /* + * Left a channel. + */ + + SILC_LOG_DEBUG(("Notify: LEAVE")); + + client_entry = va_arg(va, SilcClientEntry); + channel = va_arg(va, SilcChannelEntry); + + memset(buf, 0, sizeof(buf)); + if (client_entry->username) + snprintf(buf, sizeof(buf) - 1, "%s@%s", + client_entry->username, client_entry->hostname); + signal_emit("message part", 5, server, channel->channel_name, + client_entry->nickname, client_entry->username ? + buf : "", client_entry->nickname); + + chanrec = silc_channel_find_entry(server, channel); + if (chanrec != NULL) { + nickrec = silc_nicklist_find(chanrec, client_entry); + if (nickrec != NULL) + nicklist_remove(CHANNEL(chanrec), NICK(nickrec)); + } + break; + + case SILC_NOTIFY_TYPE_SIGNOFF: + /* + * Left the network. + */ + + SILC_LOG_DEBUG(("Notify: SIGNOFF")); + + client_entry = va_arg(va, SilcClientEntry); + tmp = va_arg(va, char *); + + silc_server_free_ftp(server, client_entry); + + /* Print only if we have the nickname. If this cliente has just quit + when we were only resolving it, it is possible we don't have the + nickname. */ + if (client_entry->nickname) { + memset(buf, 0, sizeof(buf)); + if (client_entry->username) + snprintf(buf, sizeof(buf) - 1, "%s@%s", + client_entry->username, client_entry->hostname); + signal_emit("message quit", 4, server, client_entry->nickname, + client_entry->username ? buf : "", + tmp ? tmp : ""); + } + + list1 = nicklist_get_same_unique(SERVER(server), client_entry); + for (list_tmp = list1; list_tmp != NULL; list_tmp = + list_tmp->next->next) { + CHANNEL_REC *channel = list_tmp->data; + NICK_REC *nickrec = list_tmp->next->data; + + nicklist_remove(channel, nickrec); + } + break; + + case SILC_NOTIFY_TYPE_TOPIC_SET: + /* + * Changed topic. + */ + + SILC_LOG_DEBUG(("Notify: TOPIC_SET")); + + idtype = va_arg(va, int); + entry = va_arg(va, void *); + tmp = va_arg(va, char *); + channel = va_arg(va, SilcChannelEntry); + + chanrec = silc_channel_find_entry(server, channel); + if (chanrec != NULL) { + char tmp2[256], *cp, *dm = NULL; + + g_free_not_null(chanrec->topic); + if (tmp && !silc_term_utf8() && silc_utf8_valid(tmp, strlen(tmp))) { + memset(tmp2, 0, sizeof(tmp2)); + cp = tmp2; + if (strlen(tmp) > sizeof(tmp2) - 1) { + dm = silc_calloc(strlen(tmp) + 1, sizeof(*dm)); + cp = dm; + } + + silc_utf8_decode(tmp, strlen(tmp), SILC_STRING_LANGUAGE, + cp, strlen(tmp)); + tmp = cp; + } + + chanrec->topic = *tmp == '\0' ? NULL : g_strdup(tmp); + signal_emit("channel topic changed", 1, chanrec); + + silc_free(dm); + } + + if (idtype == SILC_ID_CLIENT) { + client_entry = (SilcClientEntry)entry; + memset(buf, 0, sizeof(buf)); + snprintf(buf, sizeof(buf) - 1, "%s@%s", + client_entry->username, client_entry->hostname); + signal_emit("message topic", 5, server, channel->channel_name, + tmp, client_entry->nickname, buf); + } else if (idtype == SILC_ID_SERVER) { + server_entry = (SilcServerEntry)entry; + signal_emit("message topic", 5, server, channel->channel_name, + tmp, server_entry->server_name, + server_entry->server_name); + } else if (idtype == SILC_ID_CHANNEL) { + channel = (SilcChannelEntry)entry; + signal_emit("message topic", 5, server, channel->channel_name, + tmp, channel->channel_name, channel->channel_name); + } + break; + + case SILC_NOTIFY_TYPE_NICK_CHANGE: + /* + * Changed nickname. + */ + + SILC_LOG_DEBUG(("Notify: NICK_CHANGE")); + + client_entry = va_arg(va, SilcClientEntry); + client_entry2 = va_arg(va, SilcClientEntry); + + if (!strcmp(client_entry->nickname, client_entry2->nickname)) + break; + + memset(buf, 0, sizeof(buf)); + snprintf(buf, sizeof(buf) - 1, "%s@%s", + client_entry2->username, client_entry2->hostname); + nicklist_rename_unique(SERVER(server), + client_entry, client_entry->nickname, + client_entry2, client_entry2->nickname); + signal_emit("message nick", 4, server, client_entry2->nickname, + client_entry->nickname, buf); + break; + + case SILC_NOTIFY_TYPE_CMODE_CHANGE: + /* + * Changed channel mode. + */ + + SILC_LOG_DEBUG(("Notify: CMODE_CHANGE")); + + idtype = va_arg(va, int); + entry = va_arg(va, void *); + mode = va_arg(va, SilcUInt32); + (void)va_arg(va, char *); + (void)va_arg(va, char *); + channel = va_arg(va, SilcChannelEntry); + + tmp = silc_client_chmode(mode, + channel->channel_key ? + silc_cipher_get_name(channel->channel_key) : "", + channel->hmac ? + silc_hmac_get_name(channel->hmac) : ""); + + chanrec = silc_channel_find_entry(server, channel); + if (chanrec != NULL) { + g_free_not_null(chanrec->mode); + chanrec->mode = g_strdup(tmp == NULL ? "" : tmp); + signal_emit("channel mode changed", 1, chanrec); + } + + if (idtype == SILC_ID_CLIENT) { + client_entry = (SilcClientEntry)entry; + printformat_module("fe-common/silc", server, channel->channel_name, + MSGLEVEL_MODES, SILCTXT_CHANNEL_CMODE, + channel->channel_name, tmp ? tmp : "removed all", + client_entry->nickname); + } else if (idtype == SILC_ID_SERVER) { + server_entry = (SilcServerEntry)entry; + printformat_module("fe-common/silc", server, channel->channel_name, + MSGLEVEL_MODES, SILCTXT_CHANNEL_CMODE, + channel->channel_name, tmp ? tmp : "removed all", + server_entry->server_name); + } else if (idtype == SILC_ID_CHANNEL) { + channel2 = (SilcChannelEntry)entry; + printformat_module("fe-common/silc", server, channel->channel_name, + MSGLEVEL_MODES, SILCTXT_CHANNEL_CMODE, + channel->channel_name, tmp ? tmp : "removed all", + channel2->channel_name); + } + + silc_free(tmp); + break; + + case SILC_NOTIFY_TYPE_CUMODE_CHANGE: + /* + * Changed user's mode on channel. + */ + + SILC_LOG_DEBUG(("Notify: CUMODE_CHANGE")); + + idtype = va_arg(va, int); + entry = va_arg(va, void *); + mode = va_arg(va, SilcUInt32); + client_entry2 = va_arg(va, SilcClientEntry); + channel = va_arg(va, SilcChannelEntry); + + tmp = silc_client_chumode(mode); + chanrec = silc_channel_find_entry(server, channel); + if (chanrec != NULL) { + SILC_NICK_REC *nick; + + if (client_entry2 == server->conn->local_entry) + chanrec->chanop = (mode & SILC_CHANNEL_UMODE_CHANOP) != 0; + + nick = silc_nicklist_find(chanrec, client_entry2); + if (nick != NULL) { + nick->op = (mode & SILC_CHANNEL_UMODE_CHANOP) != 0; + nick->founder = (mode & SILC_CHANNEL_UMODE_CHANFO) != 0; + signal_emit("nick mode changed", 2, chanrec, nick); + } + } + + if (idtype == SILC_ID_CLIENT) { + client_entry = (SilcClientEntry)entry; + printformat_module("fe-common/silc", server, channel->channel_name, + MSGLEVEL_MODES, SILCTXT_CHANNEL_CUMODE, + channel->channel_name, client_entry2->nickname, + tmp ? tmp : "removed all", + client_entry->nickname); + } else if (idtype == SILC_ID_SERVER) { + server_entry = (SilcServerEntry)entry; + printformat_module("fe-common/silc", server, channel->channel_name, + MSGLEVEL_MODES, SILCTXT_CHANNEL_CUMODE, + channel->channel_name, client_entry2->nickname, + tmp ? tmp : "removed all", + server_entry->server_name); + } else if (idtype == SILC_ID_CHANNEL) { + channel2 = (SilcChannelEntry)entry; + printformat_module("fe-common/silc", server, channel->channel_name, + MSGLEVEL_MODES, SILCTXT_CHANNEL_CUMODE, + channel->channel_name, client_entry2->nickname, + tmp ? tmp : "removed all", + channel2->channel_name); + } + + if (mode & SILC_CHANNEL_UMODE_CHANFO) + printformat_module("fe-common/silc", + server, channel->channel_name, MSGLEVEL_CRAP, + SILCTXT_CHANNEL_FOUNDER, + channel->channel_name, client_entry2->nickname); + + if (mode & SILC_CHANNEL_UMODE_QUIET && conn->local_entry == client_entry2) + printformat_module("fe-common/silc", + server, channel->channel_name, MSGLEVEL_CRAP, + SILCTXT_CHANNEL_QUIETED, channel->channel_name); + + silc_free(tmp); + break; + + case SILC_NOTIFY_TYPE_MOTD: + /* + * Received MOTD. + */ + + SILC_LOG_DEBUG(("Notify: MOTD")); + + tmp = va_arg(va, char *); + + if (!settings_get_bool("skip_motd")) + printtext_multiline(server, NULL, MSGLEVEL_CRAP, "%s", tmp); + break; + + case SILC_NOTIFY_TYPE_KICKED: + /* + * Someone was kicked from channel. + */ + + SILC_LOG_DEBUG(("Notify: KICKED")); + + client_entry = va_arg(va, SilcClientEntry); + tmp = va_arg(va, char *); + client_entry2 = va_arg(va, SilcClientEntry); + channel = va_arg(va, SilcChannelEntry); + + chanrec = silc_channel_find_entry(server, channel); + + if (client_entry == conn->local_entry) { + printformat_module("fe-common/silc", server, channel->channel_name, + MSGLEVEL_CRAP, SILCTXT_CHANNEL_KICKED_YOU, + channel->channel_name, + client_entry ? client_entry2->nickname : "", + tmp ? tmp : ""); + if (chanrec) { + chanrec->kicked = TRUE; + channel_destroy((CHANNEL_REC *)chanrec); + } + } else { + printformat_module("fe-common/silc", server, channel->channel_name, + MSGLEVEL_CRAP, SILCTXT_CHANNEL_KICKED, + client_entry->nickname, channel->channel_name, + client_entry2 ? client_entry2->nickname : "", + tmp ? tmp : ""); + + if (chanrec) { + SILC_NICK_REC *nickrec = silc_nicklist_find(chanrec, client_entry); + if (nickrec != NULL) + nicklist_remove(CHANNEL(chanrec), NICK(nickrec)); + } + } + break; + + case SILC_NOTIFY_TYPE_KILLED: + /* + * Someone was killed from the network. + */ + + SILC_LOG_DEBUG(("Notify: KILLED")); + + client_entry = va_arg(va, SilcClientEntry); + tmp = va_arg(va, char *); + idtype = va_arg(va, int); + entry = va_arg(va, SilcClientEntry); + + if (client_entry == conn->local_entry) { + if (idtype == SILC_ID_CLIENT) { + client_entry2 = (SilcClientEntry)entry; + printformat_module("fe-common/silc", server, NULL, + MSGLEVEL_CRAP, SILCTXT_CHANNEL_KILLED_YOU, + client_entry2 ? client_entry2->nickname : "", + tmp ? tmp : ""); + } else if (idtype == SILC_ID_SERVER) { + server_entry = (SilcServerEntry)entry; + printformat_module("fe-common/silc", server, NULL, + MSGLEVEL_CRAP, SILCTXT_CHANNEL_KILLED_YOU, + server_entry->server_name, tmp ? tmp : ""); + } else if (idtype == SILC_ID_CHANNEL) { + channel = (SilcChannelEntry)entry; + printformat_module("fe-common/silc", server, NULL, + MSGLEVEL_CRAP, SILCTXT_CHANNEL_KILLED_YOU, + channel->channel_name, tmp ? tmp : ""); + } + } else { + list1 = nicklist_get_same_unique(SERVER(server), client_entry); + for (list_tmp = list1; list_tmp != NULL; list_tmp = + list_tmp->next->next) { + CHANNEL_REC *channel = list_tmp->data; + NICK_REC *nickrec = list_tmp->next->data; + nicklist_remove(channel, nickrec); + } + + if (idtype == SILC_ID_CLIENT) { + client_entry2 = (SilcClientEntry)entry; + printformat_module("fe-common/silc", server, NULL, + MSGLEVEL_CRAP, SILCTXT_CHANNEL_KILLED, + client_entry->nickname, + client_entry2 ? client_entry2->nickname : "", + tmp ? tmp : ""); + } else if (idtype == SILC_ID_SERVER) { + server_entry = (SilcServerEntry)entry; + printformat_module("fe-common/silc", server, NULL, + MSGLEVEL_CRAP, SILCTXT_CHANNEL_KILLED, + client_entry->nickname, + server_entry->server_name, tmp ? tmp : ""); + } else if (idtype == SILC_ID_CHANNEL) { + channel = (SilcChannelEntry)entry; + printformat_module("fe-common/silc", server, NULL, + MSGLEVEL_CRAP, SILCTXT_CHANNEL_KILLED, + client_entry->nickname, + channel->channel_name, tmp ? tmp : ""); + } + } + break; + + case SILC_NOTIFY_TYPE_CHANNEL_CHANGE: + break; + + case SILC_NOTIFY_TYPE_SERVER_SIGNOFF: + { + /* + * Server has quit the network. + */ + int i; + SilcClientEntry *clients; + SilcUInt32 clients_count; + + SILC_LOG_DEBUG(("Notify: SIGNOFF")); + + (void)va_arg(va, void *); + clients = va_arg(va, SilcClientEntry *); + clients_count = va_arg(va, SilcUInt32); + + for (i = 0; i < clients_count; i++) { + memset(buf, 0, sizeof(buf)); + + /* Print only if we have the nickname. If this client has just quit + when we were only resolving it, it is possible we don't have the + nickname. */ + if (clients[i]->nickname) { + if (clients[i]->username) + snprintf(buf, sizeof(buf) - 1, "%s@%s", + clients[i]->username, clients[i]->hostname); + signal_emit("message quit", 4, server, clients[i]->nickname, + clients[i]->username ? buf : "", + "server signoff"); + } + + silc_server_free_ftp(server, clients[i]); + + list1 = nicklist_get_same_unique(SERVER(server), clients[i]); + for (list_tmp = list1; list_tmp != NULL; list_tmp = + list_tmp->next->next) { + CHANNEL_REC *channel = list_tmp->data; + NICK_REC *nickrec = list_tmp->next->data; + nicklist_remove(channel, nickrec); + } + } + } + break; + + case SILC_NOTIFY_TYPE_ERROR: + { + SilcStatus error = va_arg(va, int); + + silc_say(client, conn, SILC_CLIENT_MESSAGE_ERROR, + "%s", silc_get_status_message(error)); + } + break; + + case SILC_NOTIFY_TYPE_WATCH: + { + SilcNotifyType notify; + + client_entry = va_arg(va, SilcClientEntry); + name = va_arg(va, char *); /* Maybe NULL */ + mode = va_arg(va, SilcUInt32); + notify = va_arg(va, int); + + if (notify == SILC_NOTIFY_TYPE_NICK_CHANGE) { + if (name) + printformat_module("fe-common/silc", server, NULL, + MSGLEVEL_CRAP, SILCTXT_WATCH_NICK_CHANGE, + client_entry->nickname, name); + else + printformat_module("fe-common/silc", server, NULL, + MSGLEVEL_CRAP, SILCTXT_WATCH_PRESENT, + client_entry->nickname); + } else if (notify == SILC_NOTIFY_TYPE_UMODE_CHANGE) { + /* See if client was away and is now present */ + if (!(mode & (SILC_UMODE_GONE | SILC_UMODE_INDISPOSED | + SILC_UMODE_BUSY | SILC_UMODE_PAGE | + SILC_UMODE_DETACHED)) && + (client_entry->mode & SILC_UMODE_GONE || + client_entry->mode & SILC_UMODE_INDISPOSED || + client_entry->mode & SILC_UMODE_BUSY || + client_entry->mode & SILC_UMODE_PAGE || + client_entry->mode & SILC_UMODE_DETACHED)) { + printformat_module("fe-common/silc", server, NULL, + MSGLEVEL_CRAP, SILCTXT_WATCH_PRESENT, + client_entry->nickname); + } + + if (mode) { + memset(buf, 0, sizeof(buf)); + silc_get_umode_string(mode, buf, sizeof(buf) - 1); + printformat_module("fe-common/silc", server, NULL, + MSGLEVEL_CRAP, SILCTXT_WATCH_UMODE_CHANGE, + client_entry->nickname, buf); + } + } else if (notify == SILC_NOTIFY_TYPE_KILLED) { + printformat_module("fe-common/silc", server, NULL, + MSGLEVEL_CRAP, SILCTXT_WATCH_KILLED, + client_entry->nickname); + } else if (notify == SILC_NOTIFY_TYPE_SIGNOFF || + notify == SILC_NOTIFY_TYPE_SERVER_SIGNOFF) { + printformat_module("fe-common/silc", server, NULL, + MSGLEVEL_CRAP, SILCTXT_WATCH_SIGNOFF, + client_entry->nickname); + } else if (notify == SILC_NOTIFY_TYPE_NONE) { + /* Client logged in to the network */ + printformat_module("fe-common/silc", server, NULL, + MSGLEVEL_CRAP, SILCTXT_WATCH_PRESENT, + client_entry->nickname); + } + } + break; + + default: /* Unknown notify */ printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP, SILCTXT_UNKNOWN_NOTIFY, type); + break; } va_end(va); @@ -182,28 +1037,79 @@ void silc_notify(SilcClient client, SilcClientConnection conn, /* Called to indicate that connection was either successfully established or connecting failed. This is also the first time application receives - the SilcClientConnection objecet which it should save somewhere. */ + the SilcClientConnection object which it should save somewhere. */ -void silc_connect(SilcClient client, SilcClientConnection conn, int success) +void silc_connect(SilcClient client, SilcClientConnection conn, + SilcClientConnectionStatus status) { SILC_SERVER_REC *server = conn->context; - if (success) { + if (!server || server->disconnected) { + silc_client_close_connection(client, conn); + return; + } + + switch (status) { + case SILC_CLIENT_CONN_SUCCESS: + /* We have successfully connected to server */ server->connected = TRUE; signal_emit("event connected", 1, server); - } else { + break; + + case SILC_CLIENT_CONN_SUCCESS_RESUME: + /* We have successfully resumed old detached session */ + server->connected = TRUE; + signal_emit("event connected", 1, server); + + /* If we resumed old session check whether we need to update + our nickname */ + if (strcmp(server->nick, conn->local_entry->nickname)) { + char *old; + old = g_strdup(server->nick); + server_change_nick(SERVER(server), conn->local_entry->nickname); + nicklist_rename_unique(SERVER(server), + conn->local_entry, server->nick, + conn->local_entry, conn->local_entry->nickname); + signal_emit("message own_nick", 4, server, server->nick, old, ""); + g_free(old); + } + break; + + default: server->connection_lost = TRUE; - server->conn->context = NULL; + if (server->conn) + server->conn->context = NULL; server_disconnect(SERVER(server)); + break; } } /* Called to indicate that connection was disconnected to the server. */ -void silc_disconnect(SilcClient client, SilcClientConnection conn) +void silc_disconnect(SilcClient client, SilcClientConnection conn, + SilcStatus status, const char *message) { SILC_SERVER_REC *server = conn->context; + SILC_LOG_DEBUG(("Start")); + + if (!server || server->connection_lost) + return; + + 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); + } + + if (message) + silc_say(client, conn, SILC_CLIENT_MESSAGE_AUDIT, + "Server closed connection: %s (%d) %s", + silc_get_status_message(status), status, + message ? message : ""); + server->conn->context = NULL; server->conn = NULL; server->connection_lost = TRUE; @@ -220,23 +1126,34 @@ void silc_disconnect(SilcClient client, SilcClientConnection conn) that the command really was processed. */ void silc_command(SilcClient client, SilcClientConnection conn, - SilcClientCommandContext cmd_context, int success, - SilcCommand command) + SilcClientCommandContext cmd_context, bool success, + SilcCommand command, SilcStatus status) { SILC_SERVER_REC *server = conn->context; - if (!success) + SILC_LOG_DEBUG(("Start")); + + if (!success) { + silc_say_error("%s", silc_get_status_message(status)); return; + } + + switch (command) { - switch(command) { case SILC_COMMAND_INVITE: - printformat_module("fe-common/silc", server, NULL, - MSGLEVEL_CRAP, SILCTXT_CHANNEL_INVITING, - cmd_context->argv[2], - (cmd_context->argv[1][0] == '*' ? - (char *)conn->current_channel->channel_name : - (char *)cmd_context->argv[1])); + if (cmd_context->argc > 2) + printformat_module("fe-common/silc", server, NULL, + MSGLEVEL_CRAP, SILCTXT_CHANNEL_INVITING, + cmd_context->argv[2], + (cmd_context->argv[1][0] == '*' ? + (char *)conn->current_channel->channel_name : + (char *)cmd_context->argv[1])); + break; + + case SILC_COMMAND_DETACH: + server->no_reconnect = TRUE; break; + default: break; } @@ -248,16 +1165,20 @@ void silc_command(SilcClient client, SilcClientConnection conn, static void silc_client_join_get_users(SilcClient client, SilcClientConnection conn, SilcClientEntry *clients, - uint32 clients_count, + SilcUInt32 clients_count, void *context) { SilcChannelEntry channel = (SilcChannelEntry)context; + SilcHashTableList htl; SilcChannelUser chu; SILC_SERVER_REC *server = conn->context; SILC_CHANNEL_REC *chanrec; SilcClientEntry founder = NULL; NICK_REC *ownnick; + SILC_LOG_DEBUG(("Start, channel %s, %d users", channel->channel_name, + silc_hash_table_count(channel->user_list))); + if (!clients) return; @@ -265,36 +1186,181 @@ static void silc_client_join_get_users(SilcClient client, if (chanrec == NULL) return; - silc_list_start(channel->clients); - while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) { + silc_hash_table_list(channel->user_list, &htl); + while (silc_hash_table_get(&htl, NULL, (void *)&chu)) { + if (!chu->client->nickname) + continue; if (chu->mode & SILC_CHANNEL_UMODE_CHANFO) founder = chu->client; silc_nicklist_insert(chanrec, chu, FALSE); } + silc_hash_table_list_reset(&htl); ownnick = NICK(silc_nicklist_find(chanrec, conn->local_entry)); nicklist_set_own(CHANNEL(chanrec), ownnick); signal_emit("channel joined", 1, chanrec); + chanrec->entry = channel; if (chanrec->topic) printformat_module("fe-common/silc", server, channel->channel_name, MSGLEVEL_CRAP, SILCTXT_CHANNEL_TOPIC, channel->channel_name, chanrec->topic); - fe_channels_nicklist(CHANNEL(chanrec), CHANNEL_NICKLIST_FLAG_ALL); + if (founder) { + if (founder == conn->local_entry) { + printformat_module("fe-common/silc", + server, channel->channel_name, MSGLEVEL_CRAP, + SILCTXT_CHANNEL_FOUNDER_YOU, + channel->channel_name); + signal_emit("nick mode changed", 2, chanrec, ownnick); + } else + printformat_module("fe-common/silc", + server, channel->channel_name, MSGLEVEL_CRAP, + SILCTXT_CHANNEL_FOUNDER, + channel->channel_name, founder->nickname); + } +} + +typedef struct { + SilcClient client; + SilcClientConnection conn; + void *entry; + SilcIdType id_type; + char *fingerprint; +} *GetkeyContext; + +void silc_getkey_cb(bool success, void *context) +{ + GetkeyContext getkey = (GetkeyContext)context; + char *entity = (getkey->id_type == SILC_ID_CLIENT ? "user" : "server"); + char *name = (getkey->id_type == SILC_ID_CLIENT ? + ((SilcClientEntry)getkey->entry)->nickname : + ((SilcServerEntry)getkey->entry)->server_name); + + if (success) { + printformat_module("fe-common/silc", NULL, NULL, + MSGLEVEL_CRAP, SILCTXT_PUBKEY_VERIFIED, entity, name); + } else { + printformat_module("fe-common/silc", NULL, NULL, + MSGLEVEL_CRAP, SILCTXT_PUBKEY_NOTVERIFIED, + entity, name); + } + + silc_free(getkey->fingerprint); + silc_free(getkey); +} + +/* Parse an invite or ban list */ +void silc_parse_inviteban_list(SilcClient client, + SilcClientConnection conn, + SILC_SERVER_REC *server, + SilcChannelEntry channel, + const char *list_type, + SilcArgumentPayload list) +{ + unsigned char *tmp; + SilcUInt32 type, len; + SILC_CHANNEL_REC *chanrec = silc_channel_find_entry(server, channel); + int counter=0, resolving = FALSE; + + if (!silc_argument_get_arg_num(list)) { + printformat_module("fe-common/silc", server, + (chanrec ? chanrec->visible_name : NULL), + MSGLEVEL_CRAP, SILCTXT_CHANNEL_NO_INVITEBAN_LIST, + channel->channel_name, list_type); + return; + } + + printformat_module("fe-common/silc", server, + (chanrec ? chanrec->visible_name : NULL), + MSGLEVEL_CRAP, SILCTXT_CHANNEL_INVITEBAN_LIST, + channel->channel_name, list_type); + + /* parse the list */ + tmp = silc_argument_get_first_arg(list, &type, &len); + while (tmp) { + switch (type) { + case 1: + { + /* an invite string */ + char **list; + int i=0; + + if (tmp[len-1] == ',') + tmp[len-1] = '\0'; + + list = g_strsplit(tmp, ",", -1); + while (list[i]) + printformat_module("fe-common/silc", server, + (chanrec ? chanrec->visible_name : NULL), + MSGLEVEL_CRAP, SILCTXT_CHANNEL_INVITEBAN_STRING, + ++counter, channel->channel_name, list_type, + list[i++]); + g_strfreev(list); + } + break; + + case 2: + { + /* a public key */ + char *fingerprint, *babbleprint; + + /* tmp is Public Key Payload, take public key from it. */ + fingerprint = silc_hash_fingerprint(NULL, tmp + 4, len - 4); + babbleprint = silc_hash_babbleprint(NULL, tmp + 4, len - 4); + + printformat_module("fe-common/silc", server, + (chanrec ? chanrec->visible_name : NULL), + MSGLEVEL_CRAP, SILCTXT_CHANNEL_INVITEBAN_PUBKEY, + ++counter, channel->channel_name, list_type, + fingerprint, babbleprint); + } + break; + + case 3: + { + /* a client ID */ + SilcClientID *client_id; + SilcClientEntry client_entry; + + client_id = silc_id_payload_parse_id(tmp, len, NULL); + + if (client_id == NULL) { + silc_say_error("Invalid data in %s list encountered", list_type); + break; + } + + client_entry = silc_client_get_client_by_id(client, conn, client_id); + + if (client_entry) { + printformat_module("fe-common/silc", server, + (chanrec ? chanrec->visible_name : NULL), + MSGLEVEL_CRAP, SILCTXT_CHANNEL_INVITEBAN_STRING, + ++counter, channel->channel_name, list_type, + client_entry->nickname); + } else { + resolving = TRUE; + silc_client_get_client_by_id_resolve(client, conn, client_id, + NULL, NULL, NULL); + } - if (founder) { - if (founder == conn->local_entry) - printformat_module("fe-common/silc", - server, channel->channel_name, MSGLEVEL_CRAP, - SILCTXT_CHANNEL_FOUNDER_YOU, - channel->channel_name); - else - printformat_module("fe-common/silc", - server, channel->channel_name, MSGLEVEL_CRAP, - SILCTXT_CHANNEL_FOUNDER, - channel->channel_name, founder->nickname); + silc_free(client_id); + } + break; + + default: + /* "trash" */ + silc_say_error("Unkown type in %s list: %u (len %u)", + list_type, type, len); + } + tmp = silc_argument_get_next_arg(list, &type, &len); } + + if (resolving) + printformat_module("fe-common/silc", server, + (chanrec ? chanrec->visible_name : NULL), + MSGLEVEL_CRAP, SILCTXT_CHANNEL_INVITEBAN_REGET, + list_type, channel->channel_name); } /* Command reply handler. This function is called always in the command reply @@ -315,8 +1381,8 @@ static void silc_client_join_get_users(SilcClient client, void silc_command_reply(SilcClient client, SilcClientConnection conn, - SilcCommandPayload cmd_payload, int success, - SilcCommand command, SilcCommandStatus status, ...) + SilcCommandPayload cmd_payload, bool success, + SilcCommand command, SilcStatus status, ...) { SILC_SERVER_REC *server = conn->context; @@ -325,50 +1391,86 @@ silc_command_reply(SilcClient client, SilcClientConnection conn, va_start(vp, status); + SILC_LOG_DEBUG(("Start")); + switch(command) { case SILC_COMMAND_WHOIS: { - char buf[1024], *nickname, *username, *realname; - uint32 idle, mode; - SilcBuffer channels; + char buf[1024], *nickname, *username, *realname, *nick; + unsigned char *fingerprint; + SilcUInt32 idle, mode; + SilcBuffer channels, user_modes; + SilcClientEntry client_entry; + SilcDList attrs; - if (status == SILC_STATUS_ERR_NO_SUCH_NICK || - status == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) { - char *tmp; - tmp = silc_argument_get_arg_type(silc_command_get_args(cmd_payload), - 3, NULL); + if (status == SILC_STATUS_ERR_NO_SUCH_NICK) { + /* Print the unknown nick for user */ + unsigned char *tmp = + silc_argument_get_arg_type(silc_command_get_args(cmd_payload), + 3, NULL); if (tmp) silc_say_error("%s: %s", tmp, - silc_client_command_status_message(status)); - else - silc_say_error("%s", silc_client_command_status_message(status)); + silc_get_status_message(status)); break; - } - - if (!success) + } else if (status == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) { + /* Try to find the entry for the unknown client ID, since we + might have, and print the nickname of it for user. */ + SilcUInt32 tmp_len; + unsigned char *tmp = + silc_argument_get_arg_type(silc_command_get_args(cmd_payload), + 2, &tmp_len); + if (tmp) { + SilcClientID *client_id = silc_id_payload_parse_id(tmp, tmp_len, + NULL); + if (client_id) { + client_entry = silc_client_get_client_by_id(client, conn, + client_id); + if (client_entry && client_entry->nickname) + silc_say_error("%s: %s", client_entry->nickname, + silc_get_status_message(status)); + silc_free(client_id); + } + } + break; + } else if (!success) { + silc_say_error("WHOIS: %s", silc_get_status_message(status)); return; - - (void)va_arg(vp, SilcClientEntry); + } + + client_entry = va_arg(vp, SilcClientEntry); nickname = va_arg(vp, char *); username = va_arg(vp, char *); realname = va_arg(vp, char *); channels = va_arg(vp, SilcBuffer); - mode = va_arg(vp, uint32); - idle = va_arg(vp, uint32); + mode = va_arg(vp, SilcUInt32); + idle = va_arg(vp, SilcUInt32); + fingerprint = va_arg(vp, unsigned char *); + user_modes = va_arg(vp, SilcBuffer); + attrs = va_arg(vp, SilcDList); + silc_parse_userfqdn(nickname, &nick, NULL); printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP, - SILCTXT_WHOIS_USERINFO, nickname, username, - realname); - - if (channels) { - SilcDList list = silc_channel_payload_parse_list(channels); - if (list) { + SILCTXT_WHOIS_USERINFO, nickname, + client_entry->username, client_entry->hostname, + nick, client_entry->nickname); + printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP, + SILCTXT_WHOIS_REALNAME, realname); + silc_free(nick); + + if (channels && user_modes) { + SilcUInt32 *umodes; + SilcDList list = silc_channel_payload_parse_list(channels->data, + channels->len); + if (list && silc_get_mode_list(user_modes, silc_dlist_count(list), + &umodes)) { SilcChannelPayload entry; + int i = 0; + memset(buf, 0, sizeof(buf)); silc_dlist_start(list); while ((entry = silc_dlist_get(list)) != SILC_LIST_END) { - char *m = silc_client_chumode_char(silc_channel_get_mode(entry)); - uint32 name_len; + SilcUInt32 name_len; + char *m = silc_client_chumode_char(umodes[i++]); char *name = silc_channel_get_name(entry, &name_len); if (m) @@ -381,22 +1483,13 @@ silc_command_reply(SilcClient client, SilcClientConnection conn, printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP, SILCTXT_WHOIS_CHANNELS, buf); silc_channel_payload_list_free(list); + silc_free(umodes); } } if (mode) { memset(buf, 0, sizeof(buf)); - - if ((mode & SILC_UMODE_SERVER_OPERATOR) || - (mode & SILC_UMODE_ROUTER_OPERATOR)) { - strcat(buf, (mode & SILC_UMODE_SERVER_OPERATOR) ? - "Server Operator " : - (mode & SILC_UMODE_ROUTER_OPERATOR) ? - "SILC Operator " : "[Unknown mode] "); - } - if (mode & SILC_UMODE_GONE) - strcat(buf, "away"); - + silc_get_umode_string(mode, buf, sizeof(buf - 1)); printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP, SILCTXT_WHOIS_MODES, buf); } @@ -410,9 +1503,58 @@ silc_command_reply(SilcClient client, SilcClientConnection conn, printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP, SILCTXT_WHOIS_IDLE, buf); } + + if (fingerprint) { + fingerprint = silc_fingerprint(fingerprint, 20); + printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP, + SILCTXT_WHOIS_FINGERPRINT, fingerprint); + silc_free(fingerprint); + } + + if (attrs) + silc_query_attributes_print(server, silc_client, conn, attrs, + client_entry); } break; + case SILC_COMMAND_IDENTIFY: + { + SilcClientEntry client_entry; + + if (status == SILC_STATUS_ERR_NO_SUCH_NICK) { + /* Print the unknown nick for user */ + unsigned char *tmp = + silc_argument_get_arg_type(silc_command_get_args(cmd_payload), + 3, NULL); + if (tmp) + silc_say_error("%s: %s", tmp, + silc_get_status_message(status)); + break; + } else if (status == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) { + /* Try to find the entry for the unknown client ID, since we + might have, and print the nickname of it for user. */ + SilcUInt32 tmp_len; + unsigned char *tmp = + silc_argument_get_arg_type(silc_command_get_args(cmd_payload), + 2, &tmp_len); + if (tmp) { + SilcClientID *client_id = silc_id_payload_parse_id(tmp, tmp_len, + NULL); + if (client_id) { + client_entry = silc_client_get_client_by_id(client, conn, + client_id); + if (client_entry && client_entry->nickname) + silc_say_error("%s: %s", client_entry->nickname, + silc_get_status_message(status)); + silc_free(client_id); + } + } + break; + } + + break; + } + case SILC_COMMAND_WHOWAS: { char *nickname, *username, *realname; @@ -424,14 +1566,12 @@ silc_command_reply(SilcClient client, SilcClientConnection conn, 3, NULL); if (tmp) silc_say_error("%s: %s", tmp, - silc_client_command_status_message(status)); - else - silc_say_error("%s", silc_client_command_status_message(status)); + silc_get_status_message(status)); break; - } - - if (!success) + } else if (!success) { + silc_say_error("WHOWAS: %s", silc_get_status_message(status)); return; + } (void)va_arg(vp, SilcClientEntry); nickname = va_arg(vp, char *); @@ -447,69 +1587,85 @@ silc_command_reply(SilcClient client, SilcClientConnection conn, case SILC_COMMAND_INVITE: { SilcChannelEntry channel; - char *invite_list; - SilcArgumentPayload args; - int argc = 0; + SilcBuffer payload; + SilcArgumentPayload invite_list; + SilcUInt32 argc; if (!success) return; channel = va_arg(vp, SilcChannelEntry); - invite_list = va_arg(vp, char *); - - args = silc_command_get_args(cmd_payload); - if (args) - argc = silc_argument_get_arg_num(args); - - if (invite_list) - printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP, - SILCTXT_CHANNEL_INVITE_LIST, channel->channel_name, - invite_list); - else if (argc == 3) - printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP, - SILCTXT_CHANNEL_NO_INVITE_LIST, - channel->channel_name); + payload = va_arg(vp, SilcBuffer); + + if (payload) { + SILC_GET16_MSB(argc, payload->data); + invite_list = silc_argument_payload_parse(payload->data + 2, + payload->len - 2, argc); + if (invite_list) { + silc_parse_inviteban_list(client, conn, server, channel, + "invite", invite_list); + silc_argument_payload_free(invite_list); + } + } } break; case SILC_COMMAND_JOIN: { char *channel, *mode, *topic; - uint32 modei; + SilcUInt32 modei; SilcChannelEntry channel_entry; SilcBuffer client_id_list; - uint32 list_count; + SilcUInt32 list_count; if (!success) return; channel = va_arg(vp, char *); channel_entry = va_arg(vp, SilcChannelEntry); - modei = va_arg(vp, uint32); - (void)va_arg(vp, uint32); + modei = va_arg(vp, SilcUInt32); + (void)va_arg(vp, SilcUInt32); (void)va_arg(vp, unsigned char *); (void)va_arg(vp, unsigned char *); (void)va_arg(vp, unsigned char *); topic = va_arg(vp, char *); (void)va_arg(vp, unsigned char *); - list_count = va_arg(vp, uint32); + list_count = va_arg(vp, SilcUInt32); client_id_list = va_arg(vp, SilcBuffer); chanrec = silc_channel_find(server, channel); - if (chanrec != NULL && !success) - channel_destroy(CHANNEL(chanrec)); - else if (chanrec == NULL && success) - chanrec = silc_channel_create(server, channel, TRUE); - + if (!chanrec) + chanrec = silc_channel_create(server, channel, channel, TRUE); + if (topic) { + char tmp[256], *cp, *dm = NULL; g_free_not_null(chanrec->topic); + + if (!silc_term_utf8() && silc_utf8_valid(topic, strlen(topic))) { + memset(tmp, 0, sizeof(tmp)); + cp = tmp; + if (strlen(topic) > sizeof(tmp) - 1) { + dm = silc_calloc(strlen(topic) + 1, sizeof(*dm)); + cp = dm; + } + + silc_utf8_decode(topic, strlen(topic), SILC_STRING_LANGUAGE, + cp, strlen(topic)); + topic = cp; + } + chanrec->topic = *topic == '\0' ? NULL : g_strdup(topic); signal_emit("channel topic changed", 1, chanrec); + + silc_free(dm); } mode = silc_client_chmode(modei, - channel_entry->channel_key->cipher->name, - channel_entry->hmac->hmac->name); + channel_entry->channel_key ? + silc_cipher_get_name(channel_entry-> + channel_key) : "", + channel_entry->hmac ? + silc_hmac_get_name(channel_entry->hmac) : ""); g_free_not_null(chanrec->mode); chanrec->mode = g_strdup(mode == NULL ? "" : mode); signal_emit("channel mode changed", 1, chanrec); @@ -518,23 +1674,44 @@ silc_command_reply(SilcClient client, SilcClientConnection conn, silc_client_get_clients_by_list(client, conn, list_count, client_id_list, silc_client_join_get_users, channel_entry); + break; } case SILC_COMMAND_NICK: { - SilcClientEntry client = va_arg(vp, SilcClientEntry); char *old; + SilcClientEntry client_entry = va_arg(vp, SilcClientEntry); + GSList *nicks; if (!success) return; + nicks = nicklist_get_same(SERVER(server), client_entry->nickname); + if (nicks != NULL) { + char buf[512]; + SilcClientEntry collider, old; + + old = ((SILC_NICK_REC *)(nicks->next->data))->silc_user->client; + collider = silc_client_get_client_by_id(client, conn, + old->id); + + memset(buf, 0, sizeof(buf)); + snprintf(buf, sizeof(buf) - 1, "%s@%s", + collider->username, collider->hostname); + nicklist_rename_unique(SERVER(server), + old, old->nickname, + collider, collider->nickname); + silc_print_nick_change(server, collider->nickname, + client_entry->nickname, buf); + g_slist_free(nicks); + } + old = g_strdup(server->nick); - server_change_nick(SERVER(server), client->nickname); + server_change_nick(SERVER(server), client_entry->nickname); nicklist_rename_unique(SERVER(server), server->conn->local_entry, server->nick, - client, client->nickname); - + client_entry, client_entry->nickname); signal_emit("message own_nick", 4, server, server->nick, old, ""); g_free(old); break; @@ -545,6 +1722,7 @@ silc_command_reply(SilcClient client, SilcClientConnection conn, char *topic, *name; int usercount; char users[20]; + char tmp[256], *cp, *dm = NULL; if (!success) return; @@ -553,35 +1731,73 @@ silc_command_reply(SilcClient client, SilcClientConnection conn, name = va_arg(vp, char *); topic = va_arg(vp, char *); usercount = va_arg(vp, int); + + if (topic && !silc_term_utf8() && + silc_utf8_valid(topic, strlen(topic))) { + memset(tmp, 0, sizeof(tmp)); + cp = tmp; + if (strlen(topic) > sizeof(tmp) - 1) { + dm = silc_calloc(strlen(topic) + 1, sizeof(*dm)); + cp = dm; + } + + silc_utf8_decode(topic, strlen(topic), SILC_STRING_LANGUAGE, + cp, strlen(topic)); + topic = cp; + } if (status == SILC_STATUS_LIST_START || status == SILC_STATUS_OK) printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP, SILCTXT_LIST_HEADER); - snprintf(users, sizeof(users) - 1, "%d", usercount); + if (!usercount) + snprintf(users, sizeof(users) - 1, "N/A"); + else + snprintf(users, sizeof(users) - 1, "%d", usercount); printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP, SILCTXT_LIST, name, users, topic ? topic : ""); + silc_free(dm); } break; case SILC_COMMAND_UMODE: { - uint32 mode; + SilcUInt32 mode; + char *reason; if (!success) return; - mode = va_arg(vp, uint32); + mode = va_arg(vp, SilcUInt32); - if (mode & SILC_UMODE_SERVER_OPERATOR) + if (mode & SILC_UMODE_SERVER_OPERATOR && + !(server->umode & SILC_UMODE_SERVER_OPERATOR)) printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP, SILCTXT_SERVER_OPER); - if (mode & SILC_UMODE_ROUTER_OPERATOR) + if (mode & SILC_UMODE_ROUTER_OPERATOR && + !(server->umode & SILC_UMODE_ROUTER_OPERATOR)) printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP, SILCTXT_ROUTER_OPER); + + if ((mode & SILC_UMODE_GONE) != (server->umode & SILC_UMODE_GONE)) { + if (mode & SILC_UMODE_GONE) { + if ((server->away_reason != NULL) && (server->away_reason[0] != '\0')) + reason = g_strdup(server->away_reason); + else + reason = g_strdup("away"); + } else + reason = g_strdup(""); + + silc_set_away(reason, server); + + g_free(reason); + } + + server->umode = mode; + signal_emit("user mode changed", 2, server, NULL); } break; @@ -589,6 +1805,9 @@ silc_command_reply(SilcClient client, SilcClientConnection conn, if (!success) return; + server->umode |= SILC_UMODE_SERVER_OPERATOR; + signal_emit("user mode changed", 2, server, NULL); + printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP, SILCTXT_SERVER_OPER); break; @@ -597,12 +1816,16 @@ silc_command_reply(SilcClient client, SilcClientConnection conn, if (!success) return; + server->umode |= SILC_UMODE_ROUTER_OPERATOR; + signal_emit("user mode changed", 2, server, NULL); + printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP, SILCTXT_ROUTER_OPER); break; case SILC_COMMAND_USERS: { + SilcHashTableList htl; SilcChannelEntry channel; SilcChannelUser chu; @@ -615,49 +1838,71 @@ silc_command_reply(SilcClient client, SilcClientConnection conn, MSGLEVEL_CRAP, SILCTXT_USERS_HEADER, channel->channel_name); - silc_list_start(channel->clients); - while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) { + silc_hash_table_list(channel->user_list, &htl); + while (silc_hash_table_get(&htl, NULL, (void *)&chu)) { SilcClientEntry e = chu->client; char stat[5], *mode; + + if (!e->nickname) + continue; memset(stat, 0, sizeof(stat)); mode = silc_client_chumode_char(chu->mode); if (e->mode & SILC_UMODE_GONE) strcat(stat, "G"); - else + else if (e->mode & SILC_UMODE_INDISPOSED) + strcat(stat, "I"); + else if (e->mode & SILC_UMODE_BUSY) + strcat(stat, "B"); + else if (e->mode & SILC_UMODE_PAGE) + strcat(stat, "P"); + else if (e->mode & SILC_UMODE_HYPER) strcat(stat, "H"); + else if (e->mode & SILC_UMODE_ROBOT) + strcat(stat, "R"); + else if (e->mode & SILC_UMODE_ANONYMOUS) + strcat(stat, "?"); + else + strcat(stat, "A"); if (mode) strcat(stat, mode); printformat_module("fe-common/silc", server, channel->channel_name, MSGLEVEL_CRAP, SILCTXT_USERS, - e->nickname, stat, e->username, + e->nickname, stat, + e->username ? e->username : "", + e->hostname ? e->hostname : "", e->realname ? e->realname : ""); if (mode) silc_free(mode); } + silc_hash_table_list_reset(&htl); } break; case SILC_COMMAND_BAN: { SilcChannelEntry channel; - char *ban_list; + SilcBuffer payload; + SilcArgumentPayload ban_list; + SilcUInt32 argc; if (!success) return; channel = va_arg(vp, SilcChannelEntry); - ban_list = va_arg(vp, char *); - - if (ban_list) - printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP, - SILCTXT_CHANNEL_BAN_LIST, channel->channel_name, - ban_list); - else - printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP, - SILCTXT_CHANNEL_NO_BAN_LIST, - channel->channel_name); + payload = va_arg(vp, SilcBuffer); + + if (payload) { + SILC_GET16_MSB(argc, payload->data); + ban_list = silc_argument_payload_parse(payload->data + 2, + payload->len - 2, argc); + if (ban_list) { + silc_parse_inviteban_list(client, conn, server, channel, + "ban", ban_list); + silc_argument_payload_free(ban_list); + } + } } break; @@ -667,22 +1912,63 @@ silc_command_reply(SilcClient client, SilcClientConnection conn, void *entry; SilcPublicKey public_key; unsigned char *pk; - uint32 pk_len; + SilcUInt32 pk_len; + GetkeyContext getkey; + char *name; if (!success) return; - id_type = va_arg(vp, uint32); + id_type = va_arg(vp, SilcUInt32); entry = va_arg(vp, void *); public_key = va_arg(vp, SilcPublicKey); - - pk = silc_pkcs_public_key_encode(public_key, &pk_len); - - if (id_type == SILC_ID_CLIENT) - silc_verify_public_key_internal(client, conn, SILC_SOCKET_TYPE_CLIENT, + + if (public_key) { + pk = silc_pkcs_public_key_encode(public_key, &pk_len); + + getkey = silc_calloc(1, sizeof(*getkey)); + getkey->entry = entry; + getkey->id_type = id_type; + getkey->client = client; + getkey->conn = conn; + getkey->fingerprint = silc_hash_fingerprint(NULL, pk, pk_len); + + name = (id_type == SILC_ID_CLIENT ? + ((SilcClientEntry)entry)->nickname : + ((SilcServerEntry)entry)->server_name); + + silc_verify_public_key_internal(client, conn, name, + (id_type == SILC_ID_CLIENT ? + SILC_SOCKET_TYPE_CLIENT : + SILC_SOCKET_TYPE_SERVER), pk, pk_len, SILC_SKE_PK_TYPE_SILC, - NULL, NULL); - silc_free(pk); + silc_getkey_cb, getkey); + silc_free(pk); + } else { + printformat_module("fe-common/silc", server, NULL, + MSGLEVEL_CRAP, SILCTXT_PUBKEY_NOKEY); + } + } + break; + + case SILC_COMMAND_INFO: + { + SilcServerEntry server_entry; + char *server_name; + char *server_info; + + if (!success) + return; + + server_entry = va_arg(vp, SilcServerEntry); + server_name = va_arg(vp, char *); + server_info = va_arg(vp, char *); + + if (server_name && server_info ) + { + printtext(server, NULL, MSGLEVEL_CRAP, "Server: %s", server_name); + printtext(server, NULL, MSGLEVEL_CRAP, "%s", server_info); + } } break; @@ -690,13 +1976,28 @@ silc_command_reply(SilcClient client, SilcClientConnection conn, { SilcChannelEntry channel; char *topic; + char tmp[256], *cp, *dm = NULL; if (!success) return; channel = va_arg(vp, SilcChannelEntry); topic = va_arg(vp, char *); - + + if (topic && !silc_term_utf8() && + silc_utf8_valid(topic, strlen(topic))) { + memset(tmp, 0, sizeof(tmp)); + cp = tmp; + if (strlen(topic) > sizeof(tmp) - 1) { + dm = silc_calloc(strlen(topic) + 1, sizeof(*dm)); + cp = dm; + } + + silc_utf8_decode(topic, strlen(topic), SILC_STRING_LANGUAGE, + cp, strlen(topic)); + topic = cp; + } + if (topic) { chanrec = silc_channel_find_entry(server, channel); if (chanrec) { @@ -712,6 +2013,138 @@ silc_command_reply(SilcClient client, SilcClientConnection conn, MSGLEVEL_CRAP, SILCTXT_CHANNEL_TOPIC_NOT_SET, channel->channel_name); } + silc_free(dm); + } + break; + + case SILC_COMMAND_WATCH: + break; + + case SILC_COMMAND_STATS: + { + SilcUInt32 starttime, uptime, my_clients, my_channels, my_server_ops, + my_router_ops, cell_clients, cell_channels, cell_servers, + clients, channels, servers, routers, server_ops, router_ops; + SilcUInt32 buf_len; + SilcBufferStruct buf; + unsigned char *tmp_buf; + char tmp[40]; + const char *tmptime; + int days, hours, mins, secs; + + if (!success) + return; + + tmp_buf = va_arg(vp, unsigned char *); + buf_len = va_arg(vp, SilcUInt32); + + if (!tmp_buf || !buf_len) { + printtext(server, NULL, MSGLEVEL_CRAP, "No statistics available"); + return; + } + + /* Get statistics structure */ + silc_buffer_set(&buf, tmp_buf, buf_len); + silc_buffer_unformat(&buf, + SILC_STR_UI_INT(&starttime), + SILC_STR_UI_INT(&uptime), + SILC_STR_UI_INT(&my_clients), + SILC_STR_UI_INT(&my_channels), + SILC_STR_UI_INT(&my_server_ops), + SILC_STR_UI_INT(&my_router_ops), + SILC_STR_UI_INT(&cell_clients), + SILC_STR_UI_INT(&cell_channels), + SILC_STR_UI_INT(&cell_servers), + SILC_STR_UI_INT(&clients), + SILC_STR_UI_INT(&channels), + SILC_STR_UI_INT(&servers), + SILC_STR_UI_INT(&routers), + SILC_STR_UI_INT(&server_ops), + SILC_STR_UI_INT(&router_ops), + SILC_STR_END); + + tmptime = silc_get_time(starttime); + printformat_module("fe-common/silc", server, NULL, + MSGLEVEL_CRAP, SILCTXT_STATS, + "Local server start time", tmptime); + + days = uptime / (24 * 60 * 60); + uptime -= days * (24 * 60 * 60); + hours = uptime / (60 * 60); + uptime -= hours * (60 * 60); + mins = uptime / 60; + uptime -= mins * 60; + secs = uptime; + snprintf(tmp, sizeof(tmp) - 1, "%d days %d hours %d mins %d secs", + days, hours, mins, secs); + printformat_module("fe-common/silc", server, NULL, + MSGLEVEL_CRAP, SILCTXT_STATS, + "Local server uptime", tmp); + + snprintf(tmp, sizeof(tmp) - 1, "%d", (int)my_clients); + printformat_module("fe-common/silc", server, NULL, + MSGLEVEL_CRAP, SILCTXT_STATS, + "Local server clients", tmp); + + snprintf(tmp, sizeof(tmp) - 1, "%d", (int)my_channels); + printformat_module("fe-common/silc", server, NULL, + MSGLEVEL_CRAP, SILCTXT_STATS, + "Local server channels", tmp); + + snprintf(tmp, sizeof(tmp) - 1, "%d", (int)my_server_ops); + printformat_module("fe-common/silc", server, NULL, + MSGLEVEL_CRAP, SILCTXT_STATS, + "Local server operators", tmp); + + snprintf(tmp, sizeof(tmp) - 1, "%d", (int)my_router_ops); + printformat_module("fe-common/silc", server, NULL, + MSGLEVEL_CRAP, SILCTXT_STATS, + "Local router operators", tmp); + + snprintf(tmp, sizeof(tmp) - 1, "%d", (int)cell_clients); + printformat_module("fe-common/silc", server, NULL, + MSGLEVEL_CRAP, SILCTXT_STATS, + "Local cell clients", tmp); + + snprintf(tmp, sizeof(tmp) - 1, "%d", (int)cell_channels); + printformat_module("fe-common/silc", server, NULL, + MSGLEVEL_CRAP, SILCTXT_STATS, + "Local cell channels", tmp); + + snprintf(tmp, sizeof(tmp) - 1, "%d", (int)cell_servers); + printformat_module("fe-common/silc", server, NULL, + MSGLEVEL_CRAP, SILCTXT_STATS, + "Local cell servers", tmp); + + snprintf(tmp, sizeof(tmp) - 1, "%d", (int)clients); + printformat_module("fe-common/silc", server, NULL, + MSGLEVEL_CRAP, SILCTXT_STATS, + "Total clients", tmp); + + snprintf(tmp, sizeof(tmp) - 1, "%d", (int)channels); + printformat_module("fe-common/silc", server, NULL, + MSGLEVEL_CRAP, SILCTXT_STATS, + "Total channels", tmp); + + snprintf(tmp, sizeof(tmp) - 1, "%d", (int)servers); + printformat_module("fe-common/silc", server, NULL, + MSGLEVEL_CRAP, SILCTXT_STATS, + "Total servers", tmp); + + snprintf(tmp, sizeof(tmp) - 1, "%d", (int)routers); + printformat_module("fe-common/silc", server, NULL, + MSGLEVEL_CRAP, SILCTXT_STATS, + "Total routers", tmp); + + snprintf(tmp, sizeof(tmp) - 1, "%d", (int)server_ops); + printformat_module("fe-common/silc", server, NULL, + MSGLEVEL_CRAP, SILCTXT_STATS, + "Total server operators", tmp); + + snprintf(tmp, sizeof(tmp) - 1, "%d", (int)router_ops); + printformat_module("fe-common/silc", server, NULL, + MSGLEVEL_CRAP, SILCTXT_STATS, + "Total router operators", tmp); } break; @@ -720,16 +2153,14 @@ silc_command_reply(SilcClient client, SilcClientConnection conn, va_end(vp); } -/* Internal routine to verify public key. If the `completion' is provided - it will be called to indicate whether public was verified or not. */ - typedef struct { SilcClient client; SilcClientConnection conn; char *filename; char *entity; + char *entity_name; unsigned char *pk; - uint32 pk_len; + SilcUInt32 pk_len; SilcSKEPKType pk_type; SilcVerifyPublicKey completion; void *context; @@ -753,23 +2184,33 @@ static void verify_public_key_completion(const char *line, void *context) verify->completion(FALSE, verify->context); printformat_module("fe-common/silc", NULL, NULL, - MSGLEVEL_CRAP, SILCTXT_PUBKEY_DISCARD, verify->entity); + MSGLEVEL_CRAP, SILCTXT_PUBKEY_DISCARD, + verify->entity_name ? verify->entity_name : + verify->entity); } silc_free(verify->filename); silc_free(verify->entity); + silc_free(verify->entity_name); silc_free(verify->pk); silc_free(verify); } +/* Internal routine to verify public key. If the `completion' is provided + it will be called to indicate whether public was verified or not. For + server/router public key this will check for filename that includes the + remote host's IP address and remote host's hostname. */ + static void silc_verify_public_key_internal(SilcClient client, SilcClientConnection conn, - SilcSocketType conn_type, unsigned char *pk, - uint32 pk_len, SilcSKEPKType pk_type, + const char *name, SilcSocketType conn_type, + unsigned char *pk, SilcUInt32 pk_len, + SilcSKEPKType pk_type, SilcVerifyPublicKey completion, void *context) { int i; - char file[256], filename[256], *fingerprint, *format; + char file[256], filename[256], filename2[256], *ipf, *hostf = NULL; + char *fingerprint, *babbleprint, *format; struct passwd *pw; struct stat st; char *entity = ((conn_type == SILC_SOCKET_TYPE_SERVER || @@ -794,14 +2235,32 @@ silc_verify_public_key_internal(SilcClient client, SilcClientConnection conn, } memset(filename, 0, sizeof(filename)); + memset(filename2, 0, sizeof(filename2)); memset(file, 0, sizeof(file)); if (conn_type == SILC_SOCKET_TYPE_SERVER || conn_type == SILC_SOCKET_TYPE_ROUTER) { - snprintf(file, sizeof(file) - 1, "%skey_%s_%d.pub", entity, - conn->sock->hostname, conn->sock->port); - snprintf(filename, sizeof(filename) - 1, "%s/.silc/%skeys/%s", - pw->pw_dir, entity, file); + if (!name) { + snprintf(file, sizeof(file) - 1, "%skey_%s_%d.pub", entity, + conn->sock->ip, conn->sock->port); + snprintf(filename, sizeof(filename) - 1, "%s/%skeys/%s", + get_irssi_dir(), entity, file); + + snprintf(file, sizeof(file) - 1, "%skey_%s_%d.pub", entity, + conn->sock->hostname, conn->sock->port); + snprintf(filename2, sizeof(filename2) - 1, "%s/%skeys/%s", + get_irssi_dir(), entity, file); + + ipf = filename; + hostf = filename2; + } else { + snprintf(file, sizeof(file) - 1, "%skey_%s_%d.pub", entity, + name, conn->sock->port); + snprintf(filename, sizeof(filename) - 1, "%s/%skeys/%s", + get_irssi_dir(), entity, file); + + ipf = filename; + } } else { /* Replace all whitespaces with `_'. */ fingerprint = silc_hash_fingerprint(NULL, pk, pk_len); @@ -810,34 +2269,42 @@ silc_verify_public_key_internal(SilcClient client, SilcClientConnection conn, fingerprint[i] = '_'; snprintf(file, sizeof(file) - 1, "%skey_%s.pub", entity, fingerprint); - snprintf(filename, sizeof(filename) - 1, "%s/.silc/%skeys/%s", - pw->pw_dir, entity, file); + snprintf(filename, sizeof(filename) - 1, "%s/%skeys/%s", + get_irssi_dir(), entity, file); silc_free(fingerprint); + + ipf = filename; } /* Take fingerprint of the public key */ fingerprint = silc_hash_fingerprint(NULL, pk, pk_len); + babbleprint = silc_hash_babbleprint(NULL, pk, pk_len); verify = silc_calloc(1, sizeof(*verify)); verify->client = client; verify->conn = conn; - verify->filename = strdup(filename); + verify->filename = strdup(ipf); verify->entity = strdup(entity); - verify->pk = silc_calloc(pk_len, sizeof(*verify->pk)); - memcpy(verify->pk, pk, pk_len); + verify->entity_name = (conn_type != SILC_SOCKET_TYPE_CLIENT ? + (name ? strdup(name) : strdup(conn->sock->hostname)) + : NULL); + verify->pk = silc_memdup(pk, pk_len); verify->pk_len = pk_len; verify->pk_type = pk_type; verify->completion = completion; verify->context = context; /* Check whether this key already exists */ - if (stat(filename, &st) < 0) { + if (stat(ipf, &st) < 0 && (!hostf || stat(hostf, &st) < 0)) { /* Key does not exist, ask user to verify the key and save it */ printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, - SILCTXT_PUBKEY_RECEIVED, entity); + SILCTXT_PUBKEY_RECEIVED,verify->entity_name ? + verify->entity_name : entity); printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint); + printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, + SILCTXT_PUBKEY_BABBLEPRINT, babbleprint); format = format_get_text("fe-common/silc", NULL, NULL, NULL, SILCTXT_PUBKEY_ACCEPT); keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion, @@ -849,35 +2316,45 @@ silc_verify_public_key_internal(SilcClient client, SilcClientConnection conn, /* The key already exists, verify it. */ SilcPublicKey public_key; unsigned char *encpk; - uint32 encpk_len; - - /* Load the key file */ - if (!silc_pkcs_load_public_key(filename, &public_key, - SILC_PKCS_FILE_PEM)) - if (!silc_pkcs_load_public_key(filename, &public_key, - SILC_PKCS_FILE_BIN)) { - printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, - SILCTXT_PUBKEY_RECEIVED, entity); - printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, - SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint); - printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, - SILCTXT_PUBKEY_COULD_NOT_LOAD, entity); - format = format_get_text("fe-common/silc", NULL, NULL, NULL, - SILCTXT_PUBKEY_ACCEPT_ANYWAY); - keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion, - format, 0, verify); - g_free(format); - silc_free(fingerprint); - return; - } - + SilcUInt32 encpk_len; + + /* Load the key file, try for both IP filename and hostname filename */ + if (!silc_pkcs_load_public_key(ipf, &public_key, + SILC_PKCS_FILE_PEM) && + !silc_pkcs_load_public_key(ipf, &public_key, + SILC_PKCS_FILE_BIN) && + (!hostf || (!silc_pkcs_load_public_key(hostf, &public_key, + SILC_PKCS_FILE_PEM) && + !silc_pkcs_load_public_key(hostf, &public_key, + SILC_PKCS_FILE_BIN)))) { + printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, + SILCTXT_PUBKEY_RECEIVED,verify->entity_name ? + verify->entity_name : entity); + printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, + SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint); + printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, + SILCTXT_PUBKEY_BABBLEPRINT, babbleprint); + printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, + SILCTXT_PUBKEY_COULD_NOT_LOAD, entity); + format = format_get_text("fe-common/silc", NULL, NULL, NULL, + SILCTXT_PUBKEY_ACCEPT_ANYWAY); + keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion, + format, 0, verify); + g_free(format); + silc_free(fingerprint); + return; + } + /* Encode the key data */ encpk = silc_pkcs_public_key_encode(public_key, &encpk_len); if (!encpk) { printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, - SILCTXT_PUBKEY_RECEIVED, entity); + SILCTXT_PUBKEY_RECEIVED,verify->entity_name ? + verify->entity_name : entity); printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint); + printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, + SILCTXT_PUBKEY_BABBLEPRINT, babbleprint); printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, SILCTXT_PUBKEY_MALFORMED, entity); format = format_get_text("fe-common/silc", NULL, NULL, NULL, @@ -892,9 +2369,12 @@ silc_verify_public_key_internal(SilcClient client, SilcClientConnection conn, /* Compare the keys */ if (memcmp(encpk, pk, encpk_len)) { printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, - SILCTXT_PUBKEY_RECEIVED, entity); + SILCTXT_PUBKEY_RECEIVED,verify->entity_name ? + verify->entity_name : entity); printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint); + printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, + SILCTXT_PUBKEY_BABBLEPRINT, babbleprint); printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, SILCTXT_PUBKEY_NO_MATCH, entity); printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, @@ -916,6 +2396,11 @@ silc_verify_public_key_internal(SilcClient client, SilcClientConnection conn, if (completion) completion(TRUE, context); silc_free(fingerprint); + silc_free(verify->filename); + silc_free(verify->entity); + silc_free(verify->entity_name); + silc_free(verify->pk); + silc_free(verify); } } @@ -927,10 +2412,10 @@ silc_verify_public_key_internal(SilcClient client, SilcClientConnection conn, void silc_verify_public_key(SilcClient client, SilcClientConnection conn, SilcSocketType conn_type, unsigned char *pk, - uint32 pk_len, SilcSKEPKType pk_type, + SilcUInt32 pk_len, SilcSKEPKType pk_type, SilcVerifyPublicKey completion, void *context) { - silc_verify_public_key_internal(client, conn, conn_type, pk, + silc_verify_public_key_internal(client, conn, NULL, conn_type, pk, pk_len, pk_type, completion, context); } @@ -945,13 +2430,15 @@ typedef struct { void ask_passphrase_completion(const char *passphrase, void *context) { AskPassphrase p = (AskPassphrase)context; + if (passphrase && passphrase[0] == '\0') + passphrase = NULL; p->completion((unsigned char *)passphrase, passphrase ? strlen(passphrase) : 0, p->context); silc_free(p); } void silc_ask_passphrase(SilcClient client, SilcClientConnection conn, - SilcAskPassphrase completion, void *context) + SilcAskPassphrase completion, void *context) { AskPassphrase p = silc_calloc(1, sizeof(*p)); p->completion = completion; @@ -961,34 +2448,81 @@ void silc_ask_passphrase(SilcClient client, SilcClientConnection conn, "Passphrase: ", ENTRY_REDIRECT_FLAG_HIDDEN, p); } +typedef struct { + SilcGetAuthMeth completion; + void *context; +} *InternalGetAuthMethod; + +/* Callback called when we've received the authentication method information + from the server after we've requested it. This will get the authentication + data from the user if needed. */ + +static void silc_get_auth_method_callback(SilcClient client, + SilcClientConnection conn, + SilcAuthMethod auth_meth, + void *context) +{ + InternalGetAuthMethod internal = (InternalGetAuthMethod)context; + + SILC_LOG_DEBUG(("Start")); + + switch (auth_meth) { + case SILC_AUTH_NONE: + /* No authentication required. */ + (*internal->completion)(TRUE, auth_meth, NULL, 0, internal->context); + break; + case SILC_AUTH_PASSWORD: + { + /* Check whether we find the password for this server in our + configuration. If not, then don't provide so library will ask + it from the user. */ + SERVER_SETUP_REC *setup = server_setup_find_port(conn->remote_host, + conn->remote_port); + if (!setup || !setup->password) { + (*internal->completion)(TRUE, auth_meth, NULL, 0, internal->context); + break; + } + + (*internal->completion)(TRUE, auth_meth, setup->password, + strlen(setup->password), internal->context); + } + break; + case SILC_AUTH_PUBLIC_KEY: + /* Do not get the authentication data now, the library will generate + it using our default key, if we do not provide it here. */ + /* XXX In the future when we support multiple local keys and multiple + local certificates we will need to ask from user which one to use. */ + (*internal->completion)(TRUE, auth_meth, NULL, 0, internal->context); + break; + } + + silc_free(internal); +} + /* Find authentication method and authentication data by hostname and port. The hostname may be IP address as well. The found authentication method and authentication data is returned to `auth_meth', `auth_data' and `auth_data_len'. The function returns TRUE if authentication method is found and FALSE if not. `conn' may be NULL. */ -int silc_get_auth_method(SilcClient client, SilcClientConnection conn, - char *hostname, uint16 port, - SilcProtocolAuthMeth *auth_meth, - unsigned char **auth_data, - uint32 *auth_data_len) +void silc_get_auth_method(SilcClient client, SilcClientConnection conn, + char *hostname, SilcUInt16 port, + SilcGetAuthMeth completion, void *context) { - bool ret = TRUE; - SILC_SERVER_REC *server = conn ? conn->context : NULL; + InternalGetAuthMethod internal; - /* XXX must resolve from configuration whether this connection has - any specific authentication data */ + SILC_LOG_DEBUG(("Start")); - *auth_meth = SILC_AUTH_NONE; - *auth_data = NULL; - *auth_data_len = 0; - - if (ret == FALSE) { - printformat_module("fe-common/silc", server, NULL, - MSGLEVEL_MODES, SILCTXT_AUTH_METH_UNRESOLVED); - } + /* If we do not have this connection configured by the user in a + configuration file then resolve the authentication method from the + server for this session. */ + internal = silc_calloc(1, sizeof(*internal)); + internal->completion = completion; + internal->context = context; - return ret; + silc_client_request_authentication_method(client, conn, + silc_get_auth_method_callback, + internal); } /* Notifies application that failure packet was received. This is called @@ -1001,6 +2535,8 @@ int silc_get_auth_method(SilcClient client, SilcClientConnection conn, void silc_failure(SilcClient client, SilcClientConnection conn, SilcProtocol protocol, void *failure) { + SILC_LOG_DEBUG(("Start")); + if (protocol->protocol->type == SILC_PROTOCOL_CLIENT_KEY_EXCHANGE) { SilcSKEStatus status = (SilcSKEStatus)failure; @@ -1028,10 +2564,13 @@ void silc_failure(SilcClient client, SilcClientConnection conn, if (status == SILC_SKE_STATUS_INCORRECT_SIGNATURE) printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, SILCTXT_KE_INCORRECT_SIGNATURE); + if (status == SILC_SKE_STATUS_INVALID_COOKIE) + printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, + SILCTXT_KE_INVALID_COOKIE); } if (protocol->protocol->type == SILC_PROTOCOL_CLIENT_CONNECTION_AUTH) { - uint32 err = (uint32)failure; + SilcUInt32 err = (SilcUInt32)failure; if (err == SILC_AUTH_FAILED) printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, @@ -1046,14 +2585,15 @@ void silc_failure(SilcClient client, SilcClientConnection conn, desired (application may start it later by calling the function silc_client_perform_key_agreement). */ -int silc_key_agreement(SilcClient client, SilcClientConnection conn, - SilcClientEntry client_entry, char *hostname, - int port, - SilcKeyAgreementCallback *completion, - void **context) +bool silc_key_agreement(SilcClient client, SilcClientConnection conn, + SilcClientEntry client_entry, const char *hostname, + SilcUInt16 port, SilcKeyAgreementCallback *completion, + void **context) { char portstr[12]; + SILC_LOG_DEBUG(("Start")); + /* We will just display the info on the screen and return FALSE and user will have to start the key agreement with a command. */ @@ -1061,10 +2601,10 @@ int silc_key_agreement(SilcClient client, SilcClientConnection conn, snprintf(portstr, sizeof(portstr) - 1, "%d", port); if (!hostname) - printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_NOTICES, + printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, SILCTXT_KEY_AGREEMENT_REQUEST, client_entry->nickname); else - printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_NOTICES, + printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, SILCTXT_KEY_AGREEMENT_REQUEST_HOST, client_entry->nickname, hostname, portstr); @@ -1074,6 +2614,86 @@ int silc_key_agreement(SilcClient client, SilcClientConnection conn, return FALSE; } +/* Notifies application that file transfer protocol session is being + requested by the remote client indicated by the `client_entry' from + the `hostname' and `port'. The `session_id' is the file transfer + session and it can be used to either accept or reject the file + transfer request, by calling the silc_client_file_receive or + silc_client_file_close, respectively. */ + +void silc_ftp(SilcClient client, SilcClientConnection conn, + SilcClientEntry client_entry, SilcUInt32 session_id, + const char *hostname, SilcUInt16 port) +{ + SILC_SERVER_REC *server; + char portstr[12]; + FtpSession ftp = NULL; + + SILC_LOG_DEBUG(("Start")); + + server = conn->context; + + silc_dlist_start(server->ftp_sessions); + while ((ftp = silc_dlist_get(server->ftp_sessions)) != SILC_LIST_END) { + if (ftp->client_entry == client_entry && + ftp->session_id == session_id) { + server->current_session = ftp; + break; + } + } + if (ftp == SILC_LIST_END) { + ftp = silc_calloc(1, sizeof(*ftp)); + ftp->client_entry = client_entry; + ftp->session_id = session_id; + ftp->send = FALSE; + ftp->conn = conn; + silc_dlist_add(server->ftp_sessions, ftp); + server->current_session = ftp; + } + + if (hostname) + snprintf(portstr, sizeof(portstr) - 1, "%d", port); + + if (!hostname) + printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, + SILCTXT_FILE_REQUEST, client_entry->nickname); + else + printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, + SILCTXT_FILE_REQUEST_HOST, + client_entry->nickname, hostname, portstr); +} + +/* Delivers SILC session detachment data indicated by `detach_data' to the + application. If application has issued SILC_COMMAND_DETACH command + the client session in the SILC network is not quit. The client remains + in the network but is detached. The detachment data may be used later + to resume the session in the SILC Network. The appliation is + responsible of saving the `detach_data', to for example in a file. + + The detachment data can be given as argument to the functions + silc_client_connect_to_server, or silc_client_add_connection when + creating connection to remote server, inside SilcClientConnectionParams + structure. If it is provided the client library will attempt to resume + the session in the network. After the connection is created + successfully, the application is responsible of setting the user + interface for user into the same state it was before detaching (showing + same channels, channel modes, etc). It can do this by fetching the + information (like joined channels) from the client library. */ + +void +silc_detach(SilcClient client, SilcClientConnection conn, + const unsigned char *detach_data, SilcUInt32 detach_data_len) +{ + char file[256]; + + /* Save the detachment data to file. */ + + memset(file, 0, sizeof(file)); + snprintf(file, sizeof(file) - 1, "%s/session", get_irssi_dir()); + silc_file_writefile(file, detach_data, detach_data_len); +} + + /* SILC client operations */ SilcClientOperations ops = { silc_say, @@ -1089,4 +2709,6 @@ SilcClientOperations ops = { silc_ask_passphrase, silc_failure, silc_key_agreement, + silc_ftp, + silc_detach, };