pidgin/pidgin

e87c321f05fa
Parents b7ee9469eccc
Children 9e2dd7dcdc0e
Move tzc and zeph02 to own modules and some cleanup

* Add `send_message`, `set_location`, `get_subs_from_server`, `close` to `zephyr_account`
* Remove now unused `use_tzc` and `use_zeph02`
* Format message inside `zephyr_write_message`
* Rename some functions
* Reduce scope for some variables
* Remove unused `ZEPHYR_FD_*` macros

Testing Done:
Compile.

Reviewed at https://reviews.imfreedom.org/r/471/
--- a/libpurple/protocols/zephyr/meson.build Sat Jan 30 22:33:58 2021 -0600
+++ b/libpurple/protocols/zephyr/meson.build Wed Feb 03 18:27:42 2021 -0600
@@ -53,8 +53,13 @@
'sysdep.h',
'zephyr.c',
'zephyr.h',
+ 'zephyr_account.h',
'zephyr_html.c',
- 'zephyr_html.h'
+ 'zephyr_html.h',
+ 'zephyr_tzc.c',
+ 'zephyr_tzc.h',
+ 'zephyr_zeph02.c',
+ 'zephyr_zeph02.h',
]
extdep = krb4
--- a/libpurple/protocols/zephyr/zephyr.c Sat Jan 30 22:33:58 2021 -0600
+++ b/libpurple/protocols/zephyr/zephyr.c Wed Feb 03 18:27:42 2021 -0600
@@ -36,7 +36,10 @@
#include "internal.h"
#include "zephyr.h"
+#include "zephyr_account.h"
#include "zephyr_html.h"
+#include "zephyr_tzc.h"
+#include "zephyr_zeph02.h"
#define ZEPHYR_FALLBACK_CHARSET "ISO-8859-1"
@@ -45,8 +48,6 @@
/* these are deliberately high, since most people don't send multiple "PING"s */
#define ZEPHYR_TYPING_SEND_TIMEOUT 15
#define ZEPHYR_TYPING_RECV_TIMEOUT 10
-#define ZEPHYR_FD_READ 0
-#define ZEPHYR_FD_WRITE 1
#ifndef HOST_NAME_MAX
# define HOST_NAME_MAX 255
@@ -60,46 +61,8 @@
#endif
typedef struct _zephyr_triple zephyr_triple;
-typedef struct _zephyr_account zephyr_account;
-typedef gssize (*PollableInputStreamReadFunc)(GPollableInputStream *stream, void *bufcur, GError **error);
typedef gboolean (*ZephyrLoginFunc)(zephyr_account *zephyr);
-typedef Code_t (*ZephyrSubscribeToFunc)(zephyr_account *zephyr, ZSubscription_t *sub);
-typedef gboolean (*ZephyrRequestLocationsFunc)(zephyr_account *zephyr, gchar *who);
-
-typedef enum {
- PURPLE_ZEPHYR_NONE, /* Non-kerberized ZEPH0.2 */
- PURPLE_ZEPHYR_KRB4, /* ZEPH0.2 w/ KRB4 support */
- PURPLE_ZEPHYR_TZC, /* tzc executable proxy */
- PURPLE_ZEPHYR_INTERGALACTIC_KRB4 /* Kerberized ZEPH0.3 */
-} zephyr_connection_type;
-
-struct _zephyr_account {
- PurpleAccount* account;
- char *username;
- char *realm;
- char *encoding;
- char* galaxy; /* not yet useful */
- char* krbtkfile; /* not yet useful */
- guint32 nottimer;
- guint32 loctimer;
- GList *pending_zloc_names;
- GSList *subscrips;
- int last_id;
- unsigned short port;
- char ourhost[HOST_NAME_MAX + 1];
- char ourhostcanon[HOST_NAME_MAX + 1];
- zephyr_connection_type connection_type;
- char *exposure;
- GSubprocess *tzc_proc;
- GOutputStream *tzc_stdin;
- GInputStream *tzc_stdout;
- gchar *away;
- ZephyrSubscribeToFunc subscribe_to;
- ZephyrRequestLocationsFunc request_locations;
-};
-
-#define MAXCHILDREN 20
struct _zephyr_triple {
ZSubscription_t sub;
@@ -112,59 +75,7 @@
extern const char *username;
#endif
-static char *local_zephyr_normalize(const zephyr_account *zephyr, const char *);
static void zephyr_chat_set_topic(PurpleConnection *gc, int id, const char *topic);
-static char *zephyr_tzc_deescape_str(const char *message);
-
-static inline gboolean
-use_tzc(const zephyr_account *zephyr)
-{
- return zephyr->connection_type == PURPLE_ZEPHYR_TZC;
-}
-
-static inline gboolean
-use_zeph02(const zephyr_account *zephyr)
-{
- return zephyr->connection_type == PURPLE_ZEPHYR_NONE ||
- zephyr->connection_type == PURPLE_ZEPHYR_KRB4;
-}
-
-static gboolean
-zephyr_write_message(zephyr_account *zephyr, const gchar *message)
-{
- GError *error = NULL;
- gboolean success;
-
- success = g_output_stream_write_all(zephyr->tzc_stdin, message, strlen(message),
- NULL, NULL, &error);
- if (!success) {
- purple_debug_error("zephyr", "Unable to write a message: %s", error->message);
- g_error_free(error);
- }
- return success;
-}
-
-static Code_t
-subscribe_to_tzc(zephyr_account *zephyr, ZSubscription_t *sub)
-{
- Code_t ret_val = -1;
- gchar *zsubstr;
-
- /* ((tzcfodder . subscribe) ("class" "instance" "recipient")) */
- zsubstr = g_strdup_printf("((tzcfodder . subscribe) (\"%s\" \"%s\" \"%s\"))\n",
- sub->zsub_class, sub->zsub_classinst, sub->zsub_recipient);
- if (zephyr_write_message(zephyr, zsubstr)) {
- ret_val = ZERR_NONE;
- }
- g_free(zsubstr);
- return ret_val;
-}
-
-static Code_t
-subscribe_to_zeph02(G_GNUC_UNUSED zephyr_account *zephyr, ZSubscription_t *sub)
-{
- return ZSubscribeTo(sub, 1, 0);
-}
static char *
zephyr_strip_local_realm(const zephyr_account *zephyr, const char *user)
@@ -187,25 +98,6 @@
}
}
-/* this is so bad, and if Zephyr weren't so fucked up to begin with I
- * wouldn't do this. but it is so i will. */
-
-/* just for debugging */
-static void handle_unknown(ZNotice_t *notice)
-{
- purple_debug_error("zephyr","z_packet: %s\n", notice->z_packet);
- purple_debug_error("zephyr","z_version: %s\n", notice->z_version);
- purple_debug_error("zephyr","z_kind: %d\n", (int)(notice->z_kind));
- purple_debug_error("zephyr","z_class: %s\n", notice->z_class);
- purple_debug_error("zephyr","z_class_inst: %s\n", notice->z_class_inst);
- purple_debug_error("zephyr","z_opcode: %s\n", notice->z_opcode);
- purple_debug_error("zephyr","z_sender: %s\n", notice->z_sender);
- purple_debug_error("zephyr","z_recipient: %s\n", notice->z_recipient);
- purple_debug_error("zephyr","z_message: %s\n", notice->z_message);
- purple_debug_error("zephyr","z_message_len: %d\n", notice->z_message_len);
-}
-
-
static zephyr_triple *
zephyr_triple_new(zephyr_account *zephyr, const ZSubscription_t *sub)
{
@@ -289,29 +181,31 @@
Converts strings to utf-8 if necessary using user specified encoding
*/
-static gchar *zephyr_recv_convert(PurpleConnection *gc, gchar *string)
+static gchar *
+convert_to_utf8(const gchar *string, const gchar *from_encoding)
{
gchar *utf8;
GError *err = NULL;
- zephyr_account *zephyr = purple_connection_get_protocol_data(gc);
+
if (g_utf8_validate(string, -1, NULL)) {
return g_strdup(string);
- } else {
- utf8 = g_convert(string, -1, "UTF-8", zephyr->encoding, NULL, NULL, &err);
- if (err) {
- purple_debug_error("zephyr", "recv conversion error: %s\n", err->message);
- utf8 = g_strdup(_("(There was an error converting this message. Check the 'Encoding' option in the Account Editor)"));
- g_error_free(err);
- }
+ }
- return utf8;
+ utf8 = g_convert(string, -1, "UTF-8", from_encoding, NULL, NULL, &err);
+ if (err) {
+ purple_debug_error("zephyr", "recv conversion error: %s\n", err->message);
+ utf8 = g_strdup(_("(There was an error converting this message. Check the 'Encoding' option in the Account Editor)"));
+ g_error_free(err);
}
+
+ return utf8;
}
-static gboolean pending_zloc(zephyr_account *zephyr, const char *who)
+gboolean
+pending_zloc(zephyr_account *zephyr, const char *who)
{
GList *curr;
- char* normalized_who = local_zephyr_normalize(zephyr,who);
+ char* normalized_who = zephyr_normalize_local_realm(zephyr, who);
curr = g_list_find_custom(zephyr->pending_zloc_names, normalized_who, (GCompareFunc)g_ascii_strcasecmp);
g_free(normalized_who);
@@ -323,27 +217,7 @@
return TRUE;
}
-/* Called when the server notifies us a message couldn't get sent */
-
-static void
-message_failed(PurpleConnection *gc, ZNotice_t *notice)
-{
- if (g_ascii_strcasecmp(notice->z_class, "message")) {
- gchar* chat_failed = g_strdup_printf(
- _("Unable to send to chat %s,%s,%s"),
- notice->z_class, notice->z_class_inst,
- notice->z_recipient);
- purple_notify_error(gc,"",chat_failed,NULL,
- purple_request_cpar_from_connection(gc));
- g_free(chat_failed);
- } else {
- purple_notify_error(gc, notice->z_recipient,
- _("User is offline"), NULL,
- purple_request_cpar_from_connection(gc));
- }
-}
-
-static PurpleBuddy *
+PurpleBuddy *
find_buddy(const zephyr_account *zephyr, const char *user)
{
PurpleBuddy *buddy = purple_blist_find_buddy(zephyr->account, user);
@@ -365,7 +239,8 @@
zephyr_chat_set_topic(gc, zt->id, instance);
}
-static void handle_message(PurpleConnection *gc, ZNotice_t *notice_p)
+void
+handle_message(PurpleConnection *gc, ZNotice_t *notice_p)
{
ZNotice_t notice;
zephyr_account* zephyr = purple_connection_get_protocol_data(gc);
@@ -380,6 +255,7 @@
char *user;
PurpleBuddy *b;
const char *bname;
+ const gchar *name;
/* XXX add real error reporting */
if (ZParseLocations(&notice, NULL, &nlocs, &user) != ZERR_NONE)
@@ -387,16 +263,14 @@
b = find_buddy(zephyr, user);
bname = b ? purple_buddy_get_name(b) : NULL;
+ name = b ? bname : user;
if ((b && pending_zloc(zephyr,bname)) || pending_zloc(zephyr,user)) {
- ZLocations_t locs;
- int one = 1;
PurpleNotifyUserInfo *user_info = purple_notify_user_info_new();
- char *tmp;
const char *balias;
/* TODO: Check whether it's correct to call add_pair_html,
or if we should be using add_pair_plaintext */
- purple_notify_user_info_add_pair_html(user_info, _("User"), (b ? bname : user));
+ purple_notify_user_info_add_pair_html(user_info, _("User"), name);
balias = purple_buddy_get_local_alias(b);
if (b && balias)
purple_notify_user_info_add_pair_plaintext(user_info, _("Alias"), balias);
@@ -406,6 +280,9 @@
}
for (; nlocs > 0; nlocs--) {
/* XXX add real error reporting */
+ ZLocations_t locs;
+ int one = 1;
+ char *tmp;
ZGetLocations(&locs, &one);
/* TODO: Need to escape locs.host and locs.time? */
@@ -413,61 +290,57 @@
purple_notify_user_info_add_pair_html(user_info, _("Location"), tmp);
g_free(tmp);
}
- purple_notify_userinfo(gc, (b ? bname : user),
- user_info, NULL, NULL);
+ purple_notify_userinfo(gc, name, user_info, NULL, NULL);
purple_notify_user_info_destroy(user_info);
} else {
- if (nlocs>0)
- purple_protocol_got_user_status(purple_connection_get_account(gc), b ? bname : user, "available", NULL);
- else
- purple_protocol_got_user_status(purple_connection_get_account(gc), b ? bname : user, "offline", NULL);
+ purple_protocol_got_user_status(purple_connection_get_account(gc), name, (nlocs > 0) ? "available" : "offline", NULL);
}
g_free(user);
}
} else {
- char *buf, *buf2, *buf3;
- char *send_inst;
- PurpleChatConversation *gcc;
- char *ptr = (char *) notice.z_message + (strlen(notice.z_message) + 1);
+ char *buf;
int len;
char *stripped_sender;
int signature_length = strlen(notice.z_message);
- PurpleMessageFlags flags = 0;
- gchar *tmpescape;
/* Need to deal with 0 length messages to handle typing notification (OPCODE) ping messages */
/* One field zephyrs would have caused purple to crash */
if ( (notice.z_message_len == 0) || (signature_length >= notice.z_message_len - 1)) {
len = 0;
- purple_debug_info("zephyr","message_size %d %d %d\n",len,notice.z_message_len,signature_length);
- buf3 = g_strdup("");
+ buf = g_strdup("");
} else {
+ char *tmpbuf;
+ char *ptr = (char *) notice.z_message + (strlen(notice.z_message) + 1);
+ gchar *tmpescape;
+
len = notice.z_message_len - ( signature_length +1);
- purple_debug_info("zephyr","message_size %d %d %d\n",len,notice.z_message_len,signature_length);
- buf = g_malloc(len + 1);
- g_snprintf(buf, len + 1, "%s", ptr);
- g_strchomp(buf);
- tmpescape = g_markup_escape_text(buf, -1);
- g_free(buf);
- buf2 = zephyr_to_html(tmpescape);
- buf3 = zephyr_recv_convert(gc, buf2);
- g_free(buf2);
+ tmpbuf = g_malloc(len + 1);
+ g_snprintf(tmpbuf, len + 1, "%s", ptr);
+ g_strchomp(tmpbuf);
+ tmpescape = g_markup_escape_text(tmpbuf, -1);
+ g_free(tmpbuf);
+ tmpbuf = zephyr_to_html(tmpescape);
+ buf = convert_to_utf8(tmpbuf, zephyr->encoding);
+ g_free(tmpbuf);
g_free(tmpescape);
}
+ purple_debug_info("zephyr", "message_size %d %d %d", len, notice.z_message_len, signature_length);
stripped_sender = zephyr_strip_local_realm(zephyr,notice.z_sender);
if (!g_ascii_strcasecmp(notice.z_class, "MESSAGE") && !g_ascii_strcasecmp(notice.z_class_inst, "PERSONAL")
&& !g_ascii_strcasecmp(notice.z_recipient,zephyr->username)) {
+ PurpleMessageFlags flags = 0;
+
if (!g_ascii_strcasecmp(notice.z_message, "Automated reply:"))
flags |= PURPLE_MESSAGE_AUTO_RESP;
if (!g_ascii_strcasecmp(notice.z_opcode,"PING"))
purple_serv_got_typing(gc,stripped_sender,ZEPHYR_TYPING_RECV_TIMEOUT, PURPLE_IM_TYPING);
else
- purple_serv_got_im(gc, stripped_sender, buf3, flags, time(NULL));
+ purple_serv_got_im(gc, stripped_sender, buf, flags, time(NULL));
} else {
ZSubscription_t sub = {
@@ -478,6 +351,7 @@
zephyr_triple *zt;
gchar *send_inst_utf8;
GSList *l = g_slist_find_custom(zephyr->subscrips, &sub, (GCompareFunc)zephyr_triple_subset);
+ PurpleChatConversation *gcc;
if (!l) {
/* This is a server supplied subscription */
@@ -494,8 +368,8 @@
if (!g_ascii_strcasecmp(notice.z_class_inst,"PERSONAL"))
send_inst_utf8 = g_strdup(stripped_sender);
else {
- send_inst = g_strdup_printf("[%s] %s",notice.z_class_inst,stripped_sender);
- send_inst_utf8 = zephyr_recv_convert(gc,send_inst);
+ char *send_inst = g_strdup_printf("[%s] %s", notice.z_class_inst, stripped_sender);
+ send_inst_utf8 = convert_to_utf8(send_inst, zephyr->encoding);
g_free(send_inst);
if (!send_inst_utf8) {
purple_debug_error("zephyr","Failed to convert instance for sender %s.\n", stripped_sender);
@@ -518,416 +392,53 @@
g_object_unref(inet_addr);
}
purple_serv_got_chat_in(gc, zt->id, send_inst_utf8,
- PURPLE_MESSAGE_RECV, buf3, time(NULL));
+ PURPLE_MESSAGE_RECV, buf, time(NULL));
g_free(send_inst_utf8);
}
g_free(stripped_sender);
- g_free(buf3);
- }
-}
-
-static gchar *
-tree_child_contents(GNode *tree, int index)
-{
- GNode *child = g_node_nth_child(tree, index);
- return child ? child->data : "";
-}
-
-static GNode *
-find_node(GNode *ptree, gchar *key)
-{
- guint num_children;
- gchar* tc;
-
- if (!ptree || ! key)
- return NULL;
-
- num_children = g_node_n_children(ptree);
- tc = tree_child_contents(ptree, 0);
-
- /* g_strcasecmp() is deprecated. What is the encoding here??? */
- if (num_children > 0 && tc && !g_ascii_strcasecmp(tc, key)) {
- return ptree;
- } else {
- GNode *result = NULL;
- guint i;
- for (i = 0; i < num_children; i++) {
- result = find_node(g_node_nth_child(ptree, i), key);
- if(result != NULL) {
- break;
- }
- }
- return result;
+ g_free(buf);
}
}
-static GNode *
-parse_buffer(const gchar *source, gboolean do_parse)
+static void
+check_loc_buddy(PurpleBuddy *buddy, zephyr_account *zephyr)
{
- GNode *ptree = g_node_new(NULL);
-
- if (do_parse) {
- unsigned int p = 0;
- while(p < strlen(source)) {
- unsigned int end;
- gchar *newstr;
-
- /* Eat white space: */
- if(g_ascii_isspace(source[p]) || source[p] == '\001') {
- p++;
- continue;
- }
-
- /* Skip comments */
- if(source[p] == ';') {
- while(source[p] != '\n' && p < strlen(source)) {
- p++;
- }
- continue;
- }
-
- if(source[p] == '(') {
- int nesting = 0;
- gboolean in_quote = FALSE;
- gboolean escape_next = FALSE;
- p++;
- end = p;
- while(!(source[end] == ')' && nesting == 0 && !in_quote) && end < strlen(source)) {
- if(!escape_next) {
- if(source[end] == '\\') {
- escape_next = TRUE;
- }
- if(!in_quote) {
- if(source[end] == '(') {
- nesting++;
- }
- if(source[end] == ')') {
- nesting--;
- }
- }
- if(source[end] == '"') {
- in_quote = !in_quote;
- }
- } else {
- escape_next = FALSE;
- }
- end++;
- }
- do_parse = TRUE;
-
- } else {
- gchar end_char;
- if(source[p] == '"') {
- end_char = '"';
- p++;
- } else {
- end_char = ' ';
- }
- do_parse = FALSE;
+ const char *bname = purple_buddy_get_name(buddy);
+ char *chk = zephyr_normalize_local_realm(zephyr, bname);
+#ifdef WIN32
+ int numlocs;
- end = p;
- while(source[end] != end_char && end < strlen(source)) {
- if(source[end] == '\\')
- end++;
- end++;
- }
- }
- newstr = g_new0(gchar, end+1-p);
- strncpy(newstr,source+p,end-p);
- if (g_node_n_children(ptree) < MAXCHILDREN) {
- /* In case we surpass maxchildren, ignore this */
- g_node_append(ptree, parse_buffer(newstr, do_parse));
- } else {
- purple_debug_error("zephyr","too many children in tzc output. skipping\n");
- }
- g_free(newstr);
- p = end + 1;
- }
- } else {
- /* XXX does this have to be strdup'd */
- ptree->data = g_strdup(source);
- }
-
- return ptree;
-}
-
-static gchar *
-read_from_tzc(zephyr_account *zephyr, PollableInputStreamReadFunc read_func)
-{
- GPollableInputStream *stream = G_POLLABLE_INPUT_STREAM(zephyr->tzc_stdout);
- gsize bufsize = 2048;
- gchar *buf = g_new(gchar, bufsize);
- gchar *bufcur = buf;
- gboolean selected = FALSE;
-
- while (TRUE) {
- GError *error = NULL;
- if (read_func(stream, bufcur, &error) < 0) {
- if (error->code == G_IO_ERROR_WOULD_BLOCK ||
- error->code == G_IO_ERROR_TIMED_OUT) {
- g_error_free(error);
- break;
- }
- purple_debug_error("zephyr", "couldn't read: %s", error->message);
- purple_connection_error(purple_account_get_connection(zephyr->account), PURPLE_CONNECTION_ERROR_NETWORK_ERROR, "couldn't read");
- g_error_free(error);
- g_free(buf);
- return NULL;
- }
- selected = TRUE;
- bufcur++;
- if ((bufcur - buf) > (bufsize - 1)) {
- if ((buf = g_realloc(buf, bufsize * 2)) == NULL) {
- purple_debug_error("zephyr","Ran out of memory\n");
- exit(-1);
- } else {
- bufcur = buf + bufsize;
- bufsize *= 2;
- }
- }
- }
- *bufcur = '\0';
-
- if (!selected) {
- g_free(buf);
- buf = NULL;
- }
- return buf;
-}
-
-static gssize
-pollable_input_stream_read(GPollableInputStream *stream, void *bufcur, GError **error)
-{
- return g_pollable_input_stream_read_nonblocking(stream, bufcur, 1, NULL, error);
-}
+ ZLocateUser(chk, &numlocs, ZAUTH);
+ for (int i = 0; i < numlocs; i++) {
+ ZLocations_t locations;
+ int one = 1;
-static gint check_notify_tzc(gpointer data)
-{
- PurpleConnection *gc = (PurpleConnection *)data;
- zephyr_account* zephyr = purple_connection_get_protocol_data(gc);
- GNode *newparsetree = NULL;
- gchar *buf = read_from_tzc(zephyr, pollable_input_stream_read);
-
- if (buf != NULL) {
- newparsetree = parse_buffer(buf, TRUE);
- g_free(buf);
+ ZGetLocations(&locations, &one);
+ serv_got_update(zgc, bname, 1, 0, 0, 0, 0);
}
-
- if (newparsetree != NULL) {
- gchar *spewtype;
- if ( (spewtype = tree_child_contents(find_node(newparsetree, "tzcspew"), 2)) ) {
- if (!g_ascii_strncasecmp(spewtype,"message",7)) {
- ZNotice_t notice;
- GNode *msgnode = g_node_nth_child(find_node(newparsetree, "message"), 2);
- /*char *zsig = g_strdup(" ");*/ /* purple doesn't care about zsigs */
- char *msg = zephyr_tzc_deescape_str(tree_child_contents(msgnode, 1));
- size_t bufsize = strlen(msg) + 3;
- char *buf = g_new0(char,bufsize);
- g_snprintf(buf,1+strlen(msg)+2," %c%s",'\0',msg);
- memset((char *)&notice, 0, sizeof(notice));
- notice.z_kind = ACKED;
- notice.z_port = 0;
- notice.z_opcode = tree_child_contents(find_node(newparsetree, "opcode"), 2);
- notice.z_class = zephyr_tzc_deescape_str(tree_child_contents(find_node(newparsetree, "class"), 2));
- notice.z_class_inst = tree_child_contents(find_node(newparsetree, "instance"), 2);
- notice.z_recipient = local_zephyr_normalize(zephyr, tree_child_contents(find_node(newparsetree, "recipient"), 2));
- notice.z_sender = local_zephyr_normalize(zephyr, tree_child_contents(find_node(newparsetree, "sender"), 2));
- notice.z_default_format = "Class $class, Instance $instance:\n" "To: @bold($recipient) at $time $date\n" "From: @bold($1) <$sender>\n\n$2";
- notice.z_message_len = strlen(msg) + 3;
- notice.z_message = buf;
- handle_message(gc, &notice);
- g_free(msg);
- /*g_free(zsig);*/
- g_free(buf);
- }
- else if (!g_ascii_strncasecmp(spewtype,"zlocation",9)) {
- /* check_loc or zephyr_zloc respectively */
- /* XXX fix */
- char *user;
- PurpleBuddy *b;
- const char *bname;
- const gchar *name;
- gboolean has_locations;
- GNode *locations;
- gchar *locval;
-
- user = tree_child_contents(find_node(newparsetree, "user"), 2);
- b = find_buddy(zephyr, user);
- bname = b ? purple_buddy_get_name(b) : NULL;
- name = b ? bname : user;
-
- locations = g_node_nth_child(g_node_nth_child(find_node(newparsetree, "locations"), 2), 0);
- locval = tree_child_contents(g_node_nth_child(locations, 0), 2);
- has_locations = (locval && *locval && !purple_strequal(locval, " "));
- if ((b && pending_zloc(zephyr, bname)) || pending_zloc(zephyr, user)) {
- PurpleNotifyUserInfo *user_info = purple_notify_user_info_new();
- char *tmp;
- const char *balias;
-
- /* TODO: Check whether it's correct to call add_pair_html,
- or if we should be using add_pair_plaintext */
- purple_notify_user_info_add_pair_html(user_info, _("User"), name);
-
- balias = b ? purple_buddy_get_local_alias(b) : NULL;
- if (balias)
- purple_notify_user_info_add_pair_plaintext(user_info, _("Alias"), balias);
+#else
- if (!has_locations) {
- purple_notify_user_info_add_pair_plaintext(user_info, NULL, _("Hidden or not logged-in"));
- } else {
- /* TODO: Need to escape the two strings that make up tmp? */
- tmp = g_strdup_printf(_("<br>At %s since %s"), locval,
- tree_child_contents(g_node_nth_child(locations, 2), 2));
- purple_notify_user_info_add_pair_html(user_info, _("Location"), tmp);
- g_free(tmp);
- }
-
- purple_notify_userinfo(gc, name, user_info, NULL, NULL);
- purple_notify_user_info_destroy(user_info);
- } else {
- purple_protocol_got_user_status(zephyr->account, name, has_locations ? "available" : "offline", NULL);
- }
- }
- else if (!g_ascii_strncasecmp(spewtype,"subscribed",10)) {
- }
- else if (!g_ascii_strncasecmp(spewtype,"start",5)) {
- }
- else if (!g_ascii_strncasecmp(spewtype,"error",5)) {
- /* XXX handle */
- }
- } else {
- }
- } else {
- }
-
- g_node_destroy(newparsetree);
- return TRUE;
-}
-
-static gint check_notify_zeph02(gpointer data)
-{
+ purple_debug_info("zephyr", "chk: %s, bname: %s", chk, bname);
/* XXX add real error reporting */
- PurpleConnection *gc = (PurpleConnection*) data;
- while (ZPending()) {
- ZNotice_t notice;
- /* XXX add real error reporting */
-
- if (ZReceiveNotice(&notice, NULL) != ZERR_NONE) {
- return TRUE;
- }
+ /* doesn't matter if this fails or not; we'll just move on to the next one */
+ zephyr->request_locations(zephyr, chk);
+#endif /* WIN32 */
- switch (notice.z_kind) {
- case UNSAFE:
- case UNACKED:
- case ACKED:
- handle_message(gc, &notice);
- break;
- case SERVACK:
- if (!(g_ascii_strcasecmp(notice.z_message, ZSRVACK_NOTSENT))) {
- message_failed(gc, &notice);
- }
- break;
- case CLIENTACK:
- purple_debug_error("zephyr", "Client ack received\n");
- handle_unknown(&notice); /* XXX: is it really unknown? */
- break;
- default:
- /* we'll just ignore things for now */
- handle_unknown(&notice);
- purple_debug_error("zephyr", "Unhandled notice.\n");
- break;
- }
- /* XXX add real error reporting */
- ZFreeNotice(&notice);
- }
-
- return TRUE;
+ g_free(chk);
}
static gboolean
-request_locations_tzc(zephyr_account *zephyr, gchar *who)
-{
- gchar *zlocstr;
- gboolean result;
-
- zlocstr = g_strdup_printf("((tzcfodder . zlocate) \"%s\")\n", who);
- result = zephyr_write_message(zephyr, zlocstr);
- g_free(zlocstr);
- return result;
-}
-
-static gboolean
-request_locations_zeph02(G_GNUC_UNUSED zephyr_account *zephyr, gchar *who)
+check_loc(gpointer data)
{
- ZAsyncLocateData_t ald;
- Code_t zerr;
-
- zerr = ZRequestLocations(who, &ald, UNACKED, ZAUTH);
- g_free(ald.user);
- g_free(ald.version);
- return zerr == ZERR_NONE;
-}
-
-#ifdef WIN32
-
-static gint check_loc(gpointer data)
-{
- GSList *buddies;
- ZLocations_t locations;
- PurpleConnection *gc = data;
- zephyr_account *zephyr = purple_connection_get_protocol_data(gc);
- PurpleAccount *account = purple_connection_get_account(gc);
- int numlocs;
- int one = 1;
+ zephyr_account *zephyr = (zephyr_account *)data;
+ GSList *buddies = purple_blist_find_buddies(zephyr->account, NULL);
- for (buddies = purple_blist_find_buddies(account, NULL); buddies;
- buddies = g_slist_delete_link(buddies, buddies)) {
- PurpleBuddy *b = buddies->data;
- char *chk;
- const char *bname = purple_buddy_get_name(b);
- chk = local_zephyr_normalize(zephyr, bname);
- ZLocateUser(chk,&numlocs, ZAUTH);
- if (numlocs) {
- int i;
- for(i=0;i<numlocs;i++) {
- ZGetLocations(&locations,&one);
- serv_got_update(zgc,bname,1,0,0,0,0);
- }
- }
- }
-
- return TRUE;
-}
-
-#else
+ g_slist_foreach(buddies, (GFunc)check_loc_buddy, zephyr);
+ g_slist_free(buddies);
-static gint check_loc(gpointer data)
-{
- GSList *buddies;
- PurpleConnection *gc = (PurpleConnection *)data;
- zephyr_account *zephyr = purple_connection_get_protocol_data(gc);
- PurpleAccount *account = purple_connection_get_account(gc);
-
- for (buddies = purple_blist_find_buddies(account, NULL); buddies;
- buddies = g_slist_delete_link(buddies, buddies)) {
- PurpleBuddy *b = buddies->data;
-
- char *chk;
- const char *name = purple_buddy_get_name(b);
-
- chk = local_zephyr_normalize(zephyr,name);
- purple_debug_info("zephyr","chk: %s b->name %s\n",chk,name);
- /* XXX add real error reporting */
- /* doesn't matter if this fails or not; we'll just move on to the next one */
- zephyr->request_locations(zephyr, chk);
- g_free(chk);
- }
-
- return TRUE;
+ return G_SOURCE_CONTINUE;
}
-#endif /* WIN32 */
-
static const gchar *
get_exposure_level(void)
{
@@ -1060,7 +571,7 @@
/* There should be some sort of error report listing classes that couldn't be subbed to.
Not important right now though */
- if (zephyr->subscribe_to(zephyr, &sub) != ZERR_NONE) {
+ if (!zephyr->subscribe_to(zephyr, &sub)) {
purple_debug_error("zephyr", "Couldn't subscribe to %s, %s, %s\n",
sub.zsub_class, sub.zsub_classinst, sub.zsub_recipient);
}
@@ -1131,110 +642,7 @@
return g_strdup(EXPOSE_REALMVIS);
}
-static gssize
-pollable_input_stream_read_with_timeout(GPollableInputStream *stream,
- void *bufcur, GError **error)
-{
- const gint64 timeout = 10 * G_USEC_PER_SEC;
- gint64 now = g_get_monotonic_time();
-
- while (g_get_monotonic_time() < now + timeout) {
- GError *local_error = NULL;
- gssize ret = g_pollable_input_stream_read_nonblocking(
- stream, bufcur, 1, NULL, &local_error);
- if (ret == 1) {
- return ret;
- } else {
- if (local_error->code == G_IO_ERROR_WOULD_BLOCK) {
- /* Keep on waiting if this is a blocking error. */
- g_clear_error(&local_error);
- } else {
- g_propagate_error(error, local_error);
- return ret;
- }
- }
- }
-
- g_set_error_literal(error, G_IO_ERROR, G_IO_ERROR_TIMED_OUT,
- "tzc did not respond in time");
- return -1;
-}
-
-static GSubprocess *
-get_tzc_process(const zephyr_account *zephyr)
-{
- GSubprocess *tzc_process = NULL;
- const gchar *tzc_cmd;
- gchar **tzc_cmd_array = NULL;
- GError *error = NULL;
- gboolean found_ps = FALSE;
- gint i;
-
- /* tzc_command should really be of the form
- path/to/tzc -e %s
- or
- ssh username@hostname pathtotzc -e %s
- -- this should not require a password, and ideally should be
- kerberized ssh --
- or
- fsh username@hostname pathtotzc -e %s
- */
- tzc_cmd = purple_account_get_string(zephyr->account, "tzc_command", "/usr/bin/tzc -e %s");
- if (!g_shell_parse_argv(tzc_cmd, NULL, &tzc_cmd_array, &error)) {
- purple_debug_error("zephyr", "Unable to parse tzc_command: %s", error->message);
- purple_connection_error(
- purple_account_get_connection(zephyr->account),
- PURPLE_CONNECTION_ERROR_INVALID_SETTINGS,
- "invalid tzc_command setting");
- g_error_free(error);
- return NULL;
- }
- for (i = 0; tzc_cmd_array[i] != NULL; i++) {
- if (purple_strequal(tzc_cmd_array[i], "%s")) {
- g_free(tzc_cmd_array[i]);
- tzc_cmd_array[i] = g_strdup(zephyr->exposure);
- found_ps = TRUE;
- }
- }
-
- if (!found_ps) {
- purple_debug_error("zephyr", "tzc exited early");
- purple_connection_error(
- purple_account_get_connection(zephyr->account),
- PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
- "invalid output by tzc (or bad parsing code)");
- g_strfreev(tzc_cmd_array);
- return NULL;
- }
-
- tzc_process = g_subprocess_newv(
- (const gchar *const *)tzc_cmd_array,
- G_SUBPROCESS_FLAGS_STDIN_PIPE | G_SUBPROCESS_FLAGS_STDOUT_PIPE,
- &error);
- if (tzc_process == NULL) {
- purple_debug_error("zephyr", "tzc exited early: %s", error->message);
- purple_connection_error(
- purple_account_get_connection(zephyr->account),
- PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
- "invalid output by tzc (or bad parsing code)");
- g_error_free(error);
- }
-
- g_strfreev(tzc_cmd_array);
- return tzc_process;
-}
-
-static gint
-get_paren_level(gint paren_level, gchar ch)
-{
- switch (ch) {
- case '(': return paren_level + 1;
- case ')': return paren_level - 1;
- default: return paren_level;
- }
-}
-
-static gchar *
+gchar *
get_zephyr_realm(PurpleAccount *account, const gchar *local_realm)
{
const char *realm = purple_account_get_string(account, "realm", "");
@@ -1245,151 +653,6 @@
return g_strdup(realm);
}
-static void
-parse_tzc_login_data(zephyr_account *zephyr, const gchar *buf, gint buflen)
-{
- gchar *str = g_strndup(buf, buflen);
-
- purple_debug_info("zephyr", "tempstr parsed");
-
- /* str should now be a string containing all characters from
- * buf after the first ( to the one before the last paren ).
- * We should have the following possible lisp strings but we don't care
- * (tzcspew . start) (version . "something") (pid . number)
- * We care about 'zephyrid . "username@REALM.NAME"' and
- * 'exposure . "SOMETHING"' */
- if (!g_ascii_strncasecmp(str, "zephyrid", 8)) {
- gchar **strv;
- gchar *username;
- const char *at;
-
- purple_debug_info("zephyr", "zephyrid found");
-
- strv = g_strsplit(str + 8, "\"", -1);
- username = strv[1] ? strv[1] : "";
- zephyr->username = g_strdup(username);
-
- at = strchr(username, '@');
- if (at != NULL) {
- zephyr->realm = g_strdup(at + 1);
- } else {
- zephyr->realm = get_zephyr_realm(zephyr->account, "local-realm");
- }
-
- g_strfreev(strv);
- } else {
- purple_debug_info("zephyr", "something that's not zephyr id found %s", str);
- }
-
- /* We don't care about anything else yet */
- g_free(str);
-}
-
-static gboolean
-login_tzc(zephyr_account *zephyr)
-{
- gchar *buf = NULL;
- const gchar *bufend = NULL;
- const gchar *ptr;
- const gchar *tmp;
- gint parenlevel = 0;
-
- zephyr->tzc_proc = get_tzc_process(zephyr);
- if (zephyr->tzc_proc == NULL) {
- return FALSE;
- }
- zephyr->tzc_stdin = g_subprocess_get_stdin_pipe(zephyr->tzc_proc);
- zephyr->tzc_stdout = g_subprocess_get_stdout_pipe(zephyr->tzc_proc);
-
- purple_debug_info("zephyr", "about to read from tzc");
- buf = read_from_tzc(zephyr, pollable_input_stream_read_with_timeout);
- if (buf == NULL) {
- return FALSE;
- }
- bufend = buf + strlen(buf);
- ptr = buf;
-
- /* ignore all tzcoutput till we've received the first ( */
- while (ptr < bufend && (*ptr != '(')) {
- ptr++;
- }
- if (ptr >= bufend) {
- purple_connection_error(
- purple_account_get_connection(zephyr->account),
- PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
- "invalid output by tzc (or bad parsing code)");
- g_free(buf);
- return FALSE;
- }
-
- do {
- parenlevel = get_paren_level(parenlevel, *ptr);
- purple_debug_info("zephyr", "tzc parenlevel is %d", parenlevel);
- switch (parenlevel) {
- case 1:
- /* Search for next beginning (, or for the ending */
- do {
- ptr++;
- } while ((ptr < bufend) && (*ptr != '(') && (*ptr != ')'));
- if (ptr >= bufend) {
- purple_debug_error("zephyr", "tzc parsing error");
- }
- break;
- case 2:
- /* You are probably at
- (foo . bar ) or (foo . "bar") or (foo . chars) or (foo . numbers) or (foo . () )
- Parse all the data between the first and last f, and move past )
- */
- tmp = ptr;
- do {
- ptr++;
- parenlevel = get_paren_level(parenlevel, *ptr);
- } while (parenlevel > 1);
- parse_tzc_login_data(zephyr, tmp + 1, ptr - tmp);
- ptr++;
- break;
- default:
- purple_debug_info("zephyr", "parenlevel is not 1 or 2");
- /* This shouldn't be happening */
- break;
- }
- } while (ptr < bufend && parenlevel != 0);
- purple_debug_info("zephyr", "tzc startup done");
- g_free(buf);
-
- return TRUE;
-}
-
-static gboolean
-login_zeph02(zephyr_account *zephyr)
-{
- PurpleConnection *pc = purple_account_get_connection(zephyr->account);
-
- /* XXX Should actually try to report the com_err determined error */
- if (ZInitialize() != ZERR_NONE) {
- purple_connection_error(pc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
- "Couldn't initialize zephyr");
- return FALSE;
- }
-
- if (ZOpenPort(&(zephyr->port)) != ZERR_NONE) {
- purple_connection_error(pc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
- "Couldn't open port");
- return FALSE;
- }
-
- if (ZSetLocation(zephyr->exposure) != ZERR_NONE) {
- purple_connection_error(pc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
- "Couldn't set location");
- return FALSE;
- }
-
- zephyr->username = g_strdup(ZGetSender());
- zephyr->realm = get_zephyr_realm(zephyr->account, ZGetRealm());
-
- return TRUE;
-}
-
static void zephyr_login(PurpleAccount * account)
{
PurpleConnection *gc;
@@ -1414,16 +677,24 @@
if (purple_account_get_bool(account, "use_tzc", FALSE)) {
zephyr->connection_type = PURPLE_ZEPHYR_TZC;
- login = login_tzc;
- check_notify = check_notify_tzc;
- zephyr->subscribe_to = subscribe_to_tzc;
- zephyr->request_locations = request_locations_tzc;
+ login = tzc_login;
+ check_notify = tzc_check_notify;
+ zephyr->subscribe_to = tzc_subscribe_to;
+ zephyr->request_locations = tzc_request_locations;
+ zephyr->send_message = tzc_send_message;
+ zephyr->set_location = tzc_set_location;
+ zephyr->get_subs_from_server = tzc_get_subs_from_server;
+ zephyr->close = tzc_close;
} else {
zephyr->connection_type = PURPLE_ZEPHYR_KRB4;
- login = login_zeph02;
- check_notify = check_notify_zeph02;
- zephyr->subscribe_to = subscribe_to_zeph02;
- zephyr->request_locations = request_locations_zeph02;
+ login = zeph02_login;
+ check_notify = zeph02_check_notify;
+ zephyr->subscribe_to = zeph02_subscribe_to;
+ zephyr->request_locations = zeph02_request_locations;
+ zephyr->send_message = zeph02_send_message;
+ zephyr->set_location = zeph02_set_location;
+ zephyr->get_subs_from_server = zeph02_get_subs_from_server;
+ zephyr->close = zeph02_close;
}
zephyr->encoding = (char *)purple_account_get_string(account, "encoding", ZEPHYR_FALLBACK_CHARSET);
@@ -1444,7 +715,7 @@
sub.zsub_classinst = "PERSONAL";
sub.zsub_recipient = zephyr->username;
- if (zephyr->subscribe_to(zephyr, &sub) != ZERR_NONE) {
+ if (!zephyr->subscribe_to(zephyr, &sub)) {
/* XXX don't translate this yet. It could be written better */
/* XXX error messages could be handled with more detail */
purple_notify_error(gc, NULL,
@@ -1463,7 +734,7 @@
}
zephyr->nottimer = g_timeout_add(100, check_notify, gc);
- zephyr->loctimer = g_timeout_add_seconds(20, check_loc, gc);
+ zephyr->loctimer = g_timeout_add_seconds(20, check_loc, zephyr);
}
static void write_zsubs(zephyr_account *zephyr)
@@ -1581,40 +852,35 @@
if (zephyr->loctimer)
g_source_remove(zephyr->loctimer);
zephyr->loctimer = 0;
- if (use_zeph02(zephyr)) {
- if (ZCancelSubscriptions(0) != ZERR_NONE) {
- return;
- }
- if (ZUnsetLocation() != ZERR_NONE) {
- return;
- }
- if (ZClosePort() != ZERR_NONE) {
- return;
- }
- } else {
- /* assume tzc */
-#ifdef G_OS_UNIX
- GError *error = NULL;
- g_subprocess_send_signal(zephyr->tzc_proc, SIGTERM);
- if (!g_subprocess_wait(zephyr->tzc_proc, NULL, &error)) {
- purple_debug_error("zephyr",
- "error while attempting to close tzc: %s",
- error->message);
- g_error_free(error);
- }
-#else
- g_subprocess_force_exit(zephyr->tzc_proc);
-#endif
- zephyr->tzc_stdin = NULL;
- zephyr->tzc_stdout = NULL;
- g_clear_object(&zephyr->tzc_proc);
- }
+ zephyr->close(zephyr);
}
-static gboolean zephyr_send_message(zephyr_account *zephyr, gchar *zclass,
- gchar *instance, gchar *recipient,
- const gchar *im, const gchar *sig,
- gchar *opcode);
+static gboolean
+zephyr_send_message(zephyr_account *zephyr, gchar *zclass, gchar *instance,
+ gchar *recipient, const gchar *im, const gchar *sig,
+ gchar *opcode)
+{
+ /* (From the tzc source)
+ * emacs sends something of the form:
+ * ((class . "MESSAGE")
+ * (auth . t)
+ * (recipients ("PERSONAL" . "bovik") ("test" . ""))
+ * (sender . "bovik")
+ * (message . ("Harry Bovik" "my zgram"))
+ * )
+ */
+ char *tmp_buf;
+ char *html_buf;
+ gboolean result;
+
+ tmp_buf = html_to_zephyr(im);
+ html_buf = purple_unescape_html(tmp_buf);
+ g_free(tmp_buf);
+
+ result = zephyr->send_message(zephyr, zclass, instance, recipient, html_buf, sig, opcode);
+ g_free(html_buf);
+ return result;
+}
static gint
zephyr_triple_cmp_id(gconstpointer data, gconstpointer user_data)
@@ -1661,9 +927,9 @@
inst = g_strdup("PERSONAL");
if (!g_ascii_strcasecmp(zt->sub.zsub_recipient, "*")) {
- recipient = local_zephyr_normalize(zephyr, "");
+ recipient = zephyr_normalize_local_realm(zephyr, "");
} else {
- recipient = local_zephyr_normalize(zephyr, zt->sub.zsub_recipient);
+ recipient = zephyr_normalize_local_realm(zephyr, zt->sub.zsub_recipient);
}
zephyr_send_message(zephyr, zt->sub.zsub_class, inst, recipient,
purple_message_get_contents(msg), sig, "");
@@ -1682,133 +948,16 @@
sig = zephyr_get_signature();
}
zephyr_send_message(zephyr, "MESSAGE", "PERSONAL",
- local_zephyr_normalize(zephyr, purple_message_get_recipient(msg)),
+ zephyr_normalize_local_realm(zephyr, purple_message_get_recipient(msg)),
purple_message_get_contents(msg), sig, "");
return 1;
}
-/* Munge the outgoing zephyr so that any quotes or backslashes are
- escaped and do not confuse tzc: */
-static char *
-zephyr_tzc_escape_msg(const char *message)
-{
- gsize msglen;
- char *newmsg;
-
- if (!message || !*message) {
- return g_strdup("");
- }
-
- msglen = strlen(message);
- newmsg = g_new0(char, msglen*2 + 1);
- for (gsize pos = 0, pos2 = 0; pos < msglen; pos++, pos2++) {
- if (message[pos] == '\\' || message[pos] == '"') {
- newmsg[pos2] = '\\';
- pos2++;
- }
- newmsg[pos2] = message[pos];
- }
-
- return newmsg;
-}
-
-static char *
-zephyr_tzc_deescape_str(const char *message)
-{
- gsize msglen;
- char *newmsg;
-
- if (!message || !*message) {
- return g_strdup("");
- }
-
- msglen = strlen(message);
- newmsg = g_new0(char, msglen + 1);
- for (gsize pos = 0, pos2 = 0; pos < msglen; pos++, pos2++) {
- if (message[pos] == '\\') {
- pos++;
- }
- newmsg[pos2] = message[pos];
- }
-
- return newmsg;
-}
-
-static gboolean
-zephyr_send_message(zephyr_account *zephyr, gchar *zclass, gchar *instance,
- gchar *recipient, const gchar *im, const gchar *sig,
- gchar *opcode)
-{
-
- /* (From the tzc source)
- * emacs sends something of the form:
- * ((class . "MESSAGE")
- * (auth . t)
- * (recipients ("PERSONAL" . "bovik") ("test" . ""))
- * (sender . "bovik")
- * (message . ("Harry Bovik" "my zgram"))
- * )
- */
- char *tmp_buf;
- char *html_buf;
- tmp_buf = html_to_zephyr(im);
- html_buf = purple_unescape_html(tmp_buf);
- g_free(tmp_buf);
-
- if(use_tzc(zephyr)) {
- char* zsendstr;
- /* CMU cclub tzc doesn't grok opcodes for now */
- char* tzc_sig = zephyr_tzc_escape_msg(sig);
- char *tzc_body = zephyr_tzc_escape_msg(html_buf);
- zsendstr = g_strdup_printf("((tzcfodder . send) (class . \"%s\") (auth . t) (recipients (\"%s\" . \"%s\")) (message . (\"%s\" \"%s\")) ) \n",
- zclass, instance, recipient, tzc_sig, tzc_body);
-
- if (!zephyr_write_message(zephyr, zsendstr)) {
- g_free(tzc_sig);
- g_free(tzc_body);
- g_free(zsendstr);
- g_free(html_buf);
- return FALSE;
- }
- g_free(tzc_sig);
- g_free(tzc_body);
- g_free(zsendstr);
- } else if (use_zeph02(zephyr)) {
- ZNotice_t notice;
- char *buf = g_strdup_printf("%s%c%s", sig, '\0', html_buf);
- memset((char *)&notice, 0, sizeof(notice));
-
- notice.z_kind = ACKED;
- notice.z_port = 0;
- notice.z_class = zclass;
- notice.z_class_inst = instance;
- notice.z_recipient = recipient;
- notice.z_sender = 0;
- notice.z_default_format = "Class $class, Instance $instance:\n" "To: @bold($recipient) at $time $date\n" "From: @bold($1) <$sender>\n\n$2";
- notice.z_message_len = strlen(html_buf) + strlen(sig) + 2;
- notice.z_message = buf;
- notice.z_opcode = g_strdup(opcode);
- purple_debug_info("zephyr","About to send notice\n");
- if (ZSendNotice(&notice, ZAUTH) != ZERR_NONE) {
- /* XXX handle errors here */
- g_free(buf);
- g_free(html_buf);
- return FALSE;
- }
- purple_debug_info("zephyr","notice sent\n");
- g_free(buf);
- }
-
- g_free(html_buf);
-
- return TRUE;
-}
-
/* Basically the inverse of zephyr_strip_local_realm */
-static char *
-local_zephyr_normalize(const zephyr_account *zephyr, const char *orig)
+char *
+zephyr_normalize_local_realm(const zephyr_account *zephyr, const char *orig)
{
if (*orig == '\0' || strchr(orig, '@')) {
return g_strdup(orig);
@@ -1835,7 +984,7 @@
if (gc == NULL)
return NULL;
- tmp = local_zephyr_normalize(purple_connection_get_protocol_data(gc), who);
+ tmp = zephyr_normalize_local_realm(purple_connection_get_protocol_data(gc), who);
if (strlen(tmp) >= sizeof(buf)) {
g_free(tmp);
@@ -1853,7 +1002,7 @@
const gchar *who)
{
zephyr_account *zephyr = purple_connection_get_protocol_data(gc);
- gchar *normalized_who = local_zephyr_normalize(zephyr, who);
+ gchar *normalized_who = zephyr_normalize_local_realm(zephyr, who);
if (zephyr->request_locations(zephyr, normalized_who)) {
zephyr->pending_zloc_names = g_list_append(zephyr->pending_zloc_names, normalized_who);
@@ -1864,20 +1013,6 @@
}
static void
-zephyr_set_location(zephyr_account *zephyr, char *exposure)
-{
- if (use_zeph02(zephyr)) {
- ZSetLocation(exposure);
- }
- else {
- gchar *zexpstr = g_strdup_printf("((tzcfodder . set-location) (hostname . \"%s\") (exposure . \"%s\"))\n",
- zephyr->ourhost, exposure);
- zephyr_write_message(zephyr, zexpstr);
- g_free(zexpstr);
- }
-}
-
-static void
zephyr_set_status(PurpleProtocolServer *protocol_server,
PurpleAccount *account, PurpleStatus *status)
{
@@ -1892,11 +1027,11 @@
zephyr->away = g_strdup(purple_status_get_attr_string(status,"message"));
}
else if (primitive == PURPLE_STATUS_AVAILABLE) {
- zephyr_set_location(zephyr, zephyr->exposure);
+ zephyr->set_location(zephyr, zephyr->exposure);
}
else if (primitive == PURPLE_STATUS_INVISIBLE) {
/* XXX handle errors */
- zephyr_set_location(zephyr, EXPOSE_OPSTAFF);
+ zephyr->set_location(zephyr, EXPOSE_OPSTAFF);
}
}
@@ -2028,7 +1163,7 @@
return;
}
- if (zephyr->subscribe_to(zephyr, sub) != ZERR_NONE) {
+ if (!zephyr->subscribe_to(zephyr, sub)) {
/* Called when the server notifies us a message couldn't get sent */
/* XXX output better subscription information */
gchar *subscribe_failed = g_strdup_printf(_("Attempt to subscribe to %s,%s,%s failed"),
@@ -2129,7 +1264,7 @@
/* XXX We probably should care if this fails. Or maybe we don't want to */
if (!who) {
purple_debug_info("zephyr", "who is null\n");
- recipient = local_zephyr_normalize(zephyr, "");
+ recipient = zephyr_normalize_local_realm(zephyr, "");
} else {
char *comma = strrchr(who, ',');
/* Don't ping broadcast (chat) recipients */
@@ -2139,7 +1274,7 @@
return 0;
}
- recipient = local_zephyr_normalize(zephyr, who);
+ recipient = zephyr_normalize_local_realm(zephyr, who);
}
purple_debug_info("zephyr", "about to send typing notification to %s", recipient);
@@ -2172,7 +1307,7 @@
gcc = purple_conversations_find_chat_with_account(zt->name, purple_connection_get_account(gc));
- topic_utf8 = zephyr_recv_convert(gc,(gchar *)topic);
+ topic_utf8 = convert_to_utf8(topic, zephyr->encoding);
purple_chat_conversation_set_topic(gcc, zephyr->username, topic_utf8);
g_free(topic_utf8);
}
@@ -2196,7 +1331,7 @@
if (!g_ascii_strcasecmp(args[0],"*"))
return PURPLE_CMD_RET_FAILED; /* "*" is not a valid argument */
else
- recipient = local_zephyr_normalize(zephyr,args[0]);
+ recipient = zephyr_normalize_local_realm(zephyr, args[0]);
if (strlen(recipient) < 1) {
g_free(recipient);
@@ -2411,46 +1546,7 @@
{
PurpleConnection *gc = action->connection;
zephyr_account *zephyr = purple_connection_get_protocol_data(gc);
- gchar *title;
- int retval, nsubs, one,i;
- ZSubscription_t subs;
- if (use_zeph02(zephyr)) {
- GString *subout;
-
- if (zephyr->port == 0) {
- purple_debug_error("zephyr", "error while retrieving port\n");
- return;
- }
- if ((retval = ZRetrieveSubscriptions(zephyr->port,&nsubs)) != ZERR_NONE) {
- /* XXX better error handling */
- purple_debug_error("zephyr", "error while retrieving subscriptions from server\n");
- return;
- }
-
- title = g_strdup_printf("Server subscriptions for %s",
- zephyr->username);
- subout = g_string_new("Subscription list<br>");
- for(i=0;i<nsubs;i++) {
- one = 1;
- if ((retval = ZGetSubscriptions(&subs,&one)) != ZERR_NONE) {
- /* XXX better error handling */
- g_free(title);
- g_string_free(subout, TRUE);
- purple_debug_error("zephyr", "error while retrieving individual subscription\n");
- return;
- }
- g_string_append_printf(subout, "Class %s Instance %s Recipient %s<br>",
- subs.zsub_class, subs.zsub_classinst,
- subs.zsub_recipient);
- }
- purple_notify_formatted(gc, title, title, NULL, subout->str, NULL, NULL);
- g_free(title);
- g_string_free(subout, TRUE);
- } else {
- /* XXX fix */
- purple_notify_error(gc, "", "tzc doesn't support this action",
- NULL, purple_request_cpar_from_connection(gc));
- }
+ zephyr->get_subs_from_server(zephyr, gc);
}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/protocols/zephyr/zephyr_account.h Wed Feb 03 18:27:42 2021 -0600
@@ -0,0 +1,84 @@
+/*
+ * Purple - Internet Messaging Library
+ * Copyright (C) Pidgin Developers <devel@pidgin.im>
+ *
+ * Purple is the legal property of its developers, whose names are too numerous
+ * to list here. Please refer to the COPYRIGHT file distributed with this
+ * source distribution.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef PURPLE_ZEPHYR_ZEPHYR_ACCOUNT_H
+#define PURPLE_ZEPHYR_ZEPHYR_ACCOUNT_H
+
+#include <glib.h>
+
+#include <purple.h>
+
+#include "internal.h"
+
+typedef struct _zephyr_account zephyr_account;
+
+typedef enum {
+ PURPLE_ZEPHYR_NONE, /* Non-kerberized ZEPH0.2 */
+ PURPLE_ZEPHYR_KRB4, /* ZEPH0.2 w/ KRB4 support */
+ PURPLE_ZEPHYR_TZC, /* tzc executable proxy */
+ PURPLE_ZEPHYR_INTERGALACTIC_KRB4 /* Kerberized ZEPH0.3 */
+} zephyr_connection_type;
+
+typedef gboolean (*ZephyrSubscribeToFunc)(zephyr_account *zephyr, ZSubscription_t *sub);
+typedef gboolean (*ZephyrRequestLocationsFunc)(zephyr_account *zephyr, gchar *who);
+typedef gboolean (*ZephyrSendMessageFunc)(zephyr_account *zephyr, gchar *zclass, gchar *instance, gchar *recipient,
+ const gchar *html_buf, const gchar *sig, const gchar *opcode);
+typedef void (*ZephyrSetLocationFunc)(zephyr_account *zephyr, char *exposure);
+typedef void (*ZephyrGetSubsFromServerFunc)(zephyr_account *zephyr, PurpleConnection *gc);
+typedef void (*ZephyrCloseFunc)(zephyr_account *zephyr);
+
+struct _zephyr_account {
+ PurpleAccount* account;
+ char *username;
+ char *realm;
+ char *encoding;
+ char* galaxy; /* not yet useful */
+ char* krbtkfile; /* not yet useful */
+ guint32 nottimer;
+ guint32 loctimer;
+ GList *pending_zloc_names;
+ GSList *subscrips;
+ int last_id;
+ unsigned short port;
+ char ourhost[HOST_NAME_MAX + 1];
+ char ourhostcanon[HOST_NAME_MAX + 1];
+ zephyr_connection_type connection_type;
+ char *exposure;
+ GSubprocess *tzc_proc;
+ GOutputStream *tzc_stdin;
+ GInputStream *tzc_stdout;
+ gchar *away;
+ ZephyrSubscribeToFunc subscribe_to;
+ ZephyrRequestLocationsFunc request_locations;
+ ZephyrSendMessageFunc send_message;
+ ZephyrSetLocationFunc set_location;
+ ZephyrGetSubsFromServerFunc get_subs_from_server;
+ ZephyrCloseFunc close;
+};
+
+gchar *get_zephyr_realm(PurpleAccount *account, const gchar *local_realm);
+void handle_message(PurpleConnection *gc, ZNotice_t *notice_p);
+gboolean pending_zloc(zephyr_account *zephyr, const char *who);
+PurpleBuddy *find_buddy(const zephyr_account *zephyr, const char *user);
+char *zephyr_normalize_local_realm(const zephyr_account *zephyr, const char *orig);
+
+#endif /* PURPLE_ZEPHYR_ZEPHYR_ACCOUNT_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/protocols/zephyr/zephyr_tzc.c Wed Feb 03 18:27:42 2021 -0600
@@ -0,0 +1,657 @@
+/*
+ * Purple - Internet Messaging Library
+ * Copyright (C) Pidgin Developers <devel@pidgin.im>
+ *
+ * Purple is the legal property of its developers, whose names are too numerous
+ * to list here. Please refer to the COPYRIGHT file distributed with this
+ * source distribution.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <glib/gi18n-lib.h>
+
+#include "zephyr_tzc.h"
+
+#define MAXCHILDREN 20
+
+typedef gssize (*PollableInputStreamReadFunc)(GPollableInputStream *stream, void *bufcur, GError **error);
+
+static gboolean tzc_write(zephyr_account *zephyr, const gchar *format, ...) G_GNUC_PRINTF(2, 3);
+
+static gchar *
+tzc_read(zephyr_account *zephyr, PollableInputStreamReadFunc read_func)
+{
+ GPollableInputStream *stream = G_POLLABLE_INPUT_STREAM(zephyr->tzc_stdout);
+ gsize bufsize = 2048;
+ gchar *buf = g_new(gchar, bufsize);
+ gchar *bufcur = buf;
+ gboolean selected = FALSE;
+
+ while (TRUE) {
+ GError *error = NULL;
+ if (read_func(stream, bufcur, &error) < 0) {
+ if (error->code == G_IO_ERROR_WOULD_BLOCK ||
+ error->code == G_IO_ERROR_TIMED_OUT) {
+ g_error_free(error);
+ break;
+ }
+ purple_debug_error("zephyr", "couldn't read: %s", error->message);
+ purple_connection_error(purple_account_get_connection(zephyr->account), PURPLE_CONNECTION_ERROR_NETWORK_ERROR, "couldn't read");
+ g_error_free(error);
+ g_free(buf);
+ return NULL;
+ }
+ selected = TRUE;
+ bufcur++;
+ if ((bufcur - buf) > (bufsize - 1)) {
+ if ((buf = g_realloc(buf, bufsize * 2)) == NULL) {
+ purple_debug_error("zephyr","Ran out of memory\n");
+ exit(-1);
+ } else {
+ bufcur = buf + bufsize;
+ bufsize *= 2;
+ }
+ }
+ }
+ *bufcur = '\0';
+
+ if (!selected) {
+ g_free(buf);
+ buf = NULL;
+ }
+ return buf;
+}
+
+static gboolean
+tzc_write(zephyr_account *zephyr, const gchar *format, ...)
+{
+ va_list args;
+ gchar *message;
+ GError *error = NULL;
+ gboolean success;
+
+ va_start(args, format);
+ message = g_strdup_vprintf(format, args);
+ va_end(args);
+
+ success = g_output_stream_write_all(zephyr->tzc_stdin, message, strlen(message),
+ NULL, NULL, &error);
+ if (!success) {
+ purple_debug_error("zephyr", "Unable to write a message: %s", error->message);
+ g_error_free(error);
+ }
+ g_free(message);
+ return success;
+}
+
+/* Munge the outgoing zephyr so that any quotes or backslashes are
+ escaped and do not confuse tzc: */
+static char *
+tzc_escape_msg(const char *message)
+{
+ gsize msglen;
+ char *newmsg;
+
+ if (!message || !*message) {
+ return g_strdup("");
+ }
+
+ msglen = strlen(message);
+ newmsg = g_new0(char, msglen*2 + 1);
+ for (gsize pos = 0, pos2 = 0; pos < msglen; pos++, pos2++) {
+ if (message[pos] == '\\' || message[pos] == '"') {
+ newmsg[pos2] = '\\';
+ pos2++;
+ }
+ newmsg[pos2] = message[pos];
+ }
+
+ return newmsg;
+}
+
+static char *
+tzc_deescape_str(const char *message)
+{
+ gsize msglen;
+ char *newmsg;
+
+ if (!message || !*message) {
+ return g_strdup("");
+ }
+
+ msglen = strlen(message);
+ newmsg = g_new0(char, msglen + 1);
+ for (gsize pos = 0, pos2 = 0; pos < msglen; pos++, pos2++) {
+ if (message[pos] == '\\') {
+ pos++;
+ }
+ newmsg[pos2] = message[pos];
+ }
+
+ return newmsg;
+}
+
+static GSubprocess *
+get_tzc_process(const zephyr_account *zephyr)
+{
+ GSubprocess *tzc_process = NULL;
+ const gchar *tzc_cmd;
+ gchar **tzc_cmd_array = NULL;
+ GError *error = NULL;
+ gboolean found_ps = FALSE;
+ gint i;
+
+ /* tzc_command should really be of the form
+ path/to/tzc -e %s
+ or
+ ssh username@hostname pathtotzc -e %s
+ -- this should not require a password, and ideally should be
+ kerberized ssh --
+ or
+ fsh username@hostname pathtotzc -e %s
+ */
+ tzc_cmd = purple_account_get_string(zephyr->account, "tzc_command", "/usr/bin/tzc -e %s");
+ if (!g_shell_parse_argv(tzc_cmd, NULL, &tzc_cmd_array, &error)) {
+ purple_debug_error("zephyr", "Unable to parse tzc_command: %s", error->message);
+ purple_connection_error(
+ purple_account_get_connection(zephyr->account),
+ PURPLE_CONNECTION_ERROR_INVALID_SETTINGS,
+ "invalid tzc_command setting");
+ g_error_free(error);
+ return NULL;
+ }
+ for (i = 0; tzc_cmd_array[i] != NULL; i++) {
+ if (purple_strequal(tzc_cmd_array[i], "%s")) {
+ g_free(tzc_cmd_array[i]);
+ tzc_cmd_array[i] = g_strdup(zephyr->exposure);
+ found_ps = TRUE;
+ }
+ }
+
+ if (!found_ps) {
+ purple_debug_error("zephyr", "tzc exited early");
+ purple_connection_error(
+ purple_account_get_connection(zephyr->account),
+ PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+ "invalid output by tzc (or bad parsing code)");
+ g_strfreev(tzc_cmd_array);
+ return NULL;
+ }
+
+ tzc_process = g_subprocess_newv(
+ (const gchar *const *)tzc_cmd_array,
+ G_SUBPROCESS_FLAGS_STDIN_PIPE | G_SUBPROCESS_FLAGS_STDOUT_PIPE,
+ &error);
+ if (tzc_process == NULL) {
+ purple_debug_error("zephyr", "tzc exited early: %s", error->message);
+ purple_connection_error(
+ purple_account_get_connection(zephyr->account),
+ PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+ "invalid output by tzc (or bad parsing code)");
+ g_error_free(error);
+ }
+
+ g_strfreev(tzc_cmd_array);
+ return tzc_process;
+}
+
+static gssize
+pollable_input_stream_read(GPollableInputStream *stream, void *bufcur, GError **error)
+{
+ return g_pollable_input_stream_read_nonblocking(stream, bufcur, 1, NULL, error);
+}
+
+static gssize
+pollable_input_stream_read_with_timeout(GPollableInputStream *stream,
+ void *bufcur, GError **error)
+{
+ const gint64 timeout = 10 * G_USEC_PER_SEC;
+ gint64 now = g_get_monotonic_time();
+
+ while (g_get_monotonic_time() < now + timeout) {
+ GError *local_error = NULL;
+ gssize ret = g_pollable_input_stream_read_nonblocking(
+ stream, bufcur, 1, NULL, &local_error);
+ if (ret == 1) {
+ return ret;
+ }
+ if (local_error->code != G_IO_ERROR_WOULD_BLOCK) {
+ g_propagate_error(error, local_error);
+ return ret;
+ }
+ /* Keep on waiting if this is a blocking error. */
+ g_clear_error(&local_error);
+ }
+
+ g_set_error_literal(error, G_IO_ERROR, G_IO_ERROR_TIMED_OUT,
+ "tzc did not respond in time");
+ return -1;
+}
+
+static gint
+get_paren_level(gint paren_level, gchar ch)
+{
+ switch (ch) {
+ case '(': return paren_level + 1;
+ case ')': return paren_level - 1;
+ default: return paren_level;
+ }
+}
+
+static void
+parse_tzc_login_data(zephyr_account *zephyr, const gchar *buf, gint buflen)
+{
+ gchar *str = g_strndup(buf, buflen);
+
+ purple_debug_info("zephyr", "tempstr parsed");
+
+ /* str should now be a string containing all characters from
+ * buf after the first ( to the one before the last paren ).
+ * We should have the following possible lisp strings but we don't care
+ * (tzcspew . start) (version . "something") (pid . number)
+ * We care about 'zephyrid . "username@REALM.NAME"' and
+ * 'exposure . "SOMETHING"' */
+ if (!g_ascii_strncasecmp(str, "zephyrid", 8)) {
+ gchar **strv;
+ gchar *username;
+ const char *at;
+
+ purple_debug_info("zephyr", "zephyrid found");
+
+ strv = g_strsplit(str + 8, "\"", -1);
+ username = strv[1] ? strv[1] : "";
+ zephyr->username = g_strdup(username);
+
+ at = strchr(username, '@');
+ if (at != NULL) {
+ zephyr->realm = g_strdup(at + 1);
+ } else {
+ zephyr->realm = get_zephyr_realm(zephyr->account, "local-realm");
+ }
+
+ g_strfreev(strv);
+ } else {
+ purple_debug_info("zephyr", "something that's not zephyr id found %s", str);
+ }
+
+ /* We don't care about anything else yet */
+ g_free(str);
+}
+
+static gchar *
+tree_child_contents(GNode *tree, int index)
+{
+ GNode *child = g_node_nth_child(tree, index);
+ return child ? child->data : "";
+}
+
+static GNode *
+find_node(GNode *ptree, gchar *key)
+{
+ guint num_children;
+ gchar* tc;
+
+ if (!ptree || ! key)
+ return NULL;
+
+ num_children = g_node_n_children(ptree);
+ tc = tree_child_contents(ptree, 0);
+
+ /* g_strcasecmp() is deprecated. What is the encoding here??? */
+ if (num_children > 0 && tc && !g_ascii_strcasecmp(tc, key)) {
+ return ptree;
+ } else {
+ GNode *result = NULL;
+ guint i;
+ for (i = 0; i < num_children; i++) {
+ result = find_node(g_node_nth_child(ptree, i), key);
+ if(result != NULL) {
+ break;
+ }
+ }
+ return result;
+ }
+}
+
+static GNode *
+parse_buffer(const gchar *source, gboolean do_parse)
+{
+ GNode *ptree = g_node_new(NULL);
+
+ if (do_parse) {
+ unsigned int p = 0;
+ while(p < strlen(source)) {
+ unsigned int end;
+ gchar *newstr;
+
+ /* Eat white space: */
+ if(g_ascii_isspace(source[p]) || source[p] == '\001') {
+ p++;
+ continue;
+ }
+
+ /* Skip comments */
+ if(source[p] == ';') {
+ while(source[p] != '\n' && p < strlen(source)) {
+ p++;
+ }
+ continue;
+ }
+
+ if(source[p] == '(') {
+ int nesting = 0;
+ gboolean in_quote = FALSE;
+ gboolean escape_next = FALSE;
+ p++;
+ end = p;
+ while(!(source[end] == ')' && nesting == 0 && !in_quote) && end < strlen(source)) {
+ if(!escape_next) {
+ if(source[end] == '\\') {
+ escape_next = TRUE;
+ }
+ if(!in_quote) {
+ if(source[end] == '(') {
+ nesting++;
+ }
+ if(source[end] == ')') {
+ nesting--;
+ }
+ }
+ if(source[end] == '"') {
+ in_quote = !in_quote;
+ }
+ } else {
+ escape_next = FALSE;
+ }
+ end++;
+ }
+ do_parse = TRUE;
+
+ } else {
+ gchar end_char;
+ if(source[p] == '"') {
+ end_char = '"';
+ p++;
+ } else {
+ end_char = ' ';
+ }
+ do_parse = FALSE;
+
+ end = p;
+ while(source[end] != end_char && end < strlen(source)) {
+ if(source[end] == '\\')
+ end++;
+ end++;
+ }
+ }
+ newstr = g_new0(gchar, end+1-p);
+ strncpy(newstr,source+p,end-p);
+ if (g_node_n_children(ptree) < MAXCHILDREN) {
+ /* In case we surpass maxchildren, ignore this */
+ g_node_append(ptree, parse_buffer(newstr, do_parse));
+ } else {
+ purple_debug_error("zephyr","too many children in tzc output. skipping\n");
+ }
+ g_free(newstr);
+ p = end + 1;
+ }
+ } else {
+ /* XXX does this have to be strdup'd */
+ ptree->data = g_strdup(source);
+ }
+
+ return ptree;
+}
+
+gboolean
+tzc_login(zephyr_account *zephyr)
+{
+ gchar *buf = NULL;
+ const gchar *bufend = NULL;
+ const gchar *ptr;
+ const gchar *tmp;
+ gint parenlevel = 0;
+
+ zephyr->tzc_proc = get_tzc_process(zephyr);
+ if (zephyr->tzc_proc == NULL) {
+ return FALSE;
+ }
+ zephyr->tzc_stdin = g_subprocess_get_stdin_pipe(zephyr->tzc_proc);
+ zephyr->tzc_stdout = g_subprocess_get_stdout_pipe(zephyr->tzc_proc);
+
+ purple_debug_info("zephyr", "about to read from tzc");
+ buf = tzc_read(zephyr, pollable_input_stream_read_with_timeout);
+ if (buf == NULL) {
+ return FALSE;
+ }
+ bufend = buf + strlen(buf);
+ ptr = buf;
+
+ /* ignore all tzcoutput till we've received the first ( */
+ while (ptr < bufend && (*ptr != '(')) {
+ ptr++;
+ }
+ if (ptr >= bufend) {
+ purple_connection_error(
+ purple_account_get_connection(zephyr->account),
+ PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+ "invalid output by tzc (or bad parsing code)");
+ g_free(buf);
+ return FALSE;
+ }
+
+ do {
+ parenlevel = get_paren_level(parenlevel, *ptr);
+ purple_debug_info("zephyr", "tzc parenlevel is %d", parenlevel);
+ switch (parenlevel) {
+ case 1:
+ /* Search for next beginning (, or for the ending */
+ do {
+ ptr++;
+ } while ((ptr < bufend) && (*ptr != '(') && (*ptr != ')'));
+ if (ptr >= bufend) {
+ purple_debug_error("zephyr", "tzc parsing error");
+ }
+ break;
+ case 2:
+ /* You are probably at
+ (foo . bar ) or (foo . "bar") or (foo . chars) or (foo . numbers) or (foo . () )
+ Parse all the data between the first and last f, and move past )
+ */
+ tmp = ptr;
+ do {
+ ptr++;
+ parenlevel = get_paren_level(parenlevel, *ptr);
+ } while (parenlevel > 1);
+ parse_tzc_login_data(zephyr, tmp + 1, ptr - tmp);
+ ptr++;
+ break;
+ default:
+ purple_debug_info("zephyr", "parenlevel is not 1 or 2");
+ /* This shouldn't be happening */
+ break;
+ }
+ } while (ptr < bufend && parenlevel != 0);
+ purple_debug_info("zephyr", "tzc startup done");
+ g_free(buf);
+
+ return TRUE;
+}
+
+gint
+tzc_check_notify(gpointer data)
+{
+ PurpleConnection *gc = (PurpleConnection *)data;
+ zephyr_account* zephyr = purple_connection_get_protocol_data(gc);
+ GNode *newparsetree = NULL;
+ gchar *buf = tzc_read(zephyr, pollable_input_stream_read);
+
+ if (buf != NULL) {
+ newparsetree = parse_buffer(buf, TRUE);
+ g_free(buf);
+ }
+
+ if (newparsetree != NULL) {
+ gchar *spewtype;
+ if ( (spewtype = tree_child_contents(find_node(newparsetree, "tzcspew"), 2)) ) {
+ if (!g_ascii_strncasecmp(spewtype,"message",7)) {
+ ZNotice_t notice;
+ GNode *msgnode = g_node_nth_child(find_node(newparsetree, "message"), 2);
+ /*char *zsig = g_strdup(" ");*/ /* purple doesn't care about zsigs */
+ char *msg = tzc_deescape_str(tree_child_contents(msgnode, 1));
+ char *buf = g_strdup_printf(" %c%s", '\0', msg);
+ memset((char *)&notice, 0, sizeof(notice));
+ notice.z_kind = ACKED;
+ notice.z_port = 0;
+ notice.z_opcode = tree_child_contents(find_node(newparsetree, "opcode"), 2);
+ notice.z_class = tzc_deescape_str(tree_child_contents(find_node(newparsetree, "class"), 2));
+ notice.z_class_inst = tree_child_contents(find_node(newparsetree, "instance"), 2);
+ notice.z_recipient = zephyr_normalize_local_realm(zephyr, tree_child_contents(find_node(newparsetree, "recipient"), 2));
+ notice.z_sender = zephyr_normalize_local_realm(zephyr, tree_child_contents(find_node(newparsetree, "sender"), 2));
+ notice.z_default_format = "Class $class, Instance $instance:\n" "To: @bold($recipient) at $time $date\n" "From: @bold($1) <$sender>\n\n$2";
+ notice.z_message_len = 1 + 1 + strlen(msg) + 1;
+ notice.z_message = buf;
+ handle_message(gc, &notice);
+ g_free(msg);
+ /*g_free(zsig);*/
+ g_free(buf);
+ }
+ else if (!g_ascii_strncasecmp(spewtype,"zlocation",9)) {
+ /* check_loc or zephyr_zloc respectively */
+ /* XXX fix */
+ char *user;
+ PurpleBuddy *b;
+ const char *bname;
+ const gchar *name;
+ gboolean has_locations;
+ GNode *locations;
+ gchar *locval;
+
+ user = tree_child_contents(find_node(newparsetree, "user"), 2);
+ b = find_buddy(zephyr, user);
+ bname = b ? purple_buddy_get_name(b) : NULL;
+ name = b ? bname : user;
+
+ locations = g_node_nth_child(g_node_nth_child(find_node(newparsetree, "locations"), 2), 0);
+ locval = tree_child_contents(g_node_nth_child(locations, 0), 2);
+ has_locations = (locval && *locval && !purple_strequal(locval, " "));
+ if ((b && pending_zloc(zephyr, bname)) || pending_zloc(zephyr, user)) {
+ PurpleNotifyUserInfo *user_info = purple_notify_user_info_new();
+ const char *balias;
+
+ /* TODO: Check whether it's correct to call add_pair_html,
+ or if we should be using add_pair_plaintext */
+ purple_notify_user_info_add_pair_html(user_info, _("User"), name);
+
+ balias = b ? purple_buddy_get_local_alias(b) : NULL;
+ if (balias)
+ purple_notify_user_info_add_pair_plaintext(user_info, _("Alias"), balias);
+
+ if (!has_locations) {
+ purple_notify_user_info_add_pair_plaintext(user_info, NULL, _("Hidden or not logged-in"));
+ } else {
+ /* TODO: Need to escape the two strings that make up tmp? */
+ char *tmp = g_strdup_printf(_("<br>At %s since %s"), locval,
+ tree_child_contents(g_node_nth_child(locations, 2), 2));
+ purple_notify_user_info_add_pair_html(user_info, _("Location"), tmp);
+ g_free(tmp);
+ }
+
+ purple_notify_userinfo(gc, name, user_info, NULL, NULL);
+ purple_notify_user_info_destroy(user_info);
+ } else {
+ purple_protocol_got_user_status(zephyr->account, name, has_locations ? "available" : "offline", NULL);
+ }
+ }
+ else if (!g_ascii_strncasecmp(spewtype,"subscribed",10)) {
+ }
+ else if (!g_ascii_strncasecmp(spewtype,"start",5)) {
+ }
+ else if (!g_ascii_strncasecmp(spewtype,"error",5)) {
+ /* XXX handle */
+ }
+ } else {
+ }
+ } else {
+ }
+
+ g_node_destroy(newparsetree);
+ return TRUE;
+}
+
+gboolean
+tzc_subscribe_to(zephyr_account *zephyr, ZSubscription_t *sub)
+{
+ /* ((tzcfodder . subscribe) ("class" "instance" "recipient")) */
+ return tzc_write(zephyr, "((tzcfodder . subscribe) (\"%s\" \"%s\" \"%s\"))\n",
+ sub->zsub_class, sub->zsub_classinst, sub->zsub_recipient);
+}
+
+gboolean
+tzc_request_locations(zephyr_account *zephyr, gchar *who)
+{
+ return tzc_write(zephyr, "((tzcfodder . zlocate) \"%s\")\n", who);
+}
+
+gboolean
+tzc_send_message(zephyr_account *zephyr, gchar *zclass, gchar *instance, gchar *recipient,
+ const gchar *html_buf, const gchar *sig, G_GNUC_UNUSED const gchar *opcode)
+{
+ /* CMU cclub tzc doesn't grok opcodes for now */
+ char *tzc_sig = tzc_escape_msg(sig);
+ char *tzc_body = tzc_escape_msg(html_buf);
+ gboolean result;
+
+ result = tzc_write(zephyr, "((tzcfodder . send) (class . \"%s\") (auth . t) (recipients (\"%s\" . \"%s\")) (message . (\"%s\" \"%s\")) ) \n",
+ zclass, instance, recipient, tzc_sig, tzc_body);
+ g_free(tzc_sig);
+ g_free(tzc_body);
+ return result;
+}
+
+void
+tzc_set_location(zephyr_account *zephyr, char *exposure)
+{
+ tzc_write(zephyr, "((tzcfodder . set-location) (hostname . \"%s\") (exposure . \"%s\"))\n",
+ zephyr->ourhost, exposure);
+}
+
+void
+tzc_get_subs_from_server(G_GNUC_UNUSED zephyr_account *zephyr, PurpleConnection *gc)
+{
+ /* XXX fix */
+ purple_notify_error(gc, "", "tzc doesn't support this action",
+ NULL, purple_request_cpar_from_connection(gc));
+}
+
+void
+tzc_close(zephyr_account *zephyr)
+{
+#ifdef G_OS_UNIX
+ GError *error = NULL;
+ g_subprocess_send_signal(zephyr->tzc_proc, SIGTERM);
+ if (!g_subprocess_wait(zephyr->tzc_proc, NULL, &error)) {
+ purple_debug_error("zephyr",
+ "error while attempting to close tzc: %s",
+ error->message);
+ g_error_free(error);
+ }
+#else
+ g_subprocess_force_exit(zephyr->tzc_proc);
+#endif
+ zephyr->tzc_stdin = NULL;
+ zephyr->tzc_stdout = NULL;
+ g_clear_object(&zephyr->tzc_proc);
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/protocols/zephyr/zephyr_tzc.h Wed Feb 03 18:27:42 2021 -0600
@@ -0,0 +1,38 @@
+/*
+ * Purple - Internet Messaging Library
+ * Copyright (C) Pidgin Developers <devel@pidgin.im>
+ *
+ * Purple is the legal property of its developers, whose names are too numerous
+ * to list here. Please refer to the COPYRIGHT file distributed with this
+ * source distribution.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef PURPLE_ZEPHYR_ZEPHYR_TZC_H
+#define PURPLE_ZEPHYR_ZEPHYR_TZC_H
+
+#include "zephyr_account.h"
+
+gboolean tzc_login(zephyr_account *zephyr);
+gint tzc_check_notify(gpointer data);
+gboolean tzc_subscribe_to(zephyr_account *zephyr, ZSubscription_t *sub);
+gboolean tzc_request_locations(zephyr_account *zephyr, gchar *who);
+gboolean tzc_send_message(zephyr_account *zephyr, gchar *zclass, gchar *instance, gchar *recipient,
+ const gchar *html_buf, const gchar *sig, const gchar *opcode);
+void tzc_set_location(zephyr_account *zephyr, char *exposure);
+void tzc_get_subs_from_server(zephyr_account *zephyr, PurpleConnection *gc);
+void tzc_close(zephyr_account *zephyr);
+
+#endif /* PURPLE_ZEPHYR_ZEPHYR_TZC_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/protocols/zephyr/zephyr_zeph02.c Wed Feb 03 18:27:42 2021 -0600
@@ -0,0 +1,241 @@
+/*
+ * Purple - Internet Messaging Library
+ * Copyright (C) Pidgin Developers <devel@pidgin.im>
+ *
+ * Purple is the legal property of its developers, whose names are too numerous
+ * to list here. Please refer to the COPYRIGHT file distributed with this
+ * source distribution.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <glib/gi18n-lib.h>
+
+#include "zephyr_zeph02.h"
+
+gboolean
+zeph02_login(zephyr_account *zephyr)
+{
+ PurpleConnection *pc = purple_account_get_connection(zephyr->account);
+
+ /* XXX Should actually try to report the com_err determined error */
+ if (ZInitialize() != ZERR_NONE) {
+ purple_connection_error(pc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+ "Couldn't initialize zephyr");
+ return FALSE;
+ }
+
+ if (ZOpenPort(&(zephyr->port)) != ZERR_NONE) {
+ purple_connection_error(pc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+ "Couldn't open port");
+ return FALSE;
+ }
+
+ if (ZSetLocation(zephyr->exposure) != ZERR_NONE) {
+ purple_connection_error(pc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+ "Couldn't set location");
+ return FALSE;
+ }
+
+ zephyr->username = g_strdup(ZGetSender());
+ zephyr->realm = get_zephyr_realm(zephyr->account, ZGetRealm());
+
+ return TRUE;
+}
+
+/* Called when the server notifies us a message couldn't get sent */
+static void
+message_failed(PurpleConnection *gc, ZNotice_t *notice)
+{
+ const gchar *title;
+ gchar *notify_msg;
+
+ if (g_ascii_strcasecmp(notice->z_class, "message")) {
+ title = "";
+ notify_msg = g_strdup_printf(_("Unable to send to chat %s,%s,%s"),
+ notice->z_class, notice->z_class_inst, notice->z_recipient);
+ } else {
+ title = notice->z_recipient;
+ notify_msg = g_strdup(_("User is offline"));
+ }
+ purple_notify_error(gc, title, notify_msg, NULL, purple_request_cpar_from_connection(gc));
+ g_free(notify_msg);
+}
+
+/* just for debugging */
+static void
+handle_unknown(ZNotice_t *notice)
+{
+ purple_debug_error("zephyr", "z_packet: %s\n", notice->z_packet);
+ purple_debug_error("zephyr", "z_version: %s\n", notice->z_version);
+ purple_debug_error("zephyr", "z_kind: %d\n", (int)(notice->z_kind));
+ purple_debug_error("zephyr", "z_class: %s\n", notice->z_class);
+ purple_debug_error("zephyr", "z_class_inst: %s\n", notice->z_class_inst);
+ purple_debug_error("zephyr", "z_opcode: %s\n", notice->z_opcode);
+ purple_debug_error("zephyr", "z_sender: %s\n", notice->z_sender);
+ purple_debug_error("zephyr", "z_recipient: %s\n", notice->z_recipient);
+ purple_debug_error("zephyr", "z_message: %s\n", notice->z_message);
+ purple_debug_error("zephyr", "z_message_len: %d\n", notice->z_message_len);
+}
+
+gint
+zeph02_check_notify(gpointer data)
+{
+ /* XXX add real error reporting */
+ PurpleConnection *gc = (PurpleConnection*) data;
+ while (ZPending()) {
+ ZNotice_t notice;
+ /* XXX add real error reporting */
+
+ if (ZReceiveNotice(&notice, NULL) != ZERR_NONE) {
+ return TRUE;
+ }
+
+ switch (notice.z_kind) {
+ case UNSAFE:
+ case UNACKED:
+ case ACKED:
+ handle_message(gc, &notice);
+ break;
+ case SERVACK:
+ if (!(g_ascii_strcasecmp(notice.z_message, ZSRVACK_NOTSENT))) {
+ message_failed(gc, &notice);
+ }
+ break;
+ case CLIENTACK:
+ purple_debug_error("zephyr", "Client ack received\n");
+ handle_unknown(&notice); /* XXX: is it really unknown? */
+ break;
+ default:
+ /* we'll just ignore things for now */
+ handle_unknown(&notice);
+ purple_debug_error("zephyr", "Unhandled notice.\n");
+ break;
+ }
+ /* XXX add real error reporting */
+ ZFreeNotice(&notice);
+ }
+
+ return TRUE;
+}
+
+gboolean
+zeph02_subscribe_to(G_GNUC_UNUSED zephyr_account *zephyr, ZSubscription_t *sub)
+{
+ return ZSubscribeTo(sub, 1, 0) == ZERR_NONE;
+}
+
+gboolean
+zeph02_request_locations(G_GNUC_UNUSED zephyr_account *zephyr, gchar *who)
+{
+ ZAsyncLocateData_t ald;
+ Code_t zerr;
+
+ zerr = ZRequestLocations(who, &ald, UNACKED, ZAUTH);
+ g_free(ald.user);
+ g_free(ald.version);
+ return zerr == ZERR_NONE;
+}
+
+gboolean
+zeph02_send_message(G_GNUC_UNUSED zephyr_account *zephyr, gchar *zclass, gchar *instance, gchar *recipient,
+ const gchar *html_buf, const gchar *sig, const gchar *opcode)
+{
+ ZNotice_t notice;
+ char *buf = g_strdup_printf("%s%c%s", sig, '\0', html_buf);
+ gboolean result = TRUE;
+
+ memset((char *)&notice, 0, sizeof(notice));
+ notice.z_kind = ACKED;
+ notice.z_port = 0;
+ notice.z_class = zclass;
+ notice.z_class_inst = instance;
+ notice.z_recipient = recipient;
+ notice.z_sender = 0;
+ notice.z_default_format = "Class $class, Instance $instance:\n" "To: @bold($recipient) at $time $date\n" "From: @bold($1) <$sender>\n\n$2";
+ notice.z_message_len = strlen(sig) + 1 + strlen(html_buf) + 1;
+ notice.z_message = buf;
+ notice.z_opcode = g_strdup(opcode);
+
+ purple_debug_info("zephyr","About to send notice\n");
+ if (ZSendNotice(&notice, ZAUTH) != ZERR_NONE) {
+ /* XXX handle errors here */
+ result = FALSE;
+ } else {
+ purple_debug_info("zephyr", "notice sent\n");
+ }
+ g_free(buf);
+ return result;
+}
+
+void
+zeph02_set_location(G_GNUC_UNUSED zephyr_account *zephyr, char *exposure)
+{
+ ZSetLocation(exposure);
+}
+
+void
+zeph02_get_subs_from_server(zephyr_account *zephyr, PurpleConnection *gc)
+{
+ Code_t retval;
+ int nsubs;
+ GString *subout;
+
+ if (zephyr->port == 0) {
+ purple_debug_error("zephyr", "error while retrieving port\n");
+ return;
+ }
+ if ((retval = ZRetrieveSubscriptions(zephyr->port, &nsubs)) != ZERR_NONE) {
+ /* XXX better error handling */
+ purple_debug_error("zephyr", "error while retrieving subscriptions from server\n");
+ return;
+ }
+
+ subout = g_string_new("Subscription list<br>");
+ for (int i = 0; i < nsubs; i++) {
+ ZSubscription_t subs;
+ int one = 1;
+
+ if ((retval = ZGetSubscriptions(&subs, &one)) != ZERR_NONE) {
+ break;
+ }
+ g_string_append_printf(subout, "Class %s Instance %s Recipient %s<br>",
+ subs.zsub_class, subs.zsub_classinst, subs.zsub_recipient);
+ }
+
+ if (retval == ZERR_NONE) {
+ gchar *title = g_strdup_printf("Server subscriptions for %s", zephyr->username);
+ purple_notify_formatted(gc, title, title, NULL, subout->str, NULL, NULL);
+ g_free(title);
+ } else {
+ /* XXX better error handling */
+ purple_debug_error("zephyr", "error while retrieving individual subscription\n");
+ }
+
+ g_string_free(subout, TRUE);
+}
+
+void
+zeph02_close(G_GNUC_UNUSED zephyr_account *zephyr)
+{
+ if (ZCancelSubscriptions(0) != ZERR_NONE) {
+ return;
+ }
+ if (ZUnsetLocation() != ZERR_NONE) {
+ return;
+ }
+ if (ZClosePort() != ZERR_NONE) {
+ return;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/protocols/zephyr/zephyr_zeph02.h Wed Feb 03 18:27:42 2021 -0600
@@ -0,0 +1,38 @@
+/*
+ * Purple - Internet Messaging Library
+ * Copyright (C) Pidgin Developers <devel@pidgin.im>
+ *
+ * Purple is the legal property of its developers, whose names are too numerous
+ * to list here. Please refer to the COPYRIGHT file distributed with this
+ * source distribution.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef PURPLE_ZEPHYR_ZEPHYR_ZEPH02_H
+#define PURPLE_ZEPHYR_ZEPHYR_ZEPH02_H
+
+#include "zephyr_account.h"
+
+gboolean zeph02_login(zephyr_account *zephyr);
+gint zeph02_check_notify(gpointer data);
+gboolean zeph02_subscribe_to(zephyr_account *zephyr, ZSubscription_t *sub);
+gboolean zeph02_request_locations(zephyr_account *zephyr, gchar *who);
+gboolean zeph02_send_message(zephyr_account *zephyr, gchar *zclass, gchar *instance, gchar *recipient,
+ const gchar *html_buf, const gchar *sig, const gchar *opcode);
+void zeph02_set_location(zephyr_account *zephyr, char *exposure);
+void zeph02_get_subs_from_server(zephyr_account *zephyr, PurpleConnection *gc);
+void zeph02_close(zephyr_account *zephyr);
+
+#endif /* PURPLE_ZEPHYR_ZEPHYR_ZEPH02_H */