pidgin/pidgin

zephyr_login refactoring

2021-01-11, Arkadiy Illarionov
d06e6660b39c
Parents 81f81f5d2f39
Children 1db26307f960
zephyr_login refactoring

* Add `login_tzc` and `login_zeph02` functions to remove redundant comparison
* Add `get_zephyr_realm` function to reduce code duplication
* Extract some functions
* Use `FALSE` in `purple_account_get_bool`
* Replace `read_anyone` and `read_zsubs` variables with functions calls

Testing Done:
Compile.

Reviewed at https://reviews.imfreedom.org/r/405/
--- a/libpurple/protocols/zephyr/zephyr.c Mon Jan 11 01:51:14 2021 -0600
+++ b/libpurple/protocols/zephyr/zephyr.c Mon Jan 11 20:11:21 2021 -0600
@@ -63,6 +63,7 @@
typedef struct _zephyr_account zephyr_account;
typedef gssize (*PollableInputStreamReadFunc)(GPollableInputStream *stream, void *bufcur, GError **error);
+typedef gboolean (*ZephyrLoginFunc)(zephyr_account *zephyr);
typedef enum {
PURPLE_ZEPHYR_NONE, /* Non-kerberized ZEPH0.2 */
@@ -111,8 +112,10 @@
return TRUE;
#define z_call_s(func, err) if (func != ZERR_NONE) {\
- purple_connection_error(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, err);\
- return;\
+ purple_connection_error(\
+ purple_account_get_connection(zephyr->account),\
+ PURPLE_CONNECTION_ERROR_NETWORK_ERROR, err);\
+ return FALSE;\
}
#ifdef WIN32
@@ -1196,17 +1199,228 @@
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 *
+get_zephyr_realm(PurpleAccount *account, const gchar *local_realm)
+{
+ const char *realm = purple_account_get_string(account, "realm", "");
+ if (!*realm) {
+ realm = local_realm;
+ }
+ g_strlcpy(__Zephyr_realm, realm, REALM_SZ - 1);
+ 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)
+{
+ /* XXX z_call_s should actually try to report the com_err determined error */
+ z_call_s(ZInitialize(), "Couldn't initialize zephyr");
+ z_call_s(ZOpenPort(&(zephyr->port)), "Couldn't open port");
+ z_call_s(ZSetLocation(zephyr->exposure), "Couldn't set location");
+
+ zephyr->username = g_strdup(ZGetSender());
+ zephyr->realm = get_zephyr_realm(zephyr->account, ZGetRealm());
+
+ return TRUE;
+}
+
static void zephyr_login(PurpleAccount * account)
{
PurpleConnection *gc;
zephyr_account *zephyr;
+ ZephyrLoginFunc login;
GSourceFunc check_notify;
- gboolean read_anyone;
- gboolean read_zsubs;
gc = purple_account_get_connection(account);
- read_anyone = purple_account_get_bool(account, "read_anyone", TRUE);
- read_zsubs = purple_account_get_bool(account, "read_zsubs", TRUE);
#ifdef WIN32
username = purple_account_get_username(account);
@@ -1220,224 +1434,20 @@
zephyr->account = account;
zephyr->exposure = get_zephyr_exposure(account);
- if (purple_account_get_bool(account, "use_tzc", 0)) {
+ if (purple_account_get_bool(account, "use_tzc", FALSE)) {
zephyr->connection_type = PURPLE_ZEPHYR_TZC;
+ login = login_tzc;
check_notify = check_notify_tzc;
} else {
zephyr->connection_type = PURPLE_ZEPHYR_KRB4;
+ login = login_zeph02;
check_notify = check_notify_zeph02;
}
zephyr->encoding = (char *)purple_account_get_string(account, "encoding", ZEPHYR_FALLBACK_CHARSET);
purple_connection_update_progress(gc, _("Connecting"), 0, 8);
- /* XXX z_call_s should actually try to report the com_err determined error */
- if (use_tzc(zephyr)) {
- gboolean found_ps = FALSE;
- gchar **tzc_cmd_array = NULL;
- gchar *buf = NULL;
- gchar *bufcur = NULL;
- gchar *ptr;
- gint parenlevel = 0;
- gchar *tempstr;
- gint i;
- GError *error = NULL;
-
- /* 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
- */
- if (!g_shell_parse_argv(purple_account_get_string(
- account, "tzc_command", "/usr/bin/tzc -e %s"),
- NULL, &tzc_cmd_array, &error)) {
- purple_debug_error("zephyr", "Unable to parse tzc_command: %s",
- error->message);
- purple_connection_error(gc,
- PURPLE_CONNECTION_ERROR_INVALID_SETTINGS,
- "invalid tzc_command setting");
- g_error_free(error);
- return;
- }
- 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(
- gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
- "invalid output by tzc (or bad parsing code)");
- g_strfreev(tzc_cmd_array);
- return;
- }
-
- zephyr->tzc_proc = g_subprocess_newv(
- (const gchar *const *)tzc_cmd_array,
- G_SUBPROCESS_FLAGS_STDIN_PIPE | G_SUBPROCESS_FLAGS_STDOUT_PIPE,
- &error);
- g_strfreev(tzc_cmd_array);
- if (zephyr->tzc_proc == NULL) {
- purple_debug_error("zephyr", "tzc exited early: %s",
- error->message);
- purple_connection_error(
- gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
- "invalid output by tzc (or bad parsing code)");
- g_error_free(error);
- return;
- }
- 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;
- }
- bufcur = buf + strlen(buf);
- ptr = buf;
-
- /* ignore all tzcoutput till we've received the first (*/
- while (ptr < bufcur && (*ptr != '(')) {
- ptr++;
- }
- if (ptr >= bufcur) {
- purple_connection_error(
- gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
- "invalid output by tzc (or bad parsing code)");
- g_free(buf);
- return;
- }
-
- while (ptr < bufcur) {
- if (*ptr == '(') {
- parenlevel++;
- } else if (*ptr == ')') {
- parenlevel--;
- }
- purple_debug_info("zephyr", "tzc parenlevel is %d", parenlevel);
- switch (parenlevel) {
- case 0:
- break;
- case 1:
- /* Search for next beginning (, or for the ending */
- ptr++;
- while ((*ptr != '(') && (*ptr != ')') && (ptr < bufcur)) {
- ptr++;
- }
- if (ptr >= bufcur) {
- 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 )
- */
- tempstr = g_new0(gchar, 20000);
- i = 0;
- while(parenlevel >1) {
- ptr++;
- if (*ptr == '(')
- parenlevel++;
- if (*ptr == ')')
- parenlevel--;
- if (parenlevel > 1) {
- tempstr[i++] = *ptr;
- } else {
- ptr++;
- }
- }
- purple_debug_info("zephyr", "tempstr parsed");
- /* tempstr should now be a i-length string containing all
- * characters from that 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"' */
- i = 0;
- if (!g_ascii_strncasecmp(tempstr,"zephyrid",8)) {
- gchar* username = g_malloc0(100);
- int username_idx=0;
- const char *realm;
- purple_debug_info("zephyr", "zephyrid found");
- i += 8;
- while (i < 20000 && tempstr[i] != '"') {
- i++;
- }
- i++;
- while (i < 20000 && tempstr[i] != '"') {
- username[username_idx++] = tempstr[i++];
- }
-
- zephyr->username = g_strdup_printf("%s",username);
- if ((realm = strchr(username,'@')))
- zephyr->realm = g_strdup_printf("%s",realm+1);
- else {
- realm = purple_account_get_string(account, "realm", "");
- if (!*realm) {
- realm = "local-realm";
- }
- zephyr->realm = g_strdup(realm);
- g_strlcpy(__Zephyr_realm,
- (const gchar *)zephyr->realm,
- REALM_SZ - 1);
- }
- /* else {
- zephyr->realm = g_strdup("local-realm");
- }*/
-
- g_free(username);
- } else {
- purple_debug_info(
- "zephyr",
- "something that's not zephyr id found %s",
- tempstr);
- }
-
- /* We don't care about anything else yet */
- g_free(tempstr);
- break;
- default:
- purple_debug_info("zephyr", "parenlevel is not 1 or 2");
- /* This shouldn't be happening */
- break;
- }
- if (parenlevel == 0) {
- break;
- }
- } /* while (ptr < bufcur) */
- purple_debug_info("zephyr", "tzc startup done");
- g_free(buf);
- }
- else if ( use_zeph02(zephyr)) {
- const gchar* realm;
- z_call_s(ZInitialize(), "Couldn't initialize zephyr");
- z_call_s(ZOpenPort(&(zephyr->port)), "Couldn't open port");
- z_call_s(ZSetLocation((char *)zephyr->exposure), "Couldn't set location");
-
- realm = purple_account_get_string(account, "realm", "");
- if (!*realm) {
- realm = ZGetRealm();
- }
- zephyr->realm = g_strdup(realm);
- g_strlcpy(__Zephyr_realm, (const char*)zephyr->realm, REALM_SZ-1);
- zephyr->username = g_strdup(ZGetSender());
-
- /* zephyr->realm = g_strdup(ZGetRealm()); */
- purple_debug_info("zephyr","realm: %s\n",zephyr->realm);
- }
- else {
- purple_connection_error(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, "Only ZEPH0.2 supported currently");
+ if (!login(zephyr)) {
return;
}
purple_debug_info("zephyr","does it get here\n");
@@ -1459,14 +1469,15 @@
purple_connection_set_state(gc, PURPLE_CONNECTION_CONNECTED);
- if (read_anyone)
+ if (purple_account_get_bool(account, "read_anyone", TRUE)) {
process_anyone(zephyr);
- if (read_zsubs)
+ }
+ if (purple_account_get_bool(account, "read_zsubs", TRUE)) {
process_zsubs(zephyr);
+ }
zephyr->nottimer = g_timeout_add(100, check_notify, gc);
zephyr->loctimer = g_timeout_add_seconds(20, check_loc, gc);
-
}
static void write_zsubs(zephyr_account *zephyr)