diff options
-rw-r--r-- | lib/misc.c | 26 | ||||
-rw-r--r-- | lib/misc.h | 1 | ||||
-rw-r--r-- | lib/sha1.c | 50 | ||||
-rw-r--r-- | protocols/jabber/iq.c | 59 | ||||
-rw-r--r-- | protocols/jabber/jabber.c | 18 | ||||
-rw-r--r-- | protocols/jabber/jabber.h | 1 | ||||
-rw-r--r-- | root_commands.c | 35 |
7 files changed, 170 insertions, 20 deletions
@@ -786,3 +786,29 @@ char *str_reject_chars(char *string, const char *reject, char replacement) return string; } + +/* Returns a string that is exactly 'char_len' utf8 characters long (not bytes), + * padded to the right with spaces or truncated with the 'ellipsis' parameter + * if specified (can be NULL). + * Returns a newly allocated string, or NULL on invalid parameters. */ +char *str_pad_and_truncate(const char *string, long char_len, const char *ellipsis) +{ + size_t string_len = strlen(string); + size_t ellipsis_len = (ellipsis) ? strlen(ellipsis) : 0; + long orig_len = g_utf8_strlen(string, -1); + + g_return_val_if_fail(char_len > ellipsis_len, NULL); + + if (orig_len > char_len) { + char *ret = g_malloc(string_len + 1); + g_utf8_strncpy(ret, string, char_len - ellipsis_len); + if (ellipsis) { + g_strlcat(ret, ellipsis, string_len); + } + return ret; + } else if (orig_len < char_len) { + return g_strdup_printf("%s%*s", string, (int) (char_len - orig_len), ""); + } else { + return g_strdup(string); + } +} @@ -150,5 +150,6 @@ G_MODULE_EXPORT char *get_rfc822_header(const char *text, const char *header, in G_MODULE_EXPORT int truncate_utf8(char *string, int maxlen); G_MODULE_EXPORT gboolean parse_int64(char *string, int base, guint64 *number); G_MODULE_EXPORT char *str_reject_chars(char *string, const char *reject, char replacement); +G_MODULE_EXPORT char *str_pad_and_truncate(const char *string, long char_len, const char *ellipsis); #endif @@ -23,14 +23,23 @@ void sha1_finish(sha1_state_t *ctx, guint8 digest[SHA1_HASH_SIZE]) #define HMAC_BLOCK_SIZE 64 -/* BitlBee addition: */ -void sha1_hmac(const char *key_, size_t key_len, const char *payload, size_t payload_len, guint8 digest[SHA1_HASH_SIZE]) +void b_hmac(GChecksumType checksum_type, const char *key_, size_t key_len, + const char *payload, size_t payload_len, guint8 **digest) { - sha1_state_t sha1; - guint8 hash[SHA1_HASH_SIZE]; + GChecksum *checksum; + size_t hash_len; + guint8 *hash; guint8 key[HMAC_BLOCK_SIZE + 1]; int i; + hash_len = g_checksum_type_get_length(checksum_type); + + if (hash_len == (size_t) -1) { + return; + } + + hash = g_malloc(hash_len); + if (key_len == 0) { key_len = strlen(key_); } @@ -42,32 +51,43 @@ void sha1_hmac(const char *key_, size_t key_len, const char *payload, size_t pay otherwise just pad. */ memset(key, 0, HMAC_BLOCK_SIZE + 1); if (key_len > HMAC_BLOCK_SIZE) { - sha1_init(&sha1); - sha1_append(&sha1, (guint8 *) key_, key_len); - sha1_finish(&sha1, key); + checksum = g_checksum_new(checksum_type); + g_checksum_update(checksum, (guint8 *) key_, key_len); + g_checksum_get_digest(checksum, key, &hash_len); + g_checksum_free(checksum); } else { memcpy(key, key_, key_len); } /* Inner part: H(K XOR 0x36, text) */ - sha1_init(&sha1); + checksum = g_checksum_new(checksum_type); for (i = 0; i < HMAC_BLOCK_SIZE; i++) { key[i] ^= 0x36; } - sha1_append(&sha1, key, HMAC_BLOCK_SIZE); - sha1_append(&sha1, (const guint8 *) payload, payload_len); - sha1_finish(&sha1, hash); + g_checksum_update(checksum, key, HMAC_BLOCK_SIZE); + g_checksum_update(checksum, (const guint8 *) payload, payload_len); + g_checksum_get_digest(checksum, hash, &hash_len); + g_checksum_free(checksum); /* Final result: H(K XOR 0x5C, inner stuff) */ - sha1_init(&sha1); + checksum = g_checksum_new(checksum_type); for (i = 0; i < HMAC_BLOCK_SIZE; i++) { key[i] ^= 0x36 ^ 0x5c; } - sha1_append(&sha1, key, HMAC_BLOCK_SIZE); - sha1_append(&sha1, hash, SHA1_HASH_SIZE); - sha1_finish(&sha1, digest); + g_checksum_update(checksum, key, HMAC_BLOCK_SIZE); + g_checksum_update(checksum, hash, hash_len); + g_checksum_get_digest(checksum, *digest, &hash_len); + g_checksum_free(checksum); + + g_free(hash); } +void sha1_hmac(const char *key_, size_t key_len, const char *payload, size_t payload_len, guint8 digest[SHA1_HASH_SIZE]) +{ + b_hmac(G_CHECKSUM_SHA1, key_, key_len, payload, payload_len, &digest); +} + + /* I think this follows the scheme described on: http://en.wikipedia.org/wiki/Universally_unique_identifier#Version_4_.28random.29 My random data comes from a SHA1 generator but hey, it's random enough for diff --git a/protocols/jabber/iq.c b/protocols/jabber/iq.c index 2fa418fe..8518439e 100644 --- a/protocols/jabber/iq.c +++ b/protocols/jabber/iq.c @@ -1058,3 +1058,62 @@ static xt_status jabber_iq_carbons_response(struct im_connection *ic, return XT_HANDLED; } + +xt_status jabber_iq_disco_muc_response(struct im_connection *ic, struct xt_node *node, struct xt_node *orig); + +int jabber_iq_disco_muc(struct im_connection *ic, const char *muc_server) +{ + struct xt_node *node; + int st; + + node = xt_new_node("query", NULL, NULL); + xt_add_attr(node, "xmlns", XMLNS_DISCO_ITEMS); + node = jabber_make_packet("iq", "get", (char *) muc_server, node); + + jabber_cache_add(ic, node, jabber_iq_disco_muc_response); + st = jabber_write_packet(ic, node); + + return st; +} + +xt_status jabber_iq_disco_muc_response(struct im_connection *ic, struct xt_node *node, struct xt_node *orig) +{ + struct xt_node *query, *c; + struct jabber_error *err; + GSList *rooms = NULL; + + if ((err = jabber_error_parse(xt_find_node(node->children, "error"), XMLNS_STANZA_ERROR))) { + imcb_error(ic, "The server replied with an error: %s%s%s", + err->code, err->text ? ": " : "", err->text ? err->text : ""); + jabber_error_free(err); + return XT_HANDLED; + } + + if (!(query = xt_find_node(node->children, "query"))) { + imcb_error(ic, "Received incomplete MUC list reply"); + return XT_HANDLED; + } + + c = query->children; + while ((c = xt_find_node(c, "item"))) { + char *jid = xt_find_attr(c, "jid"); + + if (!jid || !strchr(jid, '@')) { + c = c->next; + continue; + } + + bee_chat_info_t *ci = g_new(bee_chat_info_t, 1); + ci->title = g_strdup(xt_find_attr(c, "jid")); + ci->topic = g_strdup(xt_find_attr(c, "name")); + rooms = g_slist_prepend(rooms, ci); + + c = c->next; + } + + imcb_chat_list_free(ic); + ic->chatlist = g_slist_reverse(rooms); + imcb_chat_list_finish(ic); + + return XT_HANDLED; +} diff --git a/protocols/jabber/jabber.c b/protocols/jabber/jabber.c index 05bd9cf9..73bc77ea 100644 --- a/protocols/jabber/jabber.c +++ b/protocols/jabber/jabber.c @@ -323,6 +323,8 @@ static void jabber_logout(struct im_connection *ic) { struct jabber_data *jd = ic->proto_data; + imcb_chat_list_free(ic); + while (jd->filetransfers) { imcb_file_canceled(ic, (( struct jabber_transfer *) jd->filetransfers->data)->ft, "Logging out"); } @@ -598,6 +600,21 @@ static struct groupchat *jabber_chat_with_(struct im_connection *ic, char *who) return jabber_chat_with(ic, who); } +static void jabber_chat_list_(struct im_connection *ic, const char *server) +{ + struct jabber_data *jd = ic->proto_data; + + if (server && *server) { + jabber_iq_disco_muc(ic, server); + } else if (jd->muc_host && *jd->muc_host) { + jabber_iq_disco_muc(ic, jd->muc_host); + } else { + /* throw an error here, don't query conference.[server] directly. + * for things like jabber.org it gets you 18000 results of garbage */ + imcb_error(ic, "Please specify a server name such as `conference.%s'", jd->server); + } +} + static void jabber_chat_msg_(struct groupchat *c, char *message, int flags) { if (c && message) { @@ -773,6 +790,7 @@ void jabber_initmodule() ret->chat_leave = jabber_chat_leave_; ret->chat_join = jabber_chat_join_; ret->chat_with = jabber_chat_with_; + ret->chat_list = jabber_chat_list_; ret->chat_add_settings = jabber_chat_add_settings; ret->chat_free_settings = jabber_chat_free_settings; ret->keepalive = jabber_keepalive; diff --git a/protocols/jabber/jabber.h b/protocols/jabber/jabber.h index 0804ebd7..a09bd060 100644 --- a/protocols/jabber/jabber.h +++ b/protocols/jabber/jabber.h @@ -280,6 +280,7 @@ xt_status jabber_iq_query_features(struct im_connection *ic, char *bare_jid); xt_status jabber_iq_query_server(struct im_connection *ic, char *jid, char *xmlns); void jabber_iq_version_send(struct im_connection *ic, struct jabber_buddy *bud, void *data); int jabber_iq_disco_server(struct im_connection *ic); +int jabber_iq_disco_muc(struct im_connection *ic, const char *muc_server); /* si.c */ int jabber_si_handle_request(struct im_connection *ic, struct xt_node *node, struct xt_node *sinode); diff --git a/root_commands.c b/root_commands.c index 62fe1f39..6ec2f179 100644 --- a/root_commands.c +++ b/root_commands.c @@ -1352,13 +1352,18 @@ static void cmd_chat(irc_t *irc, char **cmd) } } +/* some arbitrary numbers */ +#define CHAT_TITLE_LEN_MIN 20 +#define CHAT_TITLE_LEN_MAX 100 + void cmd_chat_list_finish(struct im_connection *ic) { account_t *acc = ic->acc; bee_chat_info_t *ci; - char *hformat, *iformat, *topic; + char *hformat, *iformat, *topic, *padded; GSList *l; guint i = 0; + long title_len, new_len; irc_t *irc = ic->bee->ui_data; if (ic->chatlist == NULL) { @@ -1366,20 +1371,40 @@ void cmd_chat_list_finish(struct im_connection *ic) return; } + /* find a reasonable width for the table */ + title_len = CHAT_TITLE_LEN_MIN; + + for (l = ic->chatlist; l; l = l->next) { + ci = l->data; + new_len = g_utf8_strlen(ci->title, -1); + + if (new_len >= CHAT_TITLE_LEN_MAX) { + title_len = CHAT_TITLE_LEN_MAX; + break; + } else if (title_len < new_len) { + title_len = new_len; + } + } + if (strchr(irc->umode, 'b') != NULL) { hformat = "%s\t%s\t%s"; iformat = "%u\t%s\t%s"; } else { - hformat = "%s %-20s %s"; - iformat = "%5u %-20.20s %s"; + hformat = "%s %s %s"; + iformat = "%5u %s %s"; } - irc_rootmsg(irc, hformat, "Index", "Title", "Topic"); + padded = str_pad_and_truncate("Title", title_len, NULL); + irc_rootmsg(irc, hformat, "Index", padded, "Topic"); + g_free(padded); for (l = ic->chatlist; l; l = l->next) { ci = l->data; topic = ci->topic ? ci->topic : ""; - irc_rootmsg(irc, iformat, ++i, ci->title, topic); + + padded = str_pad_and_truncate(ci->title, title_len, "[...]"); + irc_rootmsg(irc, iformat, ++i, padded, topic); + g_free(padded); } irc_rootmsg(irc, "%u %s chatrooms", i, acc->tag); |