--- 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 "zephyr_account.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
# define HOST_NAME_MAX 255
@@ -60,46 +61,8 @@
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);
- 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* galaxy; /* not yet useful */
- char* krbtkfile; /* not yet useful */
- GList *pending_zloc_names;
- char ourhost[HOST_NAME_MAX + 1];
- char ourhostcanon[HOST_NAME_MAX + 1];
- zephyr_connection_type connection_type;
- GOutputStream *tzc_stdin;
- GInputStream *tzc_stdout;
- ZephyrSubscribeToFunc subscribe_to;
- ZephyrRequestLocationsFunc request_locations;
@@ -112,59 +75,7 @@
extern const char *username;
-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);
-use_tzc(const zephyr_account *zephyr)
- return zephyr->connection_type == PURPLE_ZEPHYR_TZC;
-use_zeph02(const zephyr_account *zephyr)
- return zephyr->connection_type == PURPLE_ZEPHYR_NONE ||
- zephyr->connection_type == PURPLE_ZEPHYR_KRB4;
-zephyr_write_message(zephyr_account *zephyr, const gchar *message)
- success = g_output_stream_write_all(zephyr->tzc_stdin, message, strlen(message),
- purple_debug_error("zephyr", "Unable to write a message: %s", error->message);
-subscribe_to_tzc(zephyr_account *zephyr, ZSubscription_t *sub)
- /* ((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)) {
-subscribe_to_zeph02(G_GNUC_UNUSED zephyr_account *zephyr, ZSubscription_t *sub)
- return ZSubscribeTo(sub, 1, 0);
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);
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)
+convert_to_utf8(const gchar *string, const gchar *from_encoding) - zephyr_account *zephyr = purple_connection_get_protocol_data(gc);
if (g_utf8_validate(string, -1, NULL)) {
- utf8 = g_convert(string, -1, "UTF-8", zephyr->encoding, NULL, NULL, &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)"));
+ utf8 = g_convert(string, -1, "UTF-8", from_encoding, NULL, NULL, &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)")); -static gboolean pending_zloc(zephyr_account *zephyr, const char *who)
+pending_zloc(zephyr_account *zephyr, const char *who) - 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);
@@ -323,27 +217,7 @@
-/* Called when the server notifies us a message couldn't get sent */
-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,
- purple_notify_error(gc,"",chat_failed,NULL,
- purple_request_cpar_from_connection(gc));
- purple_notify_error(gc, notice->z_recipient,
- _("User is offline"), NULL,
- purple_request_cpar_from_connection(gc));
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)
+handle_message(PurpleConnection *gc, ZNotice_t *notice_p) zephyr_account* zephyr = purple_connection_get_protocol_data(gc);
@@ -380,6 +255,7 @@
/* XXX add real error reporting */
if (ZParseLocations(¬ice, 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)) {
PurpleNotifyUserInfo *user_info = purple_notify_user_info_new();
/* 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);
purple_notify_user_info_add_pair_plaintext(user_info, _("Alias"), balias);
@@ -406,6 +280,9 @@
for (; nlocs > 0; nlocs--) {
/* XXX add real error reporting */
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);
- 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);
- purple_protocol_got_user_status(purple_connection_get_account(gc), b ? bname : user, "available", NULL);
- 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); - char *buf, *buf2, *buf3;
- PurpleChatConversation *gcc;
- char *ptr = (char *) notice.z_message + (strlen(notice.z_message) + 1);
int signature_length = strlen(notice.z_message);
- PurpleMessageFlags flags = 0;
/* 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)) {
- purple_debug_info("zephyr","message_size %d %d %d\n",len,notice.z_message_len,signature_length);
+ char *ptr = (char *) notice.z_message + (strlen(notice.z_message) + 1); 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);
- tmpescape = g_markup_escape_text(buf, -1);
- buf2 = zephyr_to_html(tmpescape);
- buf3 = zephyr_recv_convert(gc, buf2);
+ tmpbuf = g_malloc(len + 1); + g_snprintf(tmpbuf, len + 1, "%s", ptr); + tmpescape = g_markup_escape_text(tmpbuf, -1); + tmpbuf = zephyr_to_html(tmpescape); + buf = convert_to_utf8(tmpbuf, zephyr->encoding); + 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);
- purple_serv_got_im(gc, stripped_sender, buf3, flags, time(NULL));
+ purple_serv_got_im(gc, stripped_sender, buf, flags, time(NULL)); @@ -478,6 +351,7 @@
GSList *l = g_slist_find_custom(zephyr->subscrips, &sub, (GCompareFunc)zephyr_triple_subset);
+ PurpleChatConversation *gcc; /* 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);
- 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); 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));
-tree_child_contents(GNode *tree, int index)
- GNode *child = g_node_nth_child(tree, index);
- return child ? child->data : "";
-find_node(GNode *ptree, gchar *key)
- 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)) {
- for (i = 0; i < num_children; i++) {
- result = find_node(g_node_nth_child(ptree, i), key);
-parse_buffer(const gchar *source, gboolean do_parse)
+check_loc_buddy(PurpleBuddy *buddy, zephyr_account *zephyr) - GNode *ptree = g_node_new(NULL);
- while(p < strlen(source)) {
- if(g_ascii_isspace(source[p]) || source[p] == '\001') {
- while(source[p] != '\n' && p < strlen(source)) {
- gboolean in_quote = FALSE;
- gboolean escape_next = FALSE;
- while(!(source[end] == ')' && nesting == 0 && !in_quote) && end < strlen(source)) {
- if(source[end] == '\\') {
- if(source[end] == '(') {
- if(source[end] == ')') {
- if(source[end] == '"') {
+ const char *bname = purple_buddy_get_name(buddy); + char *chk = zephyr_normalize_local_realm(zephyr, bname);
- while(source[end] != end_char && end < strlen(source)) {
- if(source[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));
- purple_debug_error("zephyr","too many children in tzc output. skipping\n");
- /* XXX does this have to be strdup'd */
- ptree->data = g_strdup(source);
-read_from_tzc(zephyr_account *zephyr, PollableInputStreamReadFunc read_func)
- GPollableInputStream *stream = G_POLLABLE_INPUT_STREAM(zephyr->tzc_stdout);
- gchar *buf = g_new(gchar, bufsize);
- gboolean selected = FALSE;
- if (read_func(stream, bufcur, &error) < 0) {
- if (error->code == G_IO_ERROR_WOULD_BLOCK ||
- error->code == G_IO_ERROR_TIMED_OUT) {
- 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");
- if ((bufcur - buf) > (bufsize - 1)) {
- if ((buf = g_realloc(buf, bufsize * 2)) == NULL) {
- purple_debug_error("zephyr","Ran out of memory\n");
- bufcur = buf + bufsize;
-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; -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);
- newparsetree = parse_buffer(buf, TRUE);
+ ZGetLocations(&locations, &one); + serv_got_update(zgc, bname, 1, 0, 0, 0, 0);
- if (newparsetree != NULL) {
- if ( (spewtype = tree_child_contents(find_node(newparsetree, "tzcspew"), 2)) ) {
- if (!g_ascii_strncasecmp(spewtype,"message",7)) {
- 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 *)¬ice, 0, sizeof(notice));
- 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, ¬ice);
- else if (!g_ascii_strncasecmp(spewtype,"zlocation",9)) {
- /* check_loc or zephyr_zloc respectively */
- gboolean has_locations;
- 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();
- /* 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;
- purple_notify_user_info_add_pair_plaintext(user_info, _("Alias"), balias);
- purple_notify_user_info_add_pair_plaintext(user_info, NULL, _("Hidden or not logged-in"));
- /* 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);
- purple_notify_userinfo(gc, name, user_info, NULL, NULL);
- purple_notify_user_info_destroy(user_info);
- 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)) {
- g_node_destroy(newparsetree);
-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;
- /* XXX add real error reporting */
- if (ZReceiveNotice(¬ice, NULL) != ZERR_NONE) {
+ /* doesn't matter if this fails or not; we'll just move on to the next one */ + zephyr->request_locations(zephyr, chk); - switch (notice.z_kind) {
- handle_message(gc, ¬ice);
- if (!(g_ascii_strcasecmp(notice.z_message, ZSRVACK_NOTSENT))) {
- message_failed(gc, ¬ice);
- purple_debug_error("zephyr", "Client ack received\n");
- handle_unknown(¬ice); /* XXX: is it really unknown? */
- /* we'll just ignore things for now */
- handle_unknown(¬ice);
- purple_debug_error("zephyr", "Unhandled notice.\n");
- /* XXX add real error reporting */
-request_locations_tzc(zephyr_account *zephyr, gchar *who)
- zlocstr = g_strdup_printf("((tzcfodder . zlocate) \"%s\")\n", who);
- result = zephyr_write_message(zephyr, zlocstr);
-request_locations_zeph02(G_GNUC_UNUSED zephyr_account *zephyr, gchar *who)
+check_loc(gpointer data) - ZAsyncLocateData_t ald;
- zerr = ZRequestLocations(who, &ald, UNACKED, ZAUTH);
- return zerr == ZERR_NONE;
-static gint check_loc(gpointer data)
- ZLocations_t locations;
- PurpleConnection *gc = data;
- zephyr_account *zephyr = purple_connection_get_protocol_data(gc);
- PurpleAccount *account = purple_connection_get_account(gc);
+ 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;
- const char *bname = purple_buddy_get_name(b);
- chk = local_zephyr_normalize(zephyr, bname);
- ZLocateUser(chk,&numlocs, ZAUTH);
- for(i=0;i<numlocs;i++) {
- ZGetLocations(&locations,&one);
- serv_got_update(zgc,bname,1,0,0,0,0);
+ g_slist_foreach(buddies, (GFunc)check_loc_buddy, zephyr); -static gint check_loc(gpointer data)
- 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;
- 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);
+ return G_SOURCE_CONTINUE;
@@ -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);
-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 (local_error->code == G_IO_ERROR_WOULD_BLOCK) {
- /* Keep on waiting if this is a blocking error. */
- g_clear_error(&local_error);
- g_propagate_error(error, local_error);
- g_set_error_literal(error, G_IO_ERROR, G_IO_ERROR_TIMED_OUT,
- "tzc did not respond in time");
-get_tzc_process(const zephyr_account *zephyr)
- GSubprocess *tzc_process = NULL;
- gchar **tzc_cmd_array = NULL;
- gboolean found_ps = FALSE;
- /* tzc_command should really be of the form
- ssh username@hostname pathtotzc -e %s
- -- this should not require a password, and ideally should be
- 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");
- 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);
- 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);
- tzc_process = g_subprocess_newv(
- (const gchar *const *)tzc_cmd_array,
- G_SUBPROCESS_FLAGS_STDIN_PIPE | G_SUBPROCESS_FLAGS_STDOUT_PIPE,
- 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_strfreev(tzc_cmd_array);
-get_paren_level(gint paren_level, gchar ch)
- case '(': return paren_level + 1;
- case ')': return paren_level - 1;
- default: return paren_level;
get_zephyr_realm(PurpleAccount *account, const gchar *local_realm)
const char *realm = purple_account_get_string(account, "realm", "");
@@ -1245,151 +653,6 @@
-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)) {
- 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, '@');
- zephyr->realm = g_strdup(at + 1);
- zephyr->realm = get_zephyr_realm(zephyr->account, "local-realm");
- purple_debug_info("zephyr", "something that's not zephyr id found %s", str);
- /* We don't care about anything else yet */
-login_tzc(zephyr_account *zephyr)
- const gchar *bufend = NULL;
- zephyr->tzc_proc = get_tzc_process(zephyr);
- if (zephyr->tzc_proc == NULL) {
- 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);
- bufend = buf + strlen(buf);
- /* ignore all tzcoutput till we've received the first ( */
- while (ptr < bufend && (*ptr != '(')) {
- purple_connection_error(
- purple_account_get_connection(zephyr->account),
- PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
- "invalid output by tzc (or bad parsing code)");
- parenlevel = get_paren_level(parenlevel, *ptr);
- purple_debug_info("zephyr", "tzc parenlevel is %d", parenlevel);
- /* Search for next beginning (, or for the ending */
- } while ((ptr < bufend) && (*ptr != '(') && (*ptr != ')'));
- purple_debug_error("zephyr", "tzc parsing error");
- (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 )
- parenlevel = get_paren_level(parenlevel, *ptr);
- } while (parenlevel > 1);
- parse_tzc_login_data(zephyr, tmp + 1, ptr - tmp);
- purple_debug_info("zephyr", "parenlevel is not 1 or 2");
- /* This shouldn't be happening */
- } while (ptr < bufend && parenlevel != 0);
- purple_debug_info("zephyr", "tzc startup done");
-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");
- if (ZOpenPort(&(zephyr->port)) != ZERR_NONE) {
- purple_connection_error(pc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
- if (ZSetLocation(zephyr->exposure) != ZERR_NONE) {
- purple_connection_error(pc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
- "Couldn't set location");
- zephyr->username = g_strdup(ZGetSender());
- zephyr->realm = get_zephyr_realm(zephyr->account, ZGetRealm());
static void zephyr_login(PurpleAccount * account)
@@ -1414,16 +677,24 @@
if (purple_account_get_bool(account, "use_tzc", FALSE)) {
zephyr->connection_type = PURPLE_ZEPHYR_TZC;
- check_notify = check_notify_tzc;
- zephyr->subscribe_to = subscribe_to_tzc;
- zephyr->request_locations = request_locations_tzc;
+ 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; zephyr->connection_type = PURPLE_ZEPHYR_KRB4;
- check_notify = check_notify_zeph02;
- zephyr->subscribe_to = subscribe_to_zeph02;
- zephyr->request_locations = request_locations_zeph02;
+ 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 @@
g_source_remove(zephyr->loctimer);
- if (use_zeph02(zephyr)) {
- if (ZCancelSubscriptions(0) != ZERR_NONE) {
- if (ZUnsetLocation() != ZERR_NONE) {
- if (ZClosePort() != ZERR_NONE) {
- 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",
- g_subprocess_force_exit(zephyr->tzc_proc);
- zephyr->tzc_stdin = NULL;
- zephyr->tzc_stdout = NULL;
- g_clear_object(&zephyr->tzc_proc);
-static gboolean zephyr_send_message(zephyr_account *zephyr, gchar *zclass,
- gchar *instance, gchar *recipient,
- const gchar *im, const gchar *sig,
+zephyr_send_message(zephyr_account *zephyr, gchar *zclass, gchar *instance, + gchar *recipient, const gchar *im, const gchar *sig, + /* (From the tzc source) + * emacs sends something of the form: + * (recipients ("PERSONAL" . "bovik") ("test" . "")) + * (message . ("Harry Bovik" "my zgram")) + tmp_buf = html_to_zephyr(im); + html_buf = purple_unescape_html(tmp_buf); + result = zephyr->send_message(zephyr, zclass, instance, recipient, html_buf, sig, opcode); 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, ""); - 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, "");
-/* Munge the outgoing zephyr so that any quotes or backslashes are
- escaped and do not confuse tzc: */
-zephyr_tzc_escape_msg(const char *message)
- if (!message || !*message) {
- 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] = message[pos];
-zephyr_tzc_deescape_str(const char *message)
- if (!message || !*message) {
- msglen = strlen(message);
- newmsg = g_new0(char, msglen + 1);
- for (gsize pos = 0, pos2 = 0; pos < msglen; pos++, pos2++) {
- if (message[pos] == '\\') {
- newmsg[pos2] = message[pos];
-zephyr_send_message(zephyr_account *zephyr, gchar *zclass, gchar *instance,
- gchar *recipient, const gchar *im, const gchar *sig,
- /* (From the tzc source)
- * emacs sends something of the form:
- * (recipients ("PERSONAL" . "bovik") ("test" . ""))
- * (message . ("Harry Bovik" "my zgram"))
- tmp_buf = html_to_zephyr(im);
- html_buf = purple_unescape_html(tmp_buf);
- /* 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)) {
- } else if (use_zeph02(zephyr)) {
- char *buf = g_strdup_printf("%s%c%s", sig, '\0', html_buf);
- memset((char *)¬ice, 0, sizeof(notice));
- notice.z_class = zclass;
- notice.z_class_inst = instance;
- notice.z_recipient = recipient;
- 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(¬ice, ZAUTH) != ZERR_NONE) {
- /* XXX handle errors here */
- purple_debug_info("zephyr","notice sent\n");
/* Basically the inverse of zephyr_strip_local_realm */
-local_zephyr_normalize(const zephyr_account *zephyr, const char *orig)
+zephyr_normalize_local_realm(const zephyr_account *zephyr, const char *orig) if (*orig == '\0' || strchr(orig, '@')) {
@@ -1835,7 +984,7 @@
- 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)) {
@@ -1853,7 +1002,7 @@
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 @@
-zephyr_set_location(zephyr_account *zephyr, char *exposure)
- if (use_zeph02(zephyr)) {
- ZSetLocation(exposure);
- gchar *zexpstr = g_strdup_printf("((tzcfodder . set-location) (hostname . \"%s\") (exposure . \"%s\"))\n",
- zephyr->ourhost, exposure);
- zephyr_write_message(zephyr, zexpstr);
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) {
- zephyr_set_location(zephyr, EXPOSE_OPSTAFF);
+ zephyr->set_location(zephyr, EXPOSE_OPSTAFF); @@ -2028,7 +1163,7 @@
- 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 */
purple_debug_info("zephyr", "who is null\n");
- recipient = local_zephyr_normalize(zephyr, "");
+ recipient = zephyr_normalize_local_realm(zephyr, ""); char *comma = strrchr(who, ',');
/* Don't ping broadcast (chat) recipients */
@@ -2139,7 +1274,7 @@
- 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);
@@ -2196,7 +1331,7 @@
if (!g_ascii_strcasecmp(args[0],"*"))
return PURPLE_CMD_RET_FAILED; /* "*" is not a valid argument */
- recipient = local_zephyr_normalize(zephyr,args[0]);
+ recipient = zephyr_normalize_local_realm(zephyr, args[0]); if (strlen(recipient) < 1) {
@@ -2411,46 +1546,7 @@
PurpleConnection *gc = action->connection;
zephyr_account *zephyr = purple_connection_get_protocol_data(gc);
- int retval, nsubs, one,i;
- if (use_zeph02(zephyr)) {
- if (zephyr->port == 0) {
- purple_debug_error("zephyr", "error while retrieving port\n");
- if ((retval = ZRetrieveSubscriptions(zephyr->port,&nsubs)) != ZERR_NONE) {
- /* XXX better error handling */
- purple_debug_error("zephyr", "error while retrieving subscriptions from server\n");
- title = g_strdup_printf("Server subscriptions for %s",
- subout = g_string_new("Subscription list<br>");
- if ((retval = ZGetSubscriptions(&subs,&one)) != ZERR_NONE) {
- /* XXX better error handling */
- g_string_free(subout, TRUE);
- purple_debug_error("zephyr", "error while retrieving individual subscription\n");
- g_string_append_printf(subout, "Class %s Instance %s Recipient %s<br>",
- subs.zsub_class, subs.zsub_classinst,
- purple_notify_formatted(gc, title, title, NULL, subout->str, NULL, NULL);
- g_string_free(subout, TRUE);
- purple_notify_error(gc, "", "tzc doesn't support this action",
- NULL, purple_request_cpar_from_connection(gc));
+ zephyr->get_subs_from_server(zephyr, gc);