aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--lib/misc.c26
-rw-r--r--lib/misc.h1
-rw-r--r--lib/sha1.c50
-rw-r--r--protocols/jabber/iq.c59
-rw-r--r--protocols/jabber/jabber.c18
-rw-r--r--protocols/jabber/jabber.h1
-rw-r--r--root_commands.c35
7 files changed, 170 insertions, 20 deletions
diff --git a/lib/misc.c b/lib/misc.c
index 1c93f65a..b4c02ed1 100644
--- a/lib/misc.c
+++ b/lib/misc.c
@@ -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);
+ }
+}
diff --git a/lib/misc.h b/lib/misc.h
index 73f71e85..581e7a76 100644
--- a/lib/misc.h
+++ b/lib/misc.h
@@ -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
diff --git a/lib/sha1.c b/lib/sha1.c
index 7f442205..bd6a8592 100644
--- a/lib/sha1.c
+++ b/lib/sha1.c
@@ -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);