diff
Makefile | 48 ++++
README.md | 12 +
include/barev.h | 92 +++++++
include/barev_avatar.h | 19 ++
include/barev_config.h | 29 ++
include/barev_ft.h | 57 ++++
include/barev_internal.h | 79 ++++++
include/barev_net.h | 15 ++
include/barev_sha1.h | 8 +
include/barev_xml.h | 21 ++
include/sha1.h | 52 ++++
src/barev_avatar.c | 101 +++++++
src/barev_client.c | 672 +++++++++++++++++++++++++++++++++++++++++++++++
src/barev_config.c | 111 ++++++++
src/barev_ft.c | 335 +++++++++++++++++++++++
src/barev_net.c | 127 +++++++++
src/barev_sha1.c | 19 ++
src/barev_xml.c | 259 ++++++++++++++++++
src/sha1.c | 302 +++++++++++++++++++++
19 files changed, 2358 insertions(+)
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..08cb6cf
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,48 @@
+CC = cc
+AR = ar
+RANLIB = ranlib
+CFLAGS = -std=c89 -pedantic -Wall -Wextra -O2
+PREFIX = /usr/local
+DESTDIR =
+
+LIB = libbarevc.a
+
+LIB_SRCS = \
+ src/barev_client.c \
+ src/barev_xml.c \
+ src/barev_net.c \
+ src/barev_config.c \
+ src/barev_avatar.c \
+ src/sha1.c \
+ src/barev_sha1.c \
+ src/barev_ft.c
+
+LIB_OBJS = \
+ src/barev_client.o \
+ src/barev_xml.o \
+ src/barev_net.o \
+ src/barev_config.o \
+ src/barev_avatar.o \
+ src/sha1.o \
+ src/barev_sha1.o \
+ src/barev_ft.o
+
+all: library
+
+library: $(LIB)
+
+$(LIB): $(LIB_OBJS)
+ $(AR) rcs $(LIB) $(LIB_OBJS)
+ $(RANLIB) $(LIB)
+
+.c.o:
+ $(CC) $(CFLAGS) -Iinclude -c $< -o $@
+
+install: library
+ mkdir -p $(DESTDIR)$(PREFIX)/lib
+ mkdir -p $(DESTDIR)$(PREFIX)/include
+ cp $(LIB) $(DESTDIR)$(PREFIX)/lib/$(LIB)
+ cp include/*.h $(DESTDIR)$(PREFIX)/include/
+
+clean:
+ rm -f $(LIB_OBJS) $(LIB)
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..9b533f6
--- /dev/null
+++ b/README.md
@@ -0,0 +1,12 @@
+barev implementation in C.
+
+build with make
+
+that will give you `libbarevc.a`
+
+install it with `doas make install`
+
+installs to
+
+- `$(PREFIX)/lib/libbarevc.a`
+- `$(PREFIX)/include/*.h`
diff --git a/include/barev.h b/include/barev.h
new file mode 100644
index 0000000..5714566
--- /dev/null
+++ b/include/barev.h
@@ -0,0 +1,92 @@
+#ifndef BAREV_H
+#define BAREV_H
+
+/*
+ * barev.h - Pure C89 Barev client public API
+ * Compatibility target: Pascal TBarevClient + barev_c_api exports.
+ */
+
+#include <stddef.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define BAREV_VERSION "0.1"
+#define BAREV_DEFAULT_PORT 1337
+
+typedef struct barev_client barev_client_t;
+typedef struct barev_buddy barev_buddy_t;
+
+typedef enum {
+ BAREV_STATUS_OFFLINE = 0,
+ BAREV_STATUS_AVAILABLE = 1,
+ BAREV_STATUS_AWAY = 2,
+ BAREV_STATUS_XA = 3,
+ BAREV_STATUS_DND = 4
+} barev_status_t;
+
+typedef enum {
+ BAREV_CONN_DISCONNECTED = 0,
+ BAREV_CONN_CONNECTING = 1,
+ BAREV_CONN_STREAM_INIT = 2,
+ BAREV_CONN_AUTHENTICATED = 3,
+ BAREV_CONN_ONLINE = 4
+} barev_conn_state_t;
+
+typedef void (*barev_log_cb)(const char *level, const char *msg, void *userdata);
+typedef void (*barev_typing_cb)(barev_buddy_t *buddy, int is_typing, void *userdata);
+typedef void (*barev_buddy_status_cb)(barev_buddy_t *buddy, int old_status, int new_status, void *userdata);
+typedef void (*barev_message_cb)(barev_buddy_t *buddy, const char *msg, void *userdata);
+typedef void (*barev_conn_state_cb)(barev_buddy_t *buddy, int state, void *userdata);
+typedef void (*barev_ft_offer_cb)(barev_buddy_t *buddy, const char *sid, const char *file_name, long file_size, void *userdata);
+typedef void (*barev_ft_progress_cb)(barev_buddy_t *buddy, const char *sid, long done, long total, void *userdata);
+typedef void (*barev_ft_complete_cb)(barev_buddy_t *buddy, const char *sid, const char *path, void *userdata);
+typedef void (*barev_ft_error_cb)(barev_buddy_t *buddy, const char *sid, const char *err, void *userdata);
+
+barev_client_t *barev_client_new(const char *nick, const char *my_ipv6, unsigned short port);
+void barev_client_free(barev_client_t *client);
+int barev_client_start(barev_client_t *client);
+void barev_client_stop(barev_client_t *client);
+void barev_client_process(barev_client_t *client);
+
+const char *barev_client_myjid(const barev_client_t *client);
+
+barev_buddy_t *barev_client_add_buddy(barev_client_t *client, const char *buddy_nick, const char *buddy_ipv6, unsigned short port);
+int barev_client_remove_buddy(barev_client_t *client, const char *buddy_jid);
+barev_buddy_t *barev_client_find_buddy(barev_client_t *client, const char *buddy_jid);
+
+int barev_client_connect(barev_client_t *client, const char *buddy_jid);
+int barev_client_send_message(barev_client_t *client, const char *buddy_jid, const char *msg);
+int barev_client_send_presence(barev_client_t *client, int status, const char *status_msg);
+int barev_client_send_typing(barev_client_t *client, const char *buddy_jid);
+int barev_client_send_paused(barev_client_t *client, const char *buddy_jid);
+int barev_client_send_file_offer(barev_client_t *client, const char *buddy_jid,
+ const char *file_name, long file_size);
+int barev_client_accept_file_offer(barev_client_t *client, const char *sid);
+int barev_client_accept_file_offer_as(barev_client_t *client, const char *sid, const char *save_path);
+int barev_client_reject_file_offer(barev_client_t *client, const char *sid);
+
+void barev_set_userdata(barev_client_t *client, void *userdata);
+void barev_set_log_cb(barev_client_t *client, barev_log_cb cb);
+void barev_set_typing_cb(barev_client_t *client, barev_typing_cb cb);
+void barev_set_buddy_status_cb(barev_client_t *client, barev_buddy_status_cb cb);
+void barev_set_message_cb(barev_client_t *client, barev_message_cb cb);
+void barev_set_conn_state_cb(barev_client_t *client, barev_conn_state_cb cb);
+void barev_set_ft_offer_cb(barev_client_t *client, barev_ft_offer_cb cb);
+void barev_set_ft_progress_cb(barev_client_t *client, barev_ft_progress_cb cb);
+void barev_set_ft_complete_cb(barev_client_t *client, barev_ft_complete_cb cb);
+void barev_set_ft_error_cb(barev_client_t *client, barev_ft_error_cb cb);
+
+const char *barev_buddy_get_jid(const barev_buddy_t *buddy);
+const char *barev_buddy_get_nick(const barev_buddy_t *buddy);
+const char *barev_buddy_get_ipv6(const barev_buddy_t *buddy);
+unsigned short barev_buddy_get_port(const barev_buddy_t *buddy);
+int barev_buddy_get_status(const barev_buddy_t *buddy);
+const char *barev_buddy_get_status_message(const barev_buddy_t *buddy);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/barev_avatar.h b/include/barev_avatar.h
new file mode 100644
index 0000000..84abc8c
--- /dev/null
+++ b/include/barev_avatar.h
@@ -0,0 +1,19 @@
+#ifndef BAREV_AVATAR_H
+#define BAREV_AVATAR_H
+
+#include <stddef.h>
+
+typedef struct {
+ char my_avatar_path[256];
+ char my_avatar_hash[41];
+ char my_avatar_mime[64];
+ char *my_avatar_b64;
+} barev_avatar_mgr_t;
+
+void barev_avatar_init(barev_avatar_mgr_t *m);
+void barev_avatar_free(barev_avatar_mgr_t *m);
+int barev_avatar_load_my(barev_avatar_mgr_t *m, const char *path);
+void barev_avatar_clear_my(barev_avatar_mgr_t *m);
+int barev_avatar_generate_vcard(const barev_avatar_mgr_t *m, char *out, size_t out_n);
+
+#endif
diff --git a/include/barev_config.h b/include/barev_config.h
new file mode 100644
index 0000000..37b122f
--- /dev/null
+++ b/include/barev_config.h
@@ -0,0 +1,29 @@
+#ifndef BAREV_CONFIG_H
+#define BAREV_CONFIG_H
+
+#include <stddef.h>
+
+#define BAREV_CFG_MAX_CONTACTS 256
+
+typedef struct {
+ char nick[64];
+ char ipv6[128];
+ unsigned short port;
+ char avatar_path[256];
+} barev_config_contact_t;
+
+typedef struct {
+ char user_nick[64];
+ char user_ipv6[128];
+ unsigned short user_port;
+ char user_avatar_path[256];
+ barev_config_contact_t contacts[BAREV_CFG_MAX_CONTACTS];
+ size_t contact_count;
+} barev_config_t;
+
+void barev_config_init(barev_config_t *cfg);
+int barev_config_load(const char *path, barev_config_t *cfg);
+int barev_config_save(const char *path, const barev_config_t *cfg);
+int barev_config_has_user(const barev_config_t *cfg);
+
+#endif
diff --git a/include/barev_ft.h b/include/barev_ft.h
new file mode 100644
index 0000000..b64869d
--- /dev/null
+++ b/include/barev_ft.h
@@ -0,0 +1,57 @@
+#ifndef BAREV_FT_H
+#define BAREV_FT_H
+
+/* File transfer foundations (C89), compatible direction/state model. */
+
+enum barev_ft_direction {
+ BAREV_FT_SEND = 0,
+ BAREV_FT_RECV = 1
+};
+
+enum barev_ft_state {
+ BAREV_FTS_IDLE = 0,
+ BAREV_FTS_OFFER_SENT,
+ BAREV_FTS_OFFER_RECV,
+ BAREV_FTS_ACCEPTED,
+ BAREV_FTS_STREAMHOST_SENT,
+ BAREV_FTS_STREAMHOST_RECV,
+ BAREV_FTS_TRANSFERRING,
+ BAREV_FTS_DONE,
+ BAREV_FTS_ERROR
+};
+
+int barev_ft_socks5_server_handshake(int sockfd);
+int barev_ft_socks5_client_handshake(int sockfd, const char *dstaddr40);
+int barev_ft_build_si_offer(const char *from_jid, const char *to_jid, const char *id,
+ const char *sid, const char *file_name, long file_size,
+ char *out, unsigned long out_n);
+int barev_ft_build_si_accept(const char *from_jid, const char *to_jid, const char *id,
+ char *out, unsigned long out_n);
+int barev_ft_build_si_reject(const char *from_jid, const char *to_jid, const char *id,
+ const char *code, char *out, unsigned long out_n);
+int barev_ft_build_bytestreams_query(const char *from_jid, const char *to_jid, const char *id,
+ const char *sid, const char *host, unsigned short port,
+ char *out, unsigned long out_n);
+int barev_ft_build_bytestreams_used(const char *from_jid, const char *to_jid, const char *id,
+ const char *sid, const char *host_jid,
+ char *out, unsigned long out_n);
+int barev_ft_is_si_offer(const char *xml);
+int barev_ft_is_bytestreams_query(const char *xml);
+
+int barev_ft_extract_attr(const char *xml, const char *attr, char *out, unsigned long out_n);
+int barev_ft_parse_si_offer(const char *xml,
+ char *sid, unsigned long sid_n,
+ char *name, unsigned long name_n,
+ long *size_out);
+int barev_ft_parse_streamhost(const char *xml,
+ char *sid, unsigned long sid_n,
+ char *host, unsigned long host_n,
+ unsigned short *port_out,
+ char *jid, unsigned long jid_n);
+int barev_ft_compute_dstaddr40(const char *sid, const char *initiator_jid, const char *target_jid,
+ char out40[41]);
+int barev_ft_recv_file_blocking(int sockfd, const char *save_path, long expected_size, long *bytes_done);
+int barev_ft_listen_range(unsigned short min_port, unsigned short max_port, unsigned short *bound_port);
+int barev_ft_send_file_blocking(int sockfd, const char *path, long *bytes_done);
+
+#endif
diff --git a/include/barev_internal.h b/include/barev_internal.h
new file mode 100644
index 0000000..90aa7cd
--- /dev/null
+++ b/include/barev_internal.h
@@ -0,0 +1,79 @@
+#ifndef BAREV_INTERNAL_H
+#define BAREV_INTERNAL_H
+
+#include "barev.h"
+#include "barev_avatar.h"
+#include <time.h>
+
+#define BAREV_MAX_NICK 63
+#define BAREV_MAX_IPV6 127
+#define BAREV_MAX_JID 255
+#define BAREV_MAX_STATUS_MSG 255
+#define BAREV_RECV_BUF 8192
+#define BAREV_MAX_PENDING_OFFERS 32
+#define BAREV_PING_INTERVAL 30
+#define BAREV_PING_TIMEOUT 10
+#define BAREV_MAX_PING_FAILURES 3
+
+typedef struct barev_buddy {
+ char jid[BAREV_MAX_JID + 1];
+ char nick[BAREV_MAX_NICK + 1];
+ char ipv6[BAREV_MAX_IPV6 + 1];
+ unsigned short port;
+ int status;
+ char status_msg[BAREV_MAX_STATUS_MSG + 1];
+
+ int sock;
+ int we_initiated;
+ int stream_sent;
+ int stream_recv;
+ int conn_state;
+ int ping_failures;
+ time_t last_activity;
+ time_t last_ping_sent;
+ unsigned int ping_seq;
+ char last_ping_id[32];
+ char recv_buf[BAREV_RECV_BUF];
+ size_t recv_len;
+} barev_buddy_impl_t;
+
+typedef struct barev_ft_offer {
+ int used;
+ int accepted;
+ int outgoing;
+ char sid[128];
+ char iq_id[128];
+ size_t buddy_index;
+ char file_name[256];
+ char save_path[512];
+ long file_size;
+} barev_ft_offer_t;
+
+struct barev_client {
+ char nick[BAREV_MAX_NICK + 1];
+ char my_ipv6[BAREV_MAX_IPV6 + 1];
+ unsigned short port;
+ char my_jid[BAREV_MAX_JID + 1];
+ int running;
+ int listen_fd;
+ barev_avatar_mgr_t avatar_mgr;
+ unsigned int ft_seq;
+ barev_ft_offer_t pending_offers[BAREV_MAX_PENDING_OFFERS];
+
+ barev_buddy_impl_t *buddies;
+ size_t buddy_count;
+ size_t buddy_cap;
+
+ void *userdata;
+ barev_log_cb on_log;
+ barev_typing_cb on_typing;
+ barev_buddy_status_cb on_buddy_status;
+ barev_message_cb on_message;
+ barev_conn_state_cb on_conn_state;
+ barev_ft_offer_cb on_ft_offer;
+ barev_ft_progress_cb on_ft_progress;
+ barev_ft_complete_cb on_ft_complete;
+ barev_ft_error_cb on_ft_error;
+};
+
+#endif
diff --git a/include/barev_net.h b/include/barev_net.h
new file mode 100644
index 0000000..1ad6b21
--- /dev/null
+++ b/include/barev_net.h
@@ -0,0 +1,15 @@
+#ifndef BAREV_NET_H
+#define BAREV_NET_H
+
+#include <stddef.h>
+
+int barev_net_set_nonblocking(int fd);
+int barev_net_listen_v6(unsigned short port);
+int barev_net_accept_v6(int listen_fd, char *addr_out, size_t addr_out_n, unsigned short *port_out);
+int barev_net_connect_v6(const char *ipv6, unsigned short port);
+int barev_net_is_readable(int fd, int timeout_ms);
+int barev_net_send_all(int fd, const char *buf, size_t n);
+int barev_net_recv_some(int fd, char *buf, size_t n);
+void barev_net_close(int fd);
+
+#endif
diff --git a/include/barev_sha1.h b/include/barev_sha1.h
new file mode 100644
index 0000000..2b03ca4
--- /dev/null
+++ b/include/barev_sha1.h
@@ -0,0 +1,8 @@
+#ifndef BAREV_SHA1_H
+#define BAREV_SHA1_H
+
+#include <stddef.h>
+
+void barev_sha1_hex(const unsigned char *data, size_t len, char out_hex40[41]);
+
+#endif
diff --git a/include/barev_xml.h b/include/barev_xml.h
new file mode 100644
index 0000000..1584575
--- /dev/null
+++ b/include/barev_xml.h
@@ -0,0 +1,21 @@
+#ifndef BAREV_XML_H
+#define BAREV_XML_H
+
+/* String-based XML helpers with zero external dependencies. */
+
+#include <stddef.h>
+
+int barev_xml_escape(const char *in, char *out, size_t out_size);
+int barev_build_stream_header(const char *from_jid, const char *to_jid, char *out, size_t out_size);
+int barev_build_stream_end(char *out, size_t out_size);
+int barev_build_presence(const char *to_jid, int status, const char *status_msg, char *out, size_t out_size);
+int barev_build_message(const char *from_jid, const char *to_jid, const char *body, char *out, size_t out_size);
+int barev_build_ping(const char *from_jid, const char *to_jid, const char *ping_id, char *out, size_t out_size);
+int barev_build_pong(const char *to_jid, const char *ping_id, char *out, size_t out_size);
+int barev_build_chatstate(const char *to_jid, const char *state_name, char *out, size_t out_size);
+int barev_build_vcard_get(const char *to_jid, const char *id, char *out, size_t out_size);
+int barev_build_vcard_result(const char *to_jid, const char *id, const char *vcard_inner, char *out, size_t out_size);
+int barev_extract_attribute(const char *xml, const char *attr_name, char *out, size_t out_size);
+int barev_extract_element_content(const char *xml, const char *element, char *out, size_t out_size);
+
+#endif
diff --git a/include/sha1.h b/include/sha1.h
new file mode 100644
index 0000000..f492009
--- /dev/null
+++ b/include/sha1.h
@@ -0,0 +1,52 @@
+#ifndef SHA1_H
+#define SHA1_H
+
+/*
+ SHA-1 in C
+ By Steve Reid <steve@edmweb.com>
+ 100% Public Domain
+ */
+
+#include "stdint.h"
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+typedef struct
+{
+ uint32_t state[5];
+ uint32_t count[2];
+ unsigned char buffer[64];
+} SHA1_CTX;
+
+void SHA1Transform(
+ uint32_t state[5],
+ const unsigned char buffer[64]
+ );
+
+void SHA1Init(
+ SHA1_CTX * context
+ );
+
+void SHA1Update(
+ SHA1_CTX * context,
+ const unsigned char *data,
+ uint32_t len
+ );
+
+void SHA1Final(
+ unsigned char digest[20],
+ SHA1_CTX * context
+ );
+
+void SHA1(
+ char *hash_out,
+ const char *str,
+ uint32_t len);
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* SHA1_H */
diff --git a/src/barev_avatar.c b/src/barev_avatar.c
new file mode 100644
index 0000000..292bd67
--- /dev/null
+++ b/src/barev_avatar.c
@@ -0,0 +1,101 @@
+#include "barev_avatar.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "barev_sha1.h"
+
+static const char b64tab[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+static void scpy(char *d, size_t n, const char *s) {
+ size_t i;
+ if (!d || n == 0) return;
+ if (!s) { d[0] = '\0'; return; }
+ for (i = 0; i + 1 < n && s[i]; ++i) d[i] = s[i];
+ d[i] = '\0';
+}
+
+static int detect_mime(const char *p, char *out, size_t n) {
+ const char *ext = strrchr(p, '.');
+ if (!ext) { scpy(out, n, "application/octet-stream"); return 1; }
+ if (strcmp(ext, ".png") == 0) scpy(out, n, "image/png");
+ else if (strcmp(ext, ".jpg") == 0 || strcmp(ext, ".jpeg") == 0) scpy(out, n, "image/jpeg");
+ else if (strcmp(ext, ".gif") == 0) scpy(out, n, "image/gif");
+ else scpy(out, n, "application/octet-stream");
+ return 1;
+}
+
+static char *b64_encode(const unsigned char *in, size_t n) {
+ size_t out_n = ((n + 2) / 3) * 4;
+ size_t i, o = 0;
+ char *out = (char *)malloc(out_n + 1);
+ if (!out) return 0;
+ for (i = 0; i < n; i += 3) {
+ unsigned a = in[i];
+ unsigned b = (i + 1 < n) ? in[i + 1] : 0;
+ unsigned c = (i + 2 < n) ? in[i + 2] : 0;
+ unsigned t = (a << 16) | (b << 8) | c;
+ out[o++] = b64tab[(t >> 18) & 63];
+ out[o++] = b64tab[(t >> 12) & 63];
+ out[o++] = (i + 1 < n) ? b64tab[(t >> 6) & 63] : '=';
+ out[o++] = (i + 2 < n) ? b64tab[t & 63] : '=';
+ }
+ out[o] = '\0';
+ return out;
+}
+
+void barev_avatar_init(barev_avatar_mgr_t *m) { if (m) memset(m, 0, sizeof(*m)); }
+
+void barev_avatar_free(barev_avatar_mgr_t *m) {
+ if (!m) return;
+ free(m->my_avatar_b64);
+ m->my_avatar_b64 = 0;
+}
+
+void barev_avatar_clear_my(barev_avatar_mgr_t *m) {
+ if (!m) return;
+ free(m->my_avatar_b64);
+ m->my_avatar_b64 = 0;
+ m->my_avatar_path[0] = '\0';
+ m->my_avatar_hash[0] = '\0';
+ m->my_avatar_mime[0] = '\0';
+}
+
+int barev_avatar_load_my(barev_avatar_mgr_t *m, const char *path) {
+ FILE *f;
+ unsigned char *buf;
+ long n;
+ if (!m || !path) return 0;
+ f = fopen(path, "rb");
+ if (!f) return 0;
+ if (fseek(f, 0, SEEK_END) != 0) { fclose(f); return 0; }
+ n = ftell(f);
+ if (n < 0) { fclose(f); return 0; }
+ if (fseek(f, 0, SEEK_SET) != 0) { fclose(f); return 0; }
+ buf = (unsigned char *)malloc((size_t)n);
+ if (!buf) { fclose(f); return 0; }
+ if ((long)fread(buf, 1, (size_t)n, f) != n) { free(buf); fclose(f); return 0; }
+ fclose(f);
+
+ barev_avatar_clear_my(m);
+ m->my_avatar_b64 = b64_encode(buf, (size_t)n);
+ if (!m->my_avatar_b64) { free(buf); return 0; }
+ detect_mime(path, m->my_avatar_mime, sizeof(m->my_avatar_mime));
+ scpy(m->my_avatar_path, sizeof(m->my_avatar_path), path);
+ barev_sha1_hex((const unsigned char *)m->my_avatar_b64, strlen(m->my_avatar_b64), m->my_avatar_hash);
+ free(buf);
+ return 1;
+}
+
+int barev_avatar_generate_vcard(const barev_avatar_mgr_t *m, char *out, size_t out_n) {
+ int w;
+ if (!m || !out || out_n == 0) return 0;
+ if (!m->my_avatar_b64 || !m->my_avatar_b64[0]) {
+ w = snprintf(out, out_n, "<vCard xmlns=\"vcard-temp\"></vCard>");
+ } else {
+ w = snprintf(out, out_n,
+ "<vCard xmlns=\"vcard-temp\"><PHOTO><TYPE>%s</TYPE><BINVAL>%s</BINVAL></PHOTO></vCard>",
+ m->my_avatar_mime, m->my_avatar_b64);
+ }
+ return (w > 0 && (size_t)w < out_n) ? 1 : 0;
+}
diff --git a/src/barev_client.c b/src/barev_client.c
new file mode 100644
index 0000000..2ce1729
--- /dev/null
+++ b/src/barev_client.c
@@ -0,0 +1,672 @@
+#include "barev_internal.h"
+#include "barev_net.h"
+#include "barev_xml.h"
+#include "barev_avatar.h"
+#include "barev_ft.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+
+static void barev_strcpy0(char *dst, size_t dst_n, const char *src) {
+ size_t i;
+ if (dst_n == 0) return;
+ if (!src) { dst[0] = '\0'; return; }
+ for (i = 0; i + 1 < dst_n && src[i] != '\0'; ++i) dst[i] = src[i];
+ dst[i] = '\0';
+}
+
+static void log_msg(struct barev_client *c, const char *lvl, const char *msg) {
+ if (c && c->on_log) c->on_log(lvl, msg, c->userdata);
+}
+
+static void emit_conn(struct barev_client *c, barev_buddy_impl_t *b, int st) {
+ b->conn_state = st;
+ if (c && c->on_conn_state) c->on_conn_state((barev_buddy_t *)b, st, c->userdata);
+}
+
+static void emit_buddy_status(struct barev_client *c, barev_buddy_impl_t *b, int old_s, int new_s) {
+ if (c && c->on_buddy_status) c->on_buddy_status((barev_buddy_t *)b, old_s, new_s, c->userdata);
+}
+
+static void barev_join_jid(char *out, size_t out_n, const char *nick, const char *ipv6) {
+ size_t nlen, ilen, i;
+ if (!out || out_n == 0) return;
+ out[0] = '\0';
+ if (!nick || !ipv6) return;
+ nlen = strlen(nick); ilen = strlen(ipv6);
+ if (nlen + 1 + ilen + 1 > out_n) return;
+ for (i = 0; i < nlen; ++i) out[i] = nick[i];
+ out[nlen] = '@';
+ for (i = 0; i < ilen; ++i) out[nlen + 1 + i] = ipv6[i];
+ out[nlen + 1 + ilen] = '\0';
+}
+
+static int barev_same_jid(const char *a, const char *b) { return (a && b && strcmp(a, b) == 0) ? 1 : 0; }
+
+static int barev_ensure_buddy_cap(struct barev_client *client) {
+ barev_buddy_impl_t *nptr;
+ size_t ncap;
+ if (client->buddy_count < client->buddy_cap) return 1;
+ ncap = client->buddy_cap == 0 ? 8 : client->buddy_cap * 2;
+ nptr = (barev_buddy_impl_t *)realloc(client->buddies, ncap * sizeof(*nptr));
+ if (!nptr) return 0;
+ client->buddies = nptr;
+ client->buddy_cap = ncap;
+ return 1;
+}
+
+static barev_buddy_impl_t *find_buddy_by_ip(struct barev_client *c, const char *ip) {
+ size_t i;
+ for (i = 0; i < c->buddy_count; ++i) if (strcmp(c->buddies[i].ipv6, ip) == 0) return &c->buddies[i];
+ return 0;
+}
+
+static size_t buddy_index_of(struct barev_client *c, barev_buddy_impl_t *b) {
+ size_t i;
+ for (i = 0; i < c->buddy_count; ++i) if (&c->buddies[i] == b) return i;
+ return (size_t)-1;
+}
+
+static barev_ft_offer_t *find_offer_by_sid(struct barev_client *c, const char *sid) {
+ size_t i;
+ for (i = 0; i < BAREV_MAX_PENDING_OFFERS; ++i) {
+ if (c->pending_offers[i].used && strcmp(c->pending_offers[i].sid, sid) == 0) return &c->pending_offers[i];
+ }
+ return 0;
+}
+
+static barev_ft_offer_t *find_offer_by_iqid(struct barev_client *c, const char *iqid) {
+ size_t i;
+ for (i = 0; i < BAREV_MAX_PENDING_OFFERS; ++i) {
+ if (c->pending_offers[i].used && strcmp(c->pending_offers[i].iq_id, iqid) == 0) return &c->pending_offers[i];
+ }
+ return 0;
+}
+
+static barev_ft_offer_t *alloc_offer_slot(struct barev_client *c) {
+ size_t i;
+ for (i = 0; i < BAREV_MAX_PENDING_OFFERS; ++i) {
+ if (!c->pending_offers[i].used) {
+ memset(&c->pending_offers[i], 0, sizeof(c->pending_offers[i]));
+ c->pending_offers[i].used = 1;
+ return &c->pending_offers[i];
+ }
+ }
+ return 0;
+}
+
+static int send_stream_header(struct barev_client *c, barev_buddy_impl_t *b) {
+ char out[2048];
+ if (!barev_build_stream_header(c->my_jid, b->jid, out, sizeof(out))) return 0;
+ if (barev_net_send_all(b->sock, out, strlen(out)) < 0) return 0;
+ b->stream_sent = 1;
+ return 1;
+}
+
+static int send_ping(struct barev_client *c, barev_buddy_impl_t *b, time_t now_ts) {
+ char out[1024];
+ unsigned int next_seq;
+ next_seq = b->ping_seq + 1;
+ b->ping_seq = next_seq;
+ b->last_ping_id[0] = '\0';
+ (void)sprintf(b->last_ping_id, "ping-%u", next_seq);
+ if (!barev_build_ping(c->my_jid, b->jid, b->last_ping_id, out, sizeof(out))) return 0;
+ if (barev_net_send_all(b->sock, out, strlen(out)) < 0) return 0;
+ b->last_ping_sent = now_ts;
+ return 1;
+}
+
+static void close_buddy_sock(barev_buddy_impl_t *b) {
+ if (b->sock >= 0) {
+ barev_net_close(b->sock);
+ b->sock = -1;
+ }
+ b->stream_sent = 0;
+ b->stream_recv = 0;
+ b->conn_state = BAREV_CONN_DISCONNECTED;
+ b->ping_failures = 0;
+ b->last_ping_sent = 0;
+ b->last_ping_id[0] = '\0';
+ b->recv_len = 0;
+ b->recv_buf[0] = '\0';
+}
+
+static void process_stanza(struct barev_client *c, barev_buddy_impl_t *b, const char *xml) {
+ char body[2048], id[256], pong[2048], vcard[1024];
+ if (strstr(xml, "<stream:stream") != 0) {
+ b->stream_recv = 1;
+ emit_conn(c, b, BAREV_CONN_STREAM_INIT);
+ if (!b->stream_sent) send_stream_header(c, b);
+ emit_conn(c, b, BAREV_CONN_AUTHENTICATED);
+ return;
+ }
+ if (strstr(xml, "<presence") != 0) {
+ int old_s = b->status;
+ b->status = BAREV_STATUS_AVAILABLE;
+ emit_buddy_status(c, b, old_s, b->status);
+ emit_conn(c, b, BAREV_CONN_ONLINE);
+ return;
+ }
+ if (strstr(xml, "<message") != 0) {
+ if (strstr(xml, "http://jabber.org/protocol/chatstates") != 0) {
+ if (c->on_typing) {
+ if (strstr(xml, "<composing ") != 0 || strstr(xml, "<composing xmlns=") != 0)
+ c->on_typing((barev_buddy_t *)b, 1, c->userdata);
+ else
+ c->on_typing((barev_buddy_t *)b, 0, c->userdata);
+ }
+ return;
+ }
+ if (barev_extract_element_content(xml, "body", body, sizeof(body))) {
+ if (c->on_message) c->on_message((barev_buddy_t *)b, body, c->userdata);
+ }
+ return;
+ }
+ if (strstr(xml, "<iq") != 0) {
+ if (strstr(xml, "urn:xmpp:ping") != 0) {
+ if (barev_extract_attribute(xml, "id", id, sizeof(id))) {
+ if (barev_build_pong(b->jid, id, pong, sizeof(pong))) {
+ (void)barev_net_send_all(b->sock, pong, strlen(pong));
+ }
+ }
+ return;
+ }
+ if (strstr(xml, "vcard-temp") != 0) {
+ if ((strstr(xml, "type=\"get\"") != 0 || strstr(xml, "type='get'") != 0) &&
+ barev_extract_attribute(xml, "id", id, sizeof(id))) {
+ if (!barev_avatar_generate_vcard(&c->avatar_mgr, vcard, sizeof(vcard))) {
+ strcpy(vcard, "<vCard xmlns=\"vcard-temp\"/>");
+ }
+ if (barev_build_vcard_result(b->jid, id, vcard, pong, sizeof(pong))) {
+ (void)barev_net_send_all(b->sock, pong, strlen(pong));
+ }
+ }
+ return;
+ }
+ if (barev_ft_is_si_offer(xml)) {
+ char sid[128], fn[256], rej[2048];
+ long fsz;
+ if (barev_ft_parse_si_offer(xml, sid, sizeof(sid), fn, sizeof(fn), &fsz)) {
+ barev_ft_offer_t *o = alloc_offer_slot(c);
+ if (o) {
+ strcpy(o->sid, sid);
+ strcpy(o->file_name, fn);
+ o->file_size = fsz;
+ o->buddy_index = buddy_index_of(c, b);
+ if (barev_extract_attribute(xml, "id", id, sizeof(id))) strcpy(o->iq_id, id);
+ }
+ if (c->on_log) {
+ char msg[512];
+ sprintf(msg, "FT offer from %s sid=%s file=%s size=%ld", b->jid, sid, fn, fsz);
+ c->on_log("INFO", msg, c->userdata);
+ }
+ if (c->on_ft_offer) c->on_ft_offer((barev_buddy_t *)b, sid, fn, fsz, c->userdata);
+ }
+ (void)rej;
+ return;
+ }
+ if (barev_ft_is_bytestreams_query(xml)) {
+ char sid[128], host[256], host_jid[256], used[2048];
+ unsigned short port;
+ sid[0] = '\0';
+ if (barev_ft_parse_streamhost(xml, sid, sizeof(sid), host, sizeof(host), &port, host_jid, sizeof(host_jid))) {
+ if (c->on_log) {
+ char msg[512];
+ sprintf(msg, "FT bytestream query sid=%s host=%s port=%u jid=%s", sid, host, (unsigned)port, host_jid);
+ c->on_log("INFO", msg, c->userdata);
+ }
+ }
+ if (sid[0]) {
+ barev_ft_offer_t *o = find_offer_by_sid(c, sid);
+ if (o && o->accepted) {
+ int dsock;
+ char dst40[41];
+ long got = 0;
+ const char *save_path = o->save_path[0] ? o->save_path : o->file_name;
+ dsock = barev_net_connect_v6(host, port);
+ if (dsock >= 0 && barev_ft_compute_dstaddr40(sid, host_jid, c->my_jid, dst40)) {
+ if (barev_ft_socks5_client_handshake(dsock, dst40)) {
+ if (barev_ft_recv_file_blocking(dsock, save_path, o->file_size, &got)) {
+ if (c->on_ft_progress) c->on_ft_progress((barev_buddy_t *)b, o->sid, got, o->file_size, c->userdata);
+ if (c->on_ft_complete) c->on_ft_complete((barev_buddy_t *)b, o->sid, save_path, c->userdata);
+ if (c->on_log) c->on_log("INFO", "FT receive complete", c->userdata);
+ } else {
+ if (c->on_ft_error) c->on_ft_error((barev_buddy_t *)b, o->sid, "receive failed", c->userdata);
+ if (c->on_log) c->on_log("ERROR", "FT receive failed", c->userdata);
+ }
+ }
+ barev_net_close(dsock);
+ }
+ o->used = 0;
+ }
+ }
+ if (barev_extract_attribute(xml, "id", id, sizeof(id))) {
+ if (barev_ft_build_bytestreams_used(c->my_jid, b->jid, id, sid[0] ? sid : "sid", c->my_jid, used, sizeof(used))) {
+ (void)barev_net_send_all(b->sock, used, strlen(used));
+ }
+ }
+ return;
+ }
+ if (strstr(xml, "type=\"result\"") != 0 || strstr(xml, "type='result'") != 0) {
+ if (barev_extract_attribute(xml, "id", id, sizeof(id))) {
+ /* FT: when our SI offer is accepted, send bytestream negotiation query. */
+ if (strncmp(id, "ft-offer-", 9) == 0) {
+ char sid[128], bsid[128];
+ char q[3072];
+ barev_ft_offer_t *o = find_offer_by_iqid(c, id);
+ strcpy(sid, id);
+ sprintf(bsid, "ft-bs-%u", ++c->ft_seq);
+ if (barev_ft_build_bytestreams_query(c->my_jid, b->jid, bsid, sid, c->my_ipv6, 50000, q, sizeof(q))) {
+ (void)barev_net_send_all(b->sock, q, strlen(q));
+ if (c->on_log) c->on_log("INFO", "FT SI accepted; sent bytestream query", c->userdata);
+ if (o) strcpy(o->iq_id, bsid);
+ }
+ } else if (strncmp(id, "ft-bs-", 6) == 0) {
+ barev_ft_offer_t *o = find_offer_by_iqid(c, id);
+ if (o && o->outgoing) {
+ unsigned short lport = 0;
+ int ls = barev_ft_listen_range(50000, 50049, &lport);
+ if (ls >= 0) {
+ int ds = accept(ls, 0, 0);
+ if (ds >= 0) {
+ long sent = 0;
+ if (barev_ft_socks5_server_handshake(ds)) {
+ if (barev_ft_send_file_blocking(ds, o->file_name, &sent)) {
+ if (c->on_ft_progress) c->on_ft_progress((barev_buddy_t *)b, o->sid, sent, o->file_size, c->userdata);
+ if (c->on_ft_complete) c->on_ft_complete((barev_buddy_t *)b, o->sid, o->file_name, c->userdata);
+ if (c->on_log) c->on_log("INFO", "FT send complete", c->userdata);
+ } else {
+ if (c->on_ft_error) c->on_ft_error((barev_buddy_t *)b, o->sid, "send failed", c->userdata);
+ if (c->on_log) c->on_log("ERROR", "FT send failed", c->userdata);
+ }
+ }
+ close(ds);
+ }
+ close(ls);
+ }
+ o->used = 0;
+ }
+ }
+ if (b->last_ping_id[0] != '\0' && strcmp(id, b->last_ping_id) == 0) {
+ b->last_ping_sent = 0;
+ b->ping_failures = 0;
+ }
+ }
+ return;
+ }
+ }
+}
+
+static void process_recv_buffer(struct barev_client *c, barev_buddy_impl_t *b) {
+ char *start;
+ char *end;
+ size_t len;
+ char stanza[4096];
+
+ if (b->recv_len == 0) return;
+ b->recv_buf[b->recv_len] = '\0';
+
+ if (strstr(b->recv_buf, "<stream:stream") != 0) {
+ process_stanza(c, b, "<stream:stream>");
+ }
+
+ for (;;) {
+ start = strchr(b->recv_buf, '<');
+ if (!start) break;
+
+ if (strncmp(start, "<message", 8) == 0) {
+ end = strstr(start, "</message>");
+ if (!end) break;
+ end += 10;
+ } else if (strncmp(start, "<presence", 9) == 0) {
+ end = strstr(start, "</presence>");
+ if (end) end += 11;
+ else {
+ end = strstr(start, "/>");
+ if (!end) break;
+ end += 2;
+ }
+ } else if (strncmp(start, "<iq", 3) == 0) {
+ end = strstr(start, "</iq>");
+ if (end) end += 5;
+ else {
+ end = strstr(start, "/>");
+ if (!end) break;
+ end += 2;
+ }
+ } else {
+ /* Drop unknown prefix byte to avoid deadlock on unparsed stanzas. */
+ memmove(b->recv_buf, b->recv_buf + 1, b->recv_len - 1);
+ b->recv_len--;
+ b->recv_buf[b->recv_len] = '\0';
+ continue;
+ }
+
+ len = (size_t)(end - start);
+ if (len >= sizeof(stanza)) len = sizeof(stanza) - 1;
+ memcpy(stanza, start, len);
+ stanza[len] = '\0';
+ process_stanza(c, b, stanza);
+
+ len = (size_t)(end - b->recv_buf);
+ if (len < b->recv_len) {
+ memmove(b->recv_buf, b->recv_buf + len, b->recv_len - len);
+ b->recv_len -= len;
+ b->recv_buf[b->recv_len] = '\0';
+ } else {
+ b->recv_len = 0;
+ b->recv_buf[0] = '\0';
+ break;
+ }
+ }
+}
+
+barev_client_t *barev_client_new(const char *nick, const char *my_ipv6, unsigned short port) {
+ struct barev_client *c;
+ c = (struct barev_client *)calloc(1, sizeof(*c));
+ if (!c) return 0;
+ c->port = port ? port : BAREV_DEFAULT_PORT;
+ c->listen_fd = -1;
+ barev_avatar_init(&c->avatar_mgr);
+ barev_strcpy0(c->nick, sizeof(c->nick), nick);
+ barev_strcpy0(c->my_ipv6, sizeof(c->my_ipv6), my_ipv6);
+ barev_join_jid(c->my_jid, sizeof(c->my_jid), c->nick, c->my_ipv6);
+ return c;
+}
+
+void barev_client_free(barev_client_t *client) {
+ size_t i;
+ if (!client) return;
+ barev_avatar_free(&client->avatar_mgr);
+ for (i = 0; i < client->buddy_count; ++i) close_buddy_sock(&client->buddies[i]);
+ if (client->listen_fd >= 0) barev_net_close(client->listen_fd);
+ free(client->buddies);
+ free(client);
+}
+
+int barev_client_start(barev_client_t *client) {
+ if (!client) return 0;
+ if (client->running) return 1;
+ client->listen_fd = barev_net_listen_v6(client->port);
+ if (client->listen_fd < 0) return 0;
+ client->running = 1;
+ log_msg(client, "INFO", "Barev C client started");
+ return 1;
+}
+
+void barev_client_stop(barev_client_t *client) {
+ size_t i;
+ if (!client) return;
+ for (i = 0; i < client->buddy_count; ++i) close_buddy_sock(&client->buddies[i]);
+ if (client->listen_fd >= 0) { barev_net_close(client->listen_fd); client->listen_fd = -1; }
+ client->running = 0;
+}
+
+void barev_client_process(barev_client_t *client) {
+ size_t i;
+ int fd;
+ char ip[128];
+ unsigned short port;
+ barev_buddy_impl_t *b;
+ time_t now_ts;
+
+ if (!client || !client->running) return;
+ now_ts = time((time_t *)0);
+
+ if (client->listen_fd >= 0 && barev_net_is_readable(client->listen_fd, 0)) {
+ fd = barev_net_accept_v6(client->listen_fd, ip, sizeof(ip), &port);
+ if (fd >= 0) {
+ b = find_buddy_by_ip(client, ip);
+ if (b) {
+ close_buddy_sock(b);
+ b->sock = fd;
+ b->we_initiated = 0;
+ b->last_activity = now_ts;
+ emit_conn(client, b, BAREV_CONN_CONNECTING);
+ } else {
+ barev_net_close(fd);
+ }
+ }
+ }
+
+ for (i = 0; i < client->buddy_count; ++i) {
+ b = &client->buddies[i];
+ if (b->sock < 0) continue;
+ if (barev_net_is_readable(b->sock, 0)) {
+ char tmp[2048];
+ int r = barev_net_recv_some(b->sock, tmp, sizeof(tmp) - 1);
+ if (r > 0) {
+ size_t can = sizeof(b->recv_buf) - 1 - b->recv_len;
+ size_t cp = (size_t)r;
+ if (cp > can) cp = can;
+ memcpy(b->recv_buf + b->recv_len, tmp, cp);
+ b->recv_len += cp;
+ b->recv_buf[b->recv_len] = '\0';
+ b->last_activity = now_ts;
+ b->ping_failures = 0;
+ process_recv_buffer(client, b);
+ } else if (r == 0 || r == -1) {
+ int old_s = b->status;
+ close_buddy_sock(b);
+ b->status = BAREV_STATUS_OFFLINE;
+ emit_buddy_status(client, b, old_s, b->status);
+ }
+ }
+
+ if (b->sock >= 0 && b->stream_recv && b->stream_sent) {
+ if (b->last_ping_sent == 0) {
+ if ((now_ts - b->last_activity) >= BAREV_PING_INTERVAL) {
+ (void)send_ping(client, b, now_ts);
+ }
+ } else {
+ if ((now_ts - b->last_ping_sent) > BAREV_PING_TIMEOUT) {
+ b->ping_failures++;
+ b->last_ping_sent = 0;
+ if (b->ping_failures >= BAREV_MAX_PING_FAILURES) {
+ int old_s = b->status;
+ close_buddy_sock(b);
+ b->status = BAREV_STATUS_OFFLINE;
+ emit_buddy_status(client, b, old_s, b->status);
+ }
+ }
+ }
+ }
+ }
+}
+
+const char *barev_client_myjid(const barev_client_t *client) { return client ? client->my_jid : ""; }
+
+barev_buddy_t *barev_client_add_buddy(barev_client_t *client, const char *buddy_nick, const char *buddy_ipv6, unsigned short port) {
+ barev_buddy_impl_t *b;
+ char jid[BAREV_MAX_JID + 1];
+ size_t i;
+
+ if (!client || !buddy_nick || !buddy_ipv6) return 0;
+ barev_join_jid(jid, sizeof(jid), buddy_nick, buddy_ipv6);
+
+ for (i = 0; i < client->buddy_count; ++i)
+ if (barev_same_jid(client->buddies[i].jid, jid)) return (barev_buddy_t *)&client->buddies[i];
+
+ if (!barev_ensure_buddy_cap(client)) return 0;
+
+ b = &client->buddies[client->buddy_count++];
+ memset(b, 0, sizeof(*b));
+ barev_strcpy0(b->nick, sizeof(b->nick), buddy_nick);
+ barev_strcpy0(b->ipv6, sizeof(b->ipv6), buddy_ipv6);
+ barev_join_jid(b->jid, sizeof(b->jid), b->nick, b->ipv6);
+ b->port = port ? port : BAREV_DEFAULT_PORT;
+ b->status = BAREV_STATUS_OFFLINE;
+ b->sock = -1;
+ return (barev_buddy_t *)b;
+}
+
+int barev_client_remove_buddy(barev_client_t *client, const char *buddy_jid) {
+ size_t i;
+ if (!client || !buddy_jid) return 0;
+ for (i = 0; i < client->buddy_count; ++i) {
+ if (barev_same_jid(client->buddies[i].jid, buddy_jid)) {
+ close_buddy_sock(&client->buddies[i]);
+ if (i + 1 < client->buddy_count)
+ memmove(&client->buddies[i], &client->buddies[i + 1], (client->buddy_count - (i + 1)) * sizeof(client->buddies[0]));
+ client->buddy_count--;
+ return 1;
+ }
+ }
+ return 0;
+}
+
+barev_buddy_t *barev_client_find_buddy(barev_client_t *client, const char *buddy_jid) {
+ size_t i;
+ if (!client || !buddy_jid) return 0;
+ for (i = 0; i < client->buddy_count; ++i) if (barev_same_jid(client->buddies[i].jid, buddy_jid)) return (barev_buddy_t *)&client->buddies[i];
+ return 0;
+}
+
+int barev_client_connect(barev_client_t *client, const char *buddy_jid) {
+ barev_buddy_impl_t *b;
+ int fd;
+ if (!client) return 0;
+ b = (barev_buddy_impl_t *)barev_client_find_buddy(client, buddy_jid);
+ if (!b) return 0;
+ if (b->sock >= 0) return 1;
+
+ fd = barev_net_connect_v6(b->ipv6, b->port);
+ if (fd < 0) return 0;
+ b->sock = fd;
+ b->we_initiated = 1;
+ b->last_activity = time((time_t *)0);
+ emit_conn(client, b, BAREV_CONN_CONNECTING);
+ if (!send_stream_header(client, b)) return 0;
+ emit_conn(client, b, BAREV_CONN_STREAM_INIT);
+ return 1;
+}
+
+int barev_client_send_message(barev_client_t *client, const char *buddy_jid, const char *msg) {
+ barev_buddy_impl_t *b;
+ char out[4096];
+ if (!client || !buddy_jid || !msg) return 0;
+ b = (barev_buddy_impl_t *)barev_client_find_buddy(client, buddy_jid);
+ if (!b || b->sock < 0) return 0;
+ if (!barev_build_message(client->my_jid, b->jid, msg, out, sizeof(out))) return 0;
+ return barev_net_send_all(b->sock, out, strlen(out)) >= 0 ? 1 : 0;
+}
+
+int barev_client_send_presence(barev_client_t *client, int status, const char *status_msg) {
+ size_t i;
+ char out[2048];
+ if (!client) return 0;
+ for (i = 0; i < client->buddy_count; ++i) {
+ barev_buddy_impl_t *b = &client->buddies[i];
+ if (b->sock < 0) continue;
+ if (!barev_build_presence(b->jid, status, status_msg ? status_msg : "", out, sizeof(out))) return 0;
+ if (barev_net_send_all(b->sock, out, strlen(out)) < 0) return 0;
+ }
+ return 1;
+}
+
+int barev_client_send_typing(barev_client_t *client, const char *buddy_jid) {
+ barev_buddy_impl_t *b;
+ char out[1024];
+ if (!client || !buddy_jid) return 0;
+ b = (barev_buddy_impl_t *)barev_client_find_buddy(client, buddy_jid);
+ if (!b || b->sock < 0) return 0;
+ if (!barev_build_chatstate(b->jid, "composing", out, sizeof(out))) return 0;
+ return barev_net_send_all(b->sock, out, strlen(out)) >= 0 ? 1 : 0;
+}
+
+int barev_client_send_paused(barev_client_t *client, const char *buddy_jid) {
+ barev_buddy_impl_t *b;
+ char out[1024];
+ if (!client || !buddy_jid) return 0;
+ b = (barev_buddy_impl_t *)barev_client_find_buddy(client, buddy_jid);
+ if (!b || b->sock < 0) return 0;
+ /* Pascal version sends 'active' for Bonjour interoperability. */
+ if (!barev_build_chatstate(b->jid, "active", out, sizeof(out))) return 0;
+ return barev_net_send_all(b->sock, out, strlen(out)) >= 0 ? 1 : 0;
+}
+
+int barev_client_send_file_offer(barev_client_t *client, const char *buddy_jid,
+ const char *file_name, long file_size) {
+ barev_buddy_impl_t *b;
+ size_t bi;
+ barev_ft_offer_t *o;
+ char id[128];
+ char offer[4096];
+ if (!client || !buddy_jid || !file_name || file_size < 0) return 0;
+ b = (barev_buddy_impl_t *)barev_client_find_buddy(client, buddy_jid);
+ if (!b || b->sock < 0) return 0;
+ bi = buddy_index_of(client, b);
+ o = alloc_offer_slot(client);
+ if (!o) return 0;
+ sprintf(id, "ft-offer-%u", ++client->ft_seq);
+ if (!barev_ft_build_si_offer(client->my_jid, b->jid, id, id, file_name, file_size, offer, sizeof(offer))) return 0;
+ if (barev_net_send_all(b->sock, offer, strlen(offer)) < 0) return 0;
+ o->outgoing = 1;
+ strcpy(o->sid, id);
+ strcpy(o->iq_id, id);
+ o->buddy_index = bi;
+ strcpy(o->file_name, file_name);
+ o->file_size = file_size;
+ return 1;
+}
+
+int barev_client_accept_file_offer(barev_client_t *client, const char *sid) {
+ return barev_client_accept_file_offer_as(client, sid, 0);
+}
+
+int barev_client_accept_file_offer_as(barev_client_t *client, const char *sid, const char *save_path) {
+ barev_ft_offer_t *o;
+ barev_buddy_impl_t *b;
+ char iq[3072];
+ if (!client || !sid) return 0;
+ o = find_offer_by_sid(client, sid);
+ if (!o) return 0;
+ if (o->buddy_index >= client->buddy_count) return 0;
+ b = &client->buddies[o->buddy_index];
+ if (b->sock < 0) return 0;
+ if (!barev_ft_build_si_accept(client->my_jid, b->jid, o->iq_id[0] ? o->iq_id : o->sid, iq, sizeof(iq))) return 0;
+ if (barev_net_send_all(b->sock, iq, strlen(iq)) < 0) return 0;
+ o->accepted = 1;
+ if (save_path && save_path[0]) strcpy(o->save_path, save_path);
+ else if (!o->save_path[0]) strcpy(o->save_path, o->file_name);
+ return 1;
+}
+
+int barev_client_reject_file_offer(barev_client_t *client, const char *sid) {
+ barev_ft_offer_t *o;
+ barev_buddy_impl_t *b;
+ char iq[3072];
+ if (!client || !sid) return 0;
+ o = find_offer_by_sid(client, sid);
+ if (!o) return 0;
+ if (o->buddy_index >= client->buddy_count) return 0;
+ b = &client->buddies[o->buddy_index];
+ if (b->sock < 0) return 0;
+ if (!barev_ft_build_si_reject(client->my_jid, b->jid, o->iq_id[0] ? o->iq_id : o->sid, "403", iq, sizeof(iq))) return 0;
+ if (barev_net_send_all(b->sock, iq, strlen(iq)) < 0) return 0;
+ o->used = 0;
+ return 1;
+}
+
+void barev_set_userdata(barev_client_t *client, void *userdata) { if (client) client->userdata = userdata; }
+void barev_set_log_cb(barev_client_t *client, barev_log_cb cb) { if (client) client->on_log = cb; }
+void barev_set_typing_cb(barev_client_t *client, barev_typing_cb cb) { if (client) client->on_typing = cb; }
+void barev_set_buddy_status_cb(barev_client_t *client, barev_buddy_status_cb cb) { if (client) client->on_buddy_status = cb; }
+void barev_set_message_cb(barev_client_t *client, barev_message_cb cb) { if (client) client->on_message = cb; }
+void barev_set_conn_state_cb(barev_client_t *client, barev_conn_state_cb cb) { if (client) client->on_conn_state = cb; }
+void barev_set_ft_offer_cb(barev_client_t *client, barev_ft_offer_cb cb) { if (client) client->on_ft_offer = cb; }
+void barev_set_ft_progress_cb(barev_client_t *client, barev_ft_progress_cb cb) { if (client) client->on_ft_progress = cb; }
+void barev_set_ft_complete_cb(barev_client_t *client, barev_ft_complete_cb cb) { if (client) client->on_ft_complete = cb; }
+void barev_set_ft_error_cb(barev_client_t *client, barev_ft_error_cb cb) { if (client) client->on_ft_error = cb; }
+
+const char *barev_buddy_get_jid(const barev_buddy_t *buddy) { const barev_buddy_impl_t *b = (const barev_buddy_impl_t *)buddy; return b ? b->jid : ""; }
+const char *barev_buddy_get_nick(const barev_buddy_t *buddy) { const barev_buddy_impl_t *b = (const barev_buddy_impl_t *)buddy; return b ? b->nick : ""; }
+const char *barev_buddy_get_ipv6(const barev_buddy_t *buddy) { const barev_buddy_impl_t *b = (const barev_buddy_impl_t *)buddy; return b ? b->ipv6 : ""; }
+unsigned short barev_buddy_get_port(const barev_buddy_t *buddy) { const barev_buddy_impl_t *b = (const barev_buddy_impl_t *)buddy; return b ? b->port : 0; }
+int barev_buddy_get_status(const barev_buddy_t *buddy) { const barev_buddy_impl_t *b = (const barev_buddy_impl_t *)buddy; return b ? b->status : -1; }
+const char *barev_buddy_get_status_message(const barev_buddy_t *buddy) { const barev_buddy_impl_t *b = (const barev_buddy_impl_t *)buddy; return b ? b->status_msg : ""; }
diff --git a/src/barev_config.c b/src/barev_config.c
new file mode 100644
index 0000000..e9f3846
--- /dev/null
+++ b/src/barev_config.c
@@ -0,0 +1,111 @@
+#include "barev_config.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+static void scpy(char *d, size_t n, const char *s) {
+ size_t i;
+ if (!d || n == 0) return;
+ if (!s) { d[0] = '\0'; return; }
+ for (i = 0; i + 1 < n && s[i]; ++i) d[i] = s[i];
+ d[i] = '\0';
+}
+
+void barev_config_init(barev_config_t *cfg) {
+ if (!cfg) return;
+ memset(cfg, 0, sizeof(*cfg));
+ cfg->user_port = 1337;
+}
+
+int barev_config_has_user(const barev_config_t *cfg) {
+ if (!cfg) return 0;
+ return (cfg->user_nick[0] && cfg->user_ipv6[0]) ? 1 : 0;
+}
+
+static void trim(char *s) {
+ size_t i, j, n;
+ if (!s) return;
+ n = strlen(s);
+ i = 0;
+ while (i < n && (s[i] == ' ' || s[i] == '\t' || s[i] == '\r' || s[i] == '\n')) i++;
+ j = n;
+ while (j > i && (s[j - 1] == ' ' || s[j - 1] == '\t' || s[j - 1] == '\r' || s[j - 1] == '\n')) j--;
+ if (i > 0 || j < n) {
+ memmove(s, s + i, j - i);
+ s[j - i] = '\0';
+ }
+}
+
+int barev_config_load(const char *path, barev_config_t *cfg) {
+ FILE *f;
+ char line[1024], sec[128];
+ int cur_contact;
+ barev_config_init(cfg);
+ if (!path || !cfg) return 0;
+ f = fopen(path, "r");
+ if (!f) return 0;
+ sec[0] = '\0';
+ cur_contact = -1;
+
+ while (fgets(line, sizeof(line), f)) {
+ char *eq;
+ trim(line);
+ if (!line[0] || line[0] == '#' || line[0] == ';') continue;
+ if (line[0] == '[') {
+ char *r = strchr(line, ']');
+ if (r) { *r = '\0'; scpy(sec, sizeof(sec), line + 1); }
+ if (strncmp(sec, "Contact-", 8) == 0) {
+ if (cfg->contact_count < BAREV_CFG_MAX_CONTACTS) {
+ cur_contact = (int)cfg->contact_count;
+ cfg->contact_count++;
+ memset(&cfg->contacts[cur_contact], 0, sizeof(cfg->contacts[cur_contact]));
+ cfg->contacts[cur_contact].port = 1337;
+ }
+ } else cur_contact = -1;
+ continue;
+ }
+ eq = strchr(line, '=');
+ if (!eq) continue;
+ *eq = '\0';
+ trim(line);
+ trim(eq + 1);
+
+ if (strcmp(sec, "User") == 0) {
+ if (strcmp(line, "Nick") == 0) scpy(cfg->user_nick, sizeof(cfg->user_nick), eq + 1);
+ else if (strcmp(line, "IPv6") == 0) scpy(cfg->user_ipv6, sizeof(cfg->user_ipv6), eq + 1);
+ else if (strcmp(line, "Port") == 0) cfg->user_port = (unsigned short)atoi(eq + 1);
+ else if (strcmp(line, "AvatarPath") == 0) scpy(cfg->user_avatar_path, sizeof(cfg->user_avatar_path), eq + 1);
+ } else if (cur_contact >= 0) {
+ barev_config_contact_t *c = &cfg->contacts[cur_contact];
+ if (strcmp(line, "Nick") == 0) scpy(c->nick, sizeof(c->nick), eq + 1);
+ else if (strcmp(line, "IPv6") == 0) scpy(c->ipv6, sizeof(c->ipv6), eq + 1);
+ else if (strcmp(line, "Port") == 0) c->port = (unsigned short)atoi(eq + 1);
+ else if (strcmp(line, "AvatarPath") == 0) scpy(c->avatar_path, sizeof(c->avatar_path), eq + 1);
+ }
+ }
+ fclose(f);
+ return 1;
+}
+
+int barev_config_save(const char *path, const barev_config_t *cfg) {
+ FILE *f;
+ size_t i;
+ if (!path || !cfg) return 0;
+ f = fopen(path, "w");
+ if (!f) return 0;
+ fprintf(f, "[User]\n");
+ fprintf(f, "Nick=%s\n", cfg->user_nick);
+ fprintf(f, "IPv6=%s\n", cfg->user_ipv6);
+ fprintf(f, "Port=%u\n", (unsigned)cfg->user_port);
+ fprintf(f, "AvatarPath=%s\n\n", cfg->user_avatar_path);
+ for (i = 0; i < cfg->contact_count; ++i) {
+ fprintf(f, "[Contact-%u]\n", (unsigned)i);
+ fprintf(f, "Nick=%s\n", cfg->contacts[i].nick);
+ fprintf(f, "IPv6=%s\n", cfg->contacts[i].ipv6);
+ fprintf(f, "Port=%u\n", (unsigned)cfg->contacts[i].port);
+ fprintf(f, "AvatarPath=%s\n\n", cfg->contacts[i].avatar_path);
+ }
+ fclose(f);
+ return 1;
+}
diff --git a/src/barev_ft.c b/src/barev_ft.c
new file mode 100644
index 0000000..c5d84c1
--- /dev/null
+++ b/src/barev_ft.c
@@ -0,0 +1,335 @@
+#include "barev_ft.h"
+#include "barev_sha1.h"
+
+#include <string.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+static int has_ns(const char *xml, const char *ns) {
+ char a[256], b[256];
+ if (!xml || !ns) return 0;
+ (void)snprintf(a, sizeof(a), "xmlns=\"%s\"", ns);
+ (void)snprintf(b, sizeof(b), "xmlns='%s'", ns);
+ return (strstr(xml, a) != 0 || strstr(xml, b) != 0) ? 1 : 0;
+}
+
+int barev_ft_extract_attr(const char *xml, const char *attr, char *out, unsigned long out_n) {
+ char p1[128], p2[128];
+ const char *p, *q;
+ unsigned long n;
+ if (!xml || !attr || !out || out_n == 0) return 0;
+ out[0] = '\0';
+ snprintf(p1, sizeof(p1), "%s=\"", attr);
+ snprintf(p2, sizeof(p2), "%s='", attr);
+ p = strstr(xml, p1);
+ if (!p) p = strstr(xml, p2);
+ if (!p) return 0;
+ p += (p[strlen(attr) + 1] == '"') ? strlen(p1) : strlen(p2);
+ q = p;
+ while (*q && *q != '"' && *q != '\'') q++;
+ n = (unsigned long)(q - p);
+ if (n + 1 > out_n) return 0;
+ memcpy(out, p, (size_t)n);
+ out[n] = '\0';
+ return 1;
+}
+
+static int rd_exact(int fd, unsigned char *buf, int n) {
+ int got = 0, r;
+ while (got < n) {
+ r = (int)read(fd, buf + got, (size_t)(n - got));
+ if (r <= 0) return 0;
+ got += r;
+ }
+ return 1;
+}
+
+static int wr_exact(int fd, const unsigned char *buf, int n) {
+ int done = 0, w;
+ while (done < n) {
+ w = (int)write(fd, buf + done, (size_t)(n - done));
+ if (w <= 0) return 0;
+ done += w;
+ }
+ return 1;
+}
+
+int barev_ft_socks5_server_handshake(int sockfd) {
+ unsigned char b[512];
+ int nmethods;
+
+ if (!rd_exact(sockfd, b, 2)) return 0;
+ if (b[0] != 5) return 0;
+ nmethods = b[1];
+ if (nmethods > 0) {
+ if (nmethods > 255) return 0;
+ if (!rd_exact(sockfd, b, nmethods)) return 0;
+ }
+
+ b[0] = 5; b[1] = 0;
+ if (!wr_exact(sockfd, b, 2)) return 0;
+
+ /* Tolerant: just consume some request bytes, as Pascal does. */
+ nmethods = (int)read(sockfd, b, sizeof(b));
+ if (nmethods <= 0) return 0;
+
+ b[0] = 5; b[1] = 0; b[2] = 0; b[3] = 3; b[4] = 20;
+ memset(b + 5, 0, 20);
+ b[25] = 0; b[26] = 0;
+ if (!wr_exact(sockfd, b, 27)) return 0;
+ return 1;
+}
+
+int barev_ft_socks5_client_handshake(int sockfd, const char *dstaddr40) {
+ unsigned char b[512];
+ int i, r;
+ unsigned char l;
+
+ if (!dstaddr40) return 0;
+
+ b[0] = 5; b[1] = 1; b[2] = 0;
+ if (!wr_exact(sockfd, b, 3)) return 0;
+ if (!rd_exact(sockfd, b, 2)) return 0;
+ if (b[0] != 5 || b[1] != 0) return 0;
+
+ b[0] = 5; b[1] = 1; b[2] = 0; b[3] = 3;
+ l = 40;
+ b[4] = l;
+ for (i = 0; i < 40; ++i) b[5 + i] = (unsigned char)dstaddr40[i];
+ b[45] = 0; b[46] = 0;
+ if (!wr_exact(sockfd, b, 47)) return 0;
+
+ if (!rd_exact(sockfd, b, 5)) return 0;
+ if (b[0] != 5 || b[1] != 0) return 0;
+
+ if (b[3] == 1) r = 4 + 2;
+ else if (b[3] == 3) r = b[4] + 2;
+ else if (b[3] == 4) r = 16 + 2;
+ else return 0;
+
+ if (r > 0) {
+ if (r > (int)sizeof(b)) return 0;
+ if (!rd_exact(sockfd, b, r)) return 0;
+ }
+
+ return 1;
+}
+
+int barev_ft_build_si_offer(const char *from_jid, const char *to_jid, const char *id,
+ const char *sid, const char *file_name, long file_size,
+ char *out, unsigned long out_n) {
+ int w;
+ if (!from_jid || !to_jid || !id || !sid || !file_name || !out || out_n == 0) return 0;
+ w = snprintf(out, (size_t)out_n,
+ "<iq type=\"set\" id=\"%s\" from=\"%s\" to=\"%s\">"
+ "<si xmlns=\"http://jabber.org/protocol/si\" id=\"%s\" profile=\"http://jabber.org/protocol/si/profile/file-transfer\">"
+ "<file xmlns=\"http://jabber.org/protocol/si/profile/file-transfer\" name=\"%s\" size=\"%ld\"/>"
+ "<feature xmlns=\"http://jabber.org/protocol/feature-neg\">"
+ "<x xmlns=\"jabber:x:data\" type=\"form\">"
+ "<field var=\"stream-method\" type=\"list-single\">"
+ "<option><value>http://jabber.org/protocol/bytestreams</value></option>"
+ "</field>"
+ "</x>"
+ "</feature>"
+ "</si>"
+ "</iq>", id, from_jid, to_jid, sid, file_name, file_size);
+ return (w > 0 && (unsigned long)w < out_n) ? 1 : 0;
+}
+
+int barev_ft_build_si_accept(const char *from_jid, const char *to_jid, const char *id,
+ char *out, unsigned long out_n) {
+ int w;
+ if (!from_jid || !to_jid || !id || !out || out_n == 0) return 0;
+ w = snprintf(out, (size_t)out_n,
+ "<iq type=\"result\" id=\"%s\" from=\"%s\" to=\"%s\">"
+ "<si xmlns=\"http://jabber.org/protocol/si\">"
+ "<feature xmlns=\"http://jabber.org/protocol/feature-neg\">"
+ "<x xmlns=\"jabber:x:data\" type=\"submit\">"
+ "<field var=\"stream-method\">"
+ "<value>http://jabber.org/protocol/bytestreams</value>"
+ "</field>"
+ "</x>"
+ "</feature>"
+ "</si>"
+ "</iq>", id, from_jid, to_jid);
+ return (w > 0 && (unsigned long)w < out_n) ? 1 : 0;
+}
+
+int barev_ft_build_si_reject(const char *from_jid, const char *to_jid, const char *id,
+ const char *code, char *out, unsigned long out_n) {
+ int w;
+ if (!from_jid || !to_jid || !id || !out || out_n == 0) return 0;
+ if (!code || !code[0]) code = "403";
+ w = snprintf(out, (size_t)out_n,
+ "<iq type=\"error\" id=\"%s\" from=\"%s\" to=\"%s\">"
+ "<error type=\"cancel\" code=\"%s\"><forbidden xmlns=\"urn:ietf:params:xml:ns:xmpp-stanzas\"/></error>"
+ "</iq>", id, from_jid, to_jid, code);
+ return (w > 0 && (unsigned long)w < out_n) ? 1 : 0;
+}
+
+int barev_ft_build_bytestreams_query(const char *from_jid, const char *to_jid, const char *id,
+ const char *sid, const char *host, unsigned short port,
+ char *out, unsigned long out_n) {
+ int w;
+ if (!from_jid || !to_jid || !id || !sid || !host || !out || out_n == 0) return 0;
+ w = snprintf(out, (size_t)out_n,
+ "<iq type=\"set\" id=\"%s\" from=\"%s\" to=\"%s\">"
+ "<query xmlns=\"http://jabber.org/protocol/bytestreams\" sid=\"%s\" mode=\"tcp\">"
+ "<streamhost jid=\"%s\" host=\"%s\" port=\"%u\"/>"
+ "</query>"
+ "</iq>", id, from_jid, to_jid, sid, from_jid, host, (unsigned)port);
+ return (w > 0 && (unsigned long)w < out_n) ? 1 : 0;
+}
+
+int barev_ft_build_bytestreams_used(const char *from_jid, const char *to_jid, const char *id,
+ const char *sid, const char *host_jid,
+ char *out, unsigned long out_n) {
+ int w;
+ if (!from_jid || !to_jid || !id || !sid || !host_jid || !out || out_n == 0) return 0;
+ w = snprintf(out, (size_t)out_n,
+ "<iq type=\"result\" id=\"%s\" from=\"%s\" to=\"%s\">"
+ "<query xmlns=\"http://jabber.org/protocol/bytestreams\" sid=\"%s\">"
+ "<streamhost-used jid=\"%s\"/>"
+ "</query>"
+ "</iq>", id, from_jid, to_jid, sid, host_jid);
+ return (w > 0 && (unsigned long)w < out_n) ? 1 : 0;
+}
+
+int barev_ft_is_si_offer(const char *xml) {
+ if (!xml) return 0;
+ if (strstr(xml, "<si ") == 0) return 0;
+ return has_ns(xml, "http://jabber.org/protocol/si");
+}
+
+int barev_ft_is_bytestreams_query(const char *xml) {
+ if (!xml) return 0;
+ if (strstr(xml, "<query") == 0) return 0;
+ return has_ns(xml, "http://jabber.org/protocol/bytestreams");
+}
+
+int barev_ft_parse_si_offer(const char *xml,
+ char *sid, unsigned long sid_n,
+ char *name, unsigned long name_n,
+ long *size_out) {
+ const char *p;
+ if (!xml || !sid || !name || !size_out) return 0;
+ if (!barev_ft_is_si_offer(xml)) return 0;
+ p = strstr(xml, "<si ");
+ if (!p) return 0;
+ if (!barev_ft_extract_attr(p, "id", sid, sid_n)) return 0;
+ p = strstr(xml, "<file ");
+ if (!p) return 0;
+ if (!barev_ft_extract_attr(p, "name", name, name_n)) return 0;
+ {
+ char sz[64];
+ if (!barev_ft_extract_attr(p, "size", sz, sizeof(sz))) return 0;
+ *size_out = atol(sz);
+ }
+ return 1;
+}
+
+int barev_ft_parse_streamhost(const char *xml,
+ char *sid, unsigned long sid_n,
+ char *host, unsigned long host_n,
+ unsigned short *port_out,
+ char *jid, unsigned long jid_n) {
+ const char *q;
+ char pstr[32];
+ if (!xml || !sid || !host || !port_out || !jid) return 0;
+ if (!barev_ft_is_bytestreams_query(xml)) return 0;
+ q = strstr(xml, "<query");
+ if (!q) return 0;
+ if (!barev_ft_extract_attr(q, "sid", sid, sid_n)) return 0;
+ q = strstr(xml, "<streamhost ");
+ if (!q) return 0;
+ if (!barev_ft_extract_attr(q, "host", host, host_n)) return 0;
+ if (!barev_ft_extract_attr(q, "jid", jid, jid_n)) return 0;
+ if (!barev_ft_extract_attr(q, "port", pstr, sizeof(pstr))) return 0;
+ *port_out = (unsigned short)atoi(pstr);
+ return 1;
+}
+
+int barev_ft_compute_dstaddr40(const char *sid, const char *initiator_jid, const char *target_jid,
+ char out40[41]) {
+ char tmp[1024];
+ size_t n1, n2, n3;
+ if (!sid || !initiator_jid || !target_jid || !out40) return 0;
+ n1 = strlen(sid); n2 = strlen(initiator_jid); n3 = strlen(target_jid);
+ if (n1 + n2 + n3 + 1 > sizeof(tmp)) return 0;
+ memcpy(tmp, sid, n1);
+ memcpy(tmp + n1, initiator_jid, n2);
+ memcpy(tmp + n1 + n2, target_jid, n3);
+ tmp[n1 + n2 + n3] = '\0';
+ barev_sha1_hex((const unsigned char *)tmp, n1 + n2 + n3, out40);
+ return 1;
+}
+
+int barev_ft_recv_file_blocking(int sockfd, const char *save_path, long expected_size, long *bytes_done) {
+ int fd;
+ unsigned char buf[8192];
+ ssize_t r;
+ long done = 0;
+ if (!save_path || expected_size < 0) return 0;
+ fd = open(save_path, O_CREAT | O_TRUNC | O_WRONLY, 0644);
+ if (fd < 0) return 0;
+ while (done < expected_size) {
+ r = read(sockfd, buf, sizeof(buf));
+ if (r <= 0) break;
+ if (write(fd, buf, (size_t)r) != r) { close(fd); return 0; }
+ done += (long)r;
+ }
+ close(fd);
+ if (bytes_done) *bytes_done = done;
+ return done >= expected_size ? 1 : 0;
+}
+
+int barev_ft_listen_range(unsigned short min_port, unsigned short max_port, unsigned short *bound_port) {
+ int s, on;
+ struct sockaddr_in6 sa;
+ unsigned short p;
+ s = socket(AF_INET6, SOCK_STREAM, 0);
+ if (s < 0) return -1;
+ on = 1;
+ setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (const char *)&on, sizeof(on));
+ memset(&sa, 0, sizeof(sa));
+ sa.sin6_family = AF_INET6;
+ sa.sin6_addr = in6addr_any;
+ for (p = min_port; p <= max_port; ++p) {
+ sa.sin6_port = htons(p);
+ if (bind(s, (struct sockaddr *)&sa, sizeof(sa)) == 0) {
+ if (listen(s, 1) == 0) {
+ if (bound_port) *bound_port = p;
+ return s;
+ }
+ }
+ }
+ close(s);
+ return -1;
+}
+
+int barev_ft_send_file_blocking(int sockfd, const char *path, long *bytes_done) {
+ int fd;
+ unsigned char buf[8192];
+ ssize_t r, w;
+ long done = 0;
+ if (!path) return 0;
+ fd = open(path, O_RDONLY);
+ if (fd < 0) return 0;
+ for (;;) {
+ r = read(fd, buf, sizeof(buf));
+ if (r < 0) { close(fd); return 0; }
+ if (r == 0) break;
+ w = write(sockfd, buf, (size_t)r);
+ if (w != r) { close(fd); return 0; }
+ done += (long)w;
+ }
+ close(fd);
+ if (bytes_done) *bytes_done = done;
+ return 1;
+}
diff --git a/src/barev_net.c b/src/barev_net.c
new file mode 100644
index 0000000..61ce9be
--- /dev/null
+++ b/src/barev_net.c
@@ -0,0 +1,127 @@
+#include "barev_net.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <sys/select.h>
+#include <arpa/inet.h>
+#include <netinet/in.h>
+
+int barev_net_set_nonblocking(int fd) {
+ int flags;
+ flags = fcntl(fd, F_GETFL, 0);
+ if (flags < 0) return 0;
+ if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) < 0) return 0;
+ return 1;
+}
+
+int barev_net_listen_v6(unsigned short port) {
+ int fd;
+ int on;
+ struct sockaddr_in6 sa;
+
+ fd = socket(AF_INET6, SOCK_STREAM, 0);
+ if (fd < 0) return -1;
+
+ on = 1;
+ setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (const char *)&on, sizeof(on));
+
+ memset(&sa, 0, sizeof(sa));
+ sa.sin6_family = AF_INET6;
+ sa.sin6_port = htons(port);
+ sa.sin6_addr = in6addr_any;
+
+ if (bind(fd, (struct sockaddr *)&sa, sizeof(sa)) < 0) { close(fd); return -1; }
+ if (listen(fd, 16) < 0) { close(fd); return -1; }
+ if (!barev_net_set_nonblocking(fd)) { close(fd); return -1; }
+ return fd;
+}
+
+int barev_net_accept_v6(int listen_fd, char *addr_out, size_t addr_out_n, unsigned short *port_out) {
+ int fd;
+ struct sockaddr_in6 sa;
+ socklen_t sl;
+ const char *r;
+
+ sl = (socklen_t)sizeof(sa);
+ memset(&sa, 0, sizeof(sa));
+ fd = accept(listen_fd, (struct sockaddr *)&sa, &sl);
+ if (fd < 0) return -1;
+
+ if (addr_out && addr_out_n > 0) {
+ r = inet_ntop(AF_INET6, &sa.sin6_addr, addr_out, (socklen_t)addr_out_n);
+ if (!r) addr_out[0] = '\0';
+ }
+ if (port_out) *port_out = ntohs(sa.sin6_port);
+ barev_net_set_nonblocking(fd);
+ return fd;
+}
+
+int barev_net_connect_v6(const char *ipv6, unsigned short port) {
+ int fd;
+ int rc;
+ struct sockaddr_in6 sa;
+
+ fd = socket(AF_INET6, SOCK_STREAM, 0);
+ if (fd < 0) return -1;
+ if (!barev_net_set_nonblocking(fd)) { close(fd); return -1; }
+
+ memset(&sa, 0, sizeof(sa));
+ sa.sin6_family = AF_INET6;
+ sa.sin6_port = htons(port);
+ if (inet_pton(AF_INET6, ipv6, &sa.sin6_addr) != 1) { close(fd); return -1; }
+
+ rc = connect(fd, (struct sockaddr *)&sa, sizeof(sa));
+ if (rc == 0) return fd;
+ if (rc < 0 && (errno == EINPROGRESS || errno == EWOULDBLOCK)) return fd;
+ close(fd);
+ return -1;
+}
+
+int barev_net_is_readable(int fd, int timeout_ms) {
+ fd_set rfds;
+ struct timeval tv;
+ int rc;
+
+ FD_ZERO(&rfds);
+ FD_SET(fd, &rfds);
+ tv.tv_sec = timeout_ms / 1000;
+ tv.tv_usec = (timeout_ms % 1000) * 1000;
+ rc = select(fd + 1, &rfds, 0, 0, &tv);
+ if (rc <= 0) return 0;
+ return FD_ISSET(fd, &rfds) ? 1 : 0;
+}
+
+int barev_net_send_all(int fd, const char *buf, size_t n) {
+ ssize_t w;
+ size_t done;
+ done = 0;
+ while (done < n) {
+ w = send(fd, buf + done, n - done, 0);
+ if (w > 0) {
+ done += (size_t)w;
+ continue;
+ }
+ if (w < 0 && (errno == EINTR)) continue;
+ if (w < 0 && (errno == EAGAIN || errno == EWOULDBLOCK)) break;
+ return -1;
+ }
+ return (int)done;
+}
+
+int barev_net_recv_some(int fd, char *buf, size_t n) {
+ ssize_t r;
+ r = recv(fd, buf, n, 0);
+ if (r > 0) return (int)r;
+ if (r == 0) return 0;
+ if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR) return -2;
+ return -1;
+}
+
+void barev_net_close(int fd) {
+ if (fd >= 0) close(fd);
+}
diff --git a/src/barev_sha1.c b/src/barev_sha1.c
new file mode 100644
index 0000000..0daf5e5
--- /dev/null
+++ b/src/barev_sha1.c
@@ -0,0 +1,19 @@
+#include "barev_sha1.h"
+#include "sha1.h"
+
+void barev_sha1_hex(const unsigned char *data, size_t len, char out_hex40[41]) {
+ unsigned char digest[20];
+ SHA1_CTX ctx;
+ static const char hex[] = "0123456789abcdef";
+ size_t i;
+
+ SHA1Init(&ctx);
+ SHA1Update(&ctx, data, (uint32_t)len);
+ SHA1Final(digest, &ctx);
+
+ for (i = 0; i < 20; ++i) {
+ out_hex40[i * 2] = hex[(digest[i] >> 4) & 0x0F];
+ out_hex40[(i * 2) + 1] = hex[digest[i] & 0x0F];
+ }
+ out_hex40[40] = '\0';
+}
diff --git a/src/barev_xml.c b/src/barev_xml.c
new file mode 100644
index 0000000..7b9350c
--- /dev/null
+++ b/src/barev_xml.c
@@ -0,0 +1,259 @@
+#include "barev_xml.h"
+
+#include <string.h>
+
+static int append_str(char *out, size_t out_size, size_t *pos, const char *s) {
+ size_t i;
+ if (!out || !s || !pos || out_size == 0) return 0;
+ for (i = 0; s[i] != '\0'; ++i) {
+ if (*pos + 1 >= out_size) return 0;
+ out[*pos] = s[i];
+ *pos = *pos + 1;
+ }
+ out[*pos] = '\0';
+ return 1;
+}
+
+int barev_xml_escape(const char *in, char *out, size_t out_size) {
+ size_t i;
+ size_t pos;
+ const char *rep;
+ if (!in || !out || out_size == 0) return 0;
+ pos = 0;
+ out[0] = '\0';
+ for (i = 0; in[i] != '\0'; ++i) {
+ rep = 0;
+ if (in[i] == '&') rep = "&";
+ else if (in[i] == '<') rep = "<";
+ else if (in[i] == '>') rep = ">";
+ else if (in[i] == '"') rep = """;
+ else if (in[i] == '\'') rep = "'";
+
+ if (rep) {
+ if (!append_str(out, out_size, &pos, rep)) return 0;
+ } else {
+ if (pos + 1 >= out_size) return 0;
+ out[pos++] = in[i];
+ out[pos] = '\0';
+ }
+ }
+ return 1;
+}
+
+int barev_build_stream_header(const char *from_jid, const char *to_jid, char *out, size_t out_size) {
+ size_t pos;
+ char from_e[512], to_e[512];
+ if (!from_jid || !to_jid || !out || out_size == 0) return 0;
+ if (!barev_xml_escape(from_jid, from_e, sizeof(from_e))) return 0;
+ if (!barev_xml_escape(to_jid, to_e, sizeof(to_e))) return 0;
+ pos = 0;
+ out[0] = '\0';
+ if (!append_str(out, out_size, &pos, "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n")) return 0;
+ if (!append_str(out, out_size, &pos, "<stream:stream xmlns=\"jabber:client\" xmlns:stream=\"http://etherx.jabber.org/streams\" from=\"")) return 0;
+ if (!append_str(out, out_size, &pos, from_e)) return 0;
+ if (!append_str(out, out_size, &pos, "\" to=\"")) return 0;
+ if (!append_str(out, out_size, &pos, to_e)) return 0;
+ if (!append_str(out, out_size, &pos, "\">")) return 0;
+ return 1;
+}
+
+int barev_build_stream_end(char *out, size_t out_size) {
+ size_t pos;
+ if (!out || out_size == 0) return 0;
+ pos = 0;
+ out[0] = '\0';
+ return append_str(out, out_size, &pos, "</stream:stream>");
+}
+
+int barev_build_presence(const char *to_jid, int status, const char *status_msg, char *out, size_t out_size) {
+ size_t pos;
+ char to_e[512], msg_e[1024];
+ const char *show = 0;
+ if (!out || out_size == 0) return 0;
+ pos = 0;
+ out[0] = '\0';
+
+ if (!append_str(out, out_size, &pos, "<presence")) return 0;
+ if (to_jid && to_jid[0] != '\0') {
+ if (!barev_xml_escape(to_jid, to_e, sizeof(to_e))) return 0;
+ if (!append_str(out, out_size, &pos, " to=\"")) return 0;
+ if (!append_str(out, out_size, &pos, to_e)) return 0;
+ if (!append_str(out, out_size, &pos, "\"")) return 0;
+ }
+ if (!append_str(out, out_size, &pos, ">")) return 0;
+
+ if (status == 2) show = "away";
+ else if (status == 3) show = "xa";
+ else if (status == 4) show = "dnd";
+ else if (status == 0) show = "offline";
+
+ if (show) {
+ if (!append_str(out, out_size, &pos, "<show>")) return 0;
+ if (!append_str(out, out_size, &pos, show)) return 0;
+ if (!append_str(out, out_size, &pos, "</show>")) return 0;
+ }
+
+ if (status_msg && status_msg[0] != '\0') {
+ if (!barev_xml_escape(status_msg, msg_e, sizeof(msg_e))) return 0;
+ if (!append_str(out, out_size, &pos, "<status>")) return 0;
+ if (!append_str(out, out_size, &pos, msg_e)) return 0;
+ if (!append_str(out, out_size, &pos, "</status>")) return 0;
+ }
+
+ if (!append_str(out, out_size, &pos, "</presence>")) return 0;
+ return 1;
+}
+
+int barev_build_message(const char *from_jid, const char *to_jid, const char *body, char *out, size_t out_size) {
+ size_t pos;
+ char from_e[512], to_e[512], body_e[2048];
+ if (!from_jid || !to_jid || !body || !out || out_size == 0) return 0;
+ if (!barev_xml_escape(from_jid, from_e, sizeof(from_e))) return 0;
+ if (!barev_xml_escape(to_jid, to_e, sizeof(to_e))) return 0;
+ if (!barev_xml_escape(body, body_e, sizeof(body_e))) return 0;
+
+ pos = 0;
+ out[0] = '\0';
+ if (!append_str(out, out_size, &pos, "<message to=\"")) return 0;
+ if (!append_str(out, out_size, &pos, to_e)) return 0;
+ if (!append_str(out, out_size, &pos, "\" from=\"")) return 0;
+ if (!append_str(out, out_size, &pos, from_e)) return 0;
+ if (!append_str(out, out_size, &pos, "\" type=\"chat\"><body>")) return 0;
+ if (!append_str(out, out_size, &pos, body_e)) return 0;
+ if (!append_str(out, out_size, &pos, "</body></message>")) return 0;
+ return 1;
+}
+
+int barev_build_ping(const char *from_jid, const char *to_jid, const char *ping_id, char *out, size_t out_size) {
+ size_t pos;
+ char from_e[512], to_e[512], id_e[256];
+ if (!from_jid || !to_jid || !ping_id || !out || out_size == 0) return 0;
+ if (!barev_xml_escape(from_jid, from_e, sizeof(from_e))) return 0;
+ if (!barev_xml_escape(to_jid, to_e, sizeof(to_e))) return 0;
+ if (!barev_xml_escape(ping_id, id_e, sizeof(id_e))) return 0;
+ pos = 0;
+ out[0] = '\0';
+ if (!append_str(out, out_size, &pos, "<iq type=\"get\" id=\"")) return 0;
+ if (!append_str(out, out_size, &pos, id_e)) return 0;
+ if (!append_str(out, out_size, &pos, "\" from=\"")) return 0;
+ if (!append_str(out, out_size, &pos, from_e)) return 0;
+ if (!append_str(out, out_size, &pos, "\" to=\"")) return 0;
+ if (!append_str(out, out_size, &pos, to_e)) return 0;
+ if (!append_str(out, out_size, &pos, "\"><ping xmlns=\"urn:xmpp:ping\"/></iq>")) return 0;
+ return 1;
+}
+
+int barev_build_pong(const char *to_jid, const char *ping_id, char *out, size_t out_size) {
+ size_t pos;
+ char to_e[512], id_e[256];
+ if (!to_jid || !ping_id || !out || out_size == 0) return 0;
+ if (!barev_xml_escape(to_jid, to_e, sizeof(to_e))) return 0;
+ if (!barev_xml_escape(ping_id, id_e, sizeof(id_e))) return 0;
+ pos = 0;
+ out[0] = '\0';
+ if (!append_str(out, out_size, &pos, "<iq type=\"result\" to=\"")) return 0;
+ if (!append_str(out, out_size, &pos, to_e)) return 0;
+ if (!append_str(out, out_size, &pos, "\" id=\"")) return 0;
+ if (!append_str(out, out_size, &pos, id_e)) return 0;
+ if (!append_str(out, out_size, &pos, "\"/>")) return 0;
+ return 1;
+}
+
+int barev_build_chatstate(const char *to_jid, const char *state_name, char *out, size_t out_size) {
+ size_t pos;
+ char to_e[512], st_e[64];
+ if (!to_jid || !state_name || !out || out_size == 0) return 0;
+ if (!barev_xml_escape(to_jid, to_e, sizeof(to_e))) return 0;
+ if (!barev_xml_escape(state_name, st_e, sizeof(st_e))) return 0;
+ pos = 0;
+ out[0] = '\0';
+ if (!append_str(out, out_size, &pos, "<message type=\"chat\" to=\"")) return 0;
+ if (!append_str(out, out_size, &pos, to_e)) return 0;
+ if (!append_str(out, out_size, &pos, "\" id=\"chat-1\"><")) return 0;
+ if (!append_str(out, out_size, &pos, st_e)) return 0;
+ if (!append_str(out, out_size, &pos, " xmlns=\"http://jabber.org/protocol/chatstates\"/></message>")) return 0;
+ return 1;
+}
+
+int barev_build_vcard_get(const char *to_jid, const char *id, char *out, size_t out_size) {
+ size_t pos;
+ char to_e[512], id_e[128];
+ if (!to_jid || !id || !out || out_size == 0) return 0;
+ if (!barev_xml_escape(to_jid, to_e, sizeof(to_e))) return 0;
+ if (!barev_xml_escape(id, id_e, sizeof(id_e))) return 0;
+ pos = 0;
+ out[0] = '\0';
+ if (!append_str(out, out_size, &pos, "<iq type=\"get\" to=\"")) return 0;
+ if (!append_str(out, out_size, &pos, to_e)) return 0;
+ if (!append_str(out, out_size, &pos, "\" id=\"")) return 0;
+ if (!append_str(out, out_size, &pos, id_e)) return 0;
+ if (!append_str(out, out_size, &pos, "\"><vCard xmlns=\"vcard-temp\"/></iq>")) return 0;
+ return 1;
+}
+
+int barev_build_vcard_result(const char *to_jid, const char *id, const char *vcard_inner, char *out, size_t out_size) {
+ size_t pos;
+ char to_e[512], id_e[128];
+ if (!to_jid || !id || !vcard_inner || !out || out_size == 0) return 0;
+ if (!barev_xml_escape(to_jid, to_e, sizeof(to_e))) return 0;
+ if (!barev_xml_escape(id, id_e, sizeof(id_e))) return 0;
+ pos = 0;
+ out[0] = '\0';
+ if (!append_str(out, out_size, &pos, "<iq type=\"result\" to=\"")) return 0;
+ if (!append_str(out, out_size, &pos, to_e)) return 0;
+ if (!append_str(out, out_size, &pos, "\" id=\"")) return 0;
+ if (!append_str(out, out_size, &pos, id_e)) return 0;
+ if (!append_str(out, out_size, &pos, "\">")) return 0;
+ if (!append_str(out, out_size, &pos, vcard_inner)) return 0;
+ if (!append_str(out, out_size, &pos, "</iq>")) return 0;
+ return 1;
+}
+
+int barev_extract_attribute(const char *xml, const char *attr_name, char *out, size_t out_size) {
+ const char *p;
+ const char *q;
+ size_t n;
+ char pat[128];
+ if (!xml || !attr_name || !out || out_size == 0) return 0;
+ out[0] = '\0';
+ if (strlen(attr_name) + 3 >= sizeof(pat)) return 0;
+ strcpy(pat, attr_name);
+ strcat(pat, "='");
+ p = strstr(xml, pat);
+ if (!p) {
+ strcpy(pat, attr_name);
+ strcat(pat, "=\"");
+ p = strstr(xml, pat);
+ if (!p) return 0;
+ }
+ p += strlen(pat);
+ q = p;
+ while (*q && *q != '\'' && *q != '"') q++;
+ n = (size_t)(q - p);
+ if (n + 1 > out_size) return 0;
+ memcpy(out, p, n);
+ out[n] = '\0';
+ return 1;
+}
+
+int barev_extract_element_content(const char *xml, const char *element, char *out, size_t out_size) {
+ char open[128], close[128];
+ const char *p;
+ const char *q;
+ size_t n;
+ if (!xml || !element || !out || out_size == 0) return 0;
+ out[0] = '\0';
+ if (strlen(element) + 3 >= sizeof(open)) return 0;
+ strcpy(open, "<"); strcat(open, element); strcat(open, ">");
+ strcpy(close, "</"); strcat(close, element); strcat(close, ">");
+ p = strstr(xml, open);
+ if (!p) return 0;
+ p += strlen(open);
+ q = strstr(p, close);
+ if (!q) return 0;
+ n = (size_t)(q - p);
+ if (n + 1 > out_size) return 0;
+ memcpy(out, p, n);
+ out[n] = '\0';
+ return 1;
+}
diff --git a/src/sha1.c b/src/sha1.c
new file mode 100644
index 0000000..8c5aa89
--- /dev/null
+++ b/src/sha1.c
@@ -0,0 +1,302 @@
+/*
+SHA-1 in C
+By Steve Reid <steve@edmweb.com>
+100% Public Domain
+
+Test Vectors (from FIPS PUB 180-1)
+"abc"
+ A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D
+"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"
+ 84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1
+A million repetitions of "a"
+ 34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F
+*/
+
+/* #define LITTLE_ENDIAN * This should be #define'd already, if true. */
+/* #define SHA1HANDSOFF * Copies data before messing with it. */
+
+#define SHA1HANDSOFF
+
+#include <string.h>
+
+/* for uint32_t */
+#include <stdint.h>
+
+#include "sha1.h"
+
+
+#define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits))))
+
+/* blk0() and blk() perform the initial expand. */
+/* I got the idea of expanding during the round function from SSLeay */
+#define blk0_le(i) (block->l[i] = (rol(block->l[i],24)&0xFF00FF00) \
+ |(rol(block->l[i],8)&0x00FF00FF))
+#define blk0_be(i) block->l[i]
+#if BYTE_ORDER == LITTLE_ENDIAN
+#define blk0(i) blk0_le(i)
+#elif BYTE_ORDER == BIG_ENDIAN
+#define blk0(i) blk0_be(i)
+#else
+/* Fall back to a runtime endian check */
+const union {
+ long l;
+ char c;
+} sha1_endian = { 1 };
+#define blk0(i) (sha1_endian.c == 0 ? blk0_be(i) : blk0_le(i))
+#endif
+
+#define blk(i) (block->l[i&15] = rol(block->l[(i+13)&15]^block->l[(i+8)&15] \
+ ^block->l[(i+2)&15]^block->l[i&15],1))
+
+/* (R0+R1), R2, R3, R4 are the different operations used in SHA1 */
+#define R0(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk0(i)+0x5A827999+rol(v,5);w=rol(w,30);
+#define R1(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=rol(w,30);
+#define R2(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=rol(w,30);
+#define R3(v,w,x,y,z,i) z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=rol(w,30);
+#define R4(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=rol(w,30);
+
+
+/* Hash a single 512-bit block. This is the core of the algorithm. */
+
+void SHA1Transform(
+ uint32_t state[5],
+ const unsigned char buffer[64]
+)
+{
+ uint32_t a, b, c, d, e;
+
+ typedef union
+ {
+ unsigned char c[64];
+ uint32_t l[16];
+ } CHAR64LONG16;
+
+#ifdef SHA1HANDSOFF
+ CHAR64LONG16 block[1]; /* use array to appear as a pointer */
+
+ memcpy(block, buffer, 64);
+#else
+ /* The following had better never be used because it causes the
+ * pointer-to-const buffer to be cast into a pointer to non-const.
+ * And the result is written through. I threw a "const" in, hoping
+ * this will cause a diagnostic.
+ */
+ CHAR64LONG16 *block = (const CHAR64LONG16 *) buffer;
+#endif
+ /* Copy context->state[] to working vars */
+ a = state[0];
+ b = state[1];
+ c = state[2];
+ d = state[3];
+ e = state[4];
+ /* 4 rounds of 20 operations each. Loop unrolled. */
+ R0(a, b, c, d, e, 0);
+ R0(e, a, b, c, d, 1);
+ R0(d, e, a, b, c, 2);
+ R0(c, d, e, a, b, 3);
+ R0(b, c, d, e, a, 4);
+ R0(a, b, c, d, e, 5);
+ R0(e, a, b, c, d, 6);
+ R0(d, e, a, b, c, 7);
+ R0(c, d, e, a, b, 8);
+ R0(b, c, d, e, a, 9);
+ R0(a, b, c, d, e, 10);
+ R0(e, a, b, c, d, 11);
+ R0(d, e, a, b, c, 12);
+ R0(c, d, e, a, b, 13);
+ R0(b, c, d, e, a, 14);
+ R0(a, b, c, d, e, 15);
+ R1(e, a, b, c, d, 16);
+ R1(d, e, a, b, c, 17);
+ R1(c, d, e, a, b, 18);
+ R1(b, c, d, e, a, 19);
+ R2(a, b, c, d, e, 20);
+ R2(e, a, b, c, d, 21);
+ R2(d, e, a, b, c, 22);
+ R2(c, d, e, a, b, 23);
+ R2(b, c, d, e, a, 24);
+ R2(a, b, c, d, e, 25);
+ R2(e, a, b, c, d, 26);
+ R2(d, e, a, b, c, 27);
+ R2(c, d, e, a, b, 28);
+ R2(b, c, d, e, a, 29);
+ R2(a, b, c, d, e, 30);
+ R2(e, a, b, c, d, 31);
+ R2(d, e, a, b, c, 32);
+ R2(c, d, e, a, b, 33);
+ R2(b, c, d, e, a, 34);
+ R2(a, b, c, d, e, 35);
+ R2(e, a, b, c, d, 36);
+ R2(d, e, a, b, c, 37);
+ R2(c, d, e, a, b, 38);
+ R2(b, c, d, e, a, 39);
+ R3(a, b, c, d, e, 40);
+ R3(e, a, b, c, d, 41);
+ R3(d, e, a, b, c, 42);
+ R3(c, d, e, a, b, 43);
+ R3(b, c, d, e, a, 44);
+ R3(a, b, c, d, e, 45);
+ R3(e, a, b, c, d, 46);
+ R3(d, e, a, b, c, 47);
+ R3(c, d, e, a, b, 48);
+ R3(b, c, d, e, a, 49);
+ R3(a, b, c, d, e, 50);
+ R3(e, a, b, c, d, 51);
+ R3(d, e, a, b, c, 52);
+ R3(c, d, e, a, b, 53);
+ R3(b, c, d, e, a, 54);
+ R3(a, b, c, d, e, 55);
+ R3(e, a, b, c, d, 56);
+ R3(d, e, a, b, c, 57);
+ R3(c, d, e, a, b, 58);
+ R3(b, c, d, e, a, 59);
+ R4(a, b, c, d, e, 60);
+ R4(e, a, b, c, d, 61);
+ R4(d, e, a, b, c, 62);
+ R4(c, d, e, a, b, 63);
+ R4(b, c, d, e, a, 64);
+ R4(a, b, c, d, e, 65);
+ R4(e, a, b, c, d, 66);
+ R4(d, e, a, b, c, 67);
+ R4(c, d, e, a, b, 68);
+ R4(b, c, d, e, a, 69);
+ R4(a, b, c, d, e, 70);
+ R4(e, a, b, c, d, 71);
+ R4(d, e, a, b, c, 72);
+ R4(c, d, e, a, b, 73);
+ R4(b, c, d, e, a, 74);
+ R4(a, b, c, d, e, 75);
+ R4(e, a, b, c, d, 76);
+ R4(d, e, a, b, c, 77);
+ R4(c, d, e, a, b, 78);
+ R4(b, c, d, e, a, 79);
+ /* Add the working vars back into context.state[] */
+ state[0] += a;
+ state[1] += b;
+ state[2] += c;
+ state[3] += d;
+ state[4] += e;
+ /* Wipe variables */
+ a = b = c = d = e = 0;
+#ifdef SHA1HANDSOFF
+ memset(block, '\0', sizeof(block));
+#endif
+}
+
+
+/* SHA1Init - Initialize new context */
+
+void SHA1Init(
+ SHA1_CTX * context
+)
+{
+ /* SHA1 initialization constants */
+ context->state[0] = 0x67452301;
+ context->state[1] = 0xEFCDAB89;
+ context->state[2] = 0x98BADCFE;
+ context->state[3] = 0x10325476;
+ context->state[4] = 0xC3D2E1F0;
+ context->count[0] = context->count[1] = 0;
+}
+
+
+/* Run your data through this. */
+
+void SHA1Update(
+ SHA1_CTX * context,
+ const unsigned char *data,
+ uint32_t len
+)
+{
+ uint32_t i;
+
+ uint32_t j;
+
+ j = context->count[0];
+ if ((context->count[0] += len << 3) < j)
+ context->count[1]++;
+ context->count[1] += (len >> 29);
+ j = (j >> 3) & 63;
+ if ((j + len) > 63)
+ {
+ memcpy(&context->buffer[j], data, (i = 64 - j));
+ SHA1Transform(context->state, context->buffer);
+ for (; i + 63 < len; i += 64)
+ {
+ SHA1Transform(context->state, &data[i]);
+ }
+ j = 0;
+ }
+ else
+ i = 0;
+ memcpy(&context->buffer[j], &data[i], len - i);
+}
+
+
+/* Add padding and return the message digest. */
+
+void SHA1Final(
+ unsigned char digest[20],
+ SHA1_CTX * context
+)
+{
+ unsigned i;
+
+ unsigned char finalcount[8];
+
+ unsigned char c;
+
+#if 0 /* untested "improvement" by DHR */
+ /* Convert context->count to a sequence of bytes
+ * in finalcount. Second element first, but
+ * big-endian order within element.
+ * But we do it all backwards.
+ */
+ unsigned char *fcp = &finalcount[8];
+
+ for (i = 0; i < 2; i++)
+ {
+ uint32_t t = context->count[i];
+
+ int j;
+
+ for (j = 0; j < 4; t >>= 8, j++)
+ *--fcp = (unsigned char) t}
+#else
+ for (i = 0; i < 8; i++)
+ {
+ finalcount[i] = (unsigned char) ((context->count[(i >= 4 ? 0 : 1)] >> ((3 - (i & 3)) * 8)) & 255); /* Endian independent */
+ }
+#endif
+ c = 0200;
+ SHA1Update(context, &c, 1);
+ while ((context->count[0] & 504) != 448)
+ {
+ c = 0000;
+ SHA1Update(context, &c, 1);
+ }
+ SHA1Update(context, finalcount, 8); /* Should cause a SHA1Transform() */
+ for (i = 0; i < 20; i++)
+ {
+ digest[i] = (unsigned char)
+ ((context->state[i >> 2] >> ((3 - (i & 3)) * 8)) & 255);
+ }
+ /* Wipe variables */
+ memset(context, '\0', sizeof(*context));
+ memset(&finalcount, '\0', sizeof(finalcount));
+}
+
+void SHA1(
+ char *hash_out,
+ const char *str,
+ uint32_t len)
+{
+ SHA1_CTX ctx;
+ unsigned int ii;
+
+ SHA1Init(&ctx);
+ for (ii=0; ii<len; ii+=1)
+ SHA1Update(&ctx, (const unsigned char*)str + ii, 1);
+ SHA1Final((unsigned char *)hash_out, &ctx);
+}
+