pidgin/pidgin

Remove the Novell GroupWise protocol plugin

18 months ago, Gary Kramlich
3bc50beccf03
Parents 201aae92d734
Children 8bdccbde0536
Remove the Novell GroupWise protocol plugin

We haven't been able to test this in forever, and as such, it hasn't received
any real maintenance. So rather than continue this cycle we're going to cut our
loses.

Testing Done:
Compiled and greped everything. I've left the man pages for now as they need their own overhaul.

Reviewed at https://reviews.imfreedom.org/r/2021/
  • +0 -1
    libpurple/protocols/meson.build
  • +0 -39
    libpurple/protocols/novell/meson.build
  • +0 -224
    libpurple/protocols/novell/nmconference.c
  • +0 -167
    libpurple/protocols/novell/nmconference.h
  • +0 -632
    libpurple/protocols/novell/nmconn.c
  • +0 -151
    libpurple/protocols/novell/nmconn.h
  • +0 -937
    libpurple/protocols/novell/nmcontact.c
  • +0 -425
    libpurple/protocols/novell/nmcontact.h
  • +0 -1061
    libpurple/protocols/novell/nmevent.c
  • +0 -178
    libpurple/protocols/novell/nmevent.h
  • +0 -362
    libpurple/protocols/novell/nmfield.c
  • +0 -224
    libpurple/protocols/novell/nmfield.h
  • +0 -95
    libpurple/protocols/novell/nmmessage.c
  • +0 -83
    libpurple/protocols/novell/nmmessage.h
  • +0 -141
    libpurple/protocols/novell/nmrequest.c
  • +0 -131
    libpurple/protocols/novell/nmrequest.h
  • +0 -822
    libpurple/protocols/novell/nmrtf.c
  • +0 -30
    libpurple/protocols/novell/nmrtf.h
  • +0 -2122
    libpurple/protocols/novell/nmuser.c
  • +0 -670
    libpurple/protocols/novell/nmuser.h
  • +0 -474
    libpurple/protocols/novell/nmuserrecord.c
  • +0 -246
    libpurple/protocols/novell/nmuserrecord.h
  • +0 -3686
    libpurple/protocols/novell/novell.c
  • +0 -32
    libpurple/protocols/novell/novell.h
  • +0 -0
    libpurple/protocols/novell/resources/icons/16x16/apps/im-novell.png
  • +0 -163
    libpurple/protocols/novell/resources/icons/16x16/apps/scalable/im-novell.svg
  • +0 -0
    libpurple/protocols/novell/resources/icons/22x22/apps/im-novell.png
  • +0 -140
    libpurple/protocols/novell/resources/icons/22x22/apps/scalable/im-novell.svg
  • +0 -0
    libpurple/protocols/novell/resources/icons/48x48/apps/im-novell.png
  • +0 -167
    libpurple/protocols/novell/resources/icons/scalable/apps/im-novell.svg
  • +0 -11
    libpurple/protocols/novell/resources/novell.gresource.xml
  • +1 -2
    meson.build
  • +0 -11
    po/POTFILES.in
  • --- a/libpurple/protocols/meson.build Fri Nov 11 19:39:24 2022 -0600
    +++ b/libpurple/protocols/meson.build Sat Nov 12 15:37:03 2022 -0600
    @@ -5,5 +5,4 @@
    subdir('irc')
    subdir('ircv3')
    subdir('jabber')
    -subdir('novell')
    subdir('sametime')
    --- a/libpurple/protocols/novell/meson.build Fri Nov 11 19:39:24 2022 -0600
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,39 +0,0 @@
    -NOVELL_SOURCES = [
    - 'nmfield.h',
    - 'nmfield.c',
    - 'nmconn.h',
    - 'nmconn.c',
    - 'nmconference.h',
    - 'nmconference.c',
    - 'nmcontact.h',
    - 'nmcontact.c',
    - 'nmevent.h',
    - 'nmevent.c',
    - 'nmmessage.h',
    - 'nmmessage.c',
    - 'nmrequest.h',
    - 'nmrequest.c',
    - 'nmrtf.h',
    - 'nmrtf.c',
    - 'nmuser.h',
    - 'nmuser.c',
    - 'nmuserrecord.h',
    - 'nmuserrecord.c',
    - 'novell.h',
    - 'novell.c'
    -]
    -
    -if DYNAMIC_NOVELL
    - novell_resources = gnome.compile_resources('novellresource',
    - 'resources/novell.gresource.xml',
    - source_dir : 'resources',
    - c_name : 'novell')
    - NOVELL_SOURCES += novell_resources
    -
    - novell_prpl = shared_library('novell', NOVELL_SOURCES,
    - c_args : ['-DG_LOG_USE_STRUCTURED', '-DG_LOG_DOMAIN="Purple-Novell"'],
    - dependencies : [libpurple_dep, glib, ws2_32],
    - install : true, install_dir : PURPLE_PLUGINDIR)
    -
    - devenv.append('PURPLE_PLUGIN_PATH', meson.current_build_dir())
    -endif
    --- a/libpurple/protocols/novell/nmconference.c Fri Nov 11 19:39:24 2022 -0600
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,224 +0,0 @@
    -/*
    - * nmconference.c
    - *
    - * Copyright (c) 2004 Novell, Inc. All Rights Reserved.
    - *
    - * 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; version 2 of the License.
    - *
    - * 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, write to the Free Software
    - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    - *
    - */
    -
    -#include <string.h>
    -#include "nmconference.h"
    -
    -static int conf_count = 0;
    -
    -struct _NMConference
    -{
    -
    - /* The conference identifier */
    - char *guid;
    -
    - /* The list of participants for the conference */
    - GSList *participants;
    -
    - /* Flags for the conference */
    - guint32 flags;
    -
    - /* User defined data */
    - gpointer data;
    -
    - /* Reference count for this object */
    - int ref_count;
    -
    -};
    -
    -
    -/*******************************************************************************
    - * Conference API -- see header file for comments
    - ******************************************************************************/
    -
    -NMConference *
    -nm_create_conference(const char *guid)
    -{
    - NMConference *conf = g_new0(NMConference, 1);
    -
    - if (guid) {
    - conf->guid = g_strdup(guid);
    - } else {
    - conf->guid = g_strdup(BLANK_GUID);
    - }
    - conf->ref_count = 1;
    -
    - purple_debug_info("novell", "Creating a conference %p, total=%d",
    - conf, conf_count++);
    -
    - return conf;
    -}
    -
    -void
    -nm_release_conference(NMConference * conference)
    -{
    - GSList *node;
    -
    - g_return_if_fail(conference != NULL);
    -
    - purple_debug_info("novell", "In release conference %p, refs=%d",
    - conference, conference->ref_count);
    - if (--conference->ref_count == 0) {
    - purple_debug_info("novell", "Releasing conference %p, total=%d",
    - conference, --conf_count);
    -
    - g_free(conference->guid);
    -
    - if (conference->participants) {
    - for (node = conference->participants; node; node = node->next) {
    - if (node->data) {
    - NMUserRecord *user_record = node->data;
    -
    - nm_release_user_record(user_record);
    - node->data = NULL;
    - }
    - }
    -
    - g_slist_free(conference->participants);
    - }
    -
    - g_free(conference);
    - }
    -}
    -
    -gboolean
    -nm_conference_is_instantiated(NMConference * conference)
    -{
    - if (conference == NULL)
    - return FALSE;
    -
    - return (strncmp(conference->guid, BLANK_GUID, CONF_GUID_END) != 0);
    -}
    -
    -int
    -nm_conference_get_participant_count(NMConference * conference)
    -{
    - if (conference == NULL)
    - return 0;
    -
    - return g_slist_length(conference->participants);
    -}
    -
    -NMUserRecord *
    -nm_conference_get_participant(NMConference * conference, int index)
    -{
    - if (conference == NULL)
    - return NULL;
    -
    - return (NMUserRecord *) g_slist_nth_data(conference->participants, index);
    -}
    -
    -void
    -nm_conference_add_participant(NMConference * conference,
    - NMUserRecord * user_record)
    -{
    - if (conference == NULL || user_record == NULL) {
    - return;
    - }
    -
    - nm_user_record_add_ref(user_record);
    - conference->participants = g_slist_append(conference->participants, user_record);
    -}
    -
    -void
    -nm_conference_remove_participant(NMConference * conference, const char *dn)
    -{
    - GSList *node, *element = NULL;
    -
    - if (conference == NULL || dn == NULL) {
    - return;
    - }
    -
    - for (node = conference->participants; node; node = node->next) {
    - NMUserRecord *user_record = node->data;
    -
    - if (user_record) {
    - if (nm_utf8_str_equal(dn, nm_user_record_get_dn(user_record))) {
    - element = node;
    - break;
    - }
    - }
    - }
    -
    - if (element) {
    - nm_release_user_record((NMUserRecord *) element->data);
    - element->data = NULL;
    - conference->participants =
    - g_slist_delete_link(conference->participants, element);
    - }
    -}
    -
    -void
    -nm_conference_add_ref(NMConference * conference)
    -{
    - if (conference)
    - conference->ref_count++;
    -}
    -
    -void
    -nm_conference_set_flags(NMConference * conference, guint32 flags)
    -{
    - if (conference) {
    - conference->flags = flags;
    - }
    -}
    -
    -void
    -nm_conference_set_guid(NMConference * conference, const char *guid)
    -{
    - if (conference) {
    -
    - /* Release memory for old guid */
    - g_free(conference->guid);
    -
    - /* Set the new guid */
    - if (guid)
    - conference->guid = g_strdup(guid);
    - else
    - conference->guid = g_strdup(BLANK_GUID);
    - }
    -}
    -
    -void
    -nm_conference_set_data(NMConference * conference, gpointer data)
    -{
    - if (conference == NULL)
    - return;
    -
    - conference->data = data;
    -}
    -
    -gpointer
    -nm_conference_get_data(NMConference * conference)
    -{
    - if (conference == NULL)
    - return NULL;
    -
    - return conference->data;
    -}
    -
    -const char *
    -nm_conference_get_guid(NMConference * conference)
    -{
    - if (conference == NULL)
    - return NULL;
    -
    - return conference->guid;
    -}
    --- a/libpurple/protocols/novell/nmconference.h Fri Nov 11 19:39:24 2022 -0600
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,167 +0,0 @@
    -/*
    - * nmconference.h
    - *
    - * Copyright (c) 2004 Novell, Inc. All Rights Reserved.
    - *
    - * 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; version 2 of the License.
    - *
    - * 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, write to the Free Software
    - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    - *
    - */
    -
    -#ifndef PURPLE_NOVELL_NMCONFERENCE_H
    -#define PURPLE_NOVELL_NMCONFERENCE_H
    -
    -typedef struct _NMConference NMConference;
    -
    -#include "nmuserrecord.h"
    -
    -/* A blank GUID -- represents an uninstantiated conference */
    -#define BLANK_GUID "[00000000-00000000-00000000-0000-0000]"
    -
    -/* This is how much of the conference GUIDs to compare when testing
    - * to see if two conferences are the same. We cannot compare the
    - * entire GUID because the last part is the session count.
    - */
    -#define CONF_GUID_END 27
    -
    -/**
    - * Creates an conference object.
    - *
    - * The conference should be released by calling
    - * nm_release_conference
    - *
    - * @param guid The GUID for the conference.
    - *
    - * @return The new NMConference
    - */
    -NMConference *nm_create_conference(const char *guid);
    -
    -/**
    - * Increments the reference count for the conference.
    - *
    - * The reference to the conference should be released
    - * by calling nm_release_conference
    - *
    - * @param conference The conference to reference
    - */
    -void nm_conference_add_ref(NMConference * conference);
    -
    -/**
    - * Releases the resources associated with the conference
    - * if there are no more references to it, otherwise just
    - * decrements the reference count.
    - *
    - * @param conf The conference to release
    - *
    - */
    -void nm_release_conference(NMConference * conf);
    -
    -/**
    - * Set the GUID for the conference.
    - *
    - * @param conference The conference
    - * @param guid The new conference GUID
    - *
    - */
    -void nm_conference_set_guid(NMConference * conference, const char *guid);
    -
    -/**
    - * Return the GUID for the conference.
    - *
    - * @param conference The conference
    - *
    - * @return The GUID for the conference
    - */
    -const char *nm_conference_get_guid(NMConference * conference);
    -
    -/**
    - * Add a participant to the conference.
    - *
    - * @param conference The conference
    - * @param user_record The user record to add as a participant
    - *
    - * @return
    - */
    -void nm_conference_add_participant(NMConference * conference,
    - NMUserRecord * user_record);
    -
    -/**
    - * Remove a participant to the conference.
    - *
    - * @param conference The conference
    - * @param dn The dn of the participant to remove
    - *
    - */
    -void nm_conference_remove_participant(NMConference * conference, const char *dn);
    -
    -/**
    - * Return the total number of participants in the conference.
    - *
    - * @param conference The conference
    - *
    - * @return The number of participants for the conference
    - *
    - */
    -int nm_conference_get_participant_count(NMConference * conference);
    -
    -/**
    - * Return a participant given an index.
    - *
    - * @param conference The conference
    - * @param index The index of the participant to get
    - *
    - * @return The participant or NULL if the index is out of range.
    - *
    - */
    -NMUserRecord *nm_conference_get_participant(NMConference * conference, int index);
    -
    -/**
    - * Check to see if the conference has been instantiated
    - *
    - * @param conference The conference
    - *
    - * @return TRUE if the conference has been instantiated,
    - * FALSE otherwise.
    - *
    - */
    -gboolean nm_conference_is_instantiated(NMConference * conf);
    -
    -/**
    - * Set the flags for the conference.
    - *
    - * @param conference The conference
    - * @param flags The conference flags.
    - *
    - */
    -void nm_conference_set_flags(NMConference * conference, guint32 flags);
    -
    -/**
    - * Set the user defined data for the conference.
    - *
    - * @param conference The conference
    - * @param data User defined data
    - *
    - */
    -void nm_conference_set_data(NMConference * conference, gpointer data);
    -
    -/**
    - * Get the user defined data for the conference.
    - *
    - * @param conference The conference
    - *
    - * @return The data if it has been set, NULL otherwise.
    - *
    - */
    -gpointer nm_conference_get_data(NMConference * conference);
    -
    -#endif /* PURPLE_NOVELL_NMCONFERENCE_H */
    --- a/libpurple/protocols/novell/nmconn.c Fri Nov 11 19:39:24 2022 -0600
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,632 +0,0 @@
    -/*
    - * nmconn.c
    - *
    - * Copyright (c) 2004 Novell, Inc. All Rights Reserved.
    - *
    - * 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; version 2 of the License.
    - *
    - * 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, write to the Free Software
    - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    - *
    - */
    -
    -#include <glib.h>
    -#ifdef HAVE_UNISTD_H
    -#include <unistd.h>
    -#endif
    -#include <string.h>
    -#include <ctype.h>
    -
    -#include <purple.h>
    -
    -#include "nmconn.h"
    -
    -#ifdef _WIN32
    -#include <windows.h>
    -#endif
    -
    -#define NO_ESCAPE(ch) ((ch == 0x20) || (ch >= 0x30 && ch <= 0x39) || \
    - (ch >= 0x41 && ch <= 0x5a) || (ch >= 0x61 && ch <= 0x7a))
    -
    -static char *
    -url_escape_string(char *src)
    -{
    - guint32 escape = 0;
    - char *p;
    - char *q;
    - char *encoded = NULL;
    - int ch;
    -
    - static const char hex_table[16] = "0123456789abcdef";
    -
    - if (src == NULL) {
    - return NULL;
    - }
    -
    - /* Find number of chars to escape */
    - for (p = src; *p != '\0'; p++) {
    - ch = (guchar) *p;
    - if (!NO_ESCAPE(ch)) {
    - escape++;
    - }
    - }
    -
    - encoded = g_malloc((p - src) + (escape * 2) + 1);
    -
    - /* Escape the string */
    - for (p = src, q = encoded; *p != '\0'; p++) {
    - ch = (guchar) * p;
    - if (NO_ESCAPE(ch)) {
    - if (ch != 0x20) {
    - *q = ch;
    - q++;
    - } else {
    - *q = '+';
    - q++;
    - }
    - } else {
    - *q = '%';
    - q++;
    -
    - *q = hex_table[ch >> 4];
    - q++;
    -
    - *q = hex_table[ch & 15];
    - q++;
    - }
    - }
    - *q = '\0';
    -
    - return encoded;
    -}
    -
    -static char *
    -encode_method(guint8 method)
    -{
    - char *str;
    -
    - switch (method) {
    - case NMFIELD_METHOD_EQUAL:
    - str = "G";
    - break;
    - case NMFIELD_METHOD_UPDATE:
    - str = "F";
    - break;
    - case NMFIELD_METHOD_GTE:
    - str = "E";
    - break;
    - case NMFIELD_METHOD_LTE:
    - str = "D";
    - break;
    - case NMFIELD_METHOD_NE:
    - str = "C";
    - break;
    - case NMFIELD_METHOD_EXIST:
    - str = "B";
    - break;
    - case NMFIELD_METHOD_NOTEXIST:
    - str = "A";
    - break;
    - case NMFIELD_METHOD_SEARCH:
    - str = "9";
    - break;
    - case NMFIELD_METHOD_MATCHBEGIN:
    - str = "8";
    - break;
    - case NMFIELD_METHOD_MATCHEND:
    - str = "7";
    - break;
    - case NMFIELD_METHOD_NOT_ARRAY:
    - str = "6";
    - break;
    - case NMFIELD_METHOD_OR_ARRAY:
    - str = "5";
    - break;
    - case NMFIELD_METHOD_AND_ARRAY:
    - str = "4";
    - break;
    - case NMFIELD_METHOD_DELETE_ALL:
    - str = "3";
    - break;
    - case NMFIELD_METHOD_DELETE:
    - str = "2";
    - break;
    - case NMFIELD_METHOD_ADD:
    - str = "1";
    - break;
    - default: /* NMFIELD_METHOD_VALID */
    - str = "0";
    - break;
    - }
    -
    - return str;
    -}
    -
    -NMConn *
    -nm_create_conn(const char *addr, int port)
    -{
    - NMConn *conn = g_new0(NMConn, 1);
    - conn->addr = g_strdup(addr);
    - conn->port = port;
    - return conn;
    -}
    -
    -void nm_release_conn(NMConn *conn)
    -{
    - g_return_if_fail(conn != NULL);
    -
    - g_slist_free_full(conn->requests, (GDestroyNotify)nm_release_request);
    - conn->requests = NULL;
    -
    - if (conn->input) {
    - purple_gio_graceful_close(conn->stream, G_INPUT_STREAM(conn->input),
    - conn->output);
    - }
    - g_clear_object(&conn->input);
    - g_clear_object(&conn->output);
    - g_clear_object(&conn->stream);
    -
    - g_clear_pointer(&conn->addr, g_free);
    - g_free(conn);
    -}
    -
    -NMERR_T
    -nm_write_fields(NMUser *user, NMField *fields)
    -{
    - NMConn *conn;
    - NMERR_T rc = NM_OK;
    - NMField *field;
    - char *value = NULL;
    - char *method = NULL;
    - char buffer[4096];
    - int ret;
    - int bytes_to_send;
    - int val = 0;
    -
    - g_return_val_if_fail(user != NULL, NMERR_BAD_PARM);
    - g_return_val_if_fail(user->conn != NULL, NMERR_BAD_PARM);
    - g_return_val_if_fail(fields != NULL, NMERR_BAD_PARM);
    -
    - conn = user->conn;
    -
    - /* Format each field as valid "post" data and write it out */
    - for (field = fields; (rc == NM_OK) && (field->tag); field++) {
    -
    - /* We don't currently handle binary types */
    - if (field->method == NMFIELD_METHOD_IGNORE ||
    - field->type == NMFIELD_TYPE_BINARY) {
    - continue;
    - }
    -
    - /* Write the field tag */
    - bytes_to_send = g_snprintf(buffer, sizeof(buffer), "&tag=%s", field->tag);
    - ret = g_output_stream_write(conn->output, buffer, bytes_to_send,
    - user->cancellable, NULL);
    - if (ret < 0) {
    - rc = NMERR_TCP_WRITE;
    - }
    -
    - /* Write the field method */
    - if (rc == NM_OK) {
    - method = encode_method(field->method);
    - bytes_to_send = g_snprintf(buffer, sizeof(buffer), "&cmd=%s", method);
    - ret = g_output_stream_write(conn->output, buffer, bytes_to_send,
    - user->cancellable, NULL);
    - if (ret < 0) {
    - rc = NMERR_TCP_WRITE;
    - }
    - }
    -
    - /* Write the field value */
    - if (rc == NM_OK) {
    - switch (field->type) {
    - case NMFIELD_TYPE_UTF8:
    - case NMFIELD_TYPE_DN:
    -
    - value = url_escape_string((char *) field->ptr_value);
    - bytes_to_send = g_snprintf(buffer, sizeof(buffer),
    - "&val=%s", value);
    - if (bytes_to_send > (int)sizeof(buffer)) {
    - ret = g_output_stream_write(conn->output, buffer,
    - sizeof(buffer),
    - user->cancellable, NULL);
    - } else {
    - ret = g_output_stream_write(conn->output, buffer,
    - bytes_to_send,
    - user->cancellable, NULL);
    - }
    -
    - if (ret < 0) {
    - rc = NMERR_TCP_WRITE;
    - }
    -
    - g_free(value);
    -
    - break;
    -
    - case NMFIELD_TYPE_ARRAY:
    - case NMFIELD_TYPE_MV:
    -
    - val = nm_count_fields((NMField *) field->ptr_value);
    - bytes_to_send = g_snprintf(buffer, sizeof(buffer),
    - "&val=%u", val);
    - ret = g_output_stream_write(conn->output, buffer,
    - bytes_to_send,
    - user->cancellable, NULL);
    - if (ret < 0) {
    - rc = NMERR_TCP_WRITE;
    - }
    -
    - break;
    -
    - default:
    -
    - bytes_to_send = g_snprintf(buffer, sizeof(buffer),
    - "&val=%u", field->value);
    - ret = g_output_stream_write(conn->output, buffer,
    - bytes_to_send,
    - user->cancellable, NULL);
    - if (ret < 0) {
    - rc = NMERR_TCP_WRITE;
    - }
    -
    - break;
    - }
    - }
    -
    - /* Write the field type */
    - if (rc == NM_OK) {
    - bytes_to_send = g_snprintf(buffer, sizeof(buffer),
    - "&type=%u", field->type);
    - ret = g_output_stream_write(conn->output, buffer, bytes_to_send,
    - user->cancellable, NULL);
    - if (ret < 0) {
    - rc = NMERR_TCP_WRITE;
    - }
    - }
    -
    - /* If the field is a sub array then post its fields */
    - if (rc == NM_OK && val > 0) {
    - if (field->type == NMFIELD_TYPE_ARRAY ||
    - field->type == NMFIELD_TYPE_MV) {
    -
    - rc = nm_write_fields(user, (NMField *)field->ptr_value);
    - }
    - }
    - }
    -
    - return rc;
    -}
    -
    -NMERR_T
    -nm_send_request(NMUser *user, char *cmd, NMField *fields, nm_response_cb cb,
    - gpointer data, NMRequest **request)
    -{
    - NMConn *conn;
    - NMERR_T rc = NM_OK;
    - char buffer[512];
    - int bytes_to_send;
    - int ret;
    - NMField *request_fields = NULL;
    - char *str = NULL;
    -
    - g_return_val_if_fail(user != NULL, NMERR_BAD_PARM);
    - g_return_val_if_fail(user->conn != NULL, NMERR_BAD_PARM);
    - g_return_val_if_fail(cmd != NULL, NMERR_BAD_PARM);
    -
    - conn = user->conn;
    -
    - /* Write the post */
    - bytes_to_send = g_snprintf(buffer, sizeof(buffer),
    - "POST /%s HTTP/1.0\r\n", cmd);
    - ret = g_output_stream_write(conn->output, buffer, bytes_to_send,
    - user->cancellable, NULL);
    - if (ret < 0) {
    - rc = NMERR_TCP_WRITE;
    - }
    -
    - /* Write headers */
    - if (rc == NM_OK) {
    - if (purple_strequal("login", cmd)) {
    - bytes_to_send = g_snprintf(buffer, sizeof(buffer),
    - "Host: %s:%d\r\n\r\n", conn->addr, conn->port);
    - ret = g_output_stream_write(conn->output, buffer, bytes_to_send,
    - user->cancellable, NULL);
    - if (ret < 0) {
    - rc = NMERR_TCP_WRITE;
    - }
    - } else {
    - bytes_to_send = g_snprintf(buffer, sizeof(buffer), "\r\n");
    - ret = g_output_stream_write(conn->output, buffer, bytes_to_send,
    - user->cancellable, NULL);
    - if (ret < 0) {
    - rc = NMERR_TCP_WRITE;
    - }
    - }
    - }
    -
    - /* Add the transaction id to the request fields */
    - if (rc == NM_OK) {
    - if (fields)
    - request_fields = nm_copy_field_array(fields);
    -
    - str = g_strdup_printf("%d", ++(conn->trans_id));
    - request_fields = nm_field_add_pointer(request_fields, NM_A_SZ_TRANSACTION_ID, 0,
    - NMFIELD_METHOD_VALID, 0,
    - str, NMFIELD_TYPE_UTF8);
    - }
    -
    - /* Send the request to the server */
    - if (rc == NM_OK) {
    - rc = nm_write_fields(user, request_fields);
    - }
    -
    - /* Write the CRLF to terminate the data */
    - if (rc == NM_OK) {
    - ret = g_output_stream_write(conn->output, "\r\n", strlen("\r\n"),
    - user->cancellable, NULL);
    - if (ret < 0) {
    - rc = NMERR_TCP_WRITE;
    - }
    - }
    -
    - /* Create a request struct, add it to our queue, and return it */
    - if (rc == NM_OK) {
    - NMRequest *new_request =
    - nm_create_request(cmd, conn->trans_id, cb, NULL, data);
    - nm_conn_add_request_item(conn, new_request);
    -
    - /* Set the out param if it was sent in, otherwise release the request */
    - if (request)
    - *request = new_request;
    - else
    - nm_release_request(new_request);
    - }
    -
    - if (request_fields != NULL)
    - nm_free_fields(&request_fields);
    -
    - return rc;
    -}
    -
    -NMERR_T
    -nm_read_header(NMUser *user)
    -{
    - NMConn *conn;
    - NMERR_T rc = NM_OK;
    - gchar *buffer;
    - char *ptr = NULL;
    - int i;
    - char rtn_buf[8];
    - int rtn_code = 0;
    - GError *error = NULL;
    -
    - g_return_val_if_fail(user != NULL, NMERR_BAD_PARM);
    - g_return_val_if_fail(user->conn != NULL, NMERR_BAD_PARM);
    -
    - conn = user->conn;
    -
    - buffer = g_data_input_stream_read_line(conn->input, NULL, user->cancellable,
    - &error);
    - if (error == NULL) {
    - /* Find the return code */
    - ptr = strchr(buffer, ' ');
    - if (ptr != NULL) {
    - ptr++;
    -
    - i = 0;
    - while (isdigit(*ptr) && (i < 3)) {
    - rtn_buf[i] = *ptr;
    - i++;
    - ptr++;
    - }
    - rtn_buf[i] = '\0';
    -
    - if (i > 0)
    - rtn_code = atoi(rtn_buf);
    - }
    - }
    -
    - /* Finish reading header, in the future we might want to do more processing here */
    - /* TODO: handle more general redirects in the future */
    - while ((error == NULL) && !purple_strequal(buffer, "\r")) {
    - g_free(buffer);
    - buffer = g_data_input_stream_read_line(conn->input, NULL,
    - user->cancellable, &error);
    - }
    - g_free(buffer);
    -
    - if (error != NULL) {
    - if (error->code != G_IO_ERROR_WOULD_BLOCK &&
    - error->code != G_IO_ERROR_CANCELLED) {
    - rc = NMERR_TCP_READ;
    - }
    - g_error_free(error);
    - }
    -
    - if (rc == NM_OK && rtn_code == 301)
    - rc = NMERR_SERVER_REDIRECT;
    -
    - return rc;
    -}
    -
    -NMERR_T
    -nm_read_fields(NMUser *user, int count, NMField **fields)
    -{
    - NMConn *conn;
    - NMERR_T rc = NM_OK;
    - guint8 type;
    - guint8 method;
    - guint32 val;
    - char tag[64];
    - NMField *sub_fields = NULL;
    - char *str = NULL;
    - GError *error = NULL;
    -
    - g_return_val_if_fail(user != NULL, NMERR_BAD_PARM);
    - g_return_val_if_fail(user->conn != NULL, NMERR_BAD_PARM);
    - g_return_val_if_fail(fields != NULL, NMERR_BAD_PARM);
    -
    - conn = user->conn;
    -
    - do {
    - if (count > 0) {
    - count--;
    - }
    -
    - /* Read the field type, method, and tag */
    - type = g_data_input_stream_read_byte(conn->input, user->cancellable,
    - &error);
    - if (error != NULL || type == 0) {
    - break;
    - }
    -
    - method = g_data_input_stream_read_byte(conn->input, user->cancellable,
    - &error);
    - if (error != NULL) {
    - break;
    - }
    -
    - val = g_data_input_stream_read_uint32(conn->input, user->cancellable,
    - &error);
    - if (error != NULL) {
    - break;
    - }
    -
    - if (val > sizeof(tag)) {
    - rc = NMERR_PROTOCOL;
    - break;
    - }
    -
    - g_input_stream_read_all(G_INPUT_STREAM(conn->input), tag, val, NULL,
    - user->cancellable, &error);
    - if (error != NULL) {
    - break;
    - }
    -
    - if (type == NMFIELD_TYPE_MV || type == NMFIELD_TYPE_ARRAY) {
    -
    - /* Read the subarray (first read the number of items in the array) */
    - val = g_data_input_stream_read_uint32(conn->input,
    - user->cancellable, &error);
    - if (error != NULL) {
    - break;
    - }
    -
    - if (val > 0) {
    - rc = nm_read_fields(user, val, &sub_fields);
    - if (rc != NM_OK)
    - break;
    - }
    -
    - *fields = nm_field_add_pointer(*fields, tag, 0, method,
    - 0, sub_fields, type);
    -
    - sub_fields = NULL;
    -
    - } else if (type == NMFIELD_TYPE_UTF8 || type == NMFIELD_TYPE_DN) {
    -
    - /* Read the string (first read the length) */
    - val = g_data_input_stream_read_uint32(conn->input,
    - user->cancellable, &error);
    - if (error != NULL) {
    - break;
    - }
    -
    - if (val >= NMFIELD_MAX_STR_LENGTH) {
    - rc = NMERR_PROTOCOL;
    - break;
    - }
    -
    - if (val > 0) {
    - str = g_new0(char, val + 1);
    -
    - g_input_stream_read_all(G_INPUT_STREAM(conn->input), str, val,
    - NULL, user->cancellable, &error);
    - if (error != NULL) {
    - break;
    - }
    -
    - *fields = nm_field_add_pointer(*fields, tag, 0, method,
    - 0, str, type);
    - str = NULL;
    - }
    -
    - } else {
    -
    - /* Read the numerical value */
    - val = g_data_input_stream_read_uint32(conn->input,
    - user->cancellable, &error);
    - if (error != NULL) {
    - break;
    - }
    -
    - *fields = nm_field_add_number(*fields, tag, 0, method,
    - 0, val, type);
    - }
    -
    - } while (count != 0);
    -
    - g_free(str);
    -
    - if (sub_fields != NULL) {
    - nm_free_fields(&sub_fields);
    - }
    -
    - if (error != NULL) {
    - if (error->code != G_IO_ERROR_WOULD_BLOCK && error->code != G_IO_ERROR_CANCELLED) {
    - rc = NMERR_TCP_READ;
    - }
    - g_error_free(error);
    - }
    -
    - return rc;
    -}
    -
    -void
    -nm_conn_add_request_item(NMConn * conn, NMRequest * request)
    -{
    - if (conn == NULL || request == NULL)
    - return;
    -
    - nm_request_add_ref(request);
    - conn->requests = g_slist_append(conn->requests, request);
    -}
    -
    -void
    -nm_conn_remove_request_item(NMConn * conn, NMRequest * request)
    -{
    - if (conn == NULL || request == NULL)
    - return;
    -
    - conn->requests = g_slist_remove(conn->requests, request);
    - nm_release_request(request);
    -}
    -
    -NMRequest *
    -nm_conn_find_request(NMConn * conn, int trans_id)
    -{
    - NMRequest *req = NULL;
    - GSList *itr = NULL;
    -
    - if (conn == NULL)
    - return NULL;
    -
    - itr = conn->requests;
    - while (itr) {
    - req = (NMRequest *) itr->data;
    - if (req != NULL && nm_request_get_trans_id(req) == trans_id) {
    - return req;
    - }
    - itr = g_slist_next(itr);
    - }
    - return NULL;
    -}
    --- a/libpurple/protocols/novell/nmconn.h Fri Nov 11 19:39:24 2022 -0600
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,151 +0,0 @@
    -/*
    - * nmconn.h
    - *
    - * Copyright (c) 2004 Novell, Inc. All Rights Reserved.
    - *
    - * 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; version 2 of the License.
    - *
    - * 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, write to the Free Software
    - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    - *
    - */
    -
    -#ifndef PURPLE_NOVELL_NMCONN_H
    -#define PURPLE_NOVELL_NMCONN_H
    -
    -#include <gio/gio.h>
    -
    -typedef struct _NMConn NMConn;
    -
    -#include "nmfield.h"
    -#include "nmuser.h"
    -
    -typedef int (*nm_ssl_read_cb) (gpointer ssl_data, void *buff, int len);
    -typedef int (*nm_ssl_write_cb) (gpointer ssl_data, const void *buff, int len);
    -
    -struct _NMConn
    -{
    -
    - /* The address of the server that we are connecting to. */
    - char *addr;
    -
    - /* The port that we are connecting to. */
    - int port;
    -
    - /* The transaction counter. */
    - int trans_id;
    -
    - /* A list of requests currently awaiting a response. */
    - GSList *requests;
    -
    - /* Connections to server. */
    - GSocketClient *client;
    - GIOStream *stream;
    - GDataInputStream *input;
    - GOutputStream *output;
    -};
    -
    -/**
    - * Allocate a new NMConn struct
    - *
    - * @param The address of the server that we are connecting to.
    - * @param The port that we are connecting to.
    - *
    - * @return A pointer to a newly allocated NMConn struct, should
    - * be freed by calling nm_release_conn()
    - */
    -NMConn *nm_create_conn(const char *addr, int port);
    -
    -/**
    - * Release an NMConn
    - *
    - * @param Pointer to the NMConn to release.
    - *
    - */
    -void nm_release_conn(NMConn *conn);
    -
    -/**
    - * Dispatch a request to the server.
    - *
    - * @param user The logged-in user.
    - * @param cmd The request to dispatch.
    - * @param fields The field list for the request.
    - * @param cb The response callback for the new request object.
    - * @param data The user defined data for the request (to be passed to the resp cb).
    - * @param req The request. Should be freed with nm_release_request.
    - *
    - * @return NM_OK on success.
    - */
    -NMERR_T
    -nm_send_request(NMUser *user, char *cmd, NMField *fields, nm_response_cb cb,
    - gpointer data, NMRequest **request);
    -
    -/**
    - * Write out the given field list.
    - *
    - * @param user The logged-in user.
    - * @param fields The field list to write.
    - *
    - * @return NM_OK on success.
    - */
    -NMERR_T nm_write_fields(NMUser *user, NMField *fields);
    -
    -/**
    - * Read the headers for a response.
    - *
    - * @param user The logged-in user.
    - *
    - * @return NM_OK on success.
    - */
    -NMERR_T nm_read_header(NMUser *user);
    -
    -/**
    - * Read a field list from the connection.
    - *
    - * @param user The logged-in user.
    - * @param count The maximum number of fields to read (or -1 for no max).
    - * @param fields The field list. This is an out param. It
    - * should be freed by calling nm_free_fields
    - * when finished.
    - *
    - * @return NM_OK on success.
    - */
    -NMERR_T nm_read_fields(NMUser *user, int count, NMField **fields);
    -
    -/**
    - * Add a request to the connections request list.
    - *
    - * @param conn The connection.
    - * @param request The request to add to the list.
    - */
    -void nm_conn_add_request_item(NMConn * conn, NMRequest * request);
    -
    -/**
    - * Remove a request from the connections list.
    - *
    - * @param conn The connection.
    - * @param request The request to remove from the list.
    - */
    -void nm_conn_remove_request_item(NMConn * conn, NMRequest * request);
    -
    -/**
    - * Find the request with the given transaction id in the connections
    - * request list.
    - *
    - * @param conn The connection.
    - * @param trans_id The transaction id of the request to return.
    - *
    - * @return The request, or NULL if a matching request is not
    - * found.
    - */
    -NMRequest *nm_conn_find_request(NMConn * conn, int trans_id);
    -
    -#endif /* PURPLE_NOVELL_NMCONN_H */
    --- a/libpurple/protocols/novell/nmcontact.c Fri Nov 11 19:39:24 2022 -0600
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,937 +0,0 @@
    -/*
    - * nmcontact.c
    - *
    - * Copyright (c) 2004 Novell, Inc. All Rights Reserved.
    - *
    - * 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; version 2 of the License.
    - *
    - * 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, write to the Free Software
    - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    - *
    - */
    -
    -#include <glib.h>
    -#include <string.h>
    -#include <purple.h>
    -#include "nmcontact.h"
    -#include "nmfield.h"
    -#include "nmuser.h"
    -
    -struct _NMContact
    -{
    - int id;
    - int parent_id;
    - int seq;
    - char *dn;
    - char *display_name;
    - NMUserRecord *user_record;
    - gpointer data;
    - int ref_count;
    -};
    -
    -struct _NMFolder
    -{
    - int id;
    - int seq;
    - char *name;
    - GSList *folders;
    - GSList *contacts;
    - int ref_count;
    -};
    -
    -static int count = 0;
    -
    -static void _release_folder_contacts(NMFolder * folder);
    -static void _release_folder_folders(NMFolder * folder);
    -static void _add_contacts(NMUser * user, NMFolder * folder, NMField * fields);
    -static void _add_folders(NMFolder * root, NMField * fields);
    -
    -/*********************************************************************
    - * Contact API
    - *********************************************************************/
    -
    -NMContact *
    -nm_create_contact(void)
    -{
    - NMContact *contact = g_new0(NMContact, 1);
    -
    - contact->ref_count = 1;
    -
    - purple_debug_info("novell", "Creating contact, total=%d", count++);
    -
    - return contact;
    -}
    -
    -/*
    - * This creates a contact for the contact list. The
    - * field array that is passed in should be a
    - * NM_A_FA_CONTACT array.
    - *
    - */
    -NMContact *
    -nm_create_contact_from_fields(NMField * fields)
    -{
    - NMContact *contact;
    - NMField *field;
    -
    - if ( fields == NULL || fields->tag == NULL || fields->ptr_value == 0 ||
    - !purple_strequal(fields->tag, NM_A_FA_CONTACT) )
    - {
    - return NULL;
    - }
    -
    - contact = nm_create_contact();
    -
    - if ((field = nm_locate_field(NM_A_SZ_OBJECT_ID, (NMField *) fields->ptr_value))) {
    -
    - if (field->ptr_value)
    - contact->id = atoi((char *) field->ptr_value);
    -
    - }
    -
    - if ((field = nm_locate_field(NM_A_SZ_PARENT_ID, (NMField *) fields->ptr_value))) {
    -
    - if (field->ptr_value)
    - contact->parent_id = atoi((char *) field->ptr_value);
    -
    - }
    -
    - if ((field =
    - nm_locate_field(NM_A_SZ_SEQUENCE_NUMBER, (NMField *) fields->ptr_value))) {
    -
    - if (field->ptr_value)
    - contact->seq = atoi((char *) field->ptr_value);
    -
    - }
    -
    - if ((field =
    - nm_locate_field(NM_A_SZ_DISPLAY_NAME, (NMField *) fields->ptr_value))) {
    -
    - if (field->ptr_value)
    - contact->display_name = g_strdup((char *) field->ptr_value);
    -
    - }
    -
    - if ((field = nm_locate_field(NM_A_SZ_DN, (NMField *) fields->ptr_value))) {
    -
    - if (field->ptr_value)
    - contact->dn = g_strdup((char *) field->ptr_value);
    -
    - }
    -
    - return contact;
    -}
    -
    -void
    -nm_contact_update_list_properties(NMContact * contact, NMField * fields)
    -{
    - NMField *field;
    -
    - if (contact == NULL || fields == NULL || fields->ptr_value == 0)
    - return;
    -
    - if ((field = nm_locate_field(NM_A_SZ_OBJECT_ID, (NMField *) fields->ptr_value))) {
    -
    - if (field->ptr_value)
    - contact->id = atoi((char *)field->ptr_value);
    -
    - }
    -
    - if ((field = nm_locate_field(NM_A_SZ_PARENT_ID, (NMField *) fields->ptr_value))) {
    -
    - if (field->ptr_value)
    - contact->parent_id = atoi((char *) field->ptr_value);
    -
    - }
    -
    - if ((field =
    - nm_locate_field(NM_A_SZ_SEQUENCE_NUMBER, (NMField *) fields->ptr_value))) {
    -
    - if (field->ptr_value)
    - contact->seq = atoi((char *) field->ptr_value);
    -
    - }
    -
    - if ((field =
    - nm_locate_field(NM_A_SZ_DISPLAY_NAME, (NMField *) fields->ptr_value))) {
    -
    - if (field->ptr_value) {
    - g_free(contact->display_name);
    -
    - contact->display_name = g_strdup((char *) field->ptr_value);
    - }
    -
    - }
    -
    - if ((field = nm_locate_field(NM_A_SZ_DN, (NMField *) fields->ptr_value))) {
    -
    - if (field->ptr_value) {
    - g_free(contact->dn);
    -
    - contact->dn = g_strdup((char *) field->ptr_value);
    - }
    -
    - }
    -}
    -
    -NMField *
    -nm_contact_to_fields(NMContact * contact)
    -{
    - NMField *fields = NULL;
    -
    - if (contact == NULL)
    - return NULL;
    -
    - fields = nm_field_add_pointer(fields, NM_A_SZ_OBJECT_ID, 0, NMFIELD_METHOD_VALID, 0,
    - g_strdup_printf("%d", contact->id), NMFIELD_TYPE_UTF8);
    -
    - fields = nm_field_add_pointer(fields, NM_A_SZ_PARENT_ID, 0, NMFIELD_METHOD_VALID, 0,
    - g_strdup_printf("%d", contact->parent_id), NMFIELD_TYPE_UTF8);
    -
    - fields = nm_field_add_pointer(fields, NM_A_SZ_SEQUENCE_NUMBER, 0, NMFIELD_METHOD_VALID, 0,
    - g_strdup_printf("%d", contact->seq), NMFIELD_TYPE_UTF8);
    -
    - if (contact->display_name != NULL) {
    - fields = nm_field_add_pointer(fields, NM_A_SZ_DISPLAY_NAME, 0, NMFIELD_METHOD_VALID, 0,
    - g_strdup(contact->display_name), NMFIELD_TYPE_UTF8);
    - }
    -
    - if (contact->dn != NULL) {
    - fields = nm_field_add_pointer(fields, NM_A_SZ_DN, 0, NMFIELD_METHOD_VALID, 0,
    - g_strdup(contact->dn), NMFIELD_TYPE_UTF8);
    - }
    -
    - return fields;
    -}
    -
    -void
    -nm_contact_add_ref(NMContact * contact)
    -{
    - if (contact)
    - contact->ref_count++;
    -}
    -
    -void
    -nm_release_contact(NMContact * contact)
    -{
    - if (contact == NULL)
    - return;
    -
    - if (--(contact->ref_count) == 0) {
    - purple_debug_info("novell", "Releasing contact, total=%d", --count);
    -
    - g_free(contact->display_name);
    - g_free(contact->dn);
    -
    - if (contact->user_record) {
    - nm_release_user_record(contact->user_record);
    - }
    -
    - g_free(contact);
    - }
    -
    -}
    -
    -const char *
    -nm_contact_get_display_name(NMContact * contact)
    -{
    - if (contact == NULL)
    - return NULL;
    -
    - if (contact->user_record != NULL && contact->display_name == NULL) {
    - const char *full_name, *lname, *fname, *cn, *display_id;
    -
    - full_name = nm_user_record_get_full_name(contact->user_record);
    - fname = nm_user_record_get_first_name(contact->user_record);
    - lname = nm_user_record_get_last_name(contact->user_record);
    - cn = nm_user_record_get_userid(contact->user_record);
    - display_id = nm_user_record_get_display_id(contact->user_record);
    -
    - /* Try to build a display name. */
    - if (full_name) {
    -
    - contact->display_name = g_strdup(full_name);
    -
    - } else if (fname && lname) {
    -
    - contact->display_name = g_strdup_printf("%s %s", fname, lname);
    -
    - } else {
    -
    - /* If auth attribute is set use it */
    - if (nm_user_record_get_auth_attr(contact->user_record) &&
    - display_id != NULL) {
    -
    - contact->display_name = g_strdup(display_id);
    -
    - } else {
    -
    - /* Use CN or display id */
    - if (cn) {
    -
    - contact->display_name = g_strdup(cn);
    -
    - } else if (display_id) {
    -
    - contact->display_name = g_strdup(display_id);
    -
    - }
    -
    - }
    -
    - }
    - }
    -
    - return contact->display_name;
    -}
    -
    -void
    -nm_contact_set_display_name(NMContact * contact, const char *display_name)
    -{
    - if (contact == NULL)
    - return;
    -
    - g_free(contact->display_name);
    - contact->display_name = NULL;
    -
    - if (display_name)
    - contact->display_name = g_strdup(display_name);
    -}
    -
    -void
    -nm_contact_set_dn(NMContact * contact, const char *dn)
    -{
    - if (contact == NULL)
    - return;
    -
    - g_free(contact->dn);
    - contact->dn = NULL;
    -
    - if (dn)
    - contact->dn = g_strdup(dn);
    -}
    -
    -const char *
    -nm_contact_get_dn(NMContact * contact)
    -{
    - if (contact == NULL)
    - return NULL;
    -
    - return contact->dn;
    -}
    -
    -gpointer
    -nm_contact_get_data(NMContact * contact)
    -{
    - if (contact == NULL)
    - return NULL;
    -
    - return contact->data;
    -}
    -
    -int
    -nm_contact_get_id(NMContact * contact)
    -{
    - if (contact == NULL)
    - return -1;
    -
    - return contact->id;
    -}
    -
    -int
    -nm_contact_get_parent_id(NMContact * contact)
    -{
    - if (contact == NULL)
    - return -1;
    -
    - return contact->parent_id;
    -}
    -
    -void
    -nm_contact_set_data(NMContact * contact, gpointer data)
    -{
    - if (contact == NULL)
    - return;
    -
    - contact->data = data;
    -}
    -
    -void
    -nm_contact_set_user_record(NMContact * contact, NMUserRecord * user_record)
    -{
    - if (contact == NULL)
    - return;
    -
    - if (contact->user_record) {
    - nm_release_user_record(contact->user_record);
    - }
    -
    - nm_user_record_add_ref(user_record);
    - contact->user_record = user_record;
    -}
    -
    -NMUserRecord *
    -nm_contact_get_user_record(NMContact * contact)
    -{
    - if (contact == NULL)
    - return NULL;
    -
    - return contact->user_record;
    -}
    -
    -const char *
    -nm_contact_get_userid(NMContact * contact)
    -{
    - NMUserRecord *user_record;
    - const char *userid = NULL;
    -
    - if (contact == NULL)
    - return NULL;
    -
    - user_record = nm_contact_get_user_record(contact);
    - if (user_record) {
    - userid = nm_user_record_get_userid(user_record);
    - }
    -
    - return userid;
    -}
    -
    -const char *
    -nm_contact_get_display_id(NMContact * contact)
    -{
    - NMUserRecord *user_record;
    - const char *id = NULL;
    -
    - if (contact == NULL)
    - return NULL;
    -
    - user_record = nm_contact_get_user_record(contact);
    - if (user_record) {
    - id = nm_user_record_get_display_id(user_record);
    - }
    -
    - return id;
    -}
    -
    -
    -/*********************************************************************
    - * Folder API
    - *********************************************************************/
    -
    -NMFolder *
    -nm_create_folder(const char *name)
    -{
    - NMFolder *folder = g_new0(NMFolder, 1);
    -
    - if (name)
    - folder->name = g_strdup(name);
    -
    - folder->ref_count = 1;
    -
    - return folder;
    -}
    -
    -NMFolder *
    -nm_create_folder_from_fields(NMField * fields)
    -{
    - NMField *field;
    - NMFolder *folder;
    -
    - if (fields == NULL || fields->ptr_value == 0)
    - return NULL;
    -
    - folder = g_new0(NMFolder, 1);
    -
    - if ((field = nm_locate_field(NM_A_SZ_OBJECT_ID, (NMField *) fields->ptr_value))) {
    -
    - if (field->ptr_value)
    - folder->id = atoi((char *) field->ptr_value);
    - }
    -
    - if ((field =
    - nm_locate_field(NM_A_SZ_SEQUENCE_NUMBER, (NMField *) fields->ptr_value))) {
    -
    - if (field->ptr_value)
    - folder->seq = atoi((char *) field->ptr_value);
    - }
    -
    - if ((field =
    - nm_locate_field(NM_A_SZ_DISPLAY_NAME, (NMField *) fields->ptr_value))) {
    -
    - if (field->ptr_value)
    - folder->name = g_strdup((char *) field->ptr_value);
    - }
    -
    - folder->ref_count = 1;
    - return folder;
    -}
    -
    -NMField *
    -nm_folder_to_fields(NMFolder * folder)
    -{
    - NMField *fields = NULL;
    -
    - if (folder == NULL)
    - return NULL;
    -
    - fields = nm_field_add_pointer(fields, NM_A_SZ_OBJECT_ID, 0, NMFIELD_METHOD_VALID, 0,
    - g_strdup_printf("%d", folder->id), NMFIELD_TYPE_UTF8);
    -
    - fields = nm_field_add_pointer(fields, NM_A_SZ_PARENT_ID, 0, NMFIELD_METHOD_VALID, 0,
    - g_strdup("0"), NMFIELD_TYPE_UTF8);
    -
    - fields = nm_field_add_pointer(fields, NM_A_SZ_TYPE, 0, NMFIELD_METHOD_VALID, 0,
    - g_strdup("1"), NMFIELD_TYPE_UTF8);
    -
    - fields = nm_field_add_pointer(fields, NM_A_SZ_SEQUENCE_NUMBER, 0, NMFIELD_METHOD_VALID, 0,
    - g_strdup_printf("%d", folder->seq), NMFIELD_TYPE_UTF8);
    -
    - if (folder->name != NULL) {
    - fields = nm_field_add_pointer(fields, NM_A_SZ_DISPLAY_NAME, 0, NMFIELD_METHOD_VALID, 0,
    - g_strdup(folder->name), NMFIELD_TYPE_UTF8);
    - }
    -
    -
    - return fields;
    -}
    -
    -void
    -nm_folder_update_list_properties(NMFolder * folder, NMField * fields)
    -{
    - NMField *field;
    -
    - if (folder == NULL || fields == NULL || fields->ptr_value == 0)
    - return;
    -
    - if ((field = nm_locate_field(NM_A_SZ_OBJECT_ID, (NMField *) fields->ptr_value))) {
    -
    - if (field->ptr_value)
    - folder->id = atoi((char *) field->ptr_value);
    -
    - }
    -
    - if ((field =
    - nm_locate_field(NM_A_SZ_SEQUENCE_NUMBER, (NMField *) fields->ptr_value))) {
    -
    - if (field->ptr_value)
    - folder->seq = atoi((char *) field->ptr_value);
    -
    - }
    -
    - if ((field =
    - nm_locate_field(NM_A_SZ_DISPLAY_NAME, (NMField *) fields->ptr_value))) {
    -
    - if (field->ptr_value) {
    - g_free(folder->name);
    -
    - folder->name = g_strdup((char *) field->ptr_value);
    - }
    -
    - }
    -
    -}
    -
    -void
    -nm_release_folder(NMFolder * folder)
    -{
    - if (folder == NULL)
    - return;
    -
    - if (--(folder->ref_count) == 0) {
    - g_free(folder->name);
    -
    - if (folder->folders) {
    - _release_folder_folders(folder);
    - }
    -
    - if (folder->contacts) {
    - _release_folder_contacts(folder);
    - }
    -
    - g_free(folder);
    - }
    -}
    -
    -
    -void
    -nm_folder_add_ref(NMFolder * folder)
    -{
    - if (folder)
    - folder->ref_count++;
    -}
    -
    -int
    -nm_folder_get_subfolder_count(NMFolder * folder)
    -{
    - if (folder == NULL)
    - return 0;
    -
    - if (folder->folders)
    - return g_slist_length(folder->folders);
    - else
    - return 0;
    -}
    -
    -NMFolder *
    -nm_folder_get_subfolder(NMFolder * folder, int index)
    -{
    - if (folder == NULL)
    - return NULL;
    -
    - if (folder->folders)
    - return (NMFolder *) g_slist_nth_data(folder->folders, index);
    - else
    - return NULL;
    -}
    -
    -int
    -nm_folder_get_contact_count(NMFolder * folder)
    -{
    - if (folder == NULL)
    - return 0;
    -
    - if (folder->contacts != NULL)
    - return g_slist_length(folder->contacts);
    - else
    - return 0;
    -}
    -
    -NMContact *
    -nm_folder_get_contact(NMFolder * folder, int index)
    -{
    - if (folder == NULL)
    - return NULL;
    -
    - if (folder->contacts)
    - return (NMContact *) g_slist_nth_data(folder->contacts, index);
    - else
    - return NULL;
    -}
    -
    -const char *
    -nm_folder_get_name(NMFolder * folder)
    -{
    - if (folder == NULL)
    - return NULL;
    -
    - return folder->name;
    -}
    -
    -void
    -nm_folder_set_name(NMFolder * folder, const char *name)
    -{
    - if (folder == NULL || name == NULL)
    - return;
    -
    - g_free(folder->name);
    -
    - folder->name = g_strdup(name);
    -}
    -
    -int
    -nm_folder_get_id(NMFolder * folder)
    -{
    - if (folder == NULL) {
    - return -1;
    - }
    -
    - return folder->id;
    -}
    -
    -void
    -nm_folder_add_folder_to_list(NMFolder * root, NMFolder * folder)
    -{
    - GSList *node;
    -
    - if (root == NULL || folder == NULL)
    - return;
    -
    - node = root->folders;
    - while (node) {
    - if (folder->seq <= ((NMFolder *) node->data)->seq) {
    - nm_folder_add_ref(folder);
    - root->folders = g_slist_insert_before(root->folders, node, folder);
    - break;
    - }
    - node = g_slist_next(node);
    - }
    - if (node == NULL) {
    - nm_folder_add_ref(folder);
    - root->folders = g_slist_append(root->folders, folder);
    - }
    -}
    -
    -void
    -nm_folder_remove_contact(NMFolder * folder, NMContact * contact)
    -{
    - GSList *node;
    -
    - if (folder == NULL || contact == NULL)
    - return;
    -
    - node = folder->contacts;
    - while (node) {
    - if (contact->id == ((NMContact *) (node->data))->id) {
    - folder->contacts = g_slist_delete_link(folder->contacts, node);
    - nm_release_contact(contact);
    - break;
    - }
    - node = g_slist_next(node);
    - }
    -}
    -
    -void
    -nm_folder_add_contact_to_list(NMFolder * root_folder, NMContact * contact)
    -{
    - GSList *node = NULL;
    - NMFolder *folder = root_folder;
    -
    - if (folder == NULL || contact == NULL)
    - return;
    -
    - /* Find folder to add contact to */
    - if (contact->parent_id != 0) {
    - node = folder->folders;
    - while (node) {
    - folder = (NMFolder *) node->data;
    - if (contact->parent_id == folder->id) {
    - break;
    - }
    - folder = NULL;
    - node = g_slist_next(node);
    - }
    - }
    -
    - /* Add contact to list */
    - if (folder) {
    - node = folder->contacts;
    - while (node) {
    - if (contact->seq <= ((NMContact *) (node->data))->seq) {
    - nm_contact_add_ref(contact);
    - folder->contacts =
    - g_slist_insert_before(folder->contacts, node, contact);
    - break;
    - }
    - node = g_slist_next(node);
    - }
    -
    - if (node == NULL) {
    - nm_contact_add_ref(contact);
    - folder->contacts = g_slist_append(folder->contacts, contact);
    - }
    - }
    -}
    -
    -void
    -nm_folder_add_contacts_and_folders(NMUser * user, NMFolder * root,
    - NMField * fields)
    -{
    - /* Add the contacts and folders from the field array */
    - if (user && root && fields) {
    - _add_folders(root, fields);
    - _add_contacts(user, root, fields);
    - }
    -}
    -
    -gpointer
    -nm_folder_find_item_by_object_id(NMFolder * root_folder, int object_id)
    -{
    - int cnt, cnt2, i, j;
    - gpointer item = NULL;
    - NMFolder *folder;
    - NMContact *contact;
    -
    - if (root_folder == NULL)
    - return NULL;
    -
    - /* Check all contacts for the top level folder */
    - cnt = nm_folder_get_contact_count(root_folder);
    - for (i = 0; i < cnt; i++) {
    - contact = nm_folder_get_contact(root_folder, i);
    - if (contact && (contact->id == object_id)) {
    - item = contact;
    - break;
    - }
    - }
    -
    - /* If we haven't found the item yet, check the subfolders */
    - if (item == NULL) {
    - cnt = nm_folder_get_subfolder_count(root_folder);
    - for (i = 0; (i < cnt) && (item == NULL); i++) {
    - folder = nm_folder_get_subfolder(root_folder, i);
    -
    - /* Check the id of this folder */
    - if (folder && (folder->id == object_id)) {
    - item = folder;
    - break;
    - }
    -
    - /* Check all contacts for this folder */
    - cnt2 = nm_folder_get_contact_count(folder);
    - for (j = 0; j < cnt2; j++) {
    - contact = nm_folder_get_contact(folder, j);
    - if (contact && (contact->id == object_id)) {
    - item = contact;
    - break;
    - }
    - }
    - }
    - }
    -
    - return item;
    -}
    -
    -NMContact *
    -nm_folder_find_contact_by_userid(NMFolder * folder, const char *userid)
    -{
    - int cnt, i;
    - NMContact *tmp, *contact = NULL;
    -
    - if (folder == NULL || userid == NULL)
    - return NULL;
    -
    - cnt = nm_folder_get_contact_count(folder);
    - for (i = 0; i < cnt; i++) {
    - tmp = nm_folder_get_contact(folder, i);
    - if (tmp && nm_utf8_str_equal(nm_contact_get_userid(tmp), userid)) {
    - contact = tmp;
    - break;
    - }
    - }
    -
    - return contact;
    -}
    -
    -NMContact *
    -nm_folder_find_contact_by_display_id(NMFolder * folder, const char *display_id)
    -{
    - int cnt, i;
    - NMContact *tmp, *contact = NULL;
    -
    - if (folder == NULL || display_id == NULL)
    - return NULL;
    -
    - cnt = nm_folder_get_contact_count(folder);
    - for (i = 0; i < cnt; i++) {
    - tmp = nm_folder_get_contact(folder, i);
    - if (tmp && nm_utf8_str_equal(nm_contact_get_display_id(tmp), display_id)) {
    - contact = tmp;
    - break;
    - }
    - }
    -
    - return contact;
    -}
    -
    -NMContact *
    -nm_folder_find_contact(NMFolder * folder, const char *dn)
    -{
    - int cnt, i;
    - NMContact *tmp, *contact = NULL;
    -
    - if (folder == NULL || dn == NULL)
    - return NULL;
    -
    - cnt = nm_folder_get_contact_count(folder);
    - for (i = 0; i < cnt; i++) {
    - tmp = nm_folder_get_contact(folder, i);
    - if (tmp && nm_utf8_str_equal(nm_contact_get_dn(tmp), dn)) {
    - contact = tmp;
    - break;
    - }
    - }
    -
    - return contact;
    -}
    -
    -
    -/*********************************************************************
    - * Utility functions
    - *********************************************************************/
    -
    -static void
    -_release_folder_contacts(NMFolder * folder)
    -{
    - g_slist_free_full(folder->contacts, (GDestroyNotify)nm_release_contact);
    - folder->contacts = NULL;
    -}
    -
    -static void
    -_release_folder_folders(NMFolder * folder)
    -{
    - if (folder == NULL)
    - return;
    -
    - g_slist_free_full(folder->folders, (GDestroyNotify)nm_release_folder);
    - folder->folders = NULL;
    -}
    -
    -static void
    -_add_folders(NMFolder * root, NMField * fields)
    -{
    - NMFolder *folder = NULL;
    - NMField *locate = NULL;
    -
    - locate = nm_locate_field(NM_A_FA_FOLDER, fields);
    - while (locate != NULL) {
    -
    - /* Create a new folder */
    - folder = nm_create_folder_from_fields(locate);
    -
    - /* Add subfolder to roots folder list */
    - nm_folder_add_folder_to_list(root, folder);
    -
    - /* Decrement the ref count */
    - nm_release_folder(folder);
    -
    - /* Find the next folder */
    - locate = nm_locate_field(NM_A_FA_FOLDER, locate+1);
    -
    - }
    -}
    -
    -static void
    -_add_contacts(NMUser * user, NMFolder * folder, NMField * fields)
    -{
    - NMContact *contact = NULL;
    - NMField *locate = NULL, *details;
    - NMUserRecord *user_record = NULL;
    -
    - locate = nm_locate_field(NM_A_FA_CONTACT, fields);
    - while (locate != NULL) {
    -
    - /* Create a new contact from the fields */
    - contact = nm_create_contact_from_fields(locate);
    -
    - /* Add it to our contact list */
    - nm_folder_add_contact_to_list(folder, contact);
    -
    - /* Update the contact cache */
    - nm_user_add_contact(user, contact);
    -
    - /* Update the user record cache */
    - if ((details = nm_locate_field(NM_A_FA_USER_DETAILS,
    - (NMField *) locate->ptr_value))) {
    - user_record = nm_find_user_record(user, nm_contact_get_dn(contact));
    - if (user_record == NULL) {
    - user_record = nm_create_user_record_from_fields(details);
    - nm_user_record_set_dn(user_record, nm_contact_get_dn(contact));
    - nm_user_add_user_record(user, user_record);
    - nm_release_user_record(user_record);
    - }
    - nm_contact_set_user_record(contact, user_record);
    - }
    -
    - nm_release_contact(contact);
    -
    - locate = nm_locate_field(NM_A_FA_CONTACT, locate+1);
    - }
    -}
    --- a/libpurple/protocols/novell/nmcontact.h Fri Nov 11 19:39:24 2022 -0600
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,425 +0,0 @@
    -/*
    - * nmcontact.h
    - *
    - * Copyright (c) 2004 Novell, Inc. All Rights Reserved.
    - *
    - * 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; version 2 of the License.
    - *
    - * 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, write to the Free Software
    - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    - *
    - */
    -
    -#ifndef PURPLE_NOVELL_NMCONTACT_H
    -#define PURPLE_NOVELL_NMCONTACT_H
    -
    -#include <glib.h>
    -
    -typedef struct _NMContact NMContact;
    -typedef struct _NMContactProperty NMContactProperty;
    -typedef struct _NMFolder NMFolder;
    -
    -#include "nmfield.h"
    -#include "nmuser.h"
    -
    -/**
    - * Creates a contact
    - *
    - * Should be released by calling nm_release_contact
    - *
    - * @return The new NMContact
    - *
    - */
    -NMContact *nm_create_contact(void);
    -
    -/**
    - * Creates a contact from a field array representing the
    - * contact
    - *
    - * Should be released by calling nm_release_contact
    - *
    - * @param fields Should be the NM_A_FA_CONTACT for
    - * the contact
    - *
    - * @return The new contact
    - *
    - */
    -NMContact *nm_create_contact_from_fields(NMField * fields);
    -
    -/**
    - * Add a reference to an existing contact
    - *
    - * The reference should be released by calling
    - * nm_release_contact
    - *
    - * @param contact The contact
    - *
    - */
    -void nm_contact_add_ref(NMContact * contact);
    -
    -/**
    - * Update the contact list properties of the contact (sequence, parent id, etc.)
    - *
    - * @param contact The contact to update
    - * @param fields The fields to update from (should be a NM_A_FA_CONTACT array)
    - *
    - */
    -void nm_contact_update_list_properties(NMContact * contact, NMField * fields);
    -
    -/**
    - * Release a contact reference
    - *
    - * @param contact The contact to release.
    - *
    - */
    -void nm_release_contact(NMContact * contact);
    -
    -/**
    - * Get the display name of a contact
    - *
    - * @param contact The contact
    - *
    - * @return The display name of a contact
    - *
    - */
    -const char *nm_contact_get_display_name(NMContact * contact);
    -
    -/**
    - * Get the DN of a contact
    - *
    - * @param contact The contact
    - *
    - * @return The DN of the contact
    - */
    -const char *nm_contact_get_dn(NMContact * contact);
    -
    -/**
    - * Set the display name for a contact. This is called
    - * by nm_send_rename_contact. It should not be called
    - * directly (it does not change the display name on the
    - * server side list -- nm_send_rename_conact does).
    - *
    - * @param contact The contact
    - * @param display_name The new display name
    - *
    - */
    -void nm_contact_set_display_name(NMContact * contact, const char * display_name);
    -
    -/**
    - * Set the DN for the contact
    - *
    - * @param contact The contact
    - * @param dn The new DN for the contact
    - *
    - */
    -void nm_contact_set_dn(NMContact * contact, const char * dn);
    -
    -/**
    - * Return a field array (NM_A_FA_CONTACT) representing the contact
    - *
    - * @param contact The contact
    - *
    - * @return A field array representing the contact
    - */
    -NMField *nm_contact_to_fields(NMContact * contact);
    -
    -/**
    - * Set the user record for the contact
    - *
    - * @param contact The contact
    - * @param user_record The user record
    - *
    - */
    -void nm_contact_set_user_record(NMContact * contact, NMUserRecord * user_record);
    -
    -/**
    - * Get the user record for the contact
    - *
    - * @param contact The contact
    - *
    - * @return The user record associated with the contact
    - *
    - */
    -NMUserRecord *nm_contact_get_user_record(NMContact * contact);
    -
    -/**
    - * Get the user defined data for the contact
    - *
    - * @param contact The contact
    - *
    - * @return The user defined data for the contact
    - *
    - */
    -gpointer nm_contact_get_data(NMContact * contact);
    -
    -/**
    - * Get the Object ID for the contact
    - *
    - * @param contact The contact
    - *
    - * @return The ID for the contact
    - */
    -int nm_contact_get_id(NMContact * contact);
    -
    -/**
    - * Get the ID for the folder that the contact is in
    - *
    - * @param contact The contact
    - *
    - * @return The ID of the folder that contains the contact
    - *
    - */
    -int nm_contact_get_parent_id(NMContact * contact);
    -
    -/**
    - * Get The userid of the contact.
    - *
    - * @param contact The contact
    - *
    - * @return The userid of the contact
    - *
    - */
    -const char *nm_contact_get_userid(NMContact * contact);
    -
    -/**
    - * Get the display id of the contact
    - *
    - * @param contact The contact
    - *
    - * @return The display id of the contact
    - */
    -const char *nm_contact_get_display_id(NMContact * contact);
    -
    -/**
    - * Set the user defined data for the contact
    - *
    - * @param contact The contact
    - * @param data The user defined data
    - *
    - */
    -void nm_contact_set_data(NMContact * contact, gpointer data);
    -
    -/**
    - * Create a folder with the given name
    - *
    - * @param name The name of the folder
    - *
    - * @return The new folder
    - *
    - */
    -NMFolder *nm_create_folder(const char *name);
    -
    -/**
    - * Create a folder from a NM_A_FA_FOLDER field array
    - *
    - * @param fields The NM_A_FA_FOLDER field array
    - *
    - * @return The new folder
    - *
    - */
    -NMFolder *nm_create_folder_from_fields(NMField * fields);
    -
    -/**
    - * Add a reference to an existing folder
    - *
    - * The reference should be released by calling
    - * nm_release_folder
    - *
    - * @param folder The folder
    - *
    - */
    -void nm_folder_add_ref(NMFolder * folder);
    -
    -/**
    - * Release a reference to a folder.
    - *
    - * @param folder The folder to release
    - *
    - */
    -void nm_release_folder(NMFolder * folder);
    -
    -/**
    - * Return the number of subfolders for the given
    - * folder
    - *
    - * @param folder The folder
    - *
    - * @return The number of subfolders contained by folder
    - */
    -int nm_folder_get_subfolder_count(NMFolder * folder);
    -
    -/**
    - * Get a subfolder
    - *
    - * @param folder The root folder
    - * @param index The index of the folder to get
    - *
    - * @return The subfolder at the given index
    - *
    - */
    -NMFolder *nm_folder_get_subfolder(NMFolder * folder, int index);
    -
    -/**
    - * Get the number of contacts in the given folder
    - *
    - * @param folder The folder
    - *
    - * @return The number of contacts contained by folder
    - *
    - */
    -int nm_folder_get_contact_count(NMFolder * folder);
    -
    -/**
    - * Get a contact in the given folder
    - *
    - * @param folder The folder
    - * @param index The index of the contact to get
    - *
    - * @return The contact at the given index
    - *
    - */
    -NMContact *nm_folder_get_contact(NMFolder * folder, int index);
    -
    -/**
    - * Get the name of the folder
    - *
    - * @param folder The folder
    - *
    - * @return The name of the folder.
    - *
    - */
    -const char *nm_folder_get_name(NMFolder * folder);
    -
    -/**
    - * Set the name of a folder. Do not call this directly.
    - * It does not change the name of the folder in the
    - * server side contact list. You must call
    - * nm_send_set_folder_name().
    - *
    - * @param folder The folder
    - * @param name The new name for the folder
    - *
    - */
    -void nm_folder_set_name(NMFolder * folder, const char *name);
    -
    -/**
    - * Get Object ID for folder
    - *
    - * @param folder The folder
    - *
    - * @return The ID of the folder
    - *
    - */
    -int nm_folder_get_id(NMFolder * folder);
    -
    -/**
    - * Add contacts and folders from fields into root
    - *
    - * @param user The logged in user
    - * @param root The root folder
    - * @param fields The contact list field array
    - *
    - */
    -void nm_folder_add_contacts_and_folders(NMUser * user, NMFolder * root,
    - NMField * fields);
    -/**
    - * Add a contact to the contact list.
    - *
    - * @param root_folder The root folder of the contact list
    - * @param contact The contact to add
    - *
    - */
    -void nm_folder_add_contact_to_list(NMFolder * root_folder,
    - NMContact * contact);
    -
    -/**
    - * Update the contact list properties of the folder (sequence, parent id, etc.)
    - *
    - * @param folder The folder to update
    - * @param fields The fields to update from (should be a NM_A_FA_FOLDER array)
    - *
    - */
    -void nm_folder_update_list_properties(NMFolder * folder, NMField * fields);
    -
    -/**
    - * Add folder to the contact list
    - *
    - * @param root_folder The root folder of the contact list
    - * @param folder The folder to add to the contact list
    - *
    - */
    -void nm_folder_add_folder_to_list(NMFolder * root_folder, NMFolder * folder);
    -
    -/**
    - * Find the object with the given id
    - *
    - * @param root_folder The root folder of the contact list
    - * @param object_id The object id of the object to find
    - *
    - * @return The object with object id (either a contact or a folder)
    - */
    -gpointer nm_folder_find_item_by_object_id(NMFolder * root_folder,
    - int object_id);
    -
    -/**
    - * Remove a contact from the folder
    - *
    - * @param folder The folder
    - * @param contact The contact to remove
    - *
    - */
    -void nm_folder_remove_contact(NMFolder * folder, NMContact * contact);
    -
    -/**
    - * Find a contact in a folder by DN
    - *
    - * @param folder The folder to search
    - * @param dn The DN of the contact to find
    - *
    - * @return The contact if found, NULL otherwise
    - *
    - */
    -NMContact *nm_folder_find_contact(NMFolder * folder, const char *dn);
    -
    -/**
    - * Find a contact in a folder by userid
    - *
    - * @param folder The folder to search
    - * @param userid The userid of the contact to find
    - *
    - * @return The contact if found, NULL otherwise
    - *
    - */
    -NMContact *nm_folder_find_contact_by_userid(NMFolder * folder,
    - const char *userid);
    -
    -/**
    - * Find a contact in a folder by display id
    - *
    - * @param folder The folder to search
    - * @param display_id The userid of the contact to find
    - *
    - * @return The contact if found, NULL otherwise
    - *
    - */
    -NMContact *
    -nm_folder_find_contact_by_display_id(NMFolder * folder, const char *display_id);
    -
    -/**
    - * Return a field array (NM_A_FA_FOLDER) representing the folder
    - *
    - * @param folder The folder
    - *
    - * @return A field array representing the folder
    - */
    -NMField *nm_folder_to_fields(NMFolder * folder);
    -
    -#endif /* PURPLE_NOVELL_NMCONTACT_H */
    --- a/libpurple/protocols/novell/nmevent.c Fri Nov 11 19:39:24 2022 -0600
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,1061 +0,0 @@
    -/*
    - * nmevent.c
    - *
    - * Copyright (c) 2004 Novell, Inc. All Rights Reserved.
    - *
    - * 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; version 2 of the License.
    - *
    - * 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, write to the Free Software
    - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    - *
    - */
    -
    -#include <glib.h>
    -#include <string.h>
    -#include <time.h>
    -#include "nmevent.h"
    -#include "nmfield.h"
    -#include "nmconn.h"
    -#include "nmuserrecord.h"
    -#include "nmrtf.h"
    -
    -#define MAX_UINT32 0xFFFFFFFF
    -
    -struct _NMEvent
    -{
    -
    - /* Event type */
    - int type;
    -
    - /* The DN of the event source */
    - char *source;
    -
    - /* Timestamp of the event */
    - guint32 gmt;
    -
    - /* Conference to associate with the event */
    - NMConference *conference;
    -
    - /* User record to associate with the event */
    - NMUserRecord *user_record;
    -
    - /* Text associated with the event */
    - char *text;
    -
    - /* Reference count for event structure */
    - int ref_count;
    -
    -};
    -
    -/* Handle getdetails response and set the new user record into the event */
    -static void
    -_got_user_for_event(NMUser * user, NMERR_T ret_val,
    - gpointer resp_data, gpointer user_data)
    -{
    - NMUserRecord *user_record;
    - NMEvent *event;
    - nm_event_cb cb;
    -
    - if (user == NULL)
    - return;
    -
    - user_record = resp_data;
    - event = user_data;
    -
    - if (ret_val == NM_OK) {
    - if (event && user_record) {
    -
    - /* Add the user record to the event structure
    - * and make the callback.
    - */
    - nm_event_set_user_record(event, user_record);
    - if ((cb = nm_user_get_event_callback(user))) {
    - cb(user, event);
    - }
    - }
    -
    - } else {
    - /* Cleanup resp_data */
    -
    - }
    -
    - /* Clean up */
    - if (event)
    - nm_release_event(event);
    -
    -}
    -
    -/* Handle getdetails response, set the new user record into the event
    - * and add the user record as a participant in the conference
    - */
    -static void
    -_got_user_for_conference(NMUser * user, NMERR_T ret_val,
    - gpointer resp_data, gpointer user_data)
    -{
    - NMUserRecord *user_record = resp_data;
    - NMEvent *event = user_data;
    - NMConference *conference;
    - nm_event_cb cb;
    -
    - if (user == NULL)
    - return;
    -
    - if (event && user_record) {
    -
    - conference = nm_event_get_conference(event);
    - if (conference) {
    -
    - /* Add source of event as recip of the conference */
    - nm_conference_add_participant(conference, user_record);
    -
    - /* Add the user record to the event structure
    - * and make the callback.
    - */
    - nm_event_set_user_record(event, user_record);
    - if ((cb = nm_user_get_event_callback(user))) {
    - cb(user, event);
    - }
    - }
    - }
    -
    - if (event)
    - nm_release_event(event);
    -}
    -
    -/* Read the receive message event, set up the event object, and
    - * get details for the event source if we don't have them yet.
    - */
    -static NMERR_T
    -handle_receive_message(NMUser * user, NMEvent * event, gboolean autoreply)
    -{
    - NMConference *conference;
    - NMUserRecord *user_record;
    - NMConn *conn;
    - NMERR_T rc = NM_OK;
    - guint32 size = 0, flags = 0;
    - char *msg = NULL;
    - char *nortf = NULL;
    - char *guid = NULL;
    - GError *error = NULL;
    -
    - conn = nm_user_get_conn(user);
    -
    - /* Read the conference guid */
    - size = g_data_input_stream_read_uint32(conn->input, user->cancellable,
    - &error);
    - if (size > 1000) {
    - g_clear_error(&error);
    - return NMERR_PROTOCOL;
    - }
    -
    - if (error == NULL) {
    - guid = g_new0(char, size + 1);
    - g_input_stream_read_all(G_INPUT_STREAM(conn->input), guid, size, NULL,
    - user->cancellable, &error);
    - }
    -
    - /* Read the conference flags */
    - if (error == NULL) {
    - flags = g_data_input_stream_read_uint32(conn->input, user->cancellable,
    - &error);
    - }
    -
    - /* Read the message text */
    - if (error == NULL) {
    - size = g_data_input_stream_read_uint32(conn->input, user->cancellable,
    - &error);
    - if (size > 100000) {
    - g_clear_error(&error);
    - return NMERR_PROTOCOL;
    - }
    -
    - if (error == NULL) {
    - msg = g_new0(char, size + 1);
    - g_input_stream_read_all(G_INPUT_STREAM(conn->input), msg, size,
    - NULL, user->cancellable, &error);
    -
    - purple_debug_info("novell", "Message is %s", msg);
    -
    - /* Auto replies are not in RTF format! */
    - if (!autoreply) {
    - NMRtfContext *ctx;
    -
    - ctx = nm_rtf_init();
    - nortf = nm_rtf_strip_formatting(ctx, msg);
    - nm_rtf_deinit(ctx);
    -
    - purple_debug_info("novell", "Message without RTF is %s", nortf);
    -
    - /* Store the event data */
    - nm_event_set_text(event, nortf);
    -
    - } else {
    -
    - /* Store the event data */
    - nm_event_set_text(event, msg);
    - }
    - }
    - }
    -
    - if (error != NULL) {
    - if (error->code == G_IO_ERROR_CANCELLED) {
    - rc = NM_OK;
    - } else {
    - rc = NMERR_TCP_READ;
    - }
    - g_error_free(error);
    - return rc;
    - }
    -
    - /* Check to see if we already know about the conference */
    - conference = nm_conference_list_find(user, guid);
    - if (conference) {
    -
    - nm_conference_set_flags(conference, flags);
    - nm_event_set_conference(event, conference);
    -
    - /* Add a reference to the user record in our event object */
    - user_record = nm_find_user_record(user, nm_event_get_source(event));
    - if (user_record) {
    - nm_event_set_user_record(event, user_record);
    - }
    -
    - } else {
    -
    - /* This is a new conference, so create one and add it to our list */
    - conference = nm_create_conference(guid);
    - nm_conference_set_flags(conference, flags);
    -
    - /* Add a reference to the conference in the event */
    - nm_event_set_conference(event, conference);
    -
    - /* Add new conference to the conference list */
    - nm_conference_list_add(user, conference);
    -
    - /* Check to see if we have details for the event source yet */
    - user_record = nm_find_user_record(user, nm_event_get_source(event));
    - if (user_record) {
    -
    - /* We do so add the user record as a recipient of the conference */
    - nm_conference_add_participant(conference, user_record);
    -
    - /* Add a reference to the user record in our event object */
    - nm_event_set_user_record(event, user_record);
    -
    - } else {
    -
    - /* Need to go to the server to get details for the user */
    - rc = nm_send_get_details(user, nm_event_get_source(event),
    - _got_user_for_conference, event);
    - if (rc == NM_OK)
    - rc = -1; /* Not done processing the event yet! */
    - }
    -
    - nm_release_conference(conference);
    - }
    -
    - g_free(msg);
    - g_free(nortf);
    - g_free(guid);
    -
    - return rc;
    -}
    -
    -/* Read the invite event, set up the event object, and
    - * get details for the event source if we don't have them yet.
    - */
    -static NMERR_T
    -handle_conference_invite(NMUser * user, NMEvent * event)
    -{
    - NMERR_T rc = NM_OK;
    - guint32 size = 0;
    - char *guid = NULL;
    - char *msg = NULL;
    - NMConn *conn;
    - NMUserRecord *user_record;
    - GError *error = NULL;
    -
    - conn = nm_user_get_conn(user);
    -
    - /* Read the conference guid */
    - size = g_data_input_stream_read_uint32(conn->input, user->cancellable,
    - &error);
    - if (size > 1000) {
    - g_clear_error(&error);
    - return NMERR_PROTOCOL;
    - }
    -
    - if (error == NULL) {
    - guid = g_new0(char, size + 1);
    - g_input_stream_read_all(G_INPUT_STREAM(conn->input), guid, size, NULL,
    - user->cancellable, &error);
    - }
    -
    - /* Read the the message */
    - if (error == NULL) {
    - size = g_data_input_stream_read_uint32(conn->input, user->cancellable,
    - &error);
    - if (size > 100000) {
    - g_clear_error(&error);
    - return NMERR_PROTOCOL;
    - }
    -
    - if (error == NULL) {
    - msg = g_new0(char, size + 1);
    - g_input_stream_read_all(G_INPUT_STREAM(conn->input), msg, size,
    - NULL, user->cancellable, &error);
    - }
    - }
    -
    - /* Store the event data */
    - if (error == NULL) {
    - NMConference *conference;
    -
    - nm_event_set_text(event, msg);
    -
    - conference = nm_conference_list_find(user, guid);
    - if (conference == NULL) {
    - conference = nm_create_conference(guid);
    -
    - /* Add new conference to the list and the event */
    - nm_conference_list_add(user, conference);
    - nm_event_set_conference(event, conference);
    -
    - /* Check to see if we have details for the event source yet */
    - user_record = nm_find_user_record(user, nm_event_get_source(event));
    - if (user_record) {
    -
    - /* Add a reference to the user record in our event object */
    - nm_event_set_user_record(event, user_record);
    -
    - } else {
    -
    - /* Need to go to the server to get details for the user */
    - rc = nm_send_get_details(user, nm_event_get_source(event),
    - _got_user_for_event, event);
    - if (rc == NM_OK)
    - rc = -1; /* Not done processing the event yet! */
    - }
    -
    - nm_release_conference(conference);
    -
    - }
    - } else {
    - if (error->code != G_IO_ERROR_CANCELLED) {
    - rc = NMERR_TCP_READ;
    - }
    - g_error_free(error);
    - }
    -
    - g_free(msg);
    - g_free(guid);
    -
    - return rc;
    -}
    -
    -/* Read the invite notify event, set up the event object, and
    - * get details for the event source if we don't have them yet.
    - */
    -static NMERR_T
    -handle_conference_invite_notify(NMUser * user, NMEvent * event)
    -{
    - NMERR_T rc = NM_OK;
    - guint32 size = 0;
    - char *guid = NULL;
    - NMConn *conn;
    - NMConference *conference;
    - NMUserRecord *user_record;
    - GError *error = NULL;
    -
    - conn = nm_user_get_conn(user);
    -
    - /* Read the conference guid */
    - size = g_data_input_stream_read_uint32(conn->input, user->cancellable,
    - &error);
    - if (size > 1000) {
    - g_clear_error(&error);
    - return NMERR_PROTOCOL;
    - }
    -
    - if (error == NULL) {
    - guid = g_new0(char, size + 1);
    - g_input_stream_read_all(G_INPUT_STREAM(conn->input), guid, size, NULL,
    - user->cancellable, &error);
    - }
    -
    - if (error != NULL) {
    - if (error->code == G_IO_ERROR_CANCELLED) {
    - rc = NM_OK;
    - } else {
    - rc = NMERR_TCP_READ;
    - }
    - g_error_free(error);
    - return rc;
    - }
    -
    - conference = nm_conference_list_find(user, guid);
    - if (conference) {
    - nm_event_set_conference(event, conference);
    -
    - /* Check to see if we have details for the event source yet */
    - user_record = nm_find_user_record(user, nm_event_get_source(event));
    - if (user_record) {
    -
    - /* Add a reference to the user record in our event object */
    - nm_event_set_user_record(event, user_record);
    -
    - } else {
    -
    - /* Need to go to the server to get details for the user */
    - rc = nm_send_get_details(user, nm_event_get_source(event),
    - _got_user_for_event, event);
    - if (rc == NM_OK)
    - rc = -1; /* Not done processing the event yet! */
    - }
    -
    - } else {
    - rc = NMERR_CONFERENCE_NOT_FOUND;
    - }
    -
    -
    - g_free(guid);
    -
    - return rc;
    -}
    -
    -/* Read the conference reject event and set up the event object */
    -static NMERR_T
    -handle_conference_reject(NMUser * user, NMEvent * event)
    -{
    - NMERR_T rc = NM_OK;
    - guint32 size = 0;
    - char *guid = NULL;
    - NMConn *conn;
    - NMConference *conference;
    - GError *error = NULL;
    -
    - conn = nm_user_get_conn(user);
    -
    - /* Read the conference guid */
    - size = g_data_input_stream_read_uint32(conn->input, user->cancellable,
    - &error);
    - if (size > 1000) {
    - g_clear_error(&error);
    - return NMERR_PROTOCOL;
    - }
    -
    - if (error == NULL) {
    - guid = g_new0(char, size + 1);
    - g_input_stream_read_all(G_INPUT_STREAM(conn->input), guid, size, NULL,
    - user->cancellable, &error);
    - }
    -
    - if (error == NULL) {
    - conference = nm_conference_list_find(user, guid);
    - if (conference) {
    - nm_event_set_conference(event, conference);
    - } else {
    - rc = NMERR_CONFERENCE_NOT_FOUND;
    - }
    - } else {
    - if (error->code != G_IO_ERROR_CANCELLED) {
    - rc = NMERR_TCP_READ;
    - }
    - g_error_free(error);
    - }
    -
    - g_free(guid);
    -
    - return rc;
    -}
    -
    -/* Read the conference left event, set up the event object, and
    - * remove the conference from the list if there are no more
    - * participants
    - */
    -static NMERR_T
    -handle_conference_left(NMUser * user, NMEvent * event)
    -{
    - NMERR_T rc = NM_OK;
    - guint32 size = 0, flags = 0;
    - char *guid = NULL;
    - NMConference *conference;
    - NMConn *conn;
    - GError *error = NULL;
    -
    - conn = nm_user_get_conn(user);
    -
    - /* Read the conference guid */
    - size = g_data_input_stream_read_uint32(conn->input, user->cancellable,
    - &error);
    - if (size > 1000) {
    - g_clear_error(&error);
    - return NMERR_PROTOCOL;
    - }
    -
    - if (error == NULL) {
    - guid = g_new0(char, size + 1);
    - g_input_stream_read_all(G_INPUT_STREAM(conn->input), guid, size, NULL,
    - user->cancellable, &error);
    - }
    -
    - /* Read the conference flags */
    - if (error == NULL) {
    - flags = g_data_input_stream_read_uint32(conn->input, user->cancellable,
    - &error);
    - }
    -
    - if (error == NULL) {
    - conference = nm_conference_list_find(user, guid);
    - if (conference) {
    - nm_event_set_conference(event, conference);
    - nm_conference_set_flags(conference, flags);
    -
    - nm_conference_remove_participant(conference, nm_event_get_source(event));
    - if (nm_conference_get_participant_count(conference) == 0) {
    - nm_conference_list_remove(user, conference);
    - }
    -
    - } else {
    - rc = NMERR_CONFERENCE_NOT_FOUND;
    - }
    - } else {
    - if (error->code != G_IO_ERROR_CANCELLED) {
    - rc = NMERR_TCP_READ;
    - }
    - g_error_free(error);
    - }
    -
    - g_free(guid);
    -
    - return rc;
    -}
    -
    -/* Read the conference closed, set up the event object, and
    - * remove the conference from the list
    - */
    -static NMERR_T
    -handle_conference_closed(NMUser * user, NMEvent * event)
    -{
    - NMERR_T rc = NM_OK;
    - guint32 size = 0;
    - char *guid = NULL;
    - NMConference *conference;
    - NMConn *conn;
    - GError *error = NULL;
    -
    - conn = nm_user_get_conn(user);
    -
    - /* Read the conference guid */
    - size = g_data_input_stream_read_uint32(conn->input, user->cancellable,
    - &error);
    - if (size > 1000) {
    - g_clear_error(&error);
    - return NMERR_PROTOCOL;
    - }
    -
    - if (error == NULL) {
    - guid = g_new0(char, size + 1);
    - g_input_stream_read_all(G_INPUT_STREAM(conn->input), guid, size, NULL,
    - user->cancellable, &error);
    - }
    -
    - if (error == NULL) {
    - conference = nm_conference_list_find(user, guid);
    - if (conference) {
    - nm_event_set_conference(event, conference);
    - nm_conference_list_remove(user, conference);
    - } else {
    - rc = NMERR_CONFERENCE_NOT_FOUND;
    - }
    - } else {
    - if (error->code != G_IO_ERROR_CANCELLED) {
    - rc = NMERR_TCP_READ;
    - }
    - g_error_free(error);
    - }
    -
    - g_free(guid);
    -
    - return rc;
    -}
    -
    -/* Read the conference joined event, set up the event object, and
    - * get details for the event source if we don't have them yet.
    - */
    -static NMERR_T
    -handle_conference_joined(NMUser * user, NMEvent * event)
    -{
    - NMERR_T rc = NM_OK;
    - guint32 size = 0, flags = 0;
    - char *guid = NULL;
    - NMConn *conn;
    - NMConference *conference;
    - NMUserRecord *user_record;
    - GError *error = NULL;
    -
    - conn = nm_user_get_conn(user);
    -
    - /* Read the conference guid */
    - size = g_data_input_stream_read_uint32(conn->input, user->cancellable,
    - &error);
    - if (size > 1000) {
    - g_clear_error(&error);
    - return NMERR_PROTOCOL;
    - }
    -
    - if (error == NULL) {
    - guid = g_new0(char, size + 1);
    - g_input_stream_read_all(G_INPUT_STREAM(conn->input), guid, size, NULL,
    - user->cancellable, &error);
    - }
    -
    - /* Read the conference flags */
    - if (error == NULL) {
    - flags = g_data_input_stream_read_uint32(conn->input, user->cancellable,
    - &error);
    - }
    -
    - if (error == NULL) {
    - conference = nm_conference_list_find(user, guid);
    - if (conference) {
    - nm_conference_set_flags(conference, flags);
    -
    - nm_event_set_conference(event, conference);
    -
    - /* Add the new user to the participants list */
    - user_record = nm_find_user_record(user, nm_event_get_source(event));
    - if (user_record) {
    - nm_conference_remove_participant(conference,
    - nm_user_record_get_dn(user_record));
    - nm_conference_add_participant(conference, user_record);
    - } else {
    -
    - /* Need to go to the server to get details for the user */
    - rc = nm_send_get_details(user, nm_event_get_source(event),
    - _got_user_for_conference, event);
    - if (rc == NM_OK)
    - rc = -1; /* Not done processing the event yet! */
    - }
    -
    - } else {
    - rc = NMERR_CONFERENCE_NOT_FOUND;
    - }
    - } else {
    - if (error->code != G_IO_ERROR_CANCELLED) {
    - rc = NMERR_TCP_READ;
    - }
    - g_error_free(error);
    - }
    -
    - g_free(guid);
    -
    - return rc;
    -}
    -
    -/* Read the typing event and set up the event object */
    -static NMERR_T
    -handle_typing(NMUser * user, NMEvent * event)
    -{
    - NMERR_T rc = NM_OK;
    - guint32 size = 0;
    - char *guid = NULL;
    - NMConference *conference;
    - NMConn *conn;
    - GError *error = NULL;
    -
    - conn = nm_user_get_conn(user);
    -
    - /* Read the conference guid */
    - size = g_data_input_stream_read_uint32(conn->input, user->cancellable,
    - &error);
    - if (size > 1000) {
    - g_clear_error(&error);
    - return NMERR_PROTOCOL;
    - }
    -
    - if (error == NULL) {
    - guid = g_new0(char, size + 1);
    - g_input_stream_read_all(G_INPUT_STREAM(conn->input), guid, size, NULL,
    - user->cancellable, &error);
    - }
    -
    - if (error == NULL) {
    - conference = nm_conference_list_find(user, guid);
    - if (conference) {
    - nm_event_set_conference(event, conference);
    - } else {
    - rc = NMERR_CONFERENCE_NOT_FOUND;
    - }
    - } else {
    - if (error->code != G_IO_ERROR_CANCELLED) {
    - rc = NMERR_TCP_READ;
    - }
    - g_error_free(error);
    - }
    -
    - g_free(guid);
    -
    - return rc;
    -}
    -
    -/* Read the event, set up the event object, and update
    - * the status in the user record (for the event source)
    - */
    -static NMERR_T
    -handle_status_change(NMUser * user, NMEvent * event)
    -{
    - NMERR_T rc = NM_OK;
    - guint16 status;
    - guint32 size;
    - char *text = NULL;
    - NMUserRecord *user_record;
    - NMConn *conn;
    - GError *error = NULL;
    -
    - conn = nm_user_get_conn(user);
    -
    - /* Read new status */
    - status = g_data_input_stream_read_uint16(conn->input, user->cancellable,
    - &error);
    - if (error == NULL) {
    -
    - /* Read the status text */
    - size = g_data_input_stream_read_uint32(conn->input, user->cancellable,
    - &error);
    - if (size > 10000) {
    - g_clear_error(&error);
    - return NMERR_PROTOCOL;
    - }
    -
    - if (error == NULL) {
    - text = g_new0(char, size + 1);
    - g_input_stream_read_all(G_INPUT_STREAM(conn->input), text, size,
    - NULL, user->cancellable, &error);
    - }
    - }
    -
    - if (error == NULL) {
    - nm_event_set_text(event, text);
    -
    - /* Get a reference to the user record and store the new status */
    - user_record = nm_find_user_record(user, nm_event_get_source(event));
    - if (user_record) {
    - nm_event_set_user_record(event, user_record);
    - nm_user_record_set_status(user_record, status, text);
    - }
    - } else {
    - if (error->code != G_IO_ERROR_CANCELLED) {
    - rc = NMERR_TCP_READ;
    - }
    - g_error_free(error);
    - }
    -
    - g_free(text);
    -
    - return rc;
    -}
    -
    -/* Read the undeliverable event */
    -static NMERR_T
    -handle_undeliverable_status(NMUser * user, NMEvent * event)
    -{
    - NMERR_T rc = NM_OK;
    - guint32 size = 0;
    - char *guid = NULL;
    - NMConn *conn;
    - GError *error = NULL;
    -
    - conn = nm_user_get_conn(user);
    -
    - /* Read the conference guid */
    - size = g_data_input_stream_read_uint32(conn->input, user->cancellable,
    - &error);
    - if (size > 1000) {
    - g_clear_error(&error);
    - return NMERR_PROTOCOL;
    - }
    -
    - if (error == NULL) {
    - guid = g_new0(char, size + 1);
    - rc = g_input_stream_read_all(G_INPUT_STREAM(conn->input), guid, size,
    - NULL, user->cancellable, &error);
    - } else {
    - if (error->code != G_IO_ERROR_CANCELLED) {
    - rc = NMERR_TCP_READ;
    - }
    - g_error_free(error);
    - }
    -
    - g_free(guid);
    -
    - return rc;
    -}
    -
    -/*******************************************************************************
    - * Event API -- see header file for comments
    - ******************************************************************************/
    -
    -NMEvent *
    -nm_create_event(int type, const char *source, guint32 gmt)
    -{
    - NMEvent *event = g_new0(NMEvent, 1);
    -
    - event->type = type;
    - event->gmt = gmt;
    -
    - if (source)
    - event->source = g_strdup(source);
    -
    - event->ref_count = 1;
    -
    - return event;
    -}
    -
    -void
    -nm_release_event(NMEvent * event)
    -{
    - if (event == NULL) {
    - return;
    - }
    -
    - if (--(event->ref_count) == 0) {
    -
    - g_free(event->source);
    -
    - if (event->conference)
    - nm_release_conference(event->conference);
    -
    - if (event->user_record)
    - nm_release_user_record(event->user_record);
    -
    - g_free(event->text);
    -
    - g_free(event);
    - }
    -}
    -
    -
    -NMConference *
    -nm_event_get_conference(NMEvent * event)
    -{
    - if (event)
    - return event->conference;
    - else
    - return NULL;
    -}
    -
    -void
    -nm_event_set_conference(NMEvent * event, NMConference * conference)
    -{
    - if (event && conference) {
    - nm_conference_add_ref(conference);
    - event->conference = conference;
    - }
    -}
    -
    -NMUserRecord *
    -nm_event_get_user_record(NMEvent * event)
    -{
    - if (event)
    - return event->user_record;
    - else
    - return NULL;
    -}
    -
    -void
    -nm_event_set_user_record(NMEvent * event, NMUserRecord * user_record)
    -{
    - if (event && user_record) {
    - nm_user_record_add_ref(user_record);
    - event->user_record = user_record;
    - }
    -}
    -
    -const char *
    -nm_event_get_text(NMEvent * event)
    -{
    - if (event)
    - return event->text;
    - else
    - return NULL;
    -}
    -
    -void
    -nm_event_set_text(NMEvent * event, const char *text)
    -{
    - if (event) {
    - if (text)
    - event->text = g_strdup(text);
    - else
    - event->text = NULL;
    - }
    -}
    -
    -const char *
    -nm_event_get_source(NMEvent * event)
    -{
    - if (event)
    - return event->source;
    - else
    - return NULL;
    -}
    -
    -int
    -nm_event_get_type(NMEvent * event)
    -{
    - if (event)
    - return event->type;
    - else
    - return -1;
    -}
    -
    -time_t
    -nm_event_get_gmt(NMEvent * event)
    -{
    - if (event)
    - return event->gmt;
    - else
    - return (time_t)-1;
    -}
    -
    -NMERR_T
    -nm_process_event(NMUser * user, int type)
    -{
    - NMERR_T rc = NM_OK;
    - guint32 size = 0;
    - NMEvent *event = NULL;
    - char *source = NULL;
    - nm_event_cb cb;
    - NMConn *conn;
    - GError *error = NULL;
    -
    - if (user == NULL)
    - return NMERR_BAD_PARM;
    -
    - if (type < NMEVT_START || type > NMEVT_STOP)
    - return NMERR_PROTOCOL;
    -
    - conn = nm_user_get_conn(user);
    -
    - /* Read the event source */
    - size = g_data_input_stream_read_uint32(conn->input, user->cancellable,
    - &error);
    - if (error == NULL) {
    - if (size > 1000000) {
    - /* Size is larger than our 1MB sanity check. Ignore it. */
    - rc = NMERR_PROTOCOL;
    - } else {
    - source = g_new0(char, size);
    -
    - rc = g_input_stream_read_all(G_INPUT_STREAM(conn->input), source,
    - size, NULL, user->cancellable, &error);
    - }
    - }
    -
    - /* Read the event data */
    - if (error == NULL) {
    - event = nm_create_event(type, source, time(0));
    -
    - if (event) {
    -
    - switch (type) {
    - case NMEVT_STATUS_CHANGE:
    - rc = handle_status_change(user, event);
    - break;
    -
    - case NMEVT_RECEIVE_MESSAGE:
    - rc = handle_receive_message(user, event, FALSE);
    - break;
    -
    - case NMEVT_RECEIVE_AUTOREPLY:
    - rc = handle_receive_message(user, event, TRUE);
    - break;
    -
    - case NMEVT_USER_TYPING:
    - case NMEVT_USER_NOT_TYPING:
    - rc = handle_typing(user, event);
    - break;
    -
    - case NMEVT_CONFERENCE_LEFT:
    - rc = handle_conference_left(user, event);
    - break;
    -
    - case NMEVT_CONFERENCE_CLOSED:
    - rc = handle_conference_closed(user, event);
    - break;
    -
    - case NMEVT_CONFERENCE_JOINED:
    - rc = handle_conference_joined(user, event);
    - break;
    -
    - case NMEVT_CONFERENCE_INVITE:
    - rc = handle_conference_invite(user, event);
    - break;
    -
    - case NMEVT_CONFERENCE_REJECT:
    - rc = handle_conference_reject(user, event);
    - break;
    -
    - case NMEVT_CONFERENCE_INVITE_NOTIFY:
    - rc = handle_conference_invite_notify(user, event);
    - break;
    -
    - case NMEVT_UNDELIVERABLE_STATUS:
    - rc = handle_undeliverable_status(user, event);
    - break;
    -
    - case NMEVT_INVALID_RECIPIENT:
    - /* Nothing else to read, just callback */
    - break;
    -
    - case NMEVT_USER_DISCONNECT:
    - /* Nothing else to read, just callback */
    - break;
    -
    - case NMEVT_SERVER_DISCONNECT:
    - /* Nothing else to read, just callback */
    - break;
    -
    - case NMEVT_RECEIVE_FILE:
    - case NMEVT_CONTACT_ADD:
    - /* Safely ignored for now */
    - break;
    -
    - default:
    - purple_debug_info("novell", "Unknown event %d received.", type);
    - rc = NMERR_PROTOCOL;
    - break;
    - }
    - }
    - } else {
    - if (error->code != G_IO_ERROR_CANCELLED) {
    - rc = NMERR_TCP_READ;
    - }
    - g_error_free(error);
    - }
    -
    - if (rc == (NMERR_T)-1) {
    - /* -1 means that we are not ready to callback yet. */
    - rc = NM_OK;
    - } else if (rc == NM_OK && (cb = nm_user_get_event_callback(user))) {
    - cb(user, event);
    - }
    -
    - if (event) {
    - nm_release_event(event);
    - }
    -
    - /* Cleanup */
    - g_free(source);
    -
    - return rc;
    -}
    --- a/libpurple/protocols/novell/nmevent.h Fri Nov 11 19:39:24 2022 -0600
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,178 +0,0 @@
    -/*
    - * nmevent.h
    - *
    - * Copyright (c) 2004 Novell, Inc. All Rights Reserved.
    - *
    - * 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; version 2 of the License.
    - *
    - * 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, write to the Free Software
    - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    - *
    - */
    -
    -#ifndef PURPLE_NOVELL_NMEVENT_H
    -#define PURPLE_NOVELL_NMEVENT_H
    -
    -typedef struct _NMEvent NMEvent;
    -
    -#include "nmuser.h"
    -#include <sys/types.h>
    -
    -/**
    - * Defines for the event types
    - */
    -#define NMEVT_INVALID_RECIPIENT 101
    -#define NMEVT_UNDELIVERABLE_STATUS 102
    -#define NMEVT_STATUS_CHANGE 103
    -#define NMEVT_CONTACT_ADD 104
    -#define NMEVT_CONFERENCE_CLOSED 105
    -#define NMEVT_CONFERENCE_JOINED 106
    -#define NMEVT_CONFERENCE_LEFT 107
    -#define NMEVT_RECEIVE_MESSAGE 108
    -#define NMEVT_RECEIVE_FILE 109
    -#define NMEVT_USER_TYPING 112
    -#define NMEVT_USER_NOT_TYPING 113
    -#define NMEVT_USER_DISCONNECT 114
    -#define NMEVT_SERVER_DISCONNECT 115
    -#define NMEVT_CONFERENCE_RENAME 116
    -#define NMEVT_CONFERENCE_INVITE 117
    -#define NMEVT_CONFERENCE_INVITE_NOTIFY 118
    -#define NMEVT_CONFERENCE_REJECT 119
    -#define NMEVT_RECEIVE_AUTOREPLY 121
    -#define NMEVT_START NMEVT_INVALID_RECIPIENT
    -#define NMEVT_STOP NMEVT_RECEIVE_AUTOREPLY
    -
    -/**
    - * Process the event. The event will be read, an NMEvent will
    - * be created, and the event callback will be called.
    - *
    - * @param user The main user structure.
    - * @param type The type of the event to read.
    - *
    - * @return NM_OK on success
    - */
    -NMERR_T nm_process_event(NMUser * user, int type);
    -
    -/**
    - * Creates an NMEvent
    - *
    - * The NMEvent should be released by calling
    - * nm_release_event.
    - *
    - * @param type The event type, see defines above.
    - * @param source The DN of the event source.
    - * @param gmt The time that the event occurred.
    - *
    - * @return The new NMEvent
    - */
    -NMEvent *nm_create_event(int type, const char *source, guint32 gmt);
    -
    -/**
    - * Releases an NMEvent
    - *
    - * @param event The event to release
    - *
    - */
    -void nm_release_event(NMEvent * event);
    -
    -/**
    - * Sets the conference object for the given event.
    - *
    - * @param event The event.
    - * @param conference The conference to associate with the event.
    - *
    - */
    -void nm_event_set_conference(NMEvent * event, NMConference * conference);
    -
    -/**
    - * Returns the conference object associated with the given event. This should not
    - * be released. If it needs to be kept around call nm_conference_addref().
    - *
    - * @param event The event.
    - *
    - * @return The conference associated with the event, or NULL
    - * if no conference has been set for the event.
    - */
    -NMConference *nm_event_get_conference(NMEvent * event);
    -
    -/**
    - * Sets the NMUserRecord object for the given event.
    - * The user record represents the event source.
    - *
    - * @param event The event.
    - * @param user_record The user record to associate with the event.
    - *
    - */
    -void nm_event_set_user_record(NMEvent * event, NMUserRecord * user_record);
    -
    -/**
    - * Returns the NMUserRecord object associated with the given event.
    - * The user record represents the event source. This should not
    - * be released. If it needs to be kept around call
    - * nm_user_record_add_ref().
    - *
    - * @param event The event.
    - *
    - * @return The user record associated with the event, or NULL
    - * if no user record has been set for the event.
    - */
    -NMUserRecord *nm_event_get_user_record(NMEvent * event);
    -
    -/**
    - * Sets the text to associate with the given event.
    - *
    - * @param event The event.
    - * @param text The text to associate with the event.
    - *
    - */
    -void nm_event_set_text(NMEvent * event, const char *text);
    -
    -/**
    - * Returns the text associated with the given event.
    - *
    - * @param event The event.
    - *
    - * @return The text associated with the event, or NULL
    - * if no text has been set for the event.
    - */
    -const char *nm_event_get_text(NMEvent * event);
    -
    -/**
    - * Returns the source of the event (this will be the full DN of the
    - * event source).
    - *
    - * @param event The event.
    - *
    - * @return The full DN of the event's source.
    - */
    -const char *nm_event_get_source(NMEvent * event);
    -
    -/**
    - * Returns the type of the event. See the defines above for
    - * a list of possible event types.
    - *
    - * @param event The event.
    - *
    - * @return The type of the event.
    - *
    - */
    -int nm_event_get_type(NMEvent * event);
    -
    -/**
    - * Returns the time that the event took place.
    - *
    - * @param event The event.
    - *
    - * @return The timestamp for the event.
    - */
    -time_t nm_event_get_gmt(NMEvent * event);
    -
    -#endif /* PURPLE_NOVELL_NMEVENT_H */
    --- a/libpurple/protocols/novell/nmfield.c Fri Nov 11 19:39:24 2022 -0600
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,362 +0,0 @@
    -/*
    - * nmfield.c
    - *
    - * Copyright (c) 2004 Novell, Inc. All Rights Reserved.
    - *
    - * 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; version 2 of the License.
    - *
    - * 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, write to the Free Software
    - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    - *
    - */
    -
    -#include <string.h>
    -#include <stdio.h>
    -#include "nmfield.h"
    -
    -/* Free a field value and tag */
    -static void _free_field(NMField * field);
    -
    -/* Free a field value */
    -static void _free_field_value(NMField * field);
    -
    -/* Make a deep copy of the field */
    -static void _copy_field(NMField * dest, NMField * src);
    -
    -/* Make a deep copy of the field's value */
    -static void _copy_field_value(NMField * dest, NMField * src);
    -
    -/* Create a string from a value -- for debugging */
    -static char *_value_to_string(NMField * field);
    -
    -static NMField *
    -_add_blank_field(NMField *fields, guint32 count)
    -{
    - guint32 new_len;
    -
    - if (fields == NULL) {
    - fields = g_new0(NMField, 10);
    - fields->len = 10;
    - } else {
    - if (fields->len < count + 2) {
    - new_len = count + 10;
    - fields = g_realloc(fields, new_len * sizeof(NMField));
    - fields->len = new_len;
    - }
    - }
    - return fields;
    -}
    -
    -NMField *
    -nm_field_add_number(NMField * fields, const char *tag, guint32 size, guint8 method,
    - guint8 flags, guint32 value, guint8 type)
    -{
    - guint32 count;
    - NMField *field;
    -
    - count = nm_count_fields(fields);
    - fields = _add_blank_field(fields, count);
    -
    - field = &(fields[count]);
    - field->tag = g_strdup(tag);
    - field->size = size;
    - field->method = method;
    - field->flags = flags;
    - field->value = value;
    - field->type = type;
    -
    - /* Null terminate the field array */
    - field = &((fields)[count + 1]);
    - field->tag = NULL;
    - field->value = 0;
    - field->ptr_value = NULL;
    -
    - return fields;
    -}
    -
    -NMField *
    -nm_field_add_pointer(NMField * fields, const char *tag, guint32 size, guint8 method,
    - guint8 flags, gpointer value, guint8 type)
    -{
    - guint32 count;
    - NMField *field = NULL;
    -
    - count = nm_count_fields(fields);
    - fields = _add_blank_field(fields, count);
    -
    - field = &(fields[count]);
    - field->tag = g_strdup(tag);
    - field->size = size;
    - field->method = method;
    - field->flags = flags;
    - field->ptr_value = value;
    - field->type = type;
    -
    - /* Null terminate the field array */
    - field = &((fields)[count + 1]);
    - field->tag = NULL;
    - field->value = 0;
    - field->ptr_value = NULL;
    -
    - return fields;
    -}
    -
    -guint32
    -nm_count_fields(NMField * fields)
    -{
    - guint32 count = 0;
    -
    - if (fields) {
    - while (fields->tag != NULL) {
    - count++;
    - fields++;
    - }
    - }
    -
    - return count;
    -}
    -
    -void
    -nm_free_fields(NMField ** fields)
    -{
    - NMField *field = NULL;
    -
    - if ((fields == NULL) || (*fields == NULL))
    - return;
    -
    - field = *fields;
    -
    - while (field->tag != NULL) {
    - _free_field(field);
    - field++;
    - }
    -
    - g_free(*fields);
    - *fields = NULL;
    -}
    -
    -
    -static void
    -_free_field(NMField * field)
    -{
    - if (field == NULL)
    - return;
    -
    - _free_field_value(field);
    - g_free(field->tag);
    -}
    -
    -static void
    -_free_field_value(NMField * field)
    -{
    - if (field == NULL)
    - return;
    -
    - switch (field->type) {
    - case NMFIELD_TYPE_BINARY:
    - case NMFIELD_TYPE_UTF8:
    - case NMFIELD_TYPE_DN:
    - g_free(field->ptr_value);
    - break;
    -
    - case NMFIELD_TYPE_ARRAY:
    - case NMFIELD_TYPE_MV:
    - nm_free_fields((NMField **)&field->ptr_value);
    - break;
    -
    - default:
    - break;
    - }
    -
    - field->size = 0;
    - field->ptr_value = NULL;
    -}
    -
    -NMField *
    -nm_locate_field(char *tag, NMField * fields)
    -{
    - NMField *ret_fields = NULL;
    -
    - if ((fields == NULL) || (tag == NULL)) {
    - return NULL;
    - }
    -
    - while (fields->tag != NULL) {
    - if (g_ascii_strcasecmp(fields->tag, tag) == 0) {
    - ret_fields = fields;
    - break;
    - }
    - fields++;
    - }
    -
    - return ret_fields;
    -}
    -
    -NMField *
    -nm_copy_field_array(NMField * src)
    -{
    - NMField *ptr = NULL;
    - NMField *dest = NULL;
    - int count;
    -
    - if (src != NULL) {
    - count = nm_count_fields(src) + 1;
    - dest = g_new0(NMField, count);
    - dest->len = count;
    - ptr = dest;
    - while (src->tag != NULL) {
    - _copy_field(ptr, src);
    - ptr++;
    - src++;
    - }
    - }
    -
    - return dest;
    -}
    -
    -static void
    -_copy_field(NMField * dest, NMField * src)
    -{
    - dest->type = src->type;
    - dest->flags = src->flags;
    - dest->method = src->method;
    - dest->tag = g_strdup(src->tag);
    - _copy_field_value(dest, src);
    -}
    -
    -static void
    -_copy_field_value(NMField * dest, NMField * src)
    -{
    - dest->type = src->type;
    - switch (dest->type) {
    - case NMFIELD_TYPE_UTF8:
    - case NMFIELD_TYPE_DN:
    - if (src->size == 0 && src->ptr_value != NULL) {
    - src->size = strlen((char *) src->ptr_value) + 1;
    - }
    - /* fall through */
    - case NMFIELD_TYPE_BINARY:
    - if (src->size != 0 && src->ptr_value != NULL) {
    - dest->ptr_value = g_new0(char, src->size);
    - memcpy(dest->ptr_value, src->ptr_value, src->size);
    - }
    - break;
    -
    - case NMFIELD_TYPE_ARRAY:
    - case NMFIELD_TYPE_MV:
    - dest->ptr_value = nm_copy_field_array((NMField *)src->ptr_value);
    - break;
    -
    - default:
    - /* numeric value */
    - dest->value = src->value;
    - break;
    - }
    -
    - dest->size = src->size;
    -}
    -
    -void
    -nm_remove_field(NMField * field)
    -{
    - NMField *tmp;
    - guint32 len;
    -
    - if ((field != NULL) && (field->tag != NULL)) {
    - _free_field(field);
    -
    - /* Move fields down */
    - tmp = field + 1;
    - while (1) {
    - /* Don't overwrite the size of the array */
    - len = field->len;
    -
    - *field = *tmp;
    -
    - field->len = len;
    -
    - if (tmp->tag == NULL)
    - break;
    -
    - field++;
    - tmp++;
    - }
    - }
    -}
    -
    -void
    -nm_print_fields(NMField * fields)
    -{
    - char *str = NULL;
    - NMField *field = fields;
    -
    - if (fields == NULL)
    - return;
    -
    - while (field->tag != NULL) {
    - if (field->type == NMFIELD_TYPE_ARRAY || field->type == NMFIELD_TYPE_MV) {
    - printf("Subarray START: %s Method = %d\n", field->tag, field->method);
    - nm_print_fields((NMField *) field->ptr_value);
    - printf("Subarray END: %s\n", field->tag);
    - } else {
    - str = _value_to_string(field);
    - printf("Tag=%s;Value=%s\n", field->tag, str);
    - g_free(str);
    - str = NULL;
    - }
    - field++;
    - }
    -
    -}
    -
    -static char *
    -_value_to_string(NMField * field)
    -{
    - char *value = NULL;
    -
    - if (field == NULL)
    - return NULL;
    -
    - /* This is a single value attribute */
    - if (((field->type == NMFIELD_TYPE_UTF8) ||
    - (field->type == NMFIELD_TYPE_DN)) && (field->ptr_value != NULL)) {
    - value = g_strdup((const char *) field->ptr_value);
    - } else if (field->type == NMFIELD_TYPE_BINARY && field->ptr_value != NULL) {
    - value = g_new0(char, field->size);
    - memcpy(value, (const char *) field->ptr_value, field->size);
    - } else if (field->type == NMFIELD_TYPE_BOOL) {
    - if (field->value) {
    - value = g_strdup(NM_FIELD_TRUE);
    - } else {
    - value = g_strdup(NM_FIELD_FALSE);
    - }
    - } else {
    - /* assume it is a number */
    - switch (field->type) {
    - case NMFIELD_TYPE_BYTE:
    - case NMFIELD_TYPE_WORD:
    - case NMFIELD_TYPE_DWORD:
    - value = g_strdup_printf("%ld", (long) field->value);
    - break;
    -
    - case NMFIELD_TYPE_UBYTE:
    - case NMFIELD_TYPE_UWORD:
    - case NMFIELD_TYPE_UDWORD:
    - value = g_strdup_printf("%lu", (unsigned long) field->value);
    - break;
    - }
    - }
    -
    - if (value == NULL)
    - value = g_strdup("NULL");
    -
    - return value;
    -}
    --- a/libpurple/protocols/novell/nmfield.h Fri Nov 11 19:39:24 2022 -0600
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,224 +0,0 @@
    -/*
    - * nmfield.h
    - *
    - * Copyright (c) 2004 Novell, Inc. All Rights Reserved.
    - *
    - * 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; version 2 of the License.
    - *
    - * 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, write to the Free Software
    - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    - *
    - */
    -
    -#ifndef PURPLE_NOVELL_NMFIELD_H
    -#define PURPLE_NOVELL_NMFIELD_H
    -
    -#include <glib.h>
    -
    -typedef struct {
    - char *tag; /* Field tag */
    - guint8 method; /* Method of the field */
    - guint8 flags; /* Flags */
    - guint8 type; /* Type of value */
    - guint32 size; /* Size of value if binary */
    - guint32 value; /* Value of a numeric field */
    - gpointer ptr_value; /* Value of a string or sub array field */
    - guint32 len; /* Length of the array */
    -} NMField;
    -
    -/* Field types */
    -#define NMFIELD_TYPE_INVALID 0
    -#define NMFIELD_TYPE_NUMBER 1
    -#define NMFIELD_TYPE_BINARY 2
    -#define NMFIELD_TYPE_BYTE 3
    -#define NMFIELD_TYPE_UBYTE 4
    -#define NMFIELD_TYPE_WORD 5
    -#define NMFIELD_TYPE_UWORD 6
    -#define NMFIELD_TYPE_DWORD 7
    -#define NMFIELD_TYPE_UDWORD 8
    -#define NMFIELD_TYPE_ARRAY 9
    -#define NMFIELD_TYPE_UTF8 10
    -#define NMFIELD_TYPE_BOOL 11
    -#define NMFIELD_TYPE_MV 12
    -#define NMFIELD_TYPE_DN 13
    -
    -/* Field methods */
    -#define NMFIELD_METHOD_VALID 0
    -#define NMFIELD_METHOD_IGNORE 1
    -#define NMFIELD_METHOD_DELETE 2
    -#define NMFIELD_METHOD_DELETE_ALL 3
    -#define NMFIELD_METHOD_EQUAL 4
    -#define NMFIELD_METHOD_ADD 5
    -#define NMFIELD_METHOD_UPDATE 6
    -#define NMFIELD_METHOD_GTE 10
    -#define NMFIELD_METHOD_LTE 12
    -#define NMFIELD_METHOD_NE 14
    -#define NMFIELD_METHOD_EXIST 15
    -#define NMFIELD_METHOD_NOTEXIST 16
    -#define NMFIELD_METHOD_SEARCH 17
    -#define NMFIELD_METHOD_MATCHBEGIN 19
    -#define NMFIELD_METHOD_MATCHEND 20
    -#define NMFIELD_METHOD_NOT_ARRAY 40
    -#define NMFIELD_METHOD_OR_ARRAY 41
    -#define NMFIELD_METHOD_AND_ARRAY 42
    -
    -/* Attribute Names (field tags) */
    -#define NM_A_IP_ADDRESS "nnmIPAddress"
    -#define NM_A_PORT "nnmPort"
    -#define NM_A_FA_FOLDER "NM_A_FA_FOLDER"
    -#define NM_A_FA_CONTACT "NM_A_FA_CONTACT"
    -#define NM_A_FA_CONVERSATION "NM_A_FA_CONVERSATION"
    -#define NM_A_FA_MESSAGE "NM_A_FA_MESSAGE"
    -#define NM_A_FA_CONTACT_LIST "NM_A_FA_CONTACT_LIST"
    -#define NM_A_FA_RESULTS "NM_A_FA_RESULTS"
    -#define NM_A_FA_INFO_DISPLAY_ARRAY "NM_A_FA_INFO_DISPLAY_ARRAY"
    -#define NM_A_FA_USER_DETAILS "NM_A_FA_USER_DETAILS"
    -#define NM_A_SZ_OBJECT_ID "NM_A_SZ_OBJECT_ID"
    -#define NM_A_SZ_PARENT_ID "NM_A_SZ_PARENT_ID"
    -#define NM_A_SZ_SEQUENCE_NUMBER "NM_A_SZ_SEQUENCE_NUMBER"
    -#define NM_A_SZ_TYPE "NM_A_SZ_TYPE"
    -#define NM_A_SZ_STATUS "NM_A_SZ_STATUS"
    -#define NM_A_SZ_STATUS_TEXT "NM_A_SZ_STATUS_TEXT"
    -#define NM_A_SZ_DN "NM_A_SZ_DN"
    -#define NM_A_SZ_DISPLAY_NAME "NM_A_SZ_DISPLAY_NAME"
    -#define NM_A_SZ_USERID "NM_A_SZ_USERID"
    -#define NM_A_SZ_CREDENTIALS "NM_A_SZ_CREDENTIALS"
    -#define NM_A_SZ_MESSAGE_BODY "NM_A_SZ_MESSAGE_BODY"
    -#define NM_A_SZ_MESSAGE_TEXT "NM_A_SZ_MESSAGE_TEXT"
    -#define NM_A_UD_MESSAGE_TYPE "NM_A_UD_MESSAGE_TYPE"
    -#define NM_A_FA_PARTICIPANTS "NM_A_FA_PARTICIPANTS"
    -#define NM_A_FA_INVITES "NM_A_FA_INVITES"
    -#define NM_A_FA_EVENT "NM_A_FA_EVENT"
    -#define NM_A_UD_COUNT "NM_A_UD_COUNT"
    -#define NM_A_UD_DATE "NM_A_UD_DATE"
    -#define NM_A_UD_EVENT "NM_A_UD_EVENT"
    -#define NM_A_B_NO_CONTACTS "NM_A_B_NO_CONTACTS"
    -#define NM_A_B_NO_CUSTOMS "NM_A_B_NO_CUSTOMS"
    -#define NM_A_B_NO_PRIVACY "NM_A_B_NO_PRIVACY"
    -#define NM_A_UW_STATUS "NM_A_UW_STATUS"
    -#define NM_A_UD_OBJECT_ID "NM_A_UD_OBJECT_ID"
    -#define NM_A_SZ_TRANSACTION_ID "NM_A_SZ_TRANSACTION_ID"
    -#define NM_A_SZ_RESULT_CODE "NM_A_SZ_RESULT_CODE"
    -#define NM_A_UD_BUILD "NM_A_UD_BUILD"
    -#define NM_A_SZ_AUTH_ATTRIBUTE "NM_A_SZ_AUTH_ATTRIBUTE"
    -#define NM_A_UD_KEEPALIVE "NM_A_UD_KEEPALIVE"
    -#define NM_A_SZ_USER_AGENT "NM_A_SZ_USER_AGENT"
    -#define NM_A_BLOCKING "nnmBlocking"
    -#define NM_A_BLOCKING_DENY_LIST "nnmBlockingDenyList"
    -#define NM_A_BLOCKING_ALLOW_LIST "nnmBlockingAllowList"
    -#define NM_A_SZ_BLOCKING_ALLOW_ITEM "NM_A_SZ_BLOCKING_ALLOW_ITEM"
    -#define NM_A_SZ_BLOCKING_DENY_ITEM "NM_A_SZ_BLOCKING_DENY_ITEM"
    -#define NM_A_LOCKED_ATTR_LIST "nnmLockedAttrList"
    -
    -#define NM_PROTOCOL_VERSION 2
    -
    -#define NM_FIELD_TRUE "1"
    -#define NM_FIELD_FALSE "0"
    -
    -#define NMFIELD_MAX_STR_LENGTH 32768
    -
    -/**
    - * Count the number of fields
    - *
    - * @param fields Field array
    - *
    - * @return The number of fields in the array.
    - *
    - */
    -guint32 nm_count_fields(NMField * fields);
    -
    -/**
    - * Add a field to the field array. The field should be of type NMFIELD_TYPE_UTF8,
    - * NMFIELD_TYPE_DN, NMFIELD_TYPE_ARRAY, or NMFIELD_TYPE_MV
    - *
    - * NOTE: field array that is passed in may be realloc'd so you should use
    - * the returned field array pointer not the passed in pointer after calling
    - * this function.
    - *
    - * @param fields Field array
    - * @param tag Tag for the new field
    - * @param size Size of the field value (if type = binary)
    - * @param method Field method (see method defines above)
    - * @param flags Flags for new field
    - * @param value The value of the field
    - * @param type The type of the field value
    - *
    - * @return Pointer to the updated field array
    - *
    - */
    -NMField *nm_field_add_pointer(NMField *fields, const char *tag, guint32 size, guint8 method,
    - guint8 flags, gpointer value, guint8 type);
    -
    -/**
    - * Add a numeric field to the field array.
    - *
    - * NOTE: field array that is passed in may be realloc'd so you should use
    - * the returned field array pointer not the passed in pointer after calling
    - * this function.
    - *
    - * @param fields Field array
    - * @param tag Tag for the new field
    - * @param size Size of the field value (if type = binary)
    - * @param method Field method (see method defines above)
    - * @param flags Flags for new field
    - * @param value The value of the field
    - * @param type The type of the field value
    - *
    - * @return Pointer to the updated field array
    - *
    - */
    -NMField *nm_field_add_number(NMField *fields, const char *tag, guint32 size, guint8 method,
    - guint8 flags, guint32 value, guint8 type);
    -
    -/**
    - * Recursively free an array of fields and set pointer to NULL.
    - *
    - * @param fields Pointer to a field array
    - *
    - */
    -void nm_free_fields(NMField ** fields);
    -
    -/**
    - * Find first field with given tag in field array.
    - *
    - * Note: this will only work for 7-bit ascii tags (which is all that
    - * we use currently).
    - *
    - * @param tag Tag to search for
    - * @param fields Field array
    - *
    - * @return The first matching field, or NULL if no fields match.
    - *
    - */
    -NMField *nm_locate_field(char *tag, NMField * fields);
    -
    -/**
    - * Make a deep copy of a field array
    - *
    - * @param src The array to copy
    - *
    - * @return The new (copied) array, which must be freed.
    - *
    - */
    -NMField *nm_copy_field_array(NMField * src);
    -
    -/**
    - * Remove a field and move other fields up to fill the gap
    - *
    - * @param field The field to remove
    - *
    - */
    -void nm_remove_field(NMField * field);
    -
    -/* Print a field array (for debugging purposes) */
    -void nm_print_fields(NMField * fields);
    -
    -#endif /* PURPLE_NOVELL_NMFIELD_H */
    --- a/libpurple/protocols/novell/nmmessage.c Fri Nov 11 19:39:24 2022 -0600
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,95 +0,0 @@
    -/*
    - * nmmessage.c
    - *
    - * Copyright (c) 2004 Novell, Inc. All Rights Reserved.
    - *
    - * 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; version 2 of the License.
    - *
    - * 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, write to the Free Software
    - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    - *
    - */
    -
    -#include "nmmessage.h"
    -
    -struct _NMMessage
    -{
    - NMConference *conference;
    - char *text;
    - guint32 ref_count;
    -};
    -
    -
    -/** Message API **/
    -
    -NMMessage *
    -nm_create_message(const char *text)
    -{
    - NMMessage *msg = g_new0(NMMessage, 1);
    -
    - if (text)
    - msg->text = g_strdup(text);
    -
    - msg->ref_count = 1;
    - return msg;
    -}
    -
    -void
    -nm_message_add_ref(NMMessage * msg)
    -{
    - if (msg)
    - msg->ref_count++;
    -}
    -
    -void
    -nm_release_message(NMMessage * msg)
    -{
    - if (msg && (--(msg->ref_count) == 0)) {
    - g_free(msg->text);
    -
    - if (msg->conference)
    - nm_release_conference(msg->conference);
    -
    - g_free(msg);
    - }
    -}
    -
    -const char *
    -nm_message_get_text(NMMessage * msg)
    -{
    - if (msg == NULL)
    - return NULL;
    -
    - return msg->text;
    -}
    -
    -void
    -nm_message_set_conference(NMMessage * msg, NMConference * conf)
    -{
    - if (msg == NULL || conf == NULL)
    - return;
    -
    - /* Need to ref the conference first so that it doesn't
    - * get released out from under us
    - */
    - nm_conference_add_ref(conf);
    -
    - msg->conference = conf;
    -}
    -
    -NMConference *
    -nm_message_get_conference(NMMessage * msg)
    -{
    - if (msg == NULL)
    - return NULL;
    -
    - return msg->conference;
    -}
    --- a/libpurple/protocols/novell/nmmessage.h Fri Nov 11 19:39:24 2022 -0600
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,83 +0,0 @@
    -/*
    - * nmmessage.h
    - *
    - * Copyright (c) 2004 Novell, Inc. All Rights Reserved.
    - *
    - * 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; version 2 of the License.
    - *
    - * 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, write to the Free Software
    - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    - *
    - */
    -
    -#ifndef PURPLE_NOVELL_NMMESSAGE_H
    -#define PURPLE_NOVELL_NMMESSAGE_H
    -
    -typedef struct _NMMessage NMMessage;
    -
    -#include "nmconference.h"
    -
    -/**
    - * Creates a new message.
    - *
    - * The returned message should be released by calling
    - * nm_release_message
    - *
    - * @param text The message text
    - * @return A newly allocated message
    - */
    -NMMessage *nm_create_message(const char *text);
    -
    -/**
    - * Increment the reference count for the message object.
    - *
    - * @param msg The message
    - */
    -void nm_message_add_ref(NMMessage * msg);
    -
    -/**
    - * Releases a message.
    - *
    - * @param msg The message
    - */
    -void nm_release_message(NMMessage * msg);
    -
    -/**
    - * Returns the message text
    - *
    - * @param msg The message
    - * @return The message text
    - */
    -const char *nm_message_get_text(NMMessage * msg);
    -
    -/**
    - * Sets the conference object for a message
    - *
    - * @param msg The message
    - * @param conf The conference to associate with the message
    - * @return RVALUE_OK on success
    - */
    -void nm_message_set_conference(NMMessage * msg, NMConference * conf);
    -
    -/**
    - * Returns the conference object associated with the message
    - *
    - * Note: this does not increment the reference count for the
    - * conference and the conference should NOT be released with
    - * nm_release_conference. If the reference needs to be kept
    - * around nm_conference_add_ref should be called.
    - *
    - * @param msg The message
    - * @return The conference associated with this message
    - */
    -NMConference *nm_message_get_conference(NMMessage * msg);
    -
    -#endif /* PURPLE_NOVELL_NMMESSAGE_H */
    --- a/libpurple/protocols/novell/nmrequest.c Fri Nov 11 19:39:24 2022 -0600
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,141 +0,0 @@
    -/*
    - * nmrequest.c
    - *
    - * Copyright (c) 2004 Novell, Inc. All Rights Reserved.
    - *
    - * 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; version 2 of the License.
    - *
    - * 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, write to the Free Software
    - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    - *
    - */
    -
    -#include "nmrequest.h"
    -
    -static int count = 0;
    -
    -struct _NMRequest
    -{
    - int trans_id;
    - char *cmd;
    - gpointer data;
    - gpointer user_define;
    - nm_response_cb callback;
    - int ref_count;
    -};
    -
    -NMRequest *
    -nm_create_request(const char *cmd, int trans_id, nm_response_cb cb,
    - gpointer resp_data, gpointer user_define)
    -{
    - NMRequest *req;
    -
    - if (cmd == NULL)
    - return NULL;
    -
    - req = g_new0(NMRequest, 1);
    - req->cmd = g_strdup(cmd);
    - req->trans_id = trans_id;
    - req->callback = cb;
    - req->data = resp_data;
    - req->user_define = user_define;
    - req->ref_count = 1;
    -
    - purple_debug_info("novell", "Creating NMRequest instance, total=%d\n", ++count);
    -
    - return req;
    -}
    -
    -void
    -nm_release_request(NMRequest * req)
    -{
    - if (req && (--req->ref_count == 0)) {
    - g_free(req->cmd);
    - g_free(req);
    -
    - purple_debug_info("novell",
    - "Releasing NMRequest instance, total=%d\n", --count);
    - }
    -
    -}
    -
    -void
    -nm_request_add_ref(NMRequest * req)
    -{
    - if (req)
    - req->ref_count++;
    -}
    -
    -void
    -nm_request_set_callback(NMRequest * req, nm_response_cb callback)
    -{
    - if (req)
    - req->callback = callback;
    -}
    -
    -void
    -nm_request_set_data(NMRequest * req, gpointer data)
    -{
    - if (req)
    - req->data = data;
    -}
    -
    -void
    -nm_request_set_user_define(NMRequest * req, gpointer user_define)
    -{
    - if (req)
    - req->user_define = user_define;
    -}
    -
    -int
    -nm_request_get_trans_id(NMRequest * req)
    -{
    - if (req)
    - return req->trans_id;
    - else
    - return -1;
    -}
    -
    -const char *
    -nm_request_get_cmd(NMRequest * req)
    -{
    - if (req == NULL)
    - return NULL;
    -
    - return req->cmd;
    -}
    -
    -gpointer
    -nm_request_get_data(NMRequest * req)
    -{
    - if (req == NULL)
    - return NULL;
    -
    - return req->data;
    -}
    -
    -gpointer
    -nm_request_get_user_define(NMRequest * req)
    -{
    - if (req == NULL)
    - return NULL;
    -
    - return req->user_define;
    -}
    -
    -nm_response_cb
    -nm_request_get_callback(NMRequest * req)
    -{
    - if (req == NULL)
    - return NULL;
    -
    - return req->callback;
    -}
    --- a/libpurple/protocols/novell/nmrequest.h Fri Nov 11 19:39:24 2022 -0600
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,131 +0,0 @@
    -/*
    - * nmrequest.h
    - *
    - * Copyright (c) 2004 Novell, Inc. All Rights Reserved.
    - *
    - * 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; version 2 of the License.
    - *
    - * 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, write to the Free Software
    - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    - *
    - */
    -
    -#ifndef PURPLE_NOVELL_NMREQUEST_H
    -#define PURPLE_NOVELL_NMREQUEST_H
    -
    -typedef struct _NMRequest NMRequest;
    -
    -#include "nmuser.h"
    -
    -/**
    - * Create a new request object. Object must be release with nm_release_object.
    - *
    - * @param cmd The request command string (e.g. "login")
    - * @param trans_id The request transaction id
    - *
    - * @return The new request object
    - */
    -NMRequest *nm_create_request(const char *cmd, int trans_id, nm_response_cb cb,
    - gpointer resp_data, gpointer user_define);
    -
    -/**
    - * Release a request object.
    - *
    - * @param req The request to release
    - */
    -void nm_release_request(NMRequest * req);
    -
    -/**
    - * Add a new reference to this object. This reference must be released by
    - * a call to nm_release_object.
    - *
    - * @param req The request object
    - */
    -void nm_request_add_ref(NMRequest * req);
    -
    -/**
    - * Set the response callback for this request object. This is the callback
    - * that will be made when we get a response from the server.
    - *
    - * @param req The request object
    - * @param callback The response callback
    - *
    - */
    -void nm_request_set_callback(NMRequest * req, nm_response_cb callback);
    -
    -/**
    - * Set the response data. This will be set differently depending on
    - * the request type (for example to nm_send_get_details will set this
    - * to be the newly create NMUserRecord object).
    - *
    - * @param req The request object
    - * @param data Pointer to some data
    - *
    - */
    -void nm_request_set_data(NMRequest * req, gpointer data);
    -
    -/**
    - * Set the user defined data. This is the data that the client
    - * passes to the various nm_send_* functions. We will pass it
    - * back when we make the callback.
    - *
    - * @param req The request object
    - * @param user_define Pointer to some data
    - *
    - */
    -void nm_request_set_user_define(NMRequest * req, gpointer user_define);
    -
    -/**
    - * Get the transaction id for this request.
    - *
    - * @param req The request object
    - *
    - * @return The transaction id.
    - */
    -int nm_request_get_trans_id(NMRequest * req);
    -
    -/**
    - * Get the command (request type) for this request.
    - *
    - * @param req The request object
    - *
    - * @return The request cmd
    - */
    -const char *nm_request_get_cmd(NMRequest * req);
    -
    -/**
    - * Get the response data for this request
    - *
    - * @param req The request object
    - *
    - * @return The response data
    - */
    -gpointer nm_request_get_data(NMRequest * req);
    -
    -/**
    - * Get the user defined data for this request
    - *
    - * @param req The request object
    - *
    - * @return The user defined data
    - */
    -gpointer nm_request_get_user_define(NMRequest * req);
    -
    -/**
    - * Get the response callback for this request
    - *
    - * @param req The request object
    - *
    - * @return The response callback
    - */
    -nm_response_cb nm_request_get_callback(NMRequest * req);
    -
    -#endif /* PURPLE_NOVELL_NMREQUEST_H */
    --- a/libpurple/protocols/novell/nmrtf.c Fri Nov 11 19:39:24 2022 -0600
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,822 +0,0 @@
    -/*
    - * nmrtf.c
    - *
    - * Copyright (c) 2004 Novell, Inc. All Rights Reserved.
    - *
    - * 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; version 2 of the License.
    - *
    - * 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, write to the Free Software
    - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    - *
    - */
    -
    -/* This code was adapted from the sample RTF reader found here:
    - * http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnrtfspec/html/rtfspec.asp
    - */
    -
    -#include <glib.h>
    -#include <stdlib.h>
    -#include <stdio.h>
    -#include <stddef.h>
    -#include <ctype.h>
    -#include <string.h>
    -#include <purple.h>
    -#include "nmrtf.h"
    -
    -/* Internal RTF parser error codes */
    -#define NMRTF_OK 0 /* Everything's fine! */
    -#define NMRTF_STACK_UNDERFLOW 1 /* Unmatched '}' */
    -#define NMRTF_STACK_OVERFLOW 2 /* Too many '{' -- memory exhausted */
    -#define NMRTF_UNMATCHED_BRACE 3 /* RTF ended during an open group. */
    -#define NMRTF_INVALID_HEX 4 /* invalid hex character found in data */
    -#define NMRTF_BAD_TABLE 5 /* RTF table (sym or prop) invalid */
    -#define NMRTF_ASSERTION 6 /* Assertion failure */
    -#define NMRTF_EOF 7 /* End of file reached while reading RTF */
    -#define NMRTF_CONVERT_ERROR 8 /* Error converting text */
    -
    -#define NMRTF_MAX_DEPTH 256
    -
    -typedef enum
    -{
    - NMRTF_STATE_NORMAL,
    - NMRTF_STATE_SKIP,
    - NMRTF_STATE_FONTTABLE,
    - NMRTF_STATE_BIN,
    - NMRTF_STATE_HEX
    -} NMRtfState; /* Rtf State */
    -
    -/* Property types that we care about */
    -typedef enum
    -{
    - NMRTF_PROP_FONT_IDX,
    - NMRTF_PROP_FONT_CHARSET,
    - NMRTF_PROP_MAX
    -} NMRtfProperty;
    -
    -typedef enum
    -{
    - NMRTF_SPECIAL_BIN,
    - NMRTF_SPECIAL_HEX,
    - NMRTF_SPECIAL_UNICODE,
    - NMRTF_SPECIAL_SKIP
    -} NMRtfSpecialKwd;
    -
    -typedef enum
    -{
    - NMRTF_DEST_FONTTABLE,
    - NMRTF_DEST_SKIP
    -} NMRtfDestinationType;
    -
    -typedef enum
    -{
    - NMRTF_KWD_CHAR,
    - NMRTF_KWD_DEST,
    - NMRTF_KWD_PROP,
    - NMRTF_KWD_SPEC
    -} NMRtfKeywordType;
    -
    -typedef struct
    -{
    - /* All we care about for now is the font.
    - * bold, italic, underline, etc. should be
    - * added here
    - */
    - int font_idx;
    - int font_charset;
    -} NMRtfCharProp;
    -
    -typedef struct
    -{
    - NMRtfCharProp chp;
    - NMRtfState rds;
    - NMRtfState ris;
    -} NMRtfStateSave;
    -
    -typedef struct
    -{
    - char *keyword; /* RTF keyword */
    - int default_val; /* default value to use */
    - gboolean pass_default; /* true to use default value from this table */
    - NMRtfKeywordType kwd_type; /* the type of the keyword */
    - int action; /* property type if the keyword represents a property */
    - /* destination type if the keyword represents a destination */
    - /* character to print if the keyword represents a character */
    -} NMRtfSymbol;
    -
    -
    -typedef struct
    -{
    - int number;
    - char *name;
    - int charset;
    -} NMRtfFont;
    -
    -/* RTF Context */
    -struct _NMRtfContext
    -{
    - NMRtfState rds; /* destination state */
    - NMRtfState ris; /* internal state */
    - NMRtfCharProp chp; /* current character properties (ie. font, bold, italic, etc.) */
    - GSList *font_table; /* the font table */
    - GSList *saved; /* saved state stack */
    - int param; /* numeric parameter for the current keyword */
    - long bytes_to_skip; /* number of bytes to skip (after encountering \bin) */
    - int depth; /* how many groups deep are we */
    - gboolean skip_unknown; /* if true, skip any unknown destinations (this is set after encountering '\*') */
    - char *input; /* input string */
    - guchar nextch; /* next char in input */
    - gboolean nextch_available; /* nextch value is set */
    - GString *ansi; /* Temporary ansi text, will be convert/flushed to the output string */
    - GString *output; /* The plain text UTF8 string */
    -};
    -
    -static int rtf_parse(NMRtfContext *ctx);
    -static int rtf_push_state(NMRtfContext *ctx);
    -static int rtf_pop_state(NMRtfContext *ctx);
    -static NMRtfFont *rtf_get_font(NMRtfContext *ctx, int index);
    -static int rtf_get_char(NMRtfContext *ctx, guchar *ch);
    -static int rtf_unget_char(NMRtfContext *ctx, guchar ch);
    -static int rtf_flush_data(NMRtfContext *ctx);
    -static int rtf_parse_keyword(NMRtfContext *ctx);
    -static int rtf_dispatch_control(NMRtfContext *ctx, char *keyword, int param, gboolean param_set);
    -static int rtf_dispatch_char(NMRtfContext *ctx, guchar ch);
    -static int rtf_dispatch_unicode_char(NMRtfContext *ctx, gunichar ch);
    -static int rtf_print_char(NMRtfContext *ctx, guchar ch);
    -static int rtf_print_unicode_char(NMRtfContext *ctx, gunichar ch);
    -static int rtf_change_destination(NMRtfContext *ctx, NMRtfDestinationType dest);
    -static int rtf_dispatch_special(NMRtfContext *ctx, NMRtfSpecialKwd special);
    -static int rtf_apply_property(NMRtfContext *ctx, NMRtfProperty prop, int val);
    -
    -/* RTF parser tables */
    -
    -/* Keyword descriptions */
    -NMRtfSymbol rtf_symbols[] = {
    - /* keyword, default, pass_default, keyword_type, action */
    - {"fonttbl", 0, FALSE, NMRTF_KWD_DEST, NMRTF_DEST_FONTTABLE},
    - {"f", 0, FALSE, NMRTF_KWD_PROP, NMRTF_PROP_FONT_IDX},
    - {"fcharset", 0, FALSE, NMRTF_KWD_PROP, NMRTF_PROP_FONT_CHARSET},
    - {"par", 0, FALSE, NMRTF_KWD_CHAR, 0x0a},
    - {"line", 0, FALSE, NMRTF_KWD_CHAR, 0x0a},
    - {"\0x0a", 0, FALSE, NMRTF_KWD_CHAR, 0x0a},
    - {"\0x0d", 0, FALSE, NMRTF_KWD_CHAR, 0x0a},
    - {"tab", 0, FALSE, NMRTF_KWD_CHAR, 0x09},
    - {"\r", 0, FALSE, NMRTF_KWD_CHAR, '\r'},
    - {"\n", 0, FALSE, NMRTF_KWD_CHAR, '\n'},
    - {"ldblquote",0, FALSE, NMRTF_KWD_CHAR, '"'},
    - {"rdblquote",0, FALSE, NMRTF_KWD_CHAR, '"'},
    - {"{", 0, FALSE, NMRTF_KWD_CHAR, '{'},
    - {"}", 0, FALSE, NMRTF_KWD_CHAR, '}'},
    - {"\\", 0, FALSE, NMRTF_KWD_CHAR, '\\'},
    - {"bin", 0, FALSE, NMRTF_KWD_SPEC, NMRTF_SPECIAL_BIN},
    - {"*", 0, FALSE, NMRTF_KWD_SPEC, NMRTF_SPECIAL_SKIP},
    - {"'", 0, FALSE, NMRTF_KWD_SPEC, NMRTF_SPECIAL_HEX},
    - {"u", 0, FALSE, NMRTF_KWD_SPEC, NMRTF_SPECIAL_UNICODE},
    - {"colortbl", 0, FALSE, NMRTF_KWD_DEST, NMRTF_DEST_SKIP},
    - {"author", 0, FALSE, NMRTF_KWD_DEST, NMRTF_DEST_SKIP},
    - {"buptim", 0, FALSE, NMRTF_KWD_DEST, NMRTF_DEST_SKIP},
    - {"comment", 0, FALSE, NMRTF_KWD_DEST, NMRTF_DEST_SKIP},
    - {"creatim", 0, FALSE, NMRTF_KWD_DEST, NMRTF_DEST_SKIP},
    - {"doccomm", 0, FALSE, NMRTF_KWD_DEST, NMRTF_DEST_SKIP},
    - {"footer", 0, FALSE, NMRTF_KWD_DEST, NMRTF_DEST_SKIP},
    - {"footerf", 0, FALSE, NMRTF_KWD_DEST, NMRTF_DEST_SKIP},
    - {"footerl", 0, FALSE, NMRTF_KWD_DEST, NMRTF_DEST_SKIP},
    - {"footerr", 0, FALSE, NMRTF_KWD_DEST, NMRTF_DEST_SKIP},
    - {"footnote", 0, FALSE, NMRTF_KWD_DEST, NMRTF_DEST_SKIP},
    - {"ftncn", 0, FALSE, NMRTF_KWD_DEST, NMRTF_DEST_SKIP},
    - {"ftnsep", 0, FALSE, NMRTF_KWD_DEST, NMRTF_DEST_SKIP},
    - {"ftnsepc", 0, FALSE, NMRTF_KWD_DEST, NMRTF_DEST_SKIP},
    - {"header", 0, FALSE, NMRTF_KWD_DEST, NMRTF_DEST_SKIP},
    - {"headerf", 0, FALSE, NMRTF_KWD_DEST, NMRTF_DEST_SKIP},
    - {"headerl", 0, FALSE, NMRTF_KWD_DEST, NMRTF_DEST_SKIP},
    - {"headerr", 0, FALSE, NMRTF_KWD_DEST, NMRTF_DEST_SKIP},
    - {"info", 0, FALSE, NMRTF_KWD_DEST, NMRTF_DEST_SKIP},
    - {"keywords", 0, FALSE, NMRTF_KWD_DEST, NMRTF_DEST_SKIP},
    - {"operator", 0, FALSE, NMRTF_KWD_DEST, NMRTF_DEST_SKIP},
    - {"pict", 0, FALSE, NMRTF_KWD_DEST, NMRTF_DEST_SKIP},
    - {"printim", 0, FALSE, NMRTF_KWD_DEST, NMRTF_DEST_SKIP},
    - {"private1", 0, FALSE, NMRTF_KWD_DEST, NMRTF_DEST_SKIP},
    - {"revtim", 0, FALSE, NMRTF_KWD_DEST, NMRTF_DEST_SKIP},
    - {"rxe", 0, FALSE, NMRTF_KWD_DEST, NMRTF_DEST_SKIP},
    - {"stylesheet", 0, FALSE, NMRTF_KWD_DEST, NMRTF_DEST_SKIP},
    - {"subject", 0, FALSE, NMRTF_KWD_DEST, NMRTF_DEST_SKIP},
    - {"tc", 0, FALSE, NMRTF_KWD_DEST, NMRTF_DEST_SKIP},
    - {"title", 0, FALSE, NMRTF_KWD_DEST, NMRTF_DEST_SKIP},
    - {"txe", 0, FALSE, NMRTF_KWD_DEST, NMRTF_DEST_SKIP},
    - {"xe", 0, FALSE, NMRTF_KWD_DEST, NMRTF_DEST_SKIP}
    -};
    -int table_size = sizeof(rtf_symbols) / sizeof(NMRtfSymbol);
    -
    -NMRtfContext *
    -nm_rtf_init(void)
    -{
    - NMRtfContext *ctx = g_new0(NMRtfContext, 1);
    - ctx->nextch_available = FALSE;
    - ctx->ansi = g_string_new("");
    - ctx->output = g_string_new("");
    - return ctx;
    -}
    -
    -char *
    -nm_rtf_strip_formatting(NMRtfContext *ctx, const char *input)
    -{
    - int status;
    -
    - ctx->input = (char *)input;
    - status = rtf_parse(ctx);
    - if (status == NMRTF_OK)
    - return g_strdup(ctx->output->str);
    -
    - purple_debug_info("novell", "RTF parser failed with error code %d\n", status);
    - return NULL;
    -}
    -
    -static void
    -nm_rtf_font_free(NMRtfFont *font)
    -{
    - g_return_if_fail(font != NULL);
    -
    - g_free(font->name);
    - g_free(font);
    -}
    -
    -void
    -nm_rtf_deinit(NMRtfContext *ctx)
    -{
    - if (ctx) {
    - g_slist_free_full(ctx->font_table, (GDestroyNotify)nm_rtf_font_free);
    - g_slist_free_full(ctx->saved, g_free);
    - g_string_free(ctx->ansi, TRUE);
    - g_string_free(ctx->output, TRUE);
    - g_free(ctx);
    - }
    -}
    -
    -static const char *
    -get_current_encoding(NMRtfContext *ctx)
    -{
    - NMRtfFont *font;
    -
    - font = rtf_get_font(ctx, ctx->chp.font_idx);
    -
    - switch (font->charset) {
    - case 0:
    - return "CP1252";
    - case 77:
    - return "MACINTOSH";
    - case 78:
    - return "SJIS";
    - case 128:
    - return "CP932";
    - case 129:
    - return "CP949";
    - case 130:
    - return "CP1361";
    - case 134:
    - return "CP936";
    - case 136:
    - return "CP950";
    - case 161:
    - return "CP1253";
    - case 162:
    - return "CP1254";
    - case 163:
    - return "CP1258";
    - case 181:
    - case 177:
    - return "CP1255";
    - case 178:
    - case 179:
    - case 180:
    - return "CP1256";
    - case 186:
    - return "CP1257";
    - case 204:
    - return "CP1251";
    - case 222:
    - return "CP874";
    - case 238:
    - return "CP1250";
    - case 254:
    - return "CP437";
    - default:
    - purple_debug_info("novell", "Unhandled font charset %d\n", font->charset);
    - return "CP1252";
    - }
    -}
    -
    -
    -/*
    - * Add an entry to the font table
    - */
    -static int
    -rtf_add_font_entry(NMRtfContext *ctx, int number, const char *name, int charset)
    -{
    - NMRtfFont *font = g_new0(NMRtfFont, 1);
    -
    - font->number = number;
    - font->name = g_strdup(name);
    - font->charset = charset;
    -
    - purple_debug_info("novell", "Adding font to table: #%d\t%s\t%d\n",
    - font->number, font->name, font->charset);
    -
    - ctx->font_table = g_slist_append(ctx->font_table, font);
    -
    - return NMRTF_OK;
    -}
    -
    -/*
    - * Return the nth entry in the font table
    - */
    -static NMRtfFont *
    -rtf_get_font(NMRtfContext *ctx, int nth)
    -{
    - NMRtfFont *font;
    -
    - font = g_slist_nth_data(ctx->font_table, nth);
    -
    - return font;
    -}
    -
    -/*
    - * Step 1:
    - * Isolate RTF keywords and send them to rtf_parse_keyword;
    - * Push and pop state at the start and end of RTF groups;
    - * Send text to rtf_dispatch_char for further processing.
    - */
    -static int
    -rtf_parse(NMRtfContext *ctx)
    -{
    - int status;
    - guchar ch;
    - guchar hex_byte = 0;
    - int hex_count = 2;
    - int len;
    -
    - if (ctx->input == NULL)
    - return NMRTF_OK;
    -
    - while (rtf_get_char(ctx, &ch) == NMRTF_OK) {
    - if (ctx->depth < 0)
    - return NMRTF_STACK_UNDERFLOW;
    -
    - /* if we're parsing binary data, handle it directly */
    - if (ctx->ris == NMRTF_STATE_BIN) {
    - if ((status = rtf_dispatch_char(ctx, ch)) != NMRTF_OK)
    - return status;
    - } else {
    - switch (ch) {
    - case '{':
    - if (ctx->depth > NMRTF_MAX_DEPTH)
    - return NMRTF_STACK_OVERFLOW;
    - rtf_flush_data(ctx);
    - if ((status = rtf_push_state(ctx)) != NMRTF_OK)
    - return status;
    - break;
    - case '}':
    - rtf_flush_data(ctx);
    -
    - /* for some reason there is always an unwanted '\par' at the end */
    - if (ctx->rds == NMRTF_STATE_NORMAL) {
    - len = ctx->output->len;
    - if (ctx->output->str[len-1] == '\n')
    - ctx->output = g_string_truncate(ctx->output, len-1);
    - }
    -
    - if ((status = rtf_pop_state(ctx)) != NMRTF_OK)
    - return status;
    -
    - if (ctx->depth < 0)
    - return NMRTF_STACK_OVERFLOW;
    - break;
    - case '\\':
    - if ((status = rtf_parse_keyword(ctx)) != NMRTF_OK)
    - return status;
    - break;
    - case 0x0d:
    - case 0x0a: /* cr and lf are noise characters... */
    - break;
    - default:
    - if (ctx->ris == NMRTF_STATE_NORMAL) {
    - if ((status = rtf_dispatch_char(ctx, ch)) != NMRTF_OK)
    - return status;
    - } else { /* parsing a hex encoded character */
    - if (ctx->ris != NMRTF_STATE_HEX)
    - return NMRTF_ASSERTION;
    -
    - hex_byte = hex_byte << 4;
    - if (isdigit(ch))
    - hex_byte += (char) ch - '0';
    - else {
    - if (islower(ch)) {
    - if (ch < 'a' || ch > 'f')
    - return NMRTF_INVALID_HEX;
    - hex_byte += (char) ch - 'a' + 10;
    - } else {
    - if (ch < 'A' || ch > 'F')
    - return NMRTF_INVALID_HEX;
    - hex_byte += (char) ch - 'A' + 10;
    - }
    - }
    - hex_count--;
    - if (hex_count == 0) {
    - if ((status = rtf_dispatch_char(ctx, hex_byte)) != NMRTF_OK)
    - return status;
    - hex_count = 2;
    - hex_byte = 0;
    - ctx->ris = NMRTF_STATE_NORMAL;
    - }
    - }
    - break;
    - }
    - }
    - }
    - if (ctx->depth < 0)
    - return NMRTF_STACK_OVERFLOW;
    - if (ctx->depth > 0)
    - return NMRTF_UNMATCHED_BRACE;
    - return NMRTF_OK;
    -}
    -
    -/*
    - * Push the current state onto stack
    - */
    -static int
    -rtf_push_state(NMRtfContext *ctx)
    -{
    - NMRtfStateSave *save = g_new0(NMRtfStateSave, 1);
    - save->chp = ctx->chp;
    - save->rds = ctx->rds;
    - save->ris = ctx->ris;
    - ctx->saved = g_slist_prepend(ctx->saved, save);
    - ctx->ris = NMRTF_STATE_NORMAL;
    - (ctx->depth)++;
    - return NMRTF_OK;
    -}
    -
    -/*
    - * Restore the state at the top of the stack
    - */
    -static int
    -rtf_pop_state(NMRtfContext *ctx)
    -{
    - NMRtfStateSave *save_old;
    - GSList *link_old;
    -
    - if (ctx->saved == NULL)
    - return NMRTF_STACK_UNDERFLOW;
    -
    - save_old = ctx->saved->data;
    - ctx->chp = save_old->chp;
    - ctx->rds = save_old->rds;
    - ctx->ris = save_old->ris;
    - (ctx->depth)--;
    -
    - g_free(save_old);
    - link_old = ctx->saved;
    - ctx->saved = g_slist_delete_link(ctx->saved, link_old);
    - return NMRTF_OK;
    -}
    -
    -/*
    - * Step 2:
    - * Get a control word (and its associated value) and
    - * dispatch the control.
    - */
    -static int
    -rtf_parse_keyword(NMRtfContext *ctx)
    -{
    - int status = NMRTF_OK;
    - guchar ch;
    - gboolean param_set = FALSE;
    - gboolean is_neg = FALSE;
    - int param = 0;
    - char keyword[30];
    - char parameter[20];
    - gsize i;
    -
    - keyword[0] = '\0';
    - parameter[0] = '\0';
    - if ((status = rtf_get_char(ctx, &ch)) != NMRTF_OK)
    - return status;
    -
    - if (!isalpha(ch)) {
    - /* a control symbol; no delimiter. */
    - keyword[0] = (char) ch;
    - keyword[1] = '\0';
    - return rtf_dispatch_control(ctx, keyword, 0, param_set);
    - }
    -
    - /* parse keyword */
    - for (i = 0; isalpha(ch) && (i < sizeof(keyword) - 1); rtf_get_char(ctx, &ch)) {
    - keyword[i] = (char) ch;
    - i++;
    - }
    - keyword[i] = '\0';
    -
    - /* check for '-' indicated a negative parameter value */
    - if (ch == '-') {
    - is_neg = TRUE;
    - if ((status = rtf_get_char(ctx, &ch)) != NMRTF_OK)
    - return status;
    - }
    -
    - /* check for numerical param */
    - if (isdigit(ch)) {
    -
    - param_set = TRUE;
    - for (i = 0; isdigit(ch) && (i < sizeof(parameter) - 1); rtf_get_char(ctx, &ch)) {
    - parameter[i] = (char) ch;
    - i++;
    - }
    - parameter[i] = '\0';
    -
    - ctx->param = param = atoi(parameter);
    - if (is_neg)
    - ctx->param = param = -param;
    - }
    -
    - /* space after control is optional, put character back if it is not a space */
    - if (ch != ' ')
    - rtf_unget_char(ctx, ch);
    -
    - return rtf_dispatch_control(ctx, keyword, param, param_set);
    -}
    -
    -/*
    - * Route the character to the appropriate destination
    - */
    -static int
    -rtf_dispatch_char(NMRtfContext *ctx, guchar ch)
    -{
    - if (ctx->ris == NMRTF_STATE_BIN && --(ctx->bytes_to_skip) <= 0)
    - ctx->ris = NMRTF_STATE_NORMAL;
    -
    - switch (ctx->rds) {
    - case NMRTF_STATE_SKIP:
    - return NMRTF_OK;
    - case NMRTF_STATE_NORMAL:
    - return rtf_print_char(ctx, ch);
    - case NMRTF_STATE_FONTTABLE:
    - if (ch == ';') {
    - rtf_add_font_entry(ctx, ctx->chp.font_idx,
    - ctx->ansi->str, ctx->chp.font_charset);
    - g_string_truncate(ctx->ansi, 0);
    - }
    - else {
    - return rtf_print_char(ctx, ch);
    - }
    - return NMRTF_OK;
    - default:
    - return NMRTF_OK;
    - }
    -}
    -
    -/* Handle a unicode character */
    -static int
    -rtf_dispatch_unicode_char(NMRtfContext *ctx, gunichar ch)
    -{
    - switch (ctx->rds) {
    - case NMRTF_STATE_SKIP:
    - return NMRTF_OK;
    - case NMRTF_STATE_NORMAL:
    - case NMRTF_STATE_FONTTABLE:
    - return rtf_print_unicode_char(ctx, ch);
    - default:
    - return NMRTF_OK;
    - }
    -}
    -
    -/*
    - * Output a character
    - */
    -static int
    -rtf_print_char(NMRtfContext *ctx, guchar ch)
    -{
    -
    - ctx->ansi = g_string_append_c(ctx->ansi, ch);
    -
    - return NMRTF_OK;
    -}
    -
    -/*
    - * Output a unicode character
    - */
    -static int
    -rtf_print_unicode_char(NMRtfContext *ctx, gunichar ch)
    -{
    - char buf[7];
    - int num;
    -
    - /* convert and flush the ansi buffer to the utf8 buffer */
    - rtf_flush_data(ctx);
    -
    - /* convert the unicode character to utf8 and add directly to the output buffer */
    - num = g_unichar_to_utf8((gunichar) ch, buf);
    - buf[num] = 0;
    - purple_debug_info("novell", "converted unichar 0x%X to utf8 char %s\n", ch, buf);
    -
    - ctx->output = g_string_append(ctx->output, buf);
    - return NMRTF_OK;
    -}
    -
    -/*
    - * Flush the output text
    - */
    -static int
    -rtf_flush_data(NMRtfContext *ctx)
    -{
    - int status = NMRTF_OK;
    - char *conv_data = NULL;
    - const char *enc = NULL;
    - GError *gerror = NULL;
    -
    - if (ctx->rds == NMRTF_STATE_NORMAL && ctx->ansi->len > 0) {
    - enc = get_current_encoding(ctx);
    - conv_data = g_convert(ctx->ansi->str, ctx->ansi->len, "UTF-8", enc,
    - NULL, NULL, &gerror);
    - if (conv_data) {
    - ctx->output = g_string_append(ctx->output, conv_data);
    - g_free(conv_data);
    - ctx->ansi = g_string_truncate(ctx->ansi, 0);
    - } else {
    - status = NMRTF_CONVERT_ERROR;
    - purple_debug_info("novell", "failed to convert data! error code = %d msg = %s\n",
    - gerror->code, gerror->message);
    - }
    - }
    - g_error_free(gerror);
    -
    - return status;
    -}
    -
    -/*
    - * Handle a property change
    - */
    -static int
    -rtf_apply_property(NMRtfContext *ctx, NMRtfProperty prop, int val)
    -{
    - if (ctx->rds == NMRTF_STATE_SKIP) /* If we're skipping text, */
    - return NMRTF_OK; /* don't do anything. */
    -
    - /* Need to flush any temporary data before a property change*/
    - rtf_flush_data(ctx);
    -
    - switch (prop) {
    - case NMRTF_PROP_FONT_IDX:
    - ctx->chp.font_idx = val;
    - break;
    - case NMRTF_PROP_FONT_CHARSET:
    - ctx->chp.font_charset = val;
    - break;
    - default:
    - return NMRTF_BAD_TABLE;
    - }
    -
    - return NMRTF_OK;
    -}
    -
    -/*
    - * Step 3.
    - * Search the table for keyword and evaluate it appropriately.
    - *
    - * Inputs:
    - * keyword: The RTF control to evaluate.
    - * param: The parameter of the RTF control.
    - * param_set: TRUE if the control had a parameter; (that is, if param is valid)
    - * FALSE if it did not.
    - */
    -static int
    -rtf_dispatch_control(NMRtfContext *ctx, char *keyword, int param, gboolean param_set)
    -{
    - int idx;
    -
    - for (idx = 0; idx < table_size; idx++) {
    - if (purple_strequal(keyword, rtf_symbols[idx].keyword))
    - break;
    - }
    -
    - if (idx == table_size) {
    - if (ctx->skip_unknown)
    - ctx->rds = NMRTF_STATE_SKIP;
    - ctx->skip_unknown = FALSE;
    - return NMRTF_OK;
    - }
    -
    - /* found it! use kwd_type and action to determine what to do with it. */
    - ctx->skip_unknown = FALSE;
    - switch (rtf_symbols[idx].kwd_type) {
    - case NMRTF_KWD_PROP:
    - if (rtf_symbols[idx].pass_default || !param_set)
    - param = rtf_symbols[idx].default_val;
    - return rtf_apply_property(ctx, rtf_symbols[idx].action, param);
    - case NMRTF_KWD_CHAR:
    - return rtf_dispatch_char(ctx, rtf_symbols[idx].action);
    - case NMRTF_KWD_DEST:
    - return rtf_change_destination(ctx, rtf_symbols[idx].action);
    - case NMRTF_KWD_SPEC:
    - return rtf_dispatch_special(ctx, rtf_symbols[idx].action);
    - default:
    - return NMRTF_BAD_TABLE;
    - }
    - return NMRTF_BAD_TABLE;
    -}
    -
    -/*
    - * Change to the destination specified.
    - */
    -static int
    -rtf_change_destination(NMRtfContext *ctx, NMRtfDestinationType type)
    -{
    - /* if we're skipping text, don't do anything */
    - if (ctx->rds == NMRTF_STATE_SKIP)
    - return NMRTF_OK;
    -
    - switch (type) {
    - case NMRTF_DEST_FONTTABLE:
    - ctx->rds = NMRTF_STATE_FONTTABLE;
    - g_string_truncate(ctx->ansi, 0);
    - break;
    - default:
    - ctx->rds = NMRTF_STATE_SKIP; /* when in doubt, skip it... */
    - break;
    - }
    - return NMRTF_OK;
    -}
    -
    -/*
    - * Dispatch an RTF control that needs special processing
    - */
    -static int
    -rtf_dispatch_special(NMRtfContext *ctx, NMRtfSpecialKwd type)
    -{
    - int status = NMRTF_OK;
    - guchar ch;
    -
    - if (ctx->rds == NMRTF_STATE_SKIP && type != NMRTF_SPECIAL_BIN) /* if we're skipping, and it's not */
    - return NMRTF_OK; /* the \bin keyword, ignore it. */
    -
    - switch (type) {
    - case NMRTF_SPECIAL_BIN:
    - ctx->ris = NMRTF_STATE_BIN;
    - ctx->bytes_to_skip = ctx->param;
    - break;
    - case NMRTF_SPECIAL_SKIP:
    - ctx->skip_unknown = TRUE;
    - break;
    - case NMRTF_SPECIAL_HEX:
    - ctx->ris = NMRTF_STATE_HEX;
    - break;
    - case NMRTF_SPECIAL_UNICODE:
    - purple_debug_info("novell", "parsing unichar\n");
    - status = rtf_dispatch_unicode_char(ctx, ctx->param);
    - /* Skip next char */
    - if (status == NMRTF_OK)
    - status = rtf_get_char(ctx, &ch);
    - break;
    - default:
    - status = NMRTF_BAD_TABLE;
    - break;
    - }
    -
    - return status;
    -}
    -
    -/*
    - * Get the next character from the input stream
    - */
    -static int
    -rtf_get_char(NMRtfContext *ctx, guchar *ch)
    -{
    - if (ctx->nextch_available) {
    - *ch = ctx->nextch;
    - ctx->nextch_available = FALSE;
    - } else {
    - *ch = *(ctx->input);
    - ctx->input++;
    - }
    -
    - if (*ch)
    - return NMRTF_OK;
    - else
    - return NMRTF_EOF;
    -}
    -
    -/*
    - * Move a character back into the input stream
    - */
    -static int
    -rtf_unget_char(NMRtfContext *ctx, guchar ch)
    -{
    - ctx->nextch = ch;
    - ctx->nextch_available = TRUE;
    - return NMRTF_OK;
    -}
    --- a/libpurple/protocols/novell/nmrtf.h Fri Nov 11 19:39:24 2022 -0600
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,30 +0,0 @@
    -/*
    - * nmrtf.h
    - *
    - * Copyright (c) 2004 Novell, Inc. All Rights Reserved.
    - *
    - * 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; version 2 of the License.
    - *
    - * 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, write to the Free Software
    - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    - *
    - */
    -
    -#ifndef PURPLE_NOVELL_NMRTF_H
    -#define PURPLE_NOVELL_NMRTF_H
    -
    -typedef struct _NMRtfContext NMRtfContext;
    -
    -NMRtfContext *nm_rtf_init(void);
    -char *nm_rtf_strip_formatting(NMRtfContext *ctx, const char *input);
    -void nm_rtf_deinit(NMRtfContext *ctx);
    -
    -#endif /* PURPLE_NOVELL_NMRTF_H */
    --- a/libpurple/protocols/novell/nmuser.c Fri Nov 11 19:39:24 2022 -0600
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,2122 +0,0 @@
    -/*
    - * nmuser.c
    - *
    - * Copyright (c) 2004 Novell, Inc. All Rights Reserved.
    - *
    - * 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; version 2 of the License.
    - *
    - * 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, write to the Free Software
    - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    - *
    - */
    -
    -#include <glib/gi18n-lib.h>
    -
    -#include <string.h>
    -#include <purple.h>
    -#include "nmfield.h"
    -#include "nmuser.h"
    -#include "nmconn.h"
    -#include "nmcontact.h"
    -#include "nmuserrecord.h"
    -
    -/* This is the template that we wrap outgoing messages in, since the other
    - * GW Messenger clients expect messages to be in RTF.
    - */
    -#define RTF_TEMPLATE "{\\rtf1\\ansi\n"\
    - "{\\fonttbl{\\f0\\fnil Unknown;}}\n"\
    - "{\\colortbl ;\\red0\\green0\\blue0;}\n"\
    - "\\uc1\\cf1\\f0\\fs24 %s\\par\n}"
    -#define NM_MAX_MESSAGE_SIZE 2048
    -
    -static NMERR_T nm_process_response(NMUser * user);
    -static void _update_contact_list(NMUser * user, NMField * fields);
    -static void _handle_multiple_get_details_login_cb(NMUser * user, NMERR_T ret_code,
    - gpointer resp_data, gpointer user_data);
    -static char * nm_rtfize_text(char *text);
    -
    -/**
    - * See header for comments on on "public" functions
    - */
    -
    -NMUser *
    -nm_initialize_user(const char *name, const char *server_addr,
    - int port, gpointer data, nm_event_cb event_callback)
    -{
    - NMUser *user;
    - if (name == NULL || server_addr == NULL || event_callback == NULL)
    - return NULL;
    -
    - user = g_new0(NMUser, 1);
    -
    - user->cancellable = g_cancellable_new();
    -
    - user->contacts =
    - g_hash_table_new_full(g_str_hash, nm_utf8_str_equal,
    - g_free, (GDestroyNotify) nm_release_contact);
    -
    - user->user_records =
    - g_hash_table_new_full(g_str_hash, nm_utf8_str_equal, g_free,
    - (GDestroyNotify) nm_release_user_record);
    -
    - user->display_id_to_dn = g_hash_table_new_full(g_str_hash, nm_utf8_str_equal,
    - g_free, g_free);
    -
    - user->name = g_strdup(name);
    - user->conn = nm_create_conn(server_addr, port);
    - user->evt_callback = event_callback;
    - user->client_data = data;
    -
    - return user;
    -}
    -
    -
    -void
    -nm_deinitialize_user(NMUser * user)
    -{
    - g_cancellable_cancel(user->cancellable);
    -
    - nm_release_conn(user->conn);
    -
    - if (user->contacts) {
    - g_hash_table_destroy(user->contacts);
    - }
    -
    - if (user->user_records) {
    - g_hash_table_destroy(user->user_records);
    - }
    -
    - if (user->display_id_to_dn) {
    - g_hash_table_destroy(user->display_id_to_dn);
    - }
    -
    - g_free(user->name);
    -
    - if (user->user_record) {
    - nm_release_user_record(user->user_record);
    - }
    -
    - nm_conference_list_free(user);
    - nm_destroy_contact_list(user);
    -
    - g_object_unref(user->cancellable);
    -
    - g_free(user);
    -}
    -
    -NMERR_T
    -nm_send_login(NMUser * user, const char *pwd, const char *my_addr,
    - const char *user_agent, nm_response_cb callback, gpointer data)
    -{
    - NMERR_T rc = NM_OK;
    - NMField *fields = NULL;
    -
    - if (user == NULL || pwd == NULL || user_agent == NULL) {
    - return NMERR_BAD_PARM;
    - }
    -
    - fields = nm_field_add_pointer(fields, NM_A_SZ_USERID, 0, NMFIELD_METHOD_VALID, 0,
    - g_strdup(user->name), NMFIELD_TYPE_UTF8);
    -
    - fields = nm_field_add_pointer(fields, NM_A_SZ_CREDENTIALS, 0, NMFIELD_METHOD_VALID, 0,
    - g_strdup(pwd), NMFIELD_TYPE_UTF8);
    -
    - fields = nm_field_add_pointer(fields, NM_A_SZ_USER_AGENT, 0, NMFIELD_METHOD_VALID, 0,
    - g_strdup(user_agent), NMFIELD_TYPE_UTF8);
    -
    - fields = nm_field_add_number(fields, NM_A_UD_BUILD, 0, NMFIELD_METHOD_VALID, 0,
    - NM_PROTOCOL_VERSION, NMFIELD_TYPE_UDWORD);
    - if (my_addr) {
    - fields = nm_field_add_pointer(fields, NM_A_IP_ADDRESS, 0, NMFIELD_METHOD_VALID, 0,
    - g_strdup(my_addr), NMFIELD_TYPE_UTF8);
    - }
    -
    - /* Send the login */
    - rc = nm_send_request(user, "login", fields, callback, data, NULL);
    -
    - nm_free_fields(&fields);
    - return rc;
    -}
    -
    -NMERR_T
    -nm_send_set_status(NMUser * user, int status, const char *text,
    - const char *auto_resp, nm_response_cb callback, gpointer data)
    -{
    - NMERR_T rc = NM_OK;
    - NMField *fields = NULL;
    -
    - if (user == NULL)
    - return NMERR_BAD_PARM;
    -
    - /* Add the status */
    - fields = nm_field_add_pointer(fields, NM_A_SZ_STATUS, 0, NMFIELD_METHOD_VALID, 0,
    - g_strdup_printf("%d", status), NMFIELD_TYPE_UTF8);
    -
    - /* Add the status text and auto reply text if there is any */
    - if (text) {
    - fields = nm_field_add_pointer(fields, NM_A_SZ_STATUS_TEXT, 0,
    - NMFIELD_METHOD_VALID, 0, g_strdup(text),
    - NMFIELD_TYPE_UTF8);
    - }
    -
    - if (auto_resp) {
    - fields = nm_field_add_pointer(fields, NM_A_SZ_MESSAGE_BODY, 0,
    - NMFIELD_METHOD_VALID, 0, g_strdup(auto_resp),
    - NMFIELD_TYPE_UTF8);
    - }
    -
    - rc = nm_send_request(user, "setstatus", fields, callback, data, NULL);
    -
    - nm_free_fields(&fields);
    - return rc;
    -}
    -
    -NMERR_T
    -nm_send_multiple_get_details(NMUser * user, GSList *names,
    - nm_response_cb callback, gpointer data)
    -{
    - NMERR_T rc = NM_OK;
    - NMField *fields = NULL;
    - GSList *node;
    -
    - if (user == NULL || names == NULL)
    - return NMERR_BAD_PARM;
    -
    - /* Add in DN or display id */
    - for (node = names; node; node = node->next) {
    - fields = nm_field_add_pointer(fields, NM_A_SZ_USERID, 0, NMFIELD_METHOD_VALID, 0,
    - g_strdup(node->data), NMFIELD_TYPE_UTF8);
    - }
    -
    - rc = nm_send_request(user, "getdetails", fields, callback, data, NULL);
    -
    - nm_free_fields(&fields);
    - return rc;
    -}
    -
    -NMERR_T
    -nm_send_get_details(NMUser * user, const char *name,
    - nm_response_cb callback, gpointer data)
    -{
    - NMERR_T rc = NM_OK;
    - NMField *fields = NULL;
    -
    - if (user == NULL || name == NULL)
    - return NMERR_BAD_PARM;
    -
    - /* Add in DN or display id */
    - if (strstr("=", name)) {
    - fields = nm_field_add_pointer(fields, NM_A_SZ_DN, 0, NMFIELD_METHOD_VALID, 0,
    - g_strdup(name), NMFIELD_TYPE_DN);
    - } else {
    -
    - const char *dn = nm_lookup_dn(user, name);
    - if (dn) {
    - fields = nm_field_add_pointer(fields, NM_A_SZ_DN, 0, NMFIELD_METHOD_VALID, 0,
    - g_strdup(name), NMFIELD_TYPE_DN);
    - } else {
    - fields =
    - nm_field_add_pointer(fields, NM_A_SZ_USERID, 0, NMFIELD_METHOD_VALID, 0,
    - g_strdup(name), NMFIELD_TYPE_UTF8);
    - }
    -
    - }
    -
    - rc = nm_send_request(user, "getdetails", fields, callback, data, NULL);
    -
    - nm_free_fields(&fields);
    - return rc;
    -}
    -
    -NMERR_T
    -nm_send_create_conference(NMUser * user, NMConference * conference,
    - nm_response_cb callback, gpointer data)
    -{
    - NMERR_T rc = NM_OK;
    - NMField *fields = NULL;
    - NMField *tmp = NULL;
    - NMField *field = NULL;
    - NMRequest *req = NULL;
    - int count, i;
    -
    - if (user == NULL || conference == NULL)
    - return NMERR_BAD_PARM;
    -
    - /* Add in a blank guid */
    - tmp = nm_field_add_pointer(tmp, NM_A_SZ_OBJECT_ID, 0, NMFIELD_METHOD_VALID, 0,
    - g_strdup(BLANK_GUID), NMFIELD_TYPE_UTF8);
    -
    - fields = nm_field_add_pointer(fields, NM_A_FA_CONVERSATION, 0,
    - NMFIELD_METHOD_VALID, 0, tmp,
    - NMFIELD_TYPE_ARRAY);
    - tmp = NULL;
    -
    -
    - /* Add participants in */
    - count = nm_conference_get_participant_count(conference);
    - for (i = 0; i < count; i++) {
    - NMUserRecord *user_record = nm_conference_get_participant(conference, i);
    -
    - if (user_record) {
    - fields = nm_field_add_pointer(fields, NM_A_SZ_DN,
    - 0, NMFIELD_METHOD_VALID, 0,
    - g_strdup(nm_user_record_get_dn(user_record)),
    - NMFIELD_TYPE_DN);
    - }
    - }
    -
    - /* Add our user in */
    - field = nm_locate_field(NM_A_SZ_DN, user->fields);
    - if (field) {
    - fields = nm_field_add_pointer(fields, NM_A_SZ_DN,
    - 0, NMFIELD_METHOD_VALID, 0,
    - g_strdup((char *) field->ptr_value),
    - NMFIELD_TYPE_DN);
    - }
    -
    - rc = nm_send_request(user, "createconf", fields, callback, data, &req);
    - if (rc == NM_OK && req) {
    - nm_conference_add_ref(conference);
    - nm_request_set_data(req, conference);
    - }
    -
    - if (req)
    - nm_release_request(req);
    -
    - nm_free_fields(&fields);
    - return rc;
    -}
    -
    -NMERR_T
    -nm_send_leave_conference(NMUser * user, NMConference * conference,
    - nm_response_cb callback, gpointer data)
    -{
    - NMERR_T rc = NM_OK;
    - NMField *fields = NULL;
    - NMField *tmp = NULL;
    - NMRequest *req = NULL;
    -
    - if (user == NULL || conference == NULL)
    - return NMERR_BAD_PARM;
    -
    - /* Add in the conference guid */
    - tmp = nm_field_add_pointer(tmp, NM_A_SZ_OBJECT_ID, 0, NMFIELD_METHOD_VALID, 0,
    - g_strdup(nm_conference_get_guid(conference)),
    - NMFIELD_TYPE_UTF8);
    -
    - fields = nm_field_add_pointer(fields, NM_A_FA_CONVERSATION, 0,
    - NMFIELD_METHOD_VALID, 0, tmp,
    - NMFIELD_TYPE_ARRAY);
    - tmp = NULL;
    -
    - /* Send the request to the server */
    - rc = nm_send_request(user, "leaveconf", fields, callback, data, &req);
    - if (rc == NM_OK && req)
    - nm_request_set_data(req, conference);
    -
    - if (req)
    - nm_release_request(req);
    -
    - nm_free_fields(&fields);
    - return rc;
    -}
    -
    -NMERR_T
    -nm_send_join_conference(NMUser * user, NMConference * conference,
    - nm_response_cb callback, gpointer data)
    -{
    - NMERR_T rc = NM_OK;
    - NMField *fields = NULL, *tmp = NULL;
    - NMRequest *req = NULL;
    -
    - if (user == NULL || conference == NULL)
    - return NMERR_BAD_PARM;
    -
    - /* Add in the conference guid */
    - tmp = nm_field_add_pointer(tmp, NM_A_SZ_OBJECT_ID, 0, NMFIELD_METHOD_VALID, 0,
    - g_strdup(nm_conference_get_guid(conference)),
    - NMFIELD_TYPE_UTF8);
    -
    - fields = nm_field_add_pointer(fields, NM_A_FA_CONVERSATION, 0,
    - NMFIELD_METHOD_VALID, 0, tmp,
    - NMFIELD_TYPE_ARRAY);
    - tmp = NULL;
    -
    - /* Send the request to the server */
    - rc = nm_send_request(user, "joinconf", fields, callback, data, &req);
    - if (rc == NM_OK && req)
    - nm_request_set_data(req, conference);
    -
    - if (req)
    - nm_release_request(req);
    -
    - nm_free_fields(&fields);
    - return rc;
    -}
    -
    -NMERR_T
    -nm_send_reject_conference(NMUser * user, NMConference * conference,
    - nm_response_cb callback, gpointer data)
    -{
    - NMERR_T rc = NM_OK;
    - NMField *fields = NULL;
    - NMField *tmp = NULL;
    - NMRequest *req = NULL;
    -
    - if (user == NULL || conference == NULL)
    - return NMERR_BAD_PARM;
    -
    - /* Add in the conference guid */
    - tmp = nm_field_add_pointer(tmp, NM_A_SZ_OBJECT_ID, 0, NMFIELD_METHOD_VALID, 0,
    - g_strdup(nm_conference_get_guid(conference)),
    - NMFIELD_TYPE_UTF8);
    -
    - fields = nm_field_add_pointer(fields, NM_A_FA_CONVERSATION, 0,
    - NMFIELD_METHOD_VALID, 0, tmp,
    - NMFIELD_TYPE_ARRAY);
    - tmp = NULL;
    -
    - /* Send the request to the server */
    - rc = nm_send_request(user, "rejectconf", fields, callback, data, &req);
    - if (rc == NM_OK && req)
    - nm_request_set_data(req, conference);
    -
    - if (req)
    - nm_release_request(req);
    -
    - nm_free_fields(&fields);
    - return rc;
    -}
    -
    -NMERR_T
    -nm_send_conference_invite(NMUser *user, NMConference *conference, NMUserRecord *user_record,
    - const char *message, nm_response_cb callback, gpointer data)
    -{
    - NMERR_T rc = NM_OK;
    - NMField *fields = NULL;
    - NMField *tmp = NULL;
    - NMRequest *req = NULL;
    -
    - if (user == NULL || conference == NULL || user_record == NULL)
    - return NMERR_BAD_PARM;
    -
    - /* Add in the conference guid */
    - tmp = nm_field_add_pointer(tmp, NM_A_SZ_OBJECT_ID, 0, NMFIELD_METHOD_VALID, 0,
    - g_strdup(nm_conference_get_guid(conference)),
    - NMFIELD_TYPE_UTF8);
    -
    - fields = nm_field_add_pointer(fields, NM_A_FA_CONVERSATION, 0,
    - NMFIELD_METHOD_VALID, 0, tmp,
    - NMFIELD_TYPE_ARRAY);
    - tmp = NULL;
    -
    - /* Add in DN of user to invite */
    - fields = nm_field_add_pointer(fields, NM_A_SZ_DN, 0, NMFIELD_METHOD_VALID, 0,
    - g_strdup(nm_user_record_get_dn(user_record)),
    - NMFIELD_TYPE_DN);
    -
    - /* Add the invite message if there is one */
    - if (message)
    - fields = nm_field_add_pointer(fields, NM_A_SZ_MESSAGE_BODY, 0, NMFIELD_METHOD_VALID, 0,
    - g_strdup(message), NMFIELD_TYPE_UTF8);
    -
    - /* Send the request to the server */
    - rc = nm_send_request(user, "sendinvite", fields, callback, data, &req);
    - if (rc == NM_OK && req)
    - nm_request_set_data(req, conference);
    -
    - if (req)
    - nm_release_request(req);
    -
    - nm_free_fields(&fields);
    - return rc;
    -}
    -
    -NMERR_T
    -nm_send_message(NMUser * user, NMMessage * message, nm_response_cb callback)
    -{
    - NMERR_T rc = NM_OK;
    - char *text, *rtfized;
    - NMField *fields = NULL, *tmp = NULL;
    - NMConference *conf;
    - NMUserRecord *user_record;
    - int count, i;
    -
    - if (user == NULL || message == NULL) {
    - return NMERR_BAD_PARM;
    - }
    -
    - conf = nm_message_get_conference(message);
    - if (!nm_conference_is_instantiated(conf)) {
    - rc = NMERR_CONFERENCE_NOT_INSTANTIATED;
    - } else {
    -
    - tmp = nm_field_add_pointer(tmp, NM_A_SZ_OBJECT_ID, 0, NMFIELD_METHOD_VALID, 0,
    - g_strdup(nm_conference_get_guid(conf)),
    - NMFIELD_TYPE_UTF8);
    -
    - fields =
    - nm_field_add_pointer(fields, NM_A_FA_CONVERSATION, 0, NMFIELD_METHOD_VALID, 0,
    - tmp, NMFIELD_TYPE_ARRAY);
    - tmp = NULL;
    -
    - /* Add RTF and plain text versions of the message */
    - text = g_strdup(nm_message_get_text(message));
    -
    - /* Truncate if necessary */
    - if (strlen(text) > NM_MAX_MESSAGE_SIZE)
    - text[NM_MAX_MESSAGE_SIZE] = 0;
    -
    - rtfized = nm_rtfize_text(text);
    -
    - purple_debug_info("novell", "message text is: %s\n", text);
    - purple_debug_info("novell", "message rtf is: %s\n", rtfized);
    -
    - tmp = nm_field_add_pointer(tmp, NM_A_SZ_MESSAGE_BODY, 0, NMFIELD_METHOD_VALID, 0,
    - rtfized, NMFIELD_TYPE_UTF8);
    -
    - tmp = nm_field_add_number(tmp, NM_A_UD_MESSAGE_TYPE, 0, NMFIELD_METHOD_VALID, 0,
    - 0, NMFIELD_TYPE_UDWORD);
    -
    - tmp = nm_field_add_pointer(tmp, NM_A_SZ_MESSAGE_TEXT, 0, NMFIELD_METHOD_VALID, 0,
    - text, NMFIELD_TYPE_UTF8);
    -
    - fields = nm_field_add_pointer(fields, NM_A_FA_MESSAGE, 0, NMFIELD_METHOD_VALID, 0,
    - tmp, NMFIELD_TYPE_ARRAY);
    - tmp = NULL;
    -
    - /* Add participants */
    - count = nm_conference_get_participant_count(conf);
    - for (i = 0; i < count; i++) {
    - user_record = nm_conference_get_participant(conf, i);
    - if (user_record) {
    - fields =
    - nm_field_add_pointer(fields, NM_A_SZ_DN, 0, NMFIELD_METHOD_VALID, 0,
    - g_strdup(nm_user_record_get_dn(user_record)),
    - NMFIELD_TYPE_DN);
    - }
    - }
    -
    - /* Send the request */
    - rc = nm_send_request(user, "sendmessage", fields, callback, NULL, NULL);
    - }
    -
    - nm_free_fields(&fields);
    - return rc;
    -}
    -
    -NMERR_T
    -nm_send_typing(NMUser * user, NMConference * conf,
    - gboolean typing, nm_response_cb callback)
    -{
    - NMERR_T rc = NM_OK;
    - char *str = NULL;
    - NMField *fields = NULL, *tmp = NULL;
    -
    - if (user == NULL || conf == NULL) {
    - return NMERR_BAD_PARM;
    - }
    -
    - if (!nm_conference_is_instantiated(conf)) {
    - rc = NMERR_CONFERENCE_NOT_INSTANTIATED;
    - } else {
    - /* Add the conference GUID */
    - tmp = nm_field_add_pointer(tmp, NM_A_SZ_OBJECT_ID, 0, NMFIELD_METHOD_VALID, 0,
    - g_strdup(nm_conference_get_guid(conf)),
    - NMFIELD_TYPE_UTF8);
    -
    - /* Add typing type */
    - str = g_strdup_printf("%d",
    - (typing ? NMEVT_USER_TYPING :
    - NMEVT_USER_NOT_TYPING));
    -
    - tmp = nm_field_add_pointer(tmp, NM_A_SZ_TYPE, 0, NMFIELD_METHOD_VALID, 0,
    - str, NMFIELD_TYPE_UTF8);
    -
    - fields =
    - nm_field_add_pointer(fields, NM_A_FA_CONVERSATION, 0, NMFIELD_METHOD_VALID, 0,
    - tmp, NMFIELD_TYPE_ARRAY);
    - tmp = NULL;
    -
    - rc = nm_send_request(user, "sendtyping", fields, callback, NULL, NULL);
    - }
    -
    - nm_free_fields(&fields);
    - return rc;
    -}
    -
    -NMERR_T
    -nm_send_create_contact(NMUser * user, NMFolder * folder,
    - NMContact * contact, nm_response_cb callback,
    - gpointer data)
    -{
    - NMERR_T rc = NM_OK;
    - NMField *fields = NULL;
    - NMRequest *req = NULL;
    - const char *name = NULL;
    - const char *display_name = NULL;
    -
    - if (user == NULL || folder == NULL || contact == NULL) {
    - return NMERR_BAD_PARM;
    - }
    -
    - /* Add parent ID */
    - fields = nm_field_add_pointer(fields, NM_A_SZ_PARENT_ID, 0, NMFIELD_METHOD_VALID, 0,
    - g_strdup_printf("%d", nm_folder_get_id(folder)),
    - NMFIELD_TYPE_UTF8);
    -
    - /* Check to see if userid is current user and return an error? */
    -
    - /* Check to see if contact already exists and return an error? */
    -
    - /* Add userid or dn */
    - name = nm_contact_get_dn(contact);
    - if (name == NULL)
    - return NMERR_BAD_PARM;
    -
    - if (strstr("=", name)) {
    - fields = nm_field_add_pointer(fields, NM_A_SZ_DN, 0, NMFIELD_METHOD_VALID, 0,
    - g_strdup(name), NMFIELD_TYPE_DN);
    - } else {
    - fields = nm_field_add_pointer(fields, NM_A_SZ_USERID, 0, NMFIELD_METHOD_VALID, 0,
    - g_strdup(name), NMFIELD_TYPE_UTF8);
    - }
    -
    - /* Add display name */
    - display_name = nm_contact_get_display_name(contact);
    - if (display_name)
    - fields = nm_field_add_pointer(fields, NM_A_SZ_DISPLAY_NAME, 0, NMFIELD_METHOD_VALID, 0,
    - g_strdup(display_name), NMFIELD_TYPE_UTF8);
    -
    - /* Dispatch the request */
    - rc = nm_send_request(user, "createcontact", fields, callback, data, &req);
    - if (rc == NM_OK && req)
    - nm_request_set_data(req, contact);
    -
    - if (req)
    - nm_release_request(req);
    -
    - nm_free_fields(&fields);
    - return rc;
    -}
    -
    -NMERR_T
    -nm_send_remove_contact(NMUser * user, NMFolder * folder,
    - NMContact * contact, nm_response_cb callback,
    - gpointer data)
    -{
    - NMERR_T rc = NM_OK;
    - NMField *fields = NULL;
    - NMRequest *req = NULL;
    -
    - if (user == NULL || folder == NULL || contact == NULL) {
    - return NMERR_BAD_PARM;
    - }
    -
    - /* Add parent id */
    - fields = nm_field_add_pointer(fields, NM_A_SZ_PARENT_ID, 0, NMFIELD_METHOD_VALID, 0,
    - g_strdup_printf("%d", nm_folder_get_id(folder)),
    - NMFIELD_TYPE_UTF8);
    -
    - /* Add object id */
    - fields = nm_field_add_pointer(fields, NM_A_SZ_OBJECT_ID, 0, NMFIELD_METHOD_VALID, 0,
    - g_strdup_printf("%d", nm_contact_get_id(contact)),
    - NMFIELD_TYPE_UTF8);
    -
    - /* Dispatch the request */
    - rc = nm_send_request(user, "deletecontact", fields, callback, data, &req);
    - if (rc == NM_OK && req)
    - nm_request_set_data(req, contact);
    -
    - if (req)
    - nm_release_request(req);
    -
    - nm_free_fields(&fields);
    - return rc;
    -}
    -
    -NMERR_T
    -nm_send_create_folder(NMUser * user, const char *name,
    - nm_response_cb callback, gpointer data)
    -{
    - NMERR_T rc = NM_OK;
    - NMField *fields = NULL;
    - NMRequest *req = NULL;
    -
    - if (user == NULL || name == NULL) {
    - return NMERR_BAD_PARM;
    - }
    -
    - /* Add parent ID */
    - fields = nm_field_add_pointer(fields, NM_A_SZ_PARENT_ID, 0, NMFIELD_METHOD_VALID, 0,
    - g_strdup("0"), NMFIELD_TYPE_UTF8);
    -
    - /* Add name of the folder to add */
    - fields =
    - nm_field_add_pointer(fields, NM_A_SZ_DISPLAY_NAME, 0, NMFIELD_METHOD_VALID, 0,
    - g_strdup(name), NMFIELD_TYPE_UTF8);
    -
    - /* Add sequence, for now just put it at the bottom */
    - fields =
    - nm_field_add_pointer(fields, NM_A_SZ_SEQUENCE_NUMBER, 0, NMFIELD_METHOD_VALID, 0,
    - g_strdup("-1"), NMFIELD_TYPE_UTF8);
    -
    - /* Dispatch the request */
    - rc = nm_send_request(user, "createfolder", fields, callback, data, &req);
    - if (rc == NM_OK && req)
    - nm_request_set_data(req, g_strdup(name));
    -
    - if (req)
    - nm_release_request(req);
    -
    - nm_free_fields(&fields);
    - return rc;
    -}
    -
    -NMERR_T
    -nm_send_remove_folder(NMUser * user, NMFolder * folder,
    - nm_response_cb callback, gpointer data)
    -{
    - NMERR_T rc = NM_OK;
    - NMField *fields = NULL;
    - NMRequest *req = NULL;
    -
    - if (user == NULL || folder == NULL) {
    - return NMERR_BAD_PARM;
    - }
    -
    - /* Add the object id */
    - fields = nm_field_add_pointer(fields, NM_A_SZ_OBJECT_ID, 0, NMFIELD_METHOD_VALID, 0,
    - g_strdup_printf("%d", nm_folder_get_id(folder)),
    - NMFIELD_TYPE_UTF8);
    -
    - /* Dispatch the request */
    - rc = nm_send_request(user, "deletecontact", fields, callback, data, &req);
    - if (rc == NM_OK && req)
    - nm_request_set_data(req, folder);
    -
    - if (req)
    - nm_release_request(req);
    -
    - nm_free_fields(&fields);
    - return rc;
    -}
    -
    -NMERR_T
    -nm_send_get_status(NMUser * user, NMUserRecord * user_record,
    - nm_response_cb callback, gpointer data)
    -{
    - NMERR_T rc = NM_OK;
    - NMField *fields = NULL;
    - NMRequest *req = NULL;
    - const char *dn;
    -
    - if (user == NULL || user_record == NULL)
    - return NMERR_BAD_PARM;
    -
    - /* Add DN to field list */
    - dn = nm_user_record_get_dn(user_record);
    - if (dn == NULL)
    - return (NMERR_T) -1;
    -
    - fields = nm_field_add_pointer(fields, NM_A_SZ_DN, 0, NMFIELD_METHOD_VALID, 0,
    - g_strdup(dn), NMFIELD_TYPE_UTF8);
    -
    - /* Dispatch the request */
    - rc = nm_send_request(user, "getstatus", fields, callback, data, &req);
    - if (rc == NM_OK && req)
    - nm_request_set_data(req, user_record);
    -
    - if (req)
    - nm_release_request(req);
    -
    - nm_free_fields(&fields);
    - return rc;
    -}
    -
    -NMERR_T
    -nm_send_rename_contact(NMUser * user, NMContact * contact,
    - const char *new_name, nm_response_cb callback,
    - gpointer data)
    -{
    - NMERR_T rc = NM_OK;
    - NMField *field = NULL, *fields = NULL, *list = NULL;
    - NMRequest *req = NULL;
    -
    - if (user == NULL || contact == NULL || new_name == NULL)
    - return NMERR_BAD_PARM;
    -
    - /* Create field list for current contact */
    - field = nm_contact_to_fields(contact);
    - if (field) {
    -
    - fields =
    - nm_field_add_pointer(fields, NM_A_FA_CONTACT, 0, NMFIELD_METHOD_DELETE, 0,
    - field, NMFIELD_TYPE_ARRAY);
    - field = NULL;
    -
    - /* Update the contacts display name locally */
    - nm_contact_set_display_name(contact, new_name);
    -
    - /* Create field list for updated contact */
    - field = nm_contact_to_fields(contact);
    - if (field) {
    - fields =
    - nm_field_add_pointer(fields, NM_A_FA_CONTACT, 0, NMFIELD_METHOD_ADD, 0,
    - field, NMFIELD_TYPE_ARRAY);
    - field = NULL;
    -
    - /* Package it up */
    - list =
    - nm_field_add_pointer(list, NM_A_FA_CONTACT_LIST, 0, NMFIELD_METHOD_VALID,
    - 0, fields, NMFIELD_TYPE_ARRAY);
    - fields = NULL;
    -
    - rc = nm_send_request(user, "updateitem", list, callback, data,
    - &req);
    - if (rc == NM_OK && req)
    - nm_request_set_data(req, contact);
    - }
    - }
    -
    - if (req)
    - nm_release_request(req);
    -
    - if (list)
    - nm_free_fields(&list);
    -
    - return rc;
    -}
    -
    -NMERR_T
    -nm_send_rename_folder(NMUser * user, NMFolder * folder, const char *new_name,
    - nm_response_cb callback, gpointer data)
    -{
    - NMERR_T rc = NM_OK;
    - NMField *field = NULL, *fields = NULL, *list = NULL;
    - NMRequest *req = NULL;
    -
    - if (user == NULL || folder == NULL || new_name == NULL)
    - return NMERR_BAD_PARM;
    -
    - /* Make sure folder does not already exist!? */
    - if (nm_find_folder(user, new_name))
    - return NMERR_FOLDER_EXISTS;
    -
    - /* Create field list for current folder */
    - field = nm_folder_to_fields(folder);
    - if (field) {
    -
    - fields = nm_field_add_pointer(fields, NM_A_FA_FOLDER, 0, NMFIELD_METHOD_DELETE, 0,
    - field, NMFIELD_TYPE_ARRAY);
    - field = NULL;
    -
    - /* Update the folders display name locally */
    - nm_folder_set_name(folder, new_name);
    -
    - /* Create field list for updated folder */
    - field = nm_folder_to_fields(folder);
    - if (field) {
    - fields = nm_field_add_pointer(fields, NM_A_FA_FOLDER, 0, NMFIELD_METHOD_ADD, 0,
    - field, NMFIELD_TYPE_ARRAY);
    - field = NULL;
    -
    - /* Package it up */
    - list = nm_field_add_pointer(list, NM_A_FA_CONTACT_LIST, 0, NMFIELD_METHOD_VALID,
    - 0, fields, NMFIELD_TYPE_ARRAY);
    - fields = NULL;
    -
    - rc = nm_send_request(user, "updateitem", list, callback, data,
    - &req);
    - if (rc == NM_OK && req)
    - nm_request_set_data(req, folder);
    - }
    - }
    -
    - if (req)
    - nm_release_request(req);
    -
    - if (list)
    - nm_free_fields(&list);
    -
    - return rc;
    -}
    -
    -NMERR_T
    -nm_send_move_contact(NMUser * user, NMContact * contact, NMFolder * folder,
    - nm_response_cb callback, gpointer data)
    -{
    - NMERR_T rc = NM_OK;
    - NMField *field = NULL, *fields = NULL, *list = NULL;
    - NMRequest *req = NULL;
    -
    - if (user == NULL || contact == NULL || folder == NULL)
    - return NMERR_BAD_PARM;
    -
    - /* Create field list for the contact */
    - field = nm_contact_to_fields(contact);
    - if (field) {
    -
    - fields = nm_field_add_pointer(fields, NM_A_FA_CONTACT, 0, NMFIELD_METHOD_DELETE, 0,
    - field, NMFIELD_TYPE_ARRAY);
    - field = NULL;
    -
    - /* Wrap the contact up and add it to the request field list */
    - list = nm_field_add_pointer(list, NM_A_FA_CONTACT_LIST, 0, NMFIELD_METHOD_VALID, 0,
    - fields, NMFIELD_TYPE_ARRAY);
    - fields = NULL;
    -
    - /* Add sequence number */
    - list = nm_field_add_pointer(list, NM_A_SZ_SEQUENCE_NUMBER, 0, NMFIELD_METHOD_VALID,
    - 0, g_strdup("-1"), NMFIELD_TYPE_UTF8);
    -
    - /* Add parent ID */
    - list = nm_field_add_pointer(list, NM_A_SZ_PARENT_ID, 0, NMFIELD_METHOD_VALID, 0,
    - g_strdup_printf("%d", nm_folder_get_id(folder)),
    - NMFIELD_TYPE_UTF8);
    -
    - /* Dispatch the request */
    - rc = nm_send_request(user, "movecontact", list, callback, data, &req);
    - if (rc == NM_OK && req)
    - nm_request_set_data(req, contact);
    -
    - }
    -
    - if (req)
    - nm_release_request(req);
    -
    - if (list)
    - nm_free_fields(&list);
    -
    - return rc;
    -}
    -
    -
    -NMERR_T
    -nm_send_create_privacy_item(NMUser *user, const char *who, gboolean allow_list,
    - nm_response_cb callback, gpointer data)
    -{
    - NMERR_T rc = NM_OK;
    - NMField *fields = NULL;
    - const char *tag;
    -
    - if (user == NULL || who == NULL)
    - return NMERR_BAD_PARM;
    -
    - if (allow_list)
    - tag = NM_A_SZ_BLOCKING_ALLOW_ITEM;
    - else
    - tag = NM_A_SZ_BLOCKING_DENY_ITEM;
    -
    - fields = nm_field_add_pointer(fields, tag, 0, NMFIELD_METHOD_ADD, 0,
    - g_strdup(who), NMFIELD_TYPE_UTF8);
    -
    - rc = nm_send_request(user, "createblock", fields, callback, data, NULL);
    -
    - nm_free_fields(&fields);
    - return rc;
    -}
    -
    -NMERR_T
    -nm_send_remove_privacy_item(NMUser *user, const char *dn, gboolean allow_list,
    - nm_response_cb callback, gpointer data)
    -{
    - NMERR_T rc = NM_OK;
    - NMField *fields = NULL;
    - const char *tag;
    - GSList **list_ptr, *node;
    -
    - if (user == NULL || dn == NULL)
    - return NMERR_BAD_PARM;
    -
    - if (allow_list) {
    - tag = NM_A_BLOCKING_ALLOW_LIST;
    - list_ptr = &user->allow_list;
    - } else {
    - tag = NM_A_BLOCKING_DENY_LIST;
    - list_ptr = &user->deny_list;
    - }
    -
    - /* Remove item from the cached list */
    - if ((node = g_slist_find_custom(*list_ptr, dn, (GCompareFunc)purple_utf8_strcasecmp))) {
    - *list_ptr = g_slist_delete_link(*list_ptr, node);
    - }
    -
    - fields = nm_field_add_pointer(fields, tag, 0, NMFIELD_METHOD_DELETE, 0,
    - g_strdup(dn), NMFIELD_TYPE_DN);
    -
    - rc = nm_send_request(user, "updateblocks", fields, callback, data, NULL);
    -
    - nm_free_fields(&fields);
    - return rc;
    -
    -}
    -
    -NMERR_T
    -nm_send_set_privacy_default(NMUser *user, gboolean default_deny,
    - nm_response_cb callback, gpointer data)
    -{
    - NMERR_T rc = NM_OK;
    - NMField *fields = NULL;
    -
    - if (user == NULL)
    - return NMERR_BAD_PARM;
    -
    - fields = nm_field_add_pointer(fields, NM_A_BLOCKING, 0, NMFIELD_METHOD_UPDATE, 0,
    - (default_deny ? g_strdup("1") : g_strdup("0")),
    - NMFIELD_TYPE_UTF8);
    -
    - rc = nm_send_request(user, "updateblocks", fields, callback, data, NULL);
    -
    - nm_free_fields(&fields);
    - return rc;
    -}
    -
    -NMERR_T
    -nm_send_keepalive(NMUser *user, nm_response_cb callback, gpointer data)
    -{
    - NMERR_T rc = NM_OK;
    -
    - if (user == NULL)
    - return NMERR_BAD_PARM;
    -
    - rc = nm_send_request(user, "ping", NULL, callback, data, NULL);
    -
    - return rc;
    -}
    -
    -NMERR_T
    -nm_process_new_data(NMUser * user)
    -{
    - NMConn *conn;
    - NMERR_T rc = NM_OK;
    - guint32 val;
    - GError *error = NULL;
    -
    - if (user == NULL)
    - return NMERR_BAD_PARM;
    -
    - conn = user->conn;
    -
    - /* Check to see if this is an event or a response */
    - val = g_data_input_stream_read_uint32(conn->input, user->cancellable,
    - &error);
    - if (error == NULL) {
    - if (val == ('H' + ('T' << 8) + ('T' << 16) + ('P' << 24))) {
    - rc = nm_process_response(user);
    - } else {
    - rc = nm_process_event(user, val);
    - }
    - } else {
    - if (error->code == G_IO_ERROR_WOULD_BLOCK || error->code == G_IO_ERROR_CANCELLED) {
    - /* Try again later or ignore. */
    - rc = NM_OK;
    - } else {
    - rc = NMERR_PROTOCOL;
    - }
    - g_error_free(error);
    - }
    -
    - return rc;
    -}
    -
    -NMConference *
    -nm_find_conversation(NMUser * user, const char *who)
    -{
    - NMConference *conference = NULL;
    - NMConference *tmp;
    - GSList *cnode;
    -
    - if (user && user->conferences) {
    - for (cnode = user->conferences; cnode; cnode = cnode->next) {
    - tmp = cnode->data;
    - if (nm_conference_get_participant_count(tmp) == 1) {
    - NMUserRecord *ur = nm_conference_get_participant(tmp, 0);
    -
    - if (ur) {
    - if (nm_utf8_str_equal(nm_user_record_get_dn(ur), who)) {
    - conference = tmp;
    - break;
    - }
    - }
    - }
    - }
    - }
    -
    - return conference;
    -}
    -
    -void
    -nm_conference_list_add(NMUser * user, NMConference * conf)
    -{
    - if (user == NULL || conf == NULL)
    - return;
    -
    - nm_conference_add_ref(conf);
    - user->conferences = g_slist_append(user->conferences, conf);
    -}
    -
    -void
    -nm_conference_list_remove(NMUser * user, NMConference * conf)
    -{
    - if (user == NULL || conf == NULL)
    - return;
    -
    - if (g_slist_find(user->conferences, conf)) {
    - user->conferences = g_slist_remove(user->conferences, conf);
    - nm_release_conference(conf);
    - }
    -}
    -
    -void
    -nm_conference_list_free(NMUser * user)
    -{
    - if (user == NULL)
    - return;
    -
    - g_slist_free_full(user->conferences, (GDestroyNotify)nm_release_conference);
    - user->conferences = NULL;
    -}
    -
    -NMConference *
    -nm_conference_list_find(NMUser * user, const char *guid)
    -{
    - GSList *cnode;
    - NMConference *conference = NULL, *tmp;
    -
    - if (user == NULL || guid == NULL)
    - return NULL;
    -
    - if (user->conferences) {
    - for (cnode = user->conferences; cnode; cnode = cnode->next) {
    - tmp = cnode->data;
    - if (nm_are_guids_equal(nm_conference_get_guid(tmp), guid)) {
    - conference = tmp;
    - break;
    - }
    - }
    - }
    -
    - return conference;
    -}
    -
    -gboolean
    -nm_are_guids_equal(const char *guid1, const char *guid2)
    -{
    - if (guid1 == NULL || guid2 == NULL)
    - return FALSE;
    -
    - return (strncmp(guid1, guid2, CONF_GUID_END) == 0);
    -}
    -
    -void
    -nm_user_add_contact(NMUser * user, NMContact * contact)
    -{
    - if (user == NULL || contact == NULL)
    - return;
    -
    - nm_contact_add_ref(contact);
    -
    - g_hash_table_insert(user->contacts,
    - g_utf8_strdown(nm_contact_get_dn(contact), -1), contact);
    -}
    -
    -void
    -nm_user_add_user_record(NMUser * user, NMUserRecord * user_record)
    -{
    - const char *display_id;
    - const char *dn;
    -
    - if (!user || !user_record)
    - return;
    -
    - display_id = nm_user_record_get_display_id(user_record);
    - dn = nm_user_record_get_dn(user_record);
    -
    - if (!dn || !display_id)
    - return;
    -
    - nm_user_record_add_ref(user_record);
    -
    - g_hash_table_insert(user->user_records,
    - g_utf8_strdown(dn, -1),
    - user_record);
    -
    - g_hash_table_insert(user->display_id_to_dn,
    - g_utf8_strdown(display_id, -1),
    - g_utf8_strdown(dn, -1));
    -}
    -
    -nm_event_cb
    -nm_user_get_event_callback(NMUser * user)
    -{
    - if (user == NULL)
    - return NULL;
    -
    - return user->evt_callback;
    -}
    -
    -NMConn *
    -nm_user_get_conn(NMUser * user)
    -{
    - if (user == NULL)
    - return NULL;
    -
    - return user->conn;
    -}
    -
    -NMERR_T
    -nm_create_contact_list(NMUser * user)
    -{
    - NMERR_T rc = NM_OK;
    - NMField *locate = NULL;
    -
    - if (user == NULL || user->fields == NULL) {
    - return NMERR_BAD_PARM;
    - }
    -
    - /* Create the root folder */
    - user->root_folder = nm_create_folder("");
    -
    - /* Find the contact list in the login fields */
    - locate = nm_locate_field(NM_A_FA_CONTACT_LIST, user->fields);
    - if (locate != NULL) {
    -
    - /* Add the folders and then the contacts */
    - nm_folder_add_contacts_and_folders(user, user->root_folder,
    - (NMField *) (locate->ptr_value));
    -
    - }
    -
    - return rc;
    -}
    -
    -gboolean nm_user_is_privacy_locked(NMUser *user)
    -{
    - if (user) {
    - return user->privacy_locked;
    - }
    -
    - return FALSE;
    -}
    -
    -static gboolean
    -_create_privacy_list(NMUser * user, NMRequest *request)
    -{
    - NMField *locate = NULL;
    - GSList *need_details = NULL;
    -
    - /* Are the privacy settings locked */
    - locate = nm_locate_field(NM_A_LOCKED_ATTR_LIST, user->fields);
    - if (locate && locate->ptr_value) {
    - if (locate->type == NMFIELD_TYPE_UTF8 &&
    - (purple_utf8_strcasecmp(locate->ptr_value, NM_A_BLOCKING) == 0)) {
    - user->privacy_locked = TRUE;
    - } else if (locate->type == NMFIELD_TYPE_MV ||
    - locate->type == NMFIELD_TYPE_ARRAY) {
    - NMField *tmp = (NMField *)locate->ptr_value;
    - while (tmp && tmp->tag) {
    - if (purple_utf8_strcasecmp(tmp->ptr_value, NM_A_BLOCKING) == 0) {
    - user->privacy_locked = TRUE;
    - break;
    - }
    - tmp++;
    - }
    - }
    - }
    -
    - /* Set default deny flag */
    - locate = nm_locate_field(NM_A_BLOCKING, user->fields);
    - if (locate && locate->ptr_value) {
    - user->default_deny = atoi((char *)locate->ptr_value);
    - }
    -
    - /* Read internal blocking allow list */
    - locate = nm_locate_field(NM_A_BLOCKING_ALLOW_LIST, user->fields);
    - if (locate && locate->ptr_value) {
    -
    - if (locate->type == NMFIELD_TYPE_MV) {
    - locate = (NMField *)locate->ptr_value;
    - for (; locate->tag != NULL; locate++) {
    - if (locate->ptr_value) {
    -
    - user->allow_list = g_slist_append(user->allow_list, (char *)locate->ptr_value);
    -
    - if (nm_find_user_record(user, (char *)locate->ptr_value) == NULL)
    - need_details = g_slist_append(need_details, (char *)locate->ptr_value);
    -
    - }
    - }
    - } else {
    -
    - user->allow_list = g_slist_append(user->allow_list, (char *)locate->ptr_value);
    -
    - if (nm_find_user_record(user, (char *)locate->ptr_value) == NULL)
    - need_details = g_slist_append(need_details, (char *)locate->ptr_value);
    -
    - }
    - }
    -
    - /* Read internal blocking deny list */
    - locate = nm_locate_field(NM_A_BLOCKING_DENY_LIST, user->fields);
    - if (locate && locate->ptr_value) {
    -
    - if (locate->type == NMFIELD_TYPE_MV) {
    - locate = (NMField *)locate->ptr_value;
    - for (; locate->tag != NULL; locate++) {
    - if (locate->ptr_value) {
    -
    - user->deny_list = g_slist_append(user->deny_list, (char *)locate->ptr_value);
    -
    - if (nm_find_user_record(user, (char *)locate->ptr_value) == NULL)
    - need_details = g_slist_append(need_details, (char *)locate->ptr_value);
    -
    - }
    - }
    - } else {
    -
    - user->deny_list = g_slist_append(user->deny_list, (char *)locate->ptr_value);
    -
    - if (nm_find_user_record(user, (char *)locate->ptr_value) == NULL)
    - need_details = g_slist_append(need_details, (char *)locate->ptr_value);
    -
    - }
    - }
    -
    - if (need_details) {
    -
    - nm_request_add_ref(request);
    - nm_send_multiple_get_details(user, need_details,
    - _handle_multiple_get_details_login_cb, request);
    -
    - return FALSE;
    - }
    -
    - return TRUE;
    -}
    -
    -void
    -nm_destroy_contact_list(NMUser * user)
    -{
    - if (user == NULL)
    - return;
    -
    - if (user->root_folder) {
    - nm_release_folder(user->root_folder);
    - user->root_folder = NULL;
    - }
    -}
    -
    -NMFolder *
    -nm_get_root_folder(NMUser * user)
    -{
    - if (user == NULL)
    - return NULL;
    -
    - if (user->root_folder == NULL)
    - nm_create_contact_list(user);
    -
    - return user->root_folder;
    -}
    -
    -NMContact *
    -nm_find_contact(NMUser * user, const char *name)
    -{
    - char *str;
    - const char *dn = NULL;
    - NMContact *contact = NULL;
    -
    - if (user == NULL || name == NULL)
    - return NULL;
    -
    - str = g_utf8_strdown(name, -1);
    - if (strstr(str, "=")) {
    - dn = str;
    - } else {
    - /* Assume that we have a display id instead of a dn */
    - dn = (const char *) g_hash_table_lookup(user->display_id_to_dn, str);
    - }
    -
    - /* Find contact object in reference table */
    - if (dn) {
    - contact = (NMContact *) g_hash_table_lookup(user->contacts, dn);
    - }
    -
    - g_free(str);
    - return contact;
    -}
    -
    -GList *
    -nm_find_contacts(NMUser * user, const char *dn)
    -{
    - guint32 i, cnt;
    - NMFolder *folder;
    - NMContact *contact;
    - GList *contacts = NULL;
    -
    - if (user == NULL || dn == NULL)
    - return NULL;
    -
    - /* Check for contact at the root */
    - contact = nm_folder_find_contact(user->root_folder, dn);
    - if (contact) {
    - contacts = g_list_append(contacts, contact);
    - contact = NULL;
    - }
    -
    - /* Check for contact in each subfolder */
    - cnt = nm_folder_get_subfolder_count(user->root_folder);
    - for (i = 0; i < cnt; i++) {
    - folder = nm_folder_get_subfolder(user->root_folder, i);
    - contact = nm_folder_find_contact(folder, dn);
    - if (contact) {
    - contacts = g_list_append(contacts, contact);
    - contact = NULL;
    - }
    - }
    -
    - return contacts;
    -}
    -
    -NMUserRecord *
    -nm_find_user_record(NMUser * user, const char *name)
    -{
    - char *str = NULL;
    - const char *dn = NULL;
    - NMUserRecord *user_record = NULL;
    -
    - if (user == NULL || name == NULL)
    - return NULL;
    -
    - str = g_utf8_strdown(name, -1);
    - if (strstr(str, "=")) {
    - dn = str;
    - } else {
    - /* Assume that we have a display id instead of a dn */
    - dn = (const char *) g_hash_table_lookup(user->display_id_to_dn, str);
    - }
    -
    - /* Find user record in reference table */
    - if (dn) {
    - user_record =
    - (NMUserRecord *) g_hash_table_lookup(user->user_records, dn);
    - }
    -
    - g_free(str);
    - return user_record;
    -}
    -
    -const char *
    -nm_lookup_dn(NMUser * user, const char *display_id)
    -{
    - const char *dn;
    - char *lower;
    -
    - if (user == NULL || display_id == NULL)
    - return NULL;
    -
    - lower = g_utf8_strdown(display_id, -1);
    - dn = g_hash_table_lookup(user->display_id_to_dn, lower);
    - g_free(lower);
    -
    - return dn;
    -}
    -
    -NMFolder *
    -nm_find_folder(NMUser * user, const char *name)
    -{
    - NMFolder *folder = NULL, *temp;
    - int i, num_folders;
    - const char *tname = NULL;
    -
    - if (user == NULL || name == NULL)
    - return NULL;
    -
    - if (*name == '\0')
    - return user->root_folder;
    -
    - num_folders = nm_folder_get_subfolder_count(user->root_folder);
    - for (i = 0; i < num_folders; i++) {
    - temp = nm_folder_get_subfolder(user->root_folder, i);
    - tname = nm_folder_get_name(temp);
    - if (tname && purple_strequal(tname, name)) {
    - folder = temp;
    - break;
    - }
    - }
    -
    - return folder;
    -}
    -
    -NMFolder *
    -nm_find_folder_by_id(NMUser * user, int object_id)
    -{
    - NMFolder *folder = NULL, *temp;
    - int i, num_folders;
    -
    - if (user == NULL)
    - return NULL;
    -
    - if (object_id == 0)
    - return user->root_folder;
    -
    - num_folders = nm_folder_get_subfolder_count(user->root_folder);
    - for (i = 0; i < num_folders; i++) {
    - temp = nm_folder_get_subfolder(user->root_folder, i);
    - if (nm_folder_get_id(temp) == object_id) {
    - folder = temp;
    - break;
    - }
    - }
    -
    - return folder;
    -}
    -
    -static void
    -_handle_multiple_get_details_login_cb(NMUser * user, NMERR_T ret_code,
    - gpointer resp_data, gpointer user_data)
    -{
    - nm_response_cb cb;
    - NMRequest *request = user_data;
    -
    - if (user == NULL || request == NULL)
    - return;
    -
    - if ((cb = nm_request_get_callback(request))) {
    - cb(user, ret_code, nm_request_get_data(request),
    - nm_request_get_user_define(request));
    - nm_release_request(request);
    - }
    -}
    -
    -static void
    -_handle_multiple_get_details_joinconf_cb(NMUser * user, NMERR_T ret_code,
    - gpointer resp_data, gpointer user_data)
    -{
    - NMRequest *request = user_data;
    - NMUserRecord *user_record = resp_data;
    - NMConference *conference;
    - GSList *list, *node;
    -
    - if (user == NULL || resp_data == NULL || user_data == NULL)
    - return;
    -
    - conference = nm_request_get_data(request);
    - list = nm_request_get_user_define(request);
    -
    - if (ret_code == 0 && conference && list) {
    -
    - /* Add the user to the conference */
    - nm_conference_add_participant(conference, user_record);
    -
    - /* Find the user in the list and remove it */
    - node = g_slist_find_custom(list, nm_user_record_get_dn(user_record), (GCompareFunc)purple_utf8_strcasecmp);
    - if (node) {
    - g_free(node->data);
    - list = g_slist_delete_link(list, node);
    - nm_request_set_user_define(request, list);
    - }
    -
    - /* Time to callback? */
    - if (list == NULL) {
    - nm_response_cb cb = nm_request_get_callback(request);
    -
    - if (cb) {
    - cb(user, 0, conference, conference);
    - }
    - nm_release_request(request);
    - }
    - }
    -}
    -
    -static NMERR_T
    -nm_call_handler(NMUser * user, NMRequest * request, NMField * fields)
    -{
    - NMERR_T rc = NM_OK, ret_code = NM_OK;
    - NMConference *conf = NULL;
    - NMUserRecord *user_record = NULL;
    - NMField *locate = NULL;
    - NMField *field = NULL;
    - const char *cmd;
    - nm_response_cb cb;
    - gboolean done = TRUE;
    -
    - if (user == NULL || request == NULL || fields == NULL)
    - return NMERR_BAD_PARM;
    -
    - /* Get the return code */
    - field = nm_locate_field(NM_A_SZ_RESULT_CODE, fields);
    - if (field) {
    - ret_code = atoi((char *) field->ptr_value);
    - } else {
    - ret_code = NMERR_PROTOCOL;
    - }
    -
    - cmd = nm_request_get_cmd(request);
    - if (ret_code == NM_OK && cmd != NULL) {
    -
    - if (purple_strequal("login", cmd)) {
    -
    - user->user_record = nm_create_user_record_from_fields(fields);
    -
    - /* Save the users fields */
    - user->fields = nm_copy_field_array(fields);
    -
    - nm_create_contact_list(user);
    - done = _create_privacy_list(user, request);
    -
    - } else if (purple_strequal("setstatus", cmd)) {
    -
    - /* Nothing to do */
    -
    - } else if (purple_strequal("createconf", cmd)) {
    -
    - conf = (NMConference *) nm_request_get_data(request);
    -
    - /* get the convo guid */
    - locate = nm_locate_field(NM_A_FA_CONVERSATION, fields);
    - if (locate) {
    - field =
    - nm_locate_field(NM_A_SZ_OBJECT_ID, (NMField *) fields->ptr_value);
    - if (field) {
    - nm_conference_set_guid(conf, (char *) field->ptr_value);
    - }
    - }
    -
    - nm_conference_list_add(user, conf);
    - nm_release_conference(conf);
    -
    - } else if (purple_strequal("leaveconf", cmd)) {
    -
    - conf = (NMConference *) nm_request_get_data(request);
    - nm_conference_list_remove(user, conf);
    -
    - } else if (purple_strequal("joinconf", cmd)) {
    - GSList *list = NULL, *node;
    -
    - conf = nm_request_get_data(request);
    -
    - locate = nm_locate_field(NM_A_FA_CONTACT_LIST, fields);
    - if (locate && locate->ptr_value != 0) {
    -
    - field = (NMField *) locate->ptr_value;
    - while ((field = nm_locate_field(NM_A_SZ_DN, field))) {
    - if (field->ptr_value != 0) {
    -
    - if (nm_utf8_str_equal
    - (nm_user_record_get_dn(user->user_record),
    - (const char *) field->ptr_value)) {
    - field++;
    - continue;
    - }
    -
    - user_record =
    - nm_find_user_record(user,
    - (const char *) field->ptr_value);
    - if (user_record == NULL) {
    - list =
    - g_slist_append(list,
    - g_strdup((char *) field->ptr_value));
    - } else {
    - nm_conference_add_participant(conf, user_record);
    - }
    - }
    - field++;
    - }
    -
    - if (list != NULL) {
    -
    - done = FALSE;
    - nm_request_set_user_define(request, list);
    - nm_request_add_ref(request);
    - for (node = list; node; node = node->next) {
    -
    - nm_send_get_details(user, (const char *) node->data,
    - _handle_multiple_get_details_joinconf_cb,
    - request);
    - }
    - }
    - }
    -
    - } else if (purple_strequal("getdetails", cmd)) {
    -
    - locate = nm_locate_field(NM_A_FA_RESULTS, fields);
    - while (locate && locate->ptr_value != 0) {
    -
    - user_record = nm_create_user_record_from_fields(locate);
    - if (user_record) {
    - NMUserRecord *tmp;
    -
    - tmp =
    - nm_find_user_record(user,
    - nm_user_record_get_dn(user_record));
    - if (tmp) {
    -
    - /* Update the existing user record */
    - nm_user_record_copy(tmp, user_record);
    - nm_release_user_record(user_record);
    - user_record = tmp;
    -
    - } else {
    - nm_user_add_user_record(user, user_record);
    - nm_release_user_record(user_record);
    - }
    -
    - /* Response data is new user record */
    - nm_request_set_data(request, (gpointer) user_record);
    - }
    -
    - locate = nm_locate_field(NM_A_FA_RESULTS, locate+1);
    - }
    -
    - } else if (purple_strequal("createfolder", cmd)) {
    -
    - _update_contact_list(user, fields);
    -
    - } else if (purple_strequal("createcontact", cmd)) {
    -
    - _update_contact_list(user, fields);
    -
    - locate =
    - nm_locate_field(NM_A_SZ_OBJECT_ID, (NMField *) fields->ptr_value);
    - if (locate) {
    -
    - NMContact *new_contact =
    - nm_folder_find_item_by_object_id(user->root_folder,
    - atoi((char *)locate->ptr_value));
    -
    - if (new_contact) {
    -
    - /* Add the contact to our cache */
    - nm_user_add_contact(user, new_contact);
    -
    - /* Set the contact as the response data */
    - nm_request_set_data(request, (gpointer) new_contact);
    -
    - }
    -
    - }
    -
    - } else if (purple_strequal("deletecontact", cmd)) {
    -
    - _update_contact_list(user, fields);
    -
    - } else if (purple_strequal("movecontact", cmd)) {
    -
    - _update_contact_list(user, fields);
    -
    - } else if (purple_strequal("getstatus", cmd)) {
    -
    - locate = nm_locate_field(NM_A_SZ_STATUS, fields);
    - if (locate) {
    - nm_user_record_set_status((NMUserRecord *)
    - nm_request_get_data(request),
    - atoi((char *) locate->ptr_value), NULL);
    - }
    -
    - } else if (purple_strequal("updateitem", cmd)) {
    -
    - /* Nothing extra to do here */
    -
    - } else if (purple_strequal("createblock", cmd)) {
    - if ((locate = nm_locate_field(NM_A_BLOCKING_DENY_LIST, fields))) {
    - if (locate->ptr_value) {
    - user->deny_list = g_slist_append(user->deny_list, g_strdup((char *)locate->ptr_value));
    - }
    - } else if ((locate = nm_locate_field(NM_A_BLOCKING_ALLOW_LIST, fields))) {
    - if (locate->ptr_value) {
    - user->allow_list = g_slist_append(user->allow_list, g_strdup((char *)locate->ptr_value));
    - }
    - }
    - } else if (purple_strequal("updateblocks", cmd)) {
    - /* nothing to do here */
    - } else {
    -
    - /* Nothing to do, just print debug message */
    - purple_debug_info("novell", "nm_call_handler(): Unknown request command, %s", cmd);
    -
    - }
    - }
    -
    - if (done && (cb = nm_request_get_callback(request))) {
    -
    - cb(user, ret_code, nm_request_get_data(request),
    - nm_request_get_user_define(request));
    - }
    -
    - return rc;
    -}
    -
    -static NMERR_T
    -nm_process_response(NMUser * user)
    -{
    - NMERR_T rc = NM_OK;
    - NMField *fields = NULL;
    - NMField *field = NULL;
    - NMConn *conn = user->conn;
    - NMRequest *req = NULL;
    -
    - rc = nm_read_header(user);
    - if (rc == NM_OK) {
    - rc = nm_read_fields(user, -1, &fields);
    - }
    -
    - if (rc == NM_OK) {
    - field = nm_locate_field(NM_A_SZ_TRANSACTION_ID, fields);
    - if (field != NULL && field->ptr_value != 0) {
    - req = nm_conn_find_request(conn, atoi((char *) field->ptr_value));
    - if (req != NULL) {
    - rc = nm_call_handler(user, req, fields);
    - nm_conn_remove_request_item(conn, req);
    - }
    -
    - }
    - }
    -
    - if (fields)
    - nm_free_fields(&fields);
    -
    - return rc;
    -}
    -
    -/*
    - * Some utility functions...haven't figured out where
    - * they belong yet.
    - */
    -
    -gboolean
    -nm_utf8_str_equal(gconstpointer str1, gconstpointer str2)
    -{
    - return (purple_utf8_strcasecmp(str1, str2) == 0);
    -}
    -
    -char *
    -nm_typed_to_dotted(const char *typed)
    -{
    - unsigned i = 0, j = 0;
    - char *dotted;
    -
    - if (typed == NULL)
    - return NULL;
    -
    - dotted = g_new0(char, strlen(typed) + 1);
    -
    - do {
    -
    - /* replace comma with a dot */
    - if (j != 0) {
    - dotted[j] = '.';
    - j++;
    - }
    -
    - /* skip the type */
    - while (typed[i] != '\0' && typed[i] != '=')
    - i++;
    -
    - /* verify that we aren't running off the end */
    - if (typed[i] == '\0') {
    - dotted[j] = '\0';
    - break;
    - }
    -
    - i++;
    -
    - /* copy the object name to context */
    - while (typed[i] != '\0' && typed[i] != ',') {
    - dotted[j] = typed[i];
    - j++;
    - i++;
    - }
    -
    - } while (typed[i] != '\0');
    -
    - return dotted;
    -}
    -
    -const char *
    -nm_error_to_string(NMERR_T err)
    -{
    - static char *unknown_msg = NULL;
    -
    - g_free(unknown_msg);
    - unknown_msg = NULL;
    -
    - switch (err) {
    -
    - case NMERR_BAD_PARM:
    - return _("Required parameters not passed in");
    -
    - case NMERR_TCP_WRITE:
    - return _("Unable to write to network");
    -
    - case NMERR_TCP_READ:
    - return _("Unable to read from network");
    -
    - case NMERR_PROTOCOL:
    - return _("Error communicating with server");
    -
    - case NMERR_CONFERENCE_NOT_FOUND:
    - case NMERR_CONFERENCE_NOT_FOUND_2:
    - return _("Conference not found");
    -
    - case NMERR_CONFERENCE_NOT_INSTANTIATED:
    - return _("Conference does not exist");
    -
    - case NMERR_DUPLICATE_FOLDER:
    - case NMERR_FOLDER_EXISTS:
    - return _("A folder with that name already exists");
    -
    - case NMERR_NOT_SUPPORTED:
    - return _("Not supported");
    -
    - case NMERR_PASSWORD_EXPIRED:
    - case NMERR_PASSWORD_EXPIRED_2:
    - return _("Password has expired");
    -
    - case NMERR_PASSWORD_INVALID:
    - return _("Incorrect password");
    -
    - case NMERR_USER_NOT_FOUND:
    - return _("User not found");
    -
    - case NMERR_USER_DISABLED:
    - return _("Account has been disabled");
    -
    - case NMERR_DIRECTORY_FAILURE:
    - return _("The server could not access the directory");
    -
    - case NMERR_ADMIN_LOCKED:
    - return _("Your system administrator has disabled this operation");
    -
    - case NMERR_SERVER_BUSY:
    - return _("The server is unavailable; try again later");
    -
    - case NMERR_DUPLICATE_CONTACT:
    - return _("Cannot add a contact to the same folder twice");
    -
    - case NMERR_USER_NOT_ALLOWED:
    - return _("Cannot add yourself");
    -
    - case NMERR_MASTER_ARCHIVE_MISSING:
    - return _("Master archive is misconfigured");
    -
    - case NMERR_AUTHENTICATION_FAILED:
    - case NMERR_CREDENTIALS_MISSING:
    - return _("Incorrect username or password");
    -
    - case NMERR_HOST_NOT_FOUND:
    - return _("Could not recognize the host of the username you entered");
    -
    - case NMERR_ACCESS_DENIED:
    - return _("Your account has been disabled because too many incorrect passwords were entered");
    -
    - case NMERR_DUPLICATE_PARTICIPANT:
    - return _("You cannot add the same person twice to a conversation");
    -
    - case NMERR_TOO_MANY_CONTACTS:
    - case NMERR_TOO_MANY_FOLDERS:
    - return _("You have reached your limit for the number of contacts allowed");
    -
    - case NMERR_OBJECT_NOT_FOUND:
    - return _("You have entered an incorrect username");
    -
    - case NMERR_DIRECTORY_UPDATE:
    - return _("An error occurred while updating the directory");
    -
    - case NMERR_SERVER_PROTOCOL:
    - return _("Incompatible protocol version");
    -
    - case NMERR_USER_BLOCKED:
    - return _("The user has blocked you");
    -
    - case NMERR_EVAL_CONNECTION_LIMIT:
    - return _("This evaluation version does not allow more than ten users to log in at one time");
    -
    - case NMERR_CONVERSATION_INVITE:
    - return _("The user is either offline or you are blocked");
    -
    - default:
    - unknown_msg = g_strdup_printf (_("Unknown error: 0x%X"), err);
    -
    - return unknown_msg;
    - }
    -}
    -
    -static void
    -_update_contact_list(NMUser * user, NMField * fields)
    -{
    - NMField *list, *cursor, *locate;
    - gint objid1;
    - NMContact *contact;
    - NMFolder *folder;
    - gpointer item;
    -
    - if (user == NULL || fields == NULL)
    - return;
    -
    - /* Is it wrapped in a RESULTS array? */
    - if (purple_strequal(fields->tag, NM_A_FA_RESULTS)) {
    - list = (NMField *) fields->ptr_value;
    - } else {
    - list = fields;
    - }
    -
    - /* Update the cached contact list */
    - cursor = (NMField *) list->ptr_value;
    - while (cursor->tag != NULL) {
    - if ((g_ascii_strcasecmp(cursor->tag, NM_A_FA_CONTACT) == 0) ||
    - (g_ascii_strcasecmp(cursor->tag, NM_A_FA_FOLDER) == 0)) {
    -
    - locate =
    - nm_locate_field(NM_A_SZ_OBJECT_ID, (NMField *) cursor->ptr_value);
    - if (locate != NULL && locate->ptr_value != 0) {
    - objid1 = atoi((char *) locate->ptr_value);
    - item =
    - nm_folder_find_item_by_object_id(user->root_folder, objid1);
    - if (item != NULL) {
    - if (cursor->method == NMFIELD_METHOD_ADD) {
    - if (g_ascii_strcasecmp(cursor->tag, NM_A_FA_CONTACT) == 0) {
    - contact = (NMContact *) item;
    - nm_contact_update_list_properties(contact, cursor);
    - } else if (g_ascii_strcasecmp(cursor->tag, NM_A_FA_FOLDER)
    - == 0) {
    - folder = (NMFolder *) item;
    - nm_folder_update_list_properties(folder, cursor);
    - }
    - } else if (cursor->method == NMFIELD_METHOD_DELETE) {
    - if (g_ascii_strcasecmp(cursor->tag, NM_A_FA_CONTACT) == 0) {
    - contact = (NMContact *) item;
    - folder =
    - nm_find_folder_by_id(user,
    - nm_contact_get_parent_id
    - (contact));
    - if (folder) {
    - nm_folder_remove_contact(folder, contact);
    - }
    - } else if (g_ascii_strcasecmp(cursor->tag, NM_A_FA_FOLDER)
    - == 0) {
    - /* TODO: write nm_folder_remove_folder */
    - /* ignoring for now, should not be a big deal */
    -/* folder = (NMFolder *) item;*/
    -/* nm_folder_remove_folder(user->root_folder, folder);*/
    - }
    - }
    - } else {
    -
    - if (cursor->method == NMFIELD_METHOD_ADD) {
    -
    - /* Not found, so we need to add it */
    - if (g_ascii_strcasecmp(cursor->tag, NM_A_FA_CONTACT) == 0) {
    -
    - locate =
    - nm_locate_field(NM_A_SZ_DN,
    - (NMField *) cursor->ptr_value);
    - if (locate != NULL && locate->ptr_value != NULL) {
    - contact = nm_create_contact_from_fields(cursor);
    - if (contact) {
    - nm_folder_add_contact_to_list(
    - user->root_folder, contact);
    - nm_release_contact(contact);
    - }
    - }
    - } else if (g_ascii_strcasecmp(cursor->tag, NM_A_FA_FOLDER)
    - == 0) {
    - folder = nm_create_folder_from_fields(cursor);
    - nm_folder_add_folder_to_list(user->root_folder,
    - folder);
    - nm_release_folder(folder);
    - }
    - }
    - }
    - }
    - }
    - cursor++;
    - }
    -}
    -
    -static char *
    -nm_rtfize_text(char *text)
    -{
    - GString *gstr = NULL;
    - unsigned char *pch;
    - char *uni_str = NULL, *rtf = NULL;
    - int bytes;
    - gunichar uc;
    -
    - gstr = g_string_sized_new(strlen(text)*2);
    - pch = (unsigned char *)text;
    - while (*pch) {
    - if ((*pch) <= 0x7F) {
    - switch (*pch) {
    - case '{':
    - case '}':
    - case '\\':
    - gstr = g_string_append_c(gstr, '\\');
    - gstr = g_string_append_c(gstr, *pch);
    - break;
    - case '\n':
    - gstr = g_string_append(gstr, "\\par ");
    - break;
    - default:
    - gstr = g_string_append_c(gstr, *pch);
    - break;
    - }
    - pch++;
    - } else {
    - /* convert the utf-8 character to ucs-4 for rtf encoding */
    - if(*pch <= 0xDF) {
    - uc = ((((gunichar)pch[0]) & 0x001F) << 6) |
    - (((gunichar)pch[1]) & 0x003F);
    - bytes = 2;
    - } else if(*pch <= 0xEF) {
    - uc = ((((gunichar)pch[0]) & 0x000F) << 12) |
    - ((((gunichar)pch[1]) & 0x003F) << 6) |
    - (((gunichar)pch[2]) & 0x003F);
    - bytes = 3;
    - } else if (*pch <= 0xF7) {
    - uc = ((((gunichar)pch[0]) & 0x0007) << 18) |
    - ((((gunichar)pch[1]) & 0x003F) << 12) |
    - ((((gunichar)pch[2]) & 0x003F) << 6) |
    - (((gunichar)pch[3]) & 0x003F);
    - bytes = 4;
    - } else if (*pch <= 0xFB) {
    - uc = ((((gunichar)pch[0]) & 0x0003) << 24) |
    - ((((gunichar)pch[1]) & 0x003F) << 18) |
    - ((((gunichar)pch[2]) & 0x003F) << 12) |
    - ((((gunichar)pch[3]) & 0x003F) << 6) |
    - (((gunichar)pch[4]) & 0x003F);
    - bytes = 5;
    - } else if (*pch <= 0xFD) {
    - uc = ((((gunichar)pch[0]) & 0x0001) << 30) |
    - ((((gunichar)pch[1]) & 0x003F) << 24) |
    - ((((gunichar)pch[2]) & 0x003F) << 18) |
    - ((((gunichar)pch[3]) & 0x003F) << 12) |
    - ((((gunichar)pch[4]) & 0x003F) << 6) |
    - (((gunichar)pch[5]) & 0x003F);
    - bytes = 6;
    - } else {
    - /* should never happen ... bogus utf-8! */
    - purple_debug_info("novell", "bogus utf-8 lead byte: 0x%X\n", pch[0]);
    - uc = 0x003F;
    - bytes = 1;
    - }
    - uni_str = g_strdup_printf("\\u%d?", uc);
    - purple_debug_info("novell", "unicode escaped char %s\n", uni_str);
    - gstr = g_string_append(gstr, uni_str);
    - pch += bytes;
    - g_free(uni_str);
    - }
    - }
    -
    - rtf = g_strdup_printf(RTF_TEMPLATE, gstr->str);
    - g_string_free(gstr, TRUE);
    - return rtf;
    -}
    --- a/libpurple/protocols/novell/nmuser.h Fri Nov 11 19:39:24 2022 -0600
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,670 +0,0 @@
    -/*
    - * nmuser.h
    - *
    - * Copyright (c) 2004 Novell, Inc. All Rights Reserved.
    - *
    - * 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; version 2 of the License.
    - *
    - * 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, write to the Free Software
    - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    - *
    - */
    -
    -#ifndef PURPLE_NOVELL_NMUSER_H
    -#define PURPLE_NOVELL_NMUSER_H
    -
    -#include <glib.h>
    -#include <stdio.h>
    -#include <stdlib.h>
    -
    -typedef guint32 NMERR_T;
    -typedef int NMSTATUS_T;
    -
    -typedef struct _NMUser NMUser;
    -
    -typedef enum
    -{
    - NMREQUEST_TYPE_LOGIN = 0,
    - NMREQUEST_TYPE_LOGOUT,
    - NMREQUEST_TYPE_SETSTATUS,
    - NMREQUEST_TYPE_GETDETAILS,
    - NMREQUEST_TYPE_CREATECONF,
    - NMREQUEST_TYPE_SENDMESSAGE,
    - NMREQUEST_TYPE_JOINCONF,
    - NMREQUEST_TYPE_LEAVECONF,
    - NMREQUEST_TYPE_REJECTCONF,
    - NMREQUEST_TYPE_SENDTYPING,
    - NMREQUEST_TYPE_CREATECONTACT,
    - NMREQUEST_TYPE_DELETECONTACT
    -
    -} NMRequestType;
    -
    -#include <purple.h>
    -
    -#include "nmmessage.h"
    -#include "nmconference.h"
    -#include "nmcontact.h"
    -#include "nmuserrecord.h"
    -#include "nmfield.h"
    -#include "nmevent.h"
    -
    -/* Callback typedefs */
    -typedef void (*nm_response_cb) (NMUser * user, NMERR_T ret_code,
    - gpointer resp_data, gpointer user_data);
    -
    -typedef void (*nm_event_cb) (NMUser * user, NMEvent * event);
    -
    -#include "nmrequest.h"
    -#include "nmconn.h"
    -
    -/* This represents user that we are currently logged in as */
    -struct _NMUser
    -{
    - GCancellable *cancellable;
    -
    - char *name;
    -
    - NMSTATUS_T status;
    -
    - /* A copy of the login response fields */
    - NMField *fields;
    -
    - /* The user record for this user */
    - NMUserRecord *user_record;
    -
    - /* Our connection information */
    - NMConn *conn;
    -
    - /* This is the contact list */
    - NMFolder *root_folder;
    -
    - /* All contacts that we know about hashed by dn */
    - GHashTable *contacts;
    -
    - /* All user records hashed by dn */
    - GHashTable *user_records;
    -
    - /* DN lookup */
    - GHashTable *display_id_to_dn;
    -
    - /* One on one conversations indexed by recipient's dn */
    - GSList *conferences;
    -
    - guint32 conference_count;
    -
    - /* Called when we receive an event */
    - nm_event_cb evt_callback;
    -
    - /* Privacy settings */
    - gboolean privacy_locked;
    - gboolean default_deny;
    - GSList *allow_list;
    - GSList *deny_list;
    -
    - /* Generic pointer to data needed by the client
    - * using the API
    - */
    - gpointer client_data;
    -
    - /* Have the privacy lists been synched yet */
    - gboolean privacy_synched;
    -
    - /* Has the contact list been synched */
    - gboolean clist_synched;
    -};
    -
    -#define NM_STATUS_UNKNOWN 0
    -#define NM_STATUS_OFFLINE 1
    -#define NM_STATUS_AVAILABLE 2
    -#define NM_STATUS_BUSY 3
    -#define NM_STATUS_AWAY 4
    -#define NM_STATUS_AWAY_IDLE 5
    -#define NM_STATUS_INVALID 6
    -
    -#define NMERR_BASE 0x2000L
    -#define NM_OK 0L
    -#define NMERR_BAD_PARM (NMERR_BASE + 0x0001)
    -#define NMERR_TCP_WRITE (NMERR_BASE + 0x0002)
    -#define NMERR_TCP_READ (NMERR_BASE + 0x0003)
    -#define NMERR_PROTOCOL (NMERR_BASE + 0x0004)
    -#define NMERR_SERVER_REDIRECT (NMERR_BASE + 0x0005)
    -#define NMERR_CONFERENCE_NOT_FOUND (NMERR_BASE + 0x0006)
    -#define NMERR_CONFERENCE_NOT_INSTANTIATED (NMERR_BASE + 0x0007)
    -#define NMERR_FOLDER_EXISTS (NMERR_BASE + 0x0008)
    -
    -/* Errors that are returned from the server */
    -#define NMERR_SERVER_BASE 0xD100L
    -#define NMERR_ACCESS_DENIED (NMERR_SERVER_BASE + 0x0006)
    -#define NMERR_NOT_SUPPORTED (NMERR_SERVER_BASE + 0x000A)
    -#define NMERR_PASSWORD_EXPIRED (NMERR_SERVER_BASE + 0x000B)
    -#define NMERR_PASSWORD_INVALID (NMERR_SERVER_BASE + 0x000C)
    -#define NMERR_USER_NOT_FOUND (NMERR_SERVER_BASE + 0x000D)
    -#define NMERR_USER_DISABLED (NMERR_SERVER_BASE + 0x0010)
    -#define NMERR_DIRECTORY_FAILURE (NMERR_SERVER_BASE + 0x0011)
    -#define NMERR_HOST_NOT_FOUND (NMERR_SERVER_BASE + 0x0019)
    -#define NMERR_ADMIN_LOCKED (NMERR_SERVER_BASE + 0x001C)
    -#define NMERR_DUPLICATE_PARTICIPANT (NMERR_SERVER_BASE + 0x001F)
    -#define NMERR_SERVER_BUSY (NMERR_SERVER_BASE + 0x0023)
    -#define NMERR_OBJECT_NOT_FOUND (NMERR_SERVER_BASE + 0x0024)
    -#define NMERR_DIRECTORY_UPDATE (NMERR_SERVER_BASE + 0x0025)
    -#define NMERR_DUPLICATE_FOLDER (NMERR_SERVER_BASE + 0x0026)
    -#define NMERR_DUPLICATE_CONTACT (NMERR_SERVER_BASE + 0x0027)
    -#define NMERR_USER_NOT_ALLOWED (NMERR_SERVER_BASE + 0x0028)
    -#define NMERR_TOO_MANY_CONTACTS (NMERR_SERVER_BASE + 0x0029)
    -#define NMERR_CONFERENCE_NOT_FOUND_2 (NMERR_SERVER_BASE + 0x002B)
    -#define NMERR_TOO_MANY_FOLDERS (NMERR_SERVER_BASE + 0x002C)
    -#define NMERR_SERVER_PROTOCOL (NMERR_SERVER_BASE + 0x0030)
    -#define NMERR_CONVERSATION_INVITE (NMERR_SERVER_BASE + 0x0035)
    -#define NMERR_USER_BLOCKED (NMERR_SERVER_BASE + 0x0039)
    -#define NMERR_MASTER_ARCHIVE_MISSING (NMERR_SERVER_BASE + 0x003A)
    -#define NMERR_PASSWORD_EXPIRED_2 (NMERR_SERVER_BASE + 0x0042)
    -#define NMERR_CREDENTIALS_MISSING (NMERR_SERVER_BASE + 0x0046)
    -#define NMERR_AUTHENTICATION_FAILED (NMERR_SERVER_BASE + 0x0049)
    -#define NMERR_EVAL_CONNECTION_LIMIT (NMERR_SERVER_BASE + 0x004A)
    -
    -/**
    - * Initialize the user that we are going to login to the system as.
    - *
    - * @param name The userid of the user
    - * @param server IP Address of server
    - * @param port Port to connect to on the server
    - * @param data Client data to associate with the user
    - * @param event_callback Function to call when we receive an event
    - *
    - * @return The initialized user object. Must be freed by calling
    - * nm_deinitialize_user
    - */
    -NMUser *nm_initialize_user(const char *name, const char *server, int port,
    - gpointer data, nm_event_cb event_callback);
    -
    -
    -/**
    - * Free up resources associated with the user object.
    - *
    - * @param user The user to deinitialize
    - */
    -void nm_deinitialize_user(NMUser * user);
    -
    -/**
    - * Send a login request to the server.
    - *
    - * The response data sent to the callback will be NULL.
    - *
    - * @param user The User to login -- must be initialized
    - * @param pwd The password of the user
    - * @param my_addr The address of the client machine
    - * @param user_agent String describing the client (eg. "Purple/0.76 (Linux; 2.4.20)")
    - * @param callback Function to call when we get the response from the server
    - * @param data User defined data to be passed to the callback function
    - *
    - *
    - * @return NM_OK if login is sent successfully, error otherwise.
    - */
    -NMERR_T nm_send_login(NMUser * user, const char *pwd, const char *my_addr,
    - const char *user_agent, nm_response_cb callback,
    - gpointer data);
    -
    -/**
    - * Send a set status request to the server.
    - *
    - * The response data sent to the callback will be NULL.
    - *
    - * @param user The logged in User
    - * @param dn The DN of the user (if known, or NULL if not known)
    - * @param address IP Address of server
    - * @param callback Function to call when we get the response from the server
    - *
    - *
    - * @return NM_OK if successfully sent, error otherwise
    - */
    -NMERR_T nm_send_set_status(NMUser * user, int status, const char *text,
    - const char *auto_resp, nm_response_cb callback,
    - gpointer data);
    -
    -/**
    - * Send a create conference to the server.
    - *
    - * The response data sent to the callback will be NULL.
    - *
    - * @param user The logged in User
    - * @param conference Conference to create
    - * @param add_participants Add participant list on create?
    - * @param callback Function to call when we get the response from the server
    - * @param data User defined data to be passed to the callback function
    - *
    - * @return NM_OK if successfully sent, error otherwise
    - */
    -NMERR_T nm_send_create_conference(NMUser * user, NMConference * conference,
    - nm_response_cb callback, gpointer data);
    -
    -/**
    - * Tell server we have left the conference.
    - *
    - * The response data sent to the callback will be NULL.
    - *
    - * @param user The logged in User
    - * @param conference Conference the user is leaving
    - * @param callback Function to call when we get the response from the server
    - * @param data User defined data to be passed to the callback function
    - *
    - * @return NM_OK if successfully sent, error otherwise
    - */
    -NMERR_T nm_send_leave_conference(NMUser * user, NMConference * conference,
    - nm_response_cb callback, gpointer data);
    -
    -/**
    - * Send a join conference request to the server.
    - *
    - * The response data sent to the callback will be NULL.
    - *
    - * @param user The logged in User
    - * @param conference Conference the user is joining
    - * @param callback Function to call when we get the response from the server
    - * @param data User defined data to be passed to the callback function
    - *
    - *
    - * @return NM_OK if successfully sent, error otherwise
    - */
    -NMERR_T nm_send_join_conference(NMUser * user, NMConference * conference,
    - nm_response_cb callback, gpointer data);
    -
    -/**
    - * Send a conference reject request to the server.
    - *
    - * The response data sent to the callback will be NULL.
    - *
    - * @param user The logged in User
    - * @param conference Conference the user is rejecting
    - * @param callback Function to call when we get the response from the server
    - * @param data User defined data to be passed to the callback function
    - *
    - *
    - * @return NM_OK if successfully sent, error otherwise
    - */
    -NMERR_T nm_send_reject_conference(NMUser * user, NMConference * conference,
    - nm_response_cb callback, gpointer data);
    -
    -
    -/**
    - * Send a conference invitation to the server.
    - *
    - * The response data sent to the callback will be NULL.
    - *
    - * @param user The logged in User
    - * @param conference Conference the user is rejecting
    - * @param user_record The user to invite
    - * @param message The invite message if there is one, NULL otherwise
    - * @param callback Function to call when we get the response from the server
    - * @param data User defined data to be passed to the callback function
    - *
    - *
    - * @return NM_OK if successfully sent, error otherwise
    - */
    -NMERR_T nm_send_conference_invite(NMUser *user, NMConference *conference, NMUserRecord *user_record,
    - const char *message, nm_response_cb callback, gpointer data);
    -
    -/**
    - * Get details for a more than one user from the server.
    - *
    - * The response data sent to the callback will be an NMUserRecord which should be
    - * freed with nm_release_user_record
    - *
    - * @param user The logged in User
    - * @param names Link list of user id's or dn's
    - * @param callback Function to call when we get the response from the server
    - * @param data User defined data to be passed to the callback function
    - *
    - * @return NM_OK if successfully sent, error otherwise
    - */
    -NMERR_T nm_send_multiple_get_details(NMUser * user, GSList *names,
    - nm_response_cb callback, gpointer data);
    -
    -/**
    - * Get details for a user from the server.
    - *
    - * The response data sent to the callback will be an NMUserRecord which should be
    - * freed with nm_release_user_record
    - *
    - * @param user The logged in User
    - * @param name Userid or DN of the user to look up
    - * @param callback Function to call when we get the response from the server
    - * @param data User defined data to be passed to the callback function
    - *
    - * @return NM_OK if successfully sent, error otherwise
    - */
    -NMERR_T nm_send_get_details(NMUser * user, const char *name,
    - nm_response_cb callback, gpointer data);
    -
    -/**
    - * Send a message.
    - *
    - * The response data sent to the callback will be NULL.
    - *
    - * @param user The logged in User
    - * @param message The message to send.
    - * @param callback Function to call when we get the response from the server
    - *
    - * @return NM_OK if successfully sent, error otherwise
    - */
    -NMERR_T nm_send_message(NMUser * user, NMMessage * message,
    - nm_response_cb callback);
    -
    -/**
    - * Sends a typing event to the server.
    - *
    - * The response data sent to the callback will be NULL.
    - *
    - * @param user The logged in User
    - * @param conf The conference that corresponds to the typing event
    - * @param typing TRUE if the user is typing
    - * FALSE if the user has stopped typing
    - * @param callback Function to call when we get the response from the server
    - *
    - * @return NM_OK if successfully sent, error otherwise
    - */
    -NMERR_T nm_send_typing(NMUser * user, NMConference * conf,
    - gboolean typing, nm_response_cb callback);
    -
    -/**
    - * Send a create contact request to the server.
    - *
    - * The given folder should already exist on the server. If not,
    - * the call will fail.
    - *
    - * The response data sent to the callback will be a NMContact which should
    - * be released with nm_release_contact
    - *
    - * @param user The logged in User
    - * @param folder The folder that the contact should be created in
    - * @param contact The contact to add
    - * @param callback Function to call when we get the response from the server
    - * @param data User defined data
    - *
    - * @return NM_OK if successfully sent, error otherwise
    - */
    -NMERR_T nm_send_create_contact(NMUser * user, NMFolder * folder,
    - NMContact * contact, nm_response_cb callback,
    - gpointer data);
    -
    -/**
    - * Send a remove contact request to the server.
    - *
    - * The response data sent to the callback will be NULL.
    - *
    - * @param user The logged in User
    - * @param folder The folder to remove contact from
    - * @param contact The contact to remove
    - * @param callback Function to call when we get the response from the server
    - * @param data User defined data
    - *
    - * @return NM_OK if successfully sent, error otherwise
    - */
    -NMERR_T nm_send_remove_contact(NMUser * user, NMFolder * folder,
    - NMContact * contact, nm_response_cb callback,
    - gpointer data);
    -
    -/**
    - * Send a create folder request to the server.
    - *
    - * The response data sent to the callback will be a NMFolder which should be
    - * released with nm_release_folder
    - *
    - * @param user The logged in User
    - * @param name The name of the folder to create
    - * @param callback Function to call when we get the response from the server
    - * @param data User defined data
    - *
    - * @return NM_OK if successfully sent, error otherwise
    - */
    -NMERR_T nm_send_create_folder(NMUser * user, const char *name,
    - nm_response_cb callback, gpointer data);
    -
    -/**
    - * Send a delete folder request to the server.
    - *
    - * The response data sent to the callback will be NULL.
    - *
    - * @param user The logged in User
    - * @param folder The name of the folder to remove
    - * @param callback Function to call when we get the response from the server
    - * @param data User defined data
    - *
    - * @return NM_OK if successfully sent, error otherwise
    - */
    -NMERR_T nm_send_remove_folder(NMUser * user, NMFolder * folder,
    - nm_response_cb callback, gpointer data);
    -
    -/**
    - * Send a rename contact request to the server.
    - *
    - * The response data sent to the callback will be NULL.
    - *
    - * @param user The logged in User
    - * @param contact The contact to rename
    - * @param new_name The new display name for the contact
    - * @param callback Function to call when we get the response from the server
    - * @param data User defined data
    - *
    - * @return NM_OK if successfully sent, error otherwise
    - */
    -NMERR_T nm_send_rename_contact(NMUser * user, NMContact * contact,
    - const char *new_name, nm_response_cb callback,
    - gpointer data);
    -
    -/**
    - * Send a rename folder request to the server.
    - *
    - * The response data sent to the callback will be NULL.
    - *
    - * @param user The logged in User
    - * @param folder The folder to rename
    - * @param new_name The new name of the folder
    - * @param callback Function to call when we get the response from the server
    - * @param data User defined data
    - *
    - * @return NM_OK if successfully sent, error otherwise
    - */
    -NMERR_T nm_send_rename_folder(NMUser * user, NMFolder * folder,
    - const char *new_name, nm_response_cb callback,
    - gpointer data);
    -
    -/**
    - * Send a move contact request to the server.
    - *
    - * The response data sent to the callback will be NULL.
    - *
    - * @param user The logged in User
    - * @param contact The contact to move
    - * @param folder The folder to move the contact to
    - * @param callback Function to call when we get the response from the server
    - * @param data User defined data
    - *
    - * @return NM_OK if successfully sent, error otherwise
    - */
    -NMERR_T nm_send_move_contact(NMUser * user, NMContact * contact,
    - NMFolder * folder, nm_response_cb callback,
    - gpointer data);
    -
    -/**
    - * Send a get status request to the server.
    - *
    - * The response data sent to the callback will be a NMUserRecord.
    - *
    - * @param user The logged in User
    - * @param contact The contact to move
    - * @param folder The folder to move the contact to
    - * @param callback Function to call when we get the response from the server
    - * @param data User defined data
    - *
    - * @return NM_OK if successfully sent, error otherwise
    - */
    -NMERR_T nm_send_get_status(NMUser * user, NMUserRecord * user_record,
    - nm_response_cb callback, gpointer data);
    -
    -/**
    - * Send a request to add an item to the allow or deny list.
    - *
    - * @param user The logged in User
    - * @param who The userid or DN of the user to add to list
    - * @param allow_list TRUE if adding to allow list, FALSE if adding to deny list
    - * @param callback Function to call when we get the response from the server
    - * @param data User defined data
    - *
    - * @return NM_OK if successfully sent, error otherwise
    - */
    -NMERR_T
    -nm_send_create_privacy_item(NMUser *user, const char *who, gboolean is_allowed,
    - nm_response_cb callback, gpointer data);
    -
    -/**
    - * Send a request to remove an item from the allow or deny list.
    - *
    - * @param user The logged in User
    - * @param who The userid or DN of the user to add to list
    - * @param allow_list TRUE if removing from allow list, FALSE if removing from deny list
    - * @param callback Function to call when we get the response from the server
    - * @param data User defined data
    - *
    - * @return NM_OK if successfully sent, error otherwise
    - */
    -NMERR_T
    -nm_send_remove_privacy_item(NMUser *user, const char *dn, gboolean allow_list,
    - nm_response_cb callback, gpointer data);
    -
    -/**
    - * Send a request to change the default privacy setting to deny all or allow all
    - *
    - * @param user The logged in User
    - * @param default_deny TRUE if default should be changed to deny all
    - * @param callback Function to call when we get the response from the server
    - * @param data User defined data
    - *
    - * @return NM_OK if successfully sent, error otherwise
    - */
    -NMERR_T
    -nm_send_set_privacy_default(NMUser *user, gboolean default_deny,
    - nm_response_cb callback, gpointer data);
    -
    -/**
    - * Send a ping to the server
    - *
    - * @param user The logged in User
    - * @param callback Function to call when we get the response from the server
    - * @param data User defined data
    - *
    - * @return NM_OK if successfully sent, error otherwise
    - */
    -NMERR_T
    -nm_send_keepalive(NMUser *user, nm_response_cb callback, gpointer data);
    -
    -/**
    - * Reads a response/event from the server and processes it.
    - *
    - * @param user The logged in User
    - */
    -NMERR_T nm_process_new_data(NMUser * user);
    -
    -/**
    - * Return the root folder of the contact list
    - *
    - * @param user The logged in User
    - *
    - * @return Root folder
    - */
    -NMFolder *nm_get_root_folder(NMUser * user);
    -
    -/**
    - * Create the contact list based on the login fields
    - *
    - * @param user The logged in User
    - *
    - */
    -NMERR_T nm_create_contact_list(NMUser * user);
    -
    -void nm_destroy_contact_list(NMUser * user);
    -
    -void nm_user_add_contact(NMUser * user, NMContact * contact);
    -
    -void nm_user_add_user_record(NMUser * user, NMUserRecord * user_record);
    -
    -NMContact *nm_find_contact(NMUser * user, const char *dn);
    -
    -GList *nm_find_contacts(NMUser * user, const char *dn);
    -
    -NMUserRecord *nm_find_user_record(NMUser * user, const char *dn);
    -
    -NMFolder *nm_find_folder(NMUser * user, const char *name);
    -
    -NMFolder *nm_find_folder_by_id(NMUser * user, int object_id);
    -
    -NMConference *nm_find_conversation(NMUser * user, const char *who);
    -
    -void nm_conference_list_add(NMUser * user, NMConference * conf);
    -
    -void nm_conference_list_remove(NMUser * user, NMConference * conf);
    -
    -void nm_conference_list_free(NMUser * user);
    -
    -NMConference *nm_conference_list_find(NMUser * user, const char *guid);
    -
    -const char *nm_lookup_dn(NMUser * user, const char *display_id);
    -
    -nm_event_cb nm_user_get_event_callback(NMUser * user);
    -
    -NMConn *nm_user_get_conn(NMUser * user);
    -
    -gboolean nm_user_is_privacy_locked(NMUser *user);
    -
    -/** Some utility functions **/
    -
    -/**
    - * Check to see if the conference GUIDs are equivalent.
    - *
    - * @param guid1 First guid to compare
    - * @param guid2 Second guid to compare
    - *
    - * @return TRUE if conference GUIDs are equivalent, FALSE otherwise.
    - *
    - */
    -gboolean nm_are_guids_equal(const char *guid1, const char *guid2);
    -
    -/**
    - * Compare UTF8 strings for equality only (case insensitive)
    - *
    - * @param guid1 First string to compare
    - * @param guid2 Second string to compare
    - *
    - * @return TRUE if strings are equal, FALSE otherwise
    - *
    - */
    -gboolean nm_utf8_str_equal(gconstpointer str1, gconstpointer str2);
    -
    -/**
    - * Convert a fully typed LDAP DN to dotted, untype notation
    - * e.g. cn=mike,o=novell -> mike.novell
    - *
    - * @param typed Fully typed dn
    - *
    - * @return Dotted equivalent of typed (must be freed).
    - *
    - */
    -char *nm_typed_to_dotted(const char *typed);
    -
    -/**
    - * Return a string representation of the error code.
    - *
    - * @param error NMERR_T to convert to string
    - *
    - * @return String representation.
    - */
    -const char *nm_error_to_string (NMERR_T err);
    -
    -#endif /* PURPLE_NOVELL_NMUSER_H */
    --- a/libpurple/protocols/novell/nmuserrecord.c Fri Nov 11 19:39:24 2022 -0600
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,474 +0,0 @@
    -/*
    - * nmuserrecord.c
    - *
    - * Copyright (c) 2004 Novell, Inc. All Rights Reserved.
    - *
    - * 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; version 2 of the License.
    - *
    - * 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, write to the Free Software
    - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    - *
    - */
    -
    -#include <glib.h>
    -#include <string.h>
    -#include "nmuserrecord.h"
    -#include "nmfield.h"
    -#include "nmuser.h"
    -
    -struct _NMUserRecord
    -{
    - NMSTATUS_T status;
    - char *status_text;
    - char *dn;
    - char *cn;
    - char *display_id;
    - char *fname;
    - char *lname;
    - char *full_name;
    - NMField *fields;
    - gboolean auth_attr;
    - gpointer data;
    - int ref_count;
    -};
    -
    -static int count = 0;
    -
    -/* API functions */
    -
    -NMUserRecord *
    -nm_create_user_record(void)
    -{
    - NMUserRecord *user_record = g_new0(NMUserRecord, 1);
    -
    - user_record->ref_count = 1;
    -
    - purple_debug_info("novell", "Creating user_record, total=%d", count++);
    -
    - return user_record;
    -}
    -
    -static char *
    -_get_attribute_value(NMField *field)
    -{
    - char *value = NULL;
    -
    - if (field->ptr_value == NULL)
    - return NULL;
    -
    - if (field->type == NMFIELD_TYPE_UTF8 || field->type == NMFIELD_TYPE_DN) {
    -
    - value = (char *)field->ptr_value;
    -
    - } else if (field->type == NMFIELD_TYPE_MV) {
    -
    - /* Need to handle multi-valued returns, for now
    - * just pick the first value and return it
    - */
    - NMField *tmp = (NMField *)field->ptr_value;
    - if (tmp->type == NMFIELD_TYPE_UTF8 || tmp->type == NMFIELD_TYPE_DN) {
    - value = (char *)tmp->ptr_value;
    - } else {
    - return NULL;
    - }
    -
    - } else {
    - return NULL;
    - }
    -
    - return g_strdup(value);
    -}
    -/*
    - * This creates a user_record for the reference list the
    - * field array that is passed in should be a
    - * NM_A_FA_USER_DETAILS array.
    - */
    -NMUserRecord *
    -nm_create_user_record_from_fields(NMField * details)
    -{
    - NMUserRecord *user_record;
    - NMField *field, *fields = details;
    -
    - if (details == NULL) {
    - return NULL;
    - }
    -
    - if (details->type == NMFIELD_TYPE_ARRAY) {
    - if (details->ptr_value == NULL)
    - return NULL;
    - fields = (NMField *) details->ptr_value;
    - }
    -
    - user_record = nm_create_user_record();
    -
    - if ((field = nm_locate_field(NM_A_SZ_AUTH_ATTRIBUTE, fields))) {
    -
    - if (field->ptr_value) {
    - user_record->display_id = _get_attribute_value(field);
    - user_record->auth_attr = TRUE;
    - }
    - }
    -
    - if ((field = nm_locate_field(NM_A_SZ_DN, fields))) {
    -
    - if (field->ptr_value) {
    - user_record->dn = _get_attribute_value(field);
    - }
    - }
    -
    - if ((field = nm_locate_field("CN", fields))) {
    -
    - if (field->ptr_value) {
    - user_record->cn = _get_attribute_value(field);
    - }
    - }
    -
    - if ((field = nm_locate_field("Given Name", fields))) {
    -
    - if (field->ptr_value) {
    - user_record->fname = _get_attribute_value(field);
    - }
    - }
    -
    - if ((field = nm_locate_field("Surname", fields))) {
    -
    - if (field->ptr_value) {
    - user_record->lname = _get_attribute_value(field);
    - }
    - }
    -
    - if ((field = nm_locate_field("Full Name", fields))) {
    -
    - if (field->ptr_value) {
    - user_record->full_name = _get_attribute_value(field);
    - }
    - }
    -
    - if ((field = nm_locate_field(NM_A_SZ_STATUS, fields))) {
    -
    - if (field->ptr_value)
    - user_record->status = atoi((char *) field->ptr_value);
    -
    - }
    -
    - if ((field = nm_locate_field(NM_A_SZ_MESSAGE_BODY, fields))) {
    -
    - if (field->ptr_value)
    - user_record->status_text = g_strdup((char *) field->ptr_value);
    -
    - }
    -
    - user_record->fields = nm_copy_field_array(fields);
    -
    - return user_record;
    -}
    -
    -void
    -nm_user_record_copy(NMUserRecord * dest, NMUserRecord * src)
    -{
    - if (dest == NULL || src == NULL)
    - return;
    -
    - dest->status = src->status;
    -
    - /* Copy status text */
    - g_free(dest->status_text);
    - dest->status_text = NULL;
    -
    - if (src->status_text)
    - dest->status_text = g_strdup(src->status_text);
    -
    - /* Copy DN */
    - g_free(dest->dn);
    - dest->dn = NULL;
    -
    - if (src->dn)
    - dest->dn = g_strdup(src->dn);
    -
    - /* Copy CN */
    - g_free(dest->cn);
    - dest->cn = NULL;
    -
    - if (src->cn)
    - dest->cn = g_strdup(src->cn);
    -
    - /* Copy display id */
    - g_free(dest->display_id);
    - dest->display_id = NULL;
    -
    - if (src->display_id)
    - dest->display_id = g_strdup(src->display_id);
    -
    - /* Copy first name */
    - g_free(dest->fname);
    - dest->fname = NULL;
    -
    - if (src->fname)
    - dest->fname = g_strdup(src->fname);
    -
    - /* Copy last name */
    - g_free(dest->lname);
    - dest->lname = NULL;
    -
    - if (src->lname)
    - dest->lname = g_strdup(src->lname);
    -
    - /* Copy full name */
    - g_free(dest->full_name);
    - dest->full_name = NULL;
    -
    - if (src->full_name)
    - dest->full_name = g_strdup(src->full_name);
    -
    - /* Copy fields */
    - if (src->fields) {
    -
    - if (dest->fields) {
    - nm_free_fields(&dest->fields);
    - }
    -
    - dest->fields = nm_copy_field_array(src->fields);
    - }
    -
    - /* Copy data */
    - dest->data = src->data;
    -}
    -
    -void
    -nm_user_record_add_ref(NMUserRecord * user_record)
    -{
    - if (user_record)
    - user_record->ref_count++;
    -}
    -
    -void
    -nm_release_user_record(NMUserRecord * user_record)
    -{
    - if (--(user_record->ref_count) == 0) {
    - purple_debug_info("novell", "Releasing user_record, total=%d", --count);
    -
    - g_free(user_record->dn);
    - g_free(user_record->cn);
    - g_free(user_record->display_id);
    - g_free(user_record->fname);
    - g_free(user_record->lname);
    - g_free(user_record->full_name);
    - g_free(user_record->status_text);
    -
    - nm_free_fields(&user_record->fields);
    -
    - g_free(user_record);
    - }
    -}
    -
    -/* UserRecord API */
    -
    -NMSTATUS_T
    -nm_user_record_get_status(NMUserRecord * user_record)
    -{
    - if (user_record == NULL)
    - return (NMSTATUS_T) - 1;
    -
    - return user_record->status;
    -
    -}
    -
    -const char *
    -nm_user_record_get_status_text(NMUserRecord * user_record)
    -{
    - if (user_record == NULL)
    - return NULL;
    -
    - return user_record->status_text;
    -}
    -
    -void
    -nm_user_record_set_dn(NMUserRecord * user_record, const char *dn)
    -{
    - if (user_record != NULL && dn != NULL) {
    - g_free(user_record->dn);
    -
    - user_record->dn = g_strdup(dn);
    - }
    -}
    -
    -const char *
    -nm_user_record_get_dn(NMUserRecord * user_record)
    -{
    - if (user_record == NULL)
    - return NULL;
    -
    - return user_record->dn;
    -}
    -
    -void
    -nm_user_record_set_userid(NMUserRecord * user_record, const char *userid)
    -{
    - if (user_record != NULL && userid != NULL) {
    - g_free(user_record->cn);
    -
    - user_record->cn = g_strdup(userid);
    - }
    -}
    -
    -const char *
    -nm_user_record_get_userid(NMUserRecord * user_record)
    -{
    - if (user_record == NULL)
    - return NULL;
    -
    - return user_record->cn;
    -}
    -
    -void
    -nm_user_record_set_display_id(NMUserRecord * user_record, const char *display_id)
    -{
    - if (user_record != NULL && display_id != NULL) {
    - g_free(user_record->display_id);
    -
    - user_record->display_id = g_strdup(display_id);
    - }
    -}
    -
    -const char *
    -nm_user_record_get_display_id(NMUserRecord * user_record)
    -{
    - if (user_record == NULL)
    - return NULL;
    -
    - if (user_record->display_id == NULL) {
    - user_record->display_id = nm_typed_to_dotted(user_record->dn);
    - }
    -
    - return user_record->display_id;
    -}
    -
    -const char *
    -nm_user_record_get_full_name(NMUserRecord * user_record)
    -{
    - if (user_record == NULL)
    - return NULL;
    -
    - if (user_record->full_name == NULL) {
    - if (user_record->fname && user_record->lname) {
    - user_record->full_name = g_strdup_printf("%s %s",
    - user_record->fname,
    - user_record->lname);
    -
    - }
    - }
    -
    - return user_record->full_name;
    -}
    -
    -const char *
    -nm_user_record_get_first_name(NMUserRecord * user_record)
    -{
    - if (user_record == NULL)
    - return NULL;
    -
    - return user_record->fname;
    -
    -}
    -
    -const char *
    -nm_user_record_get_last_name(NMUserRecord * user_record)
    -{
    - if (user_record == NULL)
    - return NULL;
    -
    - return user_record->lname;
    -}
    -
    -gpointer
    -nm_user_record_get_data(NMUserRecord * user_record)
    -{
    - if (user_record == NULL)
    - return NULL;
    -
    - return user_record->data;
    -}
    -
    -void
    -nm_user_record_set_data(NMUserRecord * user_record, gpointer data)
    -{
    - if (user_record == NULL)
    - return;
    -
    - user_record->data = data;
    -}
    -
    -void
    -nm_user_record_set_status(NMUserRecord * user_record,
    - int status, const char *text)
    -{
    - if (user_record == NULL)
    - return;
    -
    - user_record->status = status;
    -
    - g_free(user_record->status_text);
    - user_record->status_text = NULL;
    -
    - if (text)
    - user_record->status_text = g_strdup(text);
    -}
    -
    -gboolean
    -nm_user_record_get_auth_attr(NMUserRecord *user_record)
    -{
    - if (user_record == NULL)
    - return FALSE;
    -
    - return user_record->auth_attr;
    -}
    -
    -int
    -nm_user_record_get_property_count(NMUserRecord * user_record)
    -{
    - NMField *locate, *fields;
    -
    - int count = 0;
    -
    - if (user_record && user_record->fields) {
    - locate = nm_locate_field(NM_A_FA_INFO_DISPLAY_ARRAY,
    - (NMField *) user_record->fields);
    - if (locate && (fields = (NMField *) (locate->ptr_value))) {
    - count = (int) nm_count_fields(fields);
    - }
    - }
    - return count;
    -}
    -
    -PurpleKeyValuePair *
    -nm_user_record_get_property(NMUserRecord * user_record, int index)
    -{
    - PurpleKeyValuePair *property = NULL;
    - NMField *field = NULL, *fields, *locate;
    -
    - if (user_record && user_record->fields) {
    - locate = nm_locate_field(NM_A_FA_INFO_DISPLAY_ARRAY,
    - (NMField *) user_record->fields);
    - if (locate && (fields = (NMField *) (locate->ptr_value))) {
    - int max = nm_count_fields(fields);
    -
    - if (index < max) {
    - field = &fields[index];
    - if (field && field->tag && field->ptr_value) {
    - property = purple_key_value_pair_new_full(
    - field->tag, _get_attribute_value(field), g_free);
    - }
    - }
    - }
    - }
    -
    - return property;
    -}
    --- a/libpurple/protocols/novell/nmuserrecord.h Fri Nov 11 19:39:24 2022 -0600
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,246 +0,0 @@
    -/*
    - * nmuserrecord.h
    - *
    - * Copyright (c) 2004 Novell, Inc. All Rights Reserved.
    - *
    - * 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; version 2 of the License.
    - *
    - * 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, write to the Free Software
    - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    - *
    - */
    -
    -#ifndef PURPLE_NOVELL_NMUSERRECORD_H
    -#define PURPLE_NOVELL_NMUSERRECORD_H
    -
    -#include <glib.h>
    -
    -typedef struct _NMUserRecord NMUserRecord;
    -
    -#include "nmfield.h"
    -#include "nmuser.h"
    -
    -/**
    - * Creates an NMUserRecord
    - *
    - * The NMUserRecord should be released by calling
    - * nm_release_user_record
    - *
    - * @return The new user record
    - *
    - */
    -NMUserRecord *nm_create_user_record(void);
    -
    -/**
    - * Creates an NMUserRecord
    - *
    - * The NMUserRecord should be released by calling
    - * nm_release_user_record
    - *
    - * @param details Should be a NM_A_FA_USER_DETAILS
    - *
    - *
    - * @return The new user record
    - *
    - */
    -NMUserRecord *nm_create_user_record_from_fields(NMField * details);
    -
    -/**
    - * Add a reference to an existing user_record
    - *
    - * The reference should be released by calling
    - * nm_release_user_record
    - *
    - * @param user_record The contact to addref
    - *
    - */
    -void nm_user_record_add_ref(NMUserRecord * user_record);
    -
    -/**
    - * Release a reference to the user record
    - *
    - * @param user_record The user record
    - *
    - */
    -void nm_release_user_record(NMUserRecord * user_record);
    -
    -/**
    - * Set the status for the user record
    - *
    - * @param user_record The user record
    - * @param status The status for the user
    - * @param text The status text for the user
    - *
    - */
    -void nm_user_record_set_status(NMUserRecord * user_record, NMSTATUS_T status,
    - const char *text);
    -
    -/**
    - * Get the status for the user record
    - *
    - * @param user_record The user record
    - *
    - * @return The status for the user record
    - */
    -NMSTATUS_T nm_user_record_get_status(NMUserRecord * user_record);
    -
    -/**
    - * Get the status text for the user record
    - *
    - * @param user_record The user record
    - *
    - * @return The status text if there is any, NULL otherwise
    - *
    - */
    -const char *nm_user_record_get_status_text(NMUserRecord * user_record);
    -
    -/**
    - * Set the DN for the user record
    - *
    - * @param user_record The user record
    - * @param dn The new DN for the user record
    - *
    - */
    -void nm_user_record_set_dn(NMUserRecord * user_record, const char *dn);
    -
    -/**
    - * Get the DN for the user record
    - *
    - * @param user_record The user record
    - *
    - * @return The DN for the user record
    - */
    -const char *nm_user_record_get_dn(NMUserRecord * user_record);
    -
    -/**
    - * Set the user id for the
    - *
    - * @param user_record The user record
    - * @param userid The userid (CN) for the user record
    - *
    - */
    -void nm_user_record_set_userid(NMUserRecord * user_record, const char *userid);
    -
    -/**
    - * Get the user id for the user record
    - *
    - * @param user_record The user record
    - *
    - * @return The user id for the user record
    - */
    -const char *nm_user_record_get_userid(NMUserRecord * user_record);
    -
    -/**
    - * Set the display id for the user record
    - *
    - * @param user_record The user record
    - * @param display_id The new display id for the user
    - *
    - */
    -void nm_user_record_set_display_id(NMUserRecord * user_record,
    - const char *display_id);
    -
    -/**
    - * Get the display id for the user record
    - *
    - * @param user_record The user record
    - *
    - * @return The display id for the user record
    - */
    -const char *nm_user_record_get_display_id(NMUserRecord * user_record);
    -
    -/**
    - * Return whether or not the display id is an auth attribute or not.
    - *
    - * @param user_record The user record
    - *
    - * @return TRUE if display_id is an auth attribute, FALSE otherwise.
    - */
    -gboolean
    -nm_user_record_get_auth_attr(NMUserRecord *user_record);
    -
    -/**
    - * Get the full name for the user record
    - *
    - * @param user_record The user record
    - *
    - * @return The full name for the user
    - */
    -const char *nm_user_record_get_full_name(NMUserRecord * user_record);
    -
    -/**
    - * Get the first name for the user record
    - *
    - * @param user_record The user record
    - *
    - * @return The first name for the user
    - */
    -const char *nm_user_record_get_first_name(NMUserRecord * user_record);
    -
    -/**
    - * Get the last name for the user record
    - *
    - * @param user_record The user record
    - *
    - * @return The last name for the user
    - */
    -const char *nm_user_record_get_last_name(NMUserRecord * user_record);
    -
    -/**
    - * Set the user defined data for the user record
    - *
    - * @param user_record The user record
    - * @param data The user defined data for the user record
    - *
    - */
    -void nm_user_record_set_data(NMUserRecord * user_record, gpointer data);
    -
    -/**
    - * Get the user defined data for the user record
    - *
    - * @param user_record The user record
    - *
    - * @return The user defined data for the user record
    - */
    -gpointer nm_user_record_get_data(NMUserRecord * user_record);
    -
    -/**
    - * Get the property count for the user record
    - *
    - * @param user_record The user record
    - *
    - * @return The number of information properties for the user record
    - *
    - */
    -int nm_user_record_get_property_count(NMUserRecord * user_record);
    -
    -/**
    - * Get an info property for the user record. The property must be released
    - * by calling purple_key_value_pair_free()
    - *
    - * @param user_record The user record
    - * @param index The index of the property to get (zero based)
    - *
    - * @return The property
    - */
    -PurpleKeyValuePair *nm_user_record_get_property(NMUserRecord * user_record, int index);
    -
    -/**
    - * Copy a user record (deep copy). The dest user record must have already been
    - * created (nm_create_user_record)
    - *
    - * @param dest The destination of the copy
    - * @param src The source of the copy
    - *
    - */
    -void nm_user_record_copy(NMUserRecord * dest, NMUserRecord * src);
    -
    -#endif /* PURPLE_NOVELL_NMUSERRECORD_H */
    --- a/libpurple/protocols/novell/novell.c Fri Nov 11 19:39:24 2022 -0600
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,3686 +0,0 @@
    -/*
    - * novell.c
    - *
    - * Copyright (c) 2004 Novell, Inc. All Rights Reserved.
    - *
    - * 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; version 2 of the License.
    - *
    - * 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, write to the Free Software
    - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    - *
    - */
    -
    -#include <glib/gi18n-lib.h>
    -
    -#ifndef _WIN32
    -# include <sys/utsname.h>
    -#endif
    -
    -#include <gplugin.h>
    -#include <gplugin-native.h>
    -
    -#include <purple.h>
    -
    -#include "nmuser.h"
    -#include "novell.h"
    -
    -#define DEFAULT_PORT 8300
    -#define NOVELL_CONNECT_STEPS 4
    -#define NM_ROOT_FOLDER_NAME "GroupWise Messenger"
    -
    -#define NOVELL_STATUS_TYPE_AVAILABLE "available"
    -#define NOVELL_STATUS_TYPE_AWAY "away"
    -#define NOVELL_STATUS_TYPE_BUSY "busy"
    -#define NOVELL_STATUS_TYPE_OFFLINE "offline"
    -#define NOVELL_STATUS_TYPE_IDLE "idle"
    -#define NOVELL_STATUS_TYPE_APPEAR_OFFLINE "appearoffline"
    -
    -struct _NovellProtocol {
    - PurpleProtocol parent;
    -};
    -
    -static PurpleProtocol *my_protocol = NULL;
    -
    -static gboolean
    -_is_disconnect_error(NMERR_T err);
    -
    -static gboolean
    -_check_for_disconnect(NMUser * user, NMERR_T err);
    -
    -static void
    -_send_message(NMUser * user, NMMessage * message);
    -
    -static void
    -_update_buddy_status(NMUser *user, PurpleBuddy * buddy, int status, int gmt);
    -
    -static void
    -_remove_purple_buddies(NMUser * user);
    -
    -static void
    -_add_contacts_to_purple_blist(NMUser * user, NMFolder * folder);
    -
    -static void
    -_add_purple_buddies(NMUser * user);
    -
    -static void
    -_sync_contact_list(NMUser *user);
    -
    -static void
    -_sync_privacy_lists(NMUser *user);
    -
    -static void
    -_show_info(PurpleConnection * gc, NMUserRecord * user_record, char * name);
    -
    -const char *
    -_get_conference_name(int id);
    -
    -/*******************************************************************************
    - * Response callbacks
    - *******************************************************************************/
    -
    -/* Handle login response */
    -static void
    -_login_resp_cb(NMUser * user, NMERR_T ret_code,
    - gpointer resp_data, gpointer user_data)
    -{
    - PurpleConnection *gc;
    - const char *alias;
    - NMERR_T rc;
    -
    - if (user == NULL)
    - return;
    -
    - gc = purple_account_get_connection(user->client_data);
    - if (gc == NULL)
    - return;
    -
    - if (ret_code == NM_OK) {
    -
    - /* Set alias for user if not set (use Full Name) */
    - alias = purple_account_get_private_alias(user->client_data);
    - if (alias == NULL || *alias == '\0') {
    - alias = nm_user_record_get_full_name(user->user_record);
    -
    - if (alias)
    - purple_account_set_private_alias(user->client_data, alias);
    - }
    -
    - /* Tell Purple that we are connected */
    - purple_connection_set_state(gc, PURPLE_CONNECTION_STATE_CONNECTED);
    -
    - _sync_contact_list(user);
    -
    - rc = nm_send_set_status(user, NM_STATUS_AVAILABLE, NULL, NULL, NULL,
    - NULL);
    - _check_for_disconnect(user, rc);
    -
    - } else {
    - PurpleConnectionError reason;
    - char *err = g_strdup_printf(_("Unable to login: %s"),
    - nm_error_to_string (ret_code));
    -
    - switch (ret_code) {
    - case NMERR_AUTHENTICATION_FAILED:
    - case NMERR_CREDENTIALS_MISSING:
    - case NMERR_PASSWORD_INVALID:
    - /* Don't attempt to auto-reconnect if our
    - * password was invalid.
    - */
    - if (!purple_account_get_remember_password(purple_connection_get_account(gc))) {
    - PurpleAccount *account = NULL;
    - PurpleCredentialManager *manager = NULL;
    -
    - account = purple_connection_get_account(gc);
    - manager = purple_credential_manager_get_default();
    -
    - purple_credential_manager_clear_password_async(manager,
    - account,
    - NULL, NULL,
    - NULL);
    - }
    - reason = PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED;
    - break;
    - default:
    - /* FIXME: There are other reasons login could fail */
    - reason = PURPLE_CONNECTION_ERROR_NETWORK_ERROR;
    - }
    -
    - purple_connection_error(gc, reason, err);
    - g_free(err);
    - }
    -}
    -
    -/* Handle getstatus response*/
    -static void
    -_get_status_resp_cb(NMUser * user, NMERR_T ret_code,
    - gpointer resp_data, gpointer user_data)
    -{
    - PurpleBuddy *buddy;
    - GSList *buddies;
    - GSList *bnode;
    - NMUserRecord *user_record = (NMUserRecord *) resp_data;
    - int status;
    -
    - if (user == NULL || user_record == NULL)
    - return;
    -
    - if (ret_code == NM_OK) {
    -
    - /* Find all Purple buddies and update their statuses */
    - const char *name = nm_user_record_get_display_id(user_record);
    -
    - if (name) {
    - buddies = purple_blist_find_buddies((PurpleAccount *) user->client_data, name);
    - for (bnode = buddies; bnode; bnode = bnode->next) {
    - buddy = (PurpleBuddy *) bnode->data;
    - if (buddy) {
    - status = nm_user_record_get_status(user_record);
    - _update_buddy_status(user, buddy, status, time(0));
    - }
    - }
    - g_slist_free(buddies);
    - }
    -
    - } else {
    - purple_debug_info("novell", "_get_status_resp_cb(): rc = 0x%X", ret_code);
    - }
    -}
    -
    -/* Show an error if the rename failed */
    -static void
    -_rename_contact_resp_cb(NMUser * user, NMERR_T ret_code,
    - gpointer resp_data, gpointer user_data)
    -{
    - if (ret_code != NM_OK) {
    - purple_debug_info("novell", "_rename_contact_resp_cb(): rc = 0x%X", ret_code);
    - }
    -}
    -
    -/* Handle the getdetails response and send the message */
    -static void
    -_get_details_resp_send_msg(NMUser * user, NMERR_T ret_code,
    - gpointer resp_data, gpointer user_data)
    -{
    - PurpleConversation *gconv;
    - PurpleConnection *gc;
    - NMUserRecord *user_record = NULL;
    - NMContact *cntct = NULL;
    - NMConference *conf;
    - NMMessage *msg = user_data;
    - const char *dn = NULL;
    - const char *name;
    -
    - if (user == NULL || msg == NULL)
    - return;
    -
    - if (ret_code == NM_OK) {
    - user_record = (NMUserRecord *) resp_data;
    - if (user_record) {
    - PurpleConversationManager *manager = NULL;
    -
    - manager = purple_conversation_manager_get_default();
    -
    - /* Set the title for the conversation */
    - /* XXX - Should this be find_im_with_account? */
    - gconv = purple_conversation_manager_find(manager,
    - (PurpleAccount *)user->client_data,
    - nm_user_record_get_display_id(user_record));
    - if (gconv) {
    -
    - dn = nm_user_record_get_dn(user_record);
    - if (dn) {
    - cntct = nm_find_contact(user, dn);
    - }
    -
    - if (cntct) {
    - purple_conversation_set_title(gconv,
    - nm_contact_get_display_name(cntct));
    - } else {
    -
    - /* Not in the contact list, try to user full name */
    - name = (char *) nm_user_record_get_full_name(user_record);
    - if (name)
    - purple_conversation_set_title(gconv, name);
    - }
    - }
    -
    - /* Add the user record to particpant list */
    - conf = nm_message_get_conference(msg);
    - if (conf) {
    - nm_conference_add_participant(conf, user_record);
    - _send_message(user, msg);
    - }
    - }
    -
    - } else {
    -
    - gc = purple_account_get_connection(user->client_data);
    - if (gc != NULL) {
    - char *err = g_strdup_printf(_("Unable to send message."
    - " Could not get details for user (%s)."),
    - nm_error_to_string (ret_code));
    -
    - purple_notify_error(gc, NULL, err, NULL,
    - purple_request_cpar_from_connection(gc));
    - g_free(err);
    - }
    -
    - nm_release_message(msg);
    - }
    -}
    -
    -/* Set up the new PurpleBuddy based on the response from getdetails */
    -static void
    -_get_details_resp_setup_buddy(NMUser * user, NMERR_T ret_code,
    - gpointer resp_data, gpointer user_data)
    -{
    - NMUserRecord *user_record;
    - NMContact *contact;
    - PurpleBuddy *buddy;
    - const char *alias;
    - NMERR_T rc = NM_OK;
    -
    - if (user == NULL || resp_data == NULL || user_data == NULL)
    - return;
    -
    - contact = user_data;
    -
    - if (ret_code == NM_OK) {
    - user_record = resp_data;
    -
    - buddy = nm_contact_get_data(contact);
    -
    - nm_contact_set_user_record(contact, user_record);
    -
    - /* Set the display id */
    - purple_buddy_set_name(buddy,
    - nm_user_record_get_display_id(user_record));
    -
    - alias = purple_buddy_get_alias(buddy);
    - if (alias == NULL || *alias == '\0' || purple_strequal(alias, purple_buddy_get_name(buddy))) {
    - purple_buddy_set_local_alias(buddy,
    - nm_user_record_get_full_name(user_record));
    -
    - /* Tell the server about the new display name */
    - rc = nm_send_rename_contact(user, contact,
    - nm_user_record_get_full_name(user_record),
    - NULL, NULL);
    - _check_for_disconnect(user, rc);
    -
    - }
    -
    -
    - /* Get initial status for the buddy */
    - rc = nm_send_get_status(user, resp_data, _get_status_resp_cb, NULL);
    - _check_for_disconnect(user, rc);
    -
    -/* nm_release_contact(contact);*/
    -
    - }
    -
    - nm_release_contact(contact);
    -}
    -
    -/* Add the new contact into the PurpleBuddy list */
    -static void
    -_create_contact_resp_cb(NMUser * user, NMERR_T ret_code,
    - gpointer resp_data, gpointer user_data)
    -{
    - NMContact *tmp_contact = (NMContact *) user_data;
    - NMContact *new_contact = NULL;
    - NMFolder *folder = NULL;
    - PurpleGroup *group;
    - PurpleBuddy *buddy;
    - const char *folder_name = NULL;
    - NMERR_T rc = NM_OK;
    -
    - if (user == NULL)
    - return;
    -
    - if (ret_code == NM_OK) {
    -
    - new_contact = (NMContact *) resp_data;
    - if (new_contact == NULL || tmp_contact == NULL)
    - return;
    -
    - /* Get the userid and folder name for the new contact */
    - folder = nm_find_folder_by_id(user,
    - nm_contact_get_parent_id(new_contact));
    - if (folder) {
    - folder_name = nm_folder_get_name(folder);
    - }
    -
    - if (folder_name == NULL || *folder_name == '\0')
    - folder_name = NM_ROOT_FOLDER_NAME;
    -
    - /* Re-add the buddy now that we got the okay from the server */
    - group = purple_blist_find_group(folder_name);
    - if (group) {
    - const char *alias = nm_contact_get_display_name(tmp_contact);
    - const char *display_id = nm_contact_get_display_id(new_contact);
    -
    - if (display_id == NULL)
    - display_id = nm_contact_get_dn(new_contact);
    -
    - if (alias && !purple_strequal(alias, display_id)) {
    -
    - /* The user requested an alias, tell the server about it. */
    - rc = nm_send_rename_contact(user, new_contact, alias,
    - _rename_contact_resp_cb, NULL);
    - _check_for_disconnect(user, rc);
    -
    - } else {
    -
    - alias = "";
    -
    - }
    -
    - /* Add it to the purple buddy list if it is not there */
    - buddy = purple_blist_find_buddy_in_group(user->client_data, display_id, group);
    - if (buddy == NULL) {
    - buddy = purple_buddy_new(user->client_data, display_id, alias);
    - purple_blist_add_buddy(buddy, NULL, group, NULL);
    - }
    -
    - /* Save the new buddy as part of the contact object */
    - nm_contact_set_data(new_contact, (gpointer) buddy);
    -
    - /* We need details for the user before we can setup the
    - * new Purple buddy. We always call this because the
    - * 'createcontact' response fields do not always contain
    - * everything that we need.
    - */
    - nm_contact_add_ref(new_contact);
    -
    - rc = nm_send_get_details(user, nm_contact_get_dn(new_contact),
    - _get_details_resp_setup_buddy, new_contact);
    - _check_for_disconnect(user, rc);
    - }
    -
    - } else {
    - PurpleConnection *gc = purple_account_get_connection(user->client_data);
    - const char *name = nm_contact_get_dn(tmp_contact);
    - char *err;
    -
    - err =
    - g_strdup_printf(_("Unable to add %s to your buddy list (%s)."),
    - name, nm_error_to_string (ret_code));
    - purple_notify_error(gc, NULL, err, NULL,
    - purple_request_cpar_from_connection(gc));
    - g_free(err);
    -
    - }
    -
    - if (tmp_contact)
    - nm_release_contact(tmp_contact);
    -}
    -
    -/* Show an error if we failed to send the message */
    -static void
    -_send_message_resp_cb(NMUser * user, NMERR_T ret_code,
    - gpointer resp_data, gpointer user_data)
    -{
    - PurpleConnection *gc;
    - char *err = NULL;
    -
    - if (user == NULL)
    - return;
    -
    - if (ret_code != NM_OK) {
    - gc = purple_account_get_connection(user->client_data);
    -
    - /* TODO: Improve this! message to who or for what conference? */
    - err = g_strdup_printf(_("Unable to send message (%s)."),
    - nm_error_to_string (ret_code));
    - purple_notify_error(gc, NULL, err, NULL,
    - purple_request_cpar_from_connection(gc));
    - g_free(err);
    - }
    -}
    -
    -/* Show an error if the remove failed */
    -static void
    -_remove_contact_resp_cb(NMUser * user, NMERR_T ret_code,
    - gpointer resp_data, gpointer user_data)
    -{
    - if (ret_code != NM_OK) {
    - /* TODO: Display an error? */
    -
    - purple_debug_info("novell", "_remove_contact_resp_cb(): rc = 0x%x", ret_code);
    - }
    -}
    -
    -/* Show an error if the remove failed */
    -static void
    -_remove_folder_resp_cb(NMUser * user, NMERR_T ret_code,
    - gpointer resp_data, gpointer user_data)
    -{
    - if (ret_code != NM_OK) {
    - /* TODO: Display an error? */
    -
    - purple_debug_info("novell", "_remove_folder_resp_cb(): rc = 0x%x", ret_code);
    - }
    -}
    -
    -/* Show an error if the move failed */
    -static void
    -_move_contact_resp_cb(NMUser * user, NMERR_T ret_code,
    - gpointer resp_data, gpointer user_data)
    -{
    - if (ret_code != NM_OK) {
    - /* TODO: Display an error? */
    -
    - purple_debug_info("novell", "_move_contact_resp_cb(): rc = 0x%x", ret_code);
    - }
    -}
    -
    -/* Show an error if the rename failed */
    -static void
    -_rename_folder_resp_cb(NMUser * user, NMERR_T ret_code,
    - gpointer resp_data, gpointer user_data)
    -{
    - if (ret_code != NM_OK) {
    - /* TODO: Display an error? */
    -
    - purple_debug_info("novell", "_rename_folder_resp_cb(): rc = 0x%x", ret_code);
    - }
    -}
    -
    -static void
    -_sendinvite_resp_cb(NMUser *user, NMERR_T ret_code,
    - gpointer resp_data, gpointer user_data)
    -{
    - char *err;
    - PurpleConnection *gc;
    -
    - if (user == NULL)
    - return;
    -
    - if (ret_code != NM_OK) {
    - gc = purple_account_get_connection(user->client_data);
    - err = g_strdup_printf(_("Unable to invite user (%s)."), nm_error_to_string(ret_code));
    - purple_notify_error(gc, NULL, err, NULL,
    - purple_request_cpar_from_connection(gc));
    - g_free(err);
    -
    - purple_debug_info("novell", "_sendinvite_resp_cb(): rc = 0x%x", ret_code);
    - }
    -
    -}
    -
    -/* If the createconf was successful attempt to send the message,
    - * otherwise display an error message to the user.
    - */
    -static void
    -_createconf_resp_send_msg(NMUser * user, NMERR_T ret_code,
    - gpointer resp_data, gpointer user_data)
    -{
    - NMConference *conf;
    - NMMessage *msg = user_data;
    -
    - if (user == NULL || msg == NULL)
    - return;
    -
    - if (ret_code == NM_OK) {
    - _send_message(user, msg);
    - } else {
    -
    - if ((conf = nm_message_get_conference(msg))) {
    -
    - PurpleConnection *gc = purple_account_get_connection(user->client_data);
    - const char *name = NULL;
    - char *err;
    - NMUserRecord *ur;
    -
    - ur = nm_conference_get_participant(conf, 0);
    - if (ur)
    - name = nm_user_record_get_userid(ur);
    -
    - if (name)
    - err = g_strdup_printf(_("Unable to send message to %s."
    - " Could not create the conference (%s)."),
    - name,
    - nm_error_to_string (ret_code));
    - else
    - err = g_strdup_printf(_("Unable to send message."
    - " Could not create the conference (%s)."),
    - nm_error_to_string (ret_code));
    -
    - purple_notify_error(gc, NULL, err, NULL,
    - purple_request_cpar_from_connection(gc));
    - g_free(err);
    - }
    -
    - nm_release_message(msg);
    - }
    -}
    -
    -/* Move contact to newly created folder */
    -static void
    -_create_folder_resp_move_contact(NMUser * user, NMERR_T ret_code,
    - gpointer resp_data, gpointer user_data)
    -{
    - NMContact *contact = user_data;
    - NMFolder *new_folder;
    - char *folder_name = resp_data;
    - NMERR_T rc = NM_OK;
    -
    - if (user == NULL || folder_name == NULL || contact == NULL) {
    -
    - g_free(folder_name);
    -
    - return;
    - }
    -
    - if (ret_code == NM_OK || ret_code == NMERR_DUPLICATE_FOLDER) {
    - new_folder = nm_find_folder(user, folder_name);
    - if (new_folder) {
    -
    - /* Tell the server to move the contact to the new folder */
    -/* rc = nm_send_move_contact(user, contact, new_folder,
    - _move_contact_resp_cb, NULL); */
    -
    - rc = nm_send_create_contact(user, new_folder, contact,
    - NULL, NULL);
    -
    - _check_for_disconnect(user, rc);
    -
    - }
    - } else {
    - PurpleConnection *gc = purple_account_get_connection(user->client_data);
    - char *err = g_strdup_printf(_("Unable to move user %s"
    - " to folder %s in the server side list."
    - " Error while creating folder (%s)."),
    - nm_contact_get_dn(contact),
    - folder_name,
    - nm_error_to_string (ret_code));
    -
    - purple_notify_error(gc, NULL, err, NULL,
    - purple_request_cpar_from_connection(gc));
    - g_free(err);
    - }
    -
    - g_free(folder_name);
    -}
    -
    -/* Add contact to newly create folder */
    -static void
    -_create_folder_resp_add_contact(NMUser * user, NMERR_T ret_code,
    - gpointer resp_data, gpointer user_data)
    -{
    - NMContact *contact = (NMContact *) user_data;
    - NMFolder *folder;
    - char *folder_name = (char *) resp_data;
    - NMERR_T rc = NM_OK;
    -
    - if (user == NULL || folder_name == NULL || contact == NULL) {
    -
    - if (contact)
    - nm_release_contact(contact);
    -
    - g_free(folder_name);
    -
    - return;
    - }
    -
    - if (ret_code == NM_OK || ret_code == NMERR_DUPLICATE_FOLDER) {
    - folder = nm_find_folder(user, folder_name);
    - if (folder) {
    -
    - rc = nm_send_create_contact(user, folder, contact,
    - _create_contact_resp_cb, contact);
    - _check_for_disconnect(user, rc);
    - }
    - } else {
    - PurpleConnection *gc = purple_account_get_connection(user->client_data);
    - const char *name = nm_contact_get_dn(contact);
    - char *err =
    - g_strdup_printf(_("Unable to add %s to your buddy list."
    - " Error creating folder in server side list (%s)."),
    - name, nm_error_to_string (ret_code));
    -
    - purple_notify_error(gc, NULL, err, NULL,
    - purple_request_cpar_from_connection(gc));
    -
    - nm_release_contact(contact);
    - g_free(err);
    - }
    -
    - g_free(folder_name);
    -}
    -
    -static void
    -_join_conf_resp_cb(NMUser * user, NMERR_T ret_code,
    - gpointer resp_data, gpointer user_data)
    -{
    - PurpleConversation *chat;
    - PurpleConnection *gc;
    - NMUserRecord *ur;
    - NMConference *conference = user_data;
    - const char *name, *conf_name;
    - int i, count;
    -
    - if (user == NULL || conference == NULL)
    - return;
    -
    - gc = purple_account_get_connection(user->client_data);
    -
    - if (ret_code == NM_OK) {
    - conf_name = _get_conference_name(++user->conference_count);
    - chat = purple_serv_got_joined_chat(gc, user->conference_count, conf_name);
    - if (chat) {
    -
    - nm_conference_set_data(conference, (gpointer) chat);
    -
    - count = nm_conference_get_participant_count(conference);
    - for (i = 0; i < count; i++) {
    - ur = nm_conference_get_participant(conference, i);
    - if (ur) {
    - name = nm_user_record_get_display_id(ur);
    - purple_chat_conversation_add_user(PURPLE_CHAT_CONVERSATION(chat), name, NULL,
    - PURPLE_CHAT_USER_NONE, TRUE);
    - }
    - }
    - }
    - }
    -}
    -
    -/* Show info returned by getdetails */
    -static void
    -_get_details_resp_show_info(NMUser * user, NMERR_T ret_code,
    - gpointer resp_data, gpointer user_data)
    -{
    - PurpleConnection *gc;
    - NMUserRecord *user_record;
    - char *name;
    - char *err;
    -
    - if (user == NULL)
    - return;
    -
    - name = user_data;
    -
    - if (ret_code == NM_OK) {
    - user_record = (NMUserRecord *) resp_data;
    - if (user_record) {
    - _show_info(purple_account_get_connection(user->client_data),
    - user_record, g_strdup(name));
    - }
    - } else {
    - gc = purple_account_get_connection(user->client_data);
    - err =
    - g_strdup_printf(_("Could not get details for user %s (%s)."),
    - name, nm_error_to_string (ret_code));
    - purple_notify_error(gc, NULL, err, NULL,
    - purple_request_cpar_from_connection(gc));
    - g_free(err);
    - }
    -
    - g_free(name);
    -}
    -
    -/* Handle get details response add to privacy list */
    -static void
    -_get_details_resp_add_privacy_item(NMUser *user, NMERR_T ret_code,
    - gpointer resp_data, gpointer user_data)
    -{
    - PurpleConnection *gc;
    - PurpleAccount *account;
    - NMUserRecord *user_record = resp_data;
    - char *err;
    - gboolean allowed = GPOINTER_TO_INT(user_data);
    - const char *display_id;
    -
    - if (user == NULL)
    - return;
    -
    - gc = purple_account_get_connection(user->client_data);
    - display_id = nm_user_record_get_display_id(user_record);
    - account = purple_connection_get_account(gc);
    -
    - if (ret_code == NM_OK) {
    -
    - if (allowed) {
    -
    - if (!g_slist_find_custom(purple_account_privacy_get_denied(account),
    - display_id, (GCompareFunc)purple_utf8_strcasecmp)) {
    - purple_account_privacy_permit_add(account, display_id, TRUE);
    - }
    -
    - } else {
    -
    - if (!g_slist_find_custom(purple_account_privacy_get_denied(account),
    - display_id, (GCompareFunc)purple_utf8_strcasecmp)) {
    - purple_account_privacy_deny_add(account, display_id, TRUE);
    - }
    - }
    -
    - } else {
    -
    - err = g_strdup_printf(_("Unable to add user to privacy list (%s)."),
    - nm_error_to_string(ret_code));
    - purple_notify_error(gc, NULL, err, NULL,
    - purple_request_cpar_from_connection(gc));
    - g_free(err);
    -
    - }
    -}
    -
    -/* Handle response to create privacy item request */
    -static void
    -_create_privacy_item_deny_resp_cb(NMUser *user, NMERR_T ret_code,
    - gpointer resp_data, gpointer user_data)
    -{
    - PurpleConnection *gc;
    - PurpleAccount *account;
    - NMUserRecord *user_record;
    - char *who = user_data;
    - char *err;
    - NMERR_T rc = NM_OK;
    - const char *display_id = NULL;
    -
    - if (user == NULL)
    - return;
    -
    - gc = purple_account_get_connection(user->client_data);
    - account = purple_connection_get_account(gc);
    -
    - if (ret_code == NM_OK) {
    -
    - user_record = nm_find_user_record(user, who);
    - if (user_record)
    - display_id = nm_user_record_get_display_id(user_record);
    -
    - if (display_id) {
    -
    - if (!g_slist_find_custom(purple_account_privacy_get_denied(account),
    - display_id, (GCompareFunc)purple_utf8_strcasecmp)) {
    -
    - purple_account_privacy_deny_add(account, display_id, TRUE);
    - }
    -
    - } else {
    - rc = nm_send_get_details(user, who,
    - _get_details_resp_add_privacy_item,
    - GINT_TO_POINTER(FALSE));
    - _check_for_disconnect(user, rc);
    - }
    - } else {
    -
    - err = g_strdup_printf(_("Unable to add %s to deny list (%s)."),
    - who, nm_error_to_string(ret_code));
    - purple_notify_error(gc, NULL, err, NULL,
    - purple_request_cpar_from_connection(gc));
    - g_free(err);
    -
    - }
    -
    - g_free(who);
    -
    -}
    -
    -/* Handle response to create privacy item request */
    -static void
    -_create_privacy_item_permit_resp_cb(NMUser *user, NMERR_T ret_code,
    - gpointer resp_data, gpointer user_data)
    -{
    - PurpleConnection *gc;
    - PurpleAccount *account;
    - NMUserRecord *user_record;
    - char *who = user_data;
    - char *err;
    - NMERR_T rc = NM_OK;
    - const char *display_id = NULL;
    -
    - if (user == NULL)
    - return;
    -
    - gc = purple_account_get_connection(user->client_data);
    - account = purple_connection_get_account(gc);
    -
    - if (ret_code == NM_OK) {
    -
    - user_record = nm_find_user_record(user, who);
    - if (user_record)
    - display_id = nm_user_record_get_display_id(user_record);
    -
    - if (display_id) {
    -
    - if (!g_slist_find_custom(purple_account_privacy_get_permitted(account),
    - display_id,
    - (GCompareFunc)purple_utf8_strcasecmp)) {
    -
    - purple_account_privacy_permit_add(account, display_id, TRUE);
    - }
    -
    - } else {
    - rc = nm_send_get_details(user, who,
    - _get_details_resp_add_privacy_item,
    - GINT_TO_POINTER(TRUE));
    - _check_for_disconnect(user, rc);
    - }
    -
    - } else {
    -
    - err = g_strdup_printf(_("Unable to add %s to permit list (%s)."), who,
    - nm_error_to_string(ret_code));
    - purple_notify_error(gc, NULL, err, NULL,
    - purple_request_cpar_from_connection(gc));
    - g_free(err);
    -
    - }
    -
    - g_free(who);
    -}
    -
    -static void
    -_get_details_send_privacy_create(NMUser *user, NMERR_T ret_code,
    - gpointer resp_data, gpointer user_data)
    -{
    - NMERR_T rc = NM_OK;
    - PurpleConnection *gc;
    - NMUserRecord *user_record = resp_data;
    - char *err;
    - gboolean allowed = GPOINTER_TO_INT(user_data);
    - const char *dn, *display_id;
    -
    - if (user == NULL)
    - return;
    -
    - gc = purple_account_get_connection(user->client_data);
    - dn = nm_user_record_get_dn(user_record);
    - display_id = nm_user_record_get_display_id(user_record);
    -
    - if (ret_code == NM_OK) {
    -
    - if (allowed) {
    - rc = nm_send_create_privacy_item(user, dn, TRUE,
    - _create_privacy_item_permit_resp_cb,
    - g_strdup(display_id));
    - _check_for_disconnect(user, rc);
    -
    - } else {
    - rc = nm_send_create_privacy_item(user, dn, FALSE,
    - _create_privacy_item_deny_resp_cb,
    - g_strdup(display_id));
    - _check_for_disconnect(user, rc);
    - }
    -
    - } else {
    -
    - err = g_strdup_printf(_("Unable to add user to privacy list (%s)."),
    - nm_error_to_string(ret_code));
    - purple_notify_error(gc, NULL, err, NULL,
    - purple_request_cpar_from_connection(gc));
    - g_free(err);
    -
    - }
    -}
    -
    -static void
    -_remove_privacy_item_resp_cb(NMUser *user, NMERR_T ret_code,
    - gpointer resp_data, gpointer user_data)
    -{
    - PurpleConnection *gc;
    - char *who = user_data;
    - char *err;
    -
    - if (user == NULL)
    - return;
    -
    - if (ret_code != NM_OK) {
    -
    - gc = purple_account_get_connection(user->client_data);
    - err = g_strdup_printf(_("Unable to remove %s from privacy list (%s)."), who,
    - nm_error_to_string(ret_code));
    - purple_notify_error(gc, NULL, err, NULL,
    - purple_request_cpar_from_connection(gc));
    - g_free(err);
    - }
    -
    - g_free(who);
    -}
    -
    -static void
    -_set_privacy_default_resp_cb(NMUser *user, NMERR_T ret_code,
    - gpointer resp_data, gpointer user_data)
    -{
    - PurpleConnection *gc;
    - char *err;
    -
    - if (user == NULL)
    - return;
    -
    - if (ret_code != NM_OK) {
    -
    - gc = purple_account_get_connection(user->client_data);
    - err = g_strdup_printf(_("Unable to change server side privacy settings (%s)."),
    - nm_error_to_string(ret_code));
    - purple_notify_error(gc, NULL, err, NULL,
    - purple_request_cpar_from_connection(gc));
    - g_free(err);
    -
    - }
    -}
    -
    -/* Handle get details response add to privacy list */
    -static void
    -_get_details_resp_send_invite(NMUser *user, NMERR_T ret_code,
    - gpointer resp_data, gpointer user_data)
    -{
    - NMERR_T rc = NM_OK;
    - PurpleConnection *gc;
    - NMUserRecord *user_record = resp_data;
    - char *err;
    - GSList *cnode;
    - NMConference *conference;
    - gpointer chat;
    - int id = GPOINTER_TO_INT(user_data);
    -
    - if (user == NULL)
    - return;
    -
    - gc = purple_account_get_connection(user->client_data);
    -
    - if (ret_code == NM_OK) {
    -
    - for (cnode = user->conferences; cnode != NULL; cnode = cnode->next) {
    - conference = cnode->data;
    - if (conference && (chat = nm_conference_get_data(conference))) {
    - if (purple_chat_conversation_get_id(PURPLE_CHAT_CONVERSATION(chat)) == id) {
    - rc = nm_send_conference_invite(user, conference, user_record,
    - NULL, _sendinvite_resp_cb, NULL);
    - _check_for_disconnect(user, rc);
    - break;
    - }
    - }
    - }
    -
    - } else {
    -
    - err = g_strdup_printf(_("Unable to invite user (%s)."), nm_error_to_string(ret_code));
    - purple_notify_error(gc, NULL, err, NULL,
    - purple_request_cpar_from_connection(gc));
    - g_free(err);
    -
    - }
    -}
    -
    -static void
    -_createconf_resp_send_invite(NMUser * user, NMERR_T ret_code,
    - gpointer resp_data, gpointer user_data)
    -{
    - NMERR_T rc = NM_OK;
    - NMConference *conference = resp_data;
    - NMUserRecord *user_record = user_data;
    - PurpleConnection *gc;
    - char *err;
    -
    - if (user == NULL)
    - return;
    -
    -
    -
    - if (ret_code == NM_OK) {
    - rc = nm_send_conference_invite(user, conference, user_record,
    - NULL, _sendinvite_resp_cb, NULL);
    - _check_for_disconnect(user, rc);
    - } else {
    - err = g_strdup_printf(_("Unable to create conference (%s)."), nm_error_to_string(ret_code));
    - gc = purple_account_get_connection(user->client_data);
    - purple_notify_error(gc, NULL, err, NULL,
    - purple_request_cpar_from_connection(gc));
    - g_free(err);
    - }
    -}
    -
    -/*******************************************************************************
    - * Helper functions
    - ******************************************************************************/
    -
    -static char *
    -_user_agent_string(void)
    -{
    -
    -#if !defined(_WIN32)
    -
    - const char *sysname = "";
    - const char *release = "";
    - struct utsname u;
    -
    - if (uname(&u) == 0) {
    - sysname = u.sysname;
    - release = u.release;
    - } else {
    - sysname = "Linux";
    - release = "Unknown";
    - }
    -
    - return g_strdup_printf("Purple/%s (%s; %s)", VERSION, sysname, release);
    -
    -#else
    -
    - const char *sysname = "";
    - OSVERSIONINFO os_info;
    - SYSTEM_INFO sys_info;
    -
    - os_info.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
    - GetVersionEx(&os_info);
    - GetSystemInfo(&sys_info);
    -
    - if (os_info.dwPlatformId == VER_PLATFORM_WIN32_NT) {
    - switch (os_info.dwMajorVersion) {
    - case 3:
    - case 4:
    - sysname = "Windows NT";
    - break;
    - case 5:
    - switch (os_info.dwMinorVersion) {
    - case 0:
    - sysname = "Windows 2000";
    - break;
    - case 1:
    - sysname = "Windows XP";
    - break;
    - case 2:
    - sysname = "Windows Server 2003";
    - break;
    - default:
    - sysname = "Windows";
    - break;
    - }
    - break;
    - default:
    - sysname = "Windows";
    - break;
    - }
    -
    - } else if (os_info.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS) {
    - switch (os_info.dwMinorVersion) {
    - case 0:
    - sysname = "Windows 95";
    - break;
    - case 10:
    - sysname = "Windows 98";
    - break;
    - case 90:
    - sysname = "Windows ME";
    - break;
    - default:
    - sysname = "Windows";
    - break;
    - }
    - } else {
    - sysname = "Windows";
    - }
    -
    - return g_strdup_printf("Purple/%s (%s; %ld.%ld)", VERSION, sysname,
    - os_info.dwMajorVersion, os_info.dwMinorVersion);
    -
    -#endif
    -
    -
    -}
    -
    -static gboolean
    -_is_disconnect_error(NMERR_T err)
    -{
    - return (err == NMERR_TCP_WRITE ||
    - err == NMERR_TCP_READ || err == NMERR_PROTOCOL);
    -}
    -
    -static gboolean
    -_check_for_disconnect(NMUser * user, NMERR_T err)
    -{
    - PurpleConnection *gc = purple_account_get_connection(user->client_data);
    -
    - if (_is_disconnect_error(err)) {
    -
    - purple_connection_error(gc,
    - PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
    - _("Error communicating with server. Closing connection."));
    - return TRUE;
    -
    - }
    -
    - return FALSE;
    -}
    -
    -/* Check to see if the conference is instantiated, if so send the message.
    - * If not send the create conference -- the response handler for the createconf
    - * will call this function again.
    - */
    -static void
    -_send_message(NMUser * user, NMMessage * message)
    -{
    - NMConference *conf;
    - NMERR_T rc = NM_OK;
    -
    - conf = nm_message_get_conference(message);
    - if (conf) {
    - /* We have a conference make sure that the
    - server knows about it already. */
    - if (nm_conference_is_instantiated(conf)) {
    -
    - /* We have everything that we need...finally! */
    - rc = nm_send_message(user, message, _send_message_resp_cb);
    - _check_for_disconnect(user, rc);
    -
    - nm_release_message(message);
    -
    - } else {
    - rc = nm_send_create_conference(user, conf, _createconf_resp_send_msg, message);
    - _check_for_disconnect(user, rc);
    - }
    - }
    -}
    -
    -/*
    - * Update the status of the given buddy in the Purple buddy list
    - */
    -static void
    -_update_buddy_status(NMUser *user, PurpleBuddy * buddy, int novellstatus, int gmt)
    -{
    - PurpleAccount *account;
    - const char *status_id;
    - const char *text = NULL;
    - const char *dn;
    - const char *name;
    - int idle = 0;
    -
    - account = purple_buddy_get_account(buddy);
    - name = purple_buddy_get_name(buddy);
    -
    - switch (novellstatus) {
    - case NM_STATUS_AVAILABLE:
    - status_id = NOVELL_STATUS_TYPE_AVAILABLE;
    - break;
    - case NM_STATUS_AWAY:
    - status_id = NOVELL_STATUS_TYPE_AWAY;
    - break;
    - case NM_STATUS_BUSY:
    - status_id = NOVELL_STATUS_TYPE_BUSY;
    - break;
    - case NM_STATUS_OFFLINE:
    - status_id = NOVELL_STATUS_TYPE_OFFLINE;
    - break;
    - case NM_STATUS_AWAY_IDLE:
    - status_id = NOVELL_STATUS_TYPE_AWAY;
    - idle = gmt;
    - break;
    - default:
    - status_id = NOVELL_STATUS_TYPE_OFFLINE;
    - break;
    - }
    -
    - /* Get status text for the user */
    - dn = nm_lookup_dn(user, name);
    - if (dn) {
    - NMUserRecord *user_record = nm_find_user_record(user, dn);
    - if (user_record) {
    - text = nm_user_record_get_status_text(user_record);
    - }
    - }
    -
    - purple_protocol_got_user_status(account, name, status_id,
    - "message", text, NULL);
    - purple_protocol_got_user_idle(account, name,
    - (novellstatus == NM_STATUS_AWAY_IDLE), idle);
    -}
    -
    -/* Iterate through the cached Purple buddy list and remove buddies
    - * that are not in the server side list.
    - */
    -static void
    -_remove_purple_buddies(NMUser *user)
    -{
    - PurpleBlistNode *gnode;
    - PurpleBlistNode *cnode;
    - PurpleBlistNode *bnode;
    - PurpleGroup *group;
    - PurpleBuddy *buddy;
    - GSList *rem_list = NULL;
    - NMFolder *folder = NULL;
    - const char *gname = NULL;
    -
    - for (gnode = purple_blist_get_default_root(); gnode;
    - gnode = purple_blist_node_get_sibling_next(gnode)) {
    - if (!PURPLE_IS_GROUP(gnode))
    - continue;
    - group = (PurpleGroup *) gnode;
    - gname = purple_group_get_name(group);
    - for (cnode = purple_blist_node_get_first_child(gnode);
    - cnode;
    - cnode = purple_blist_node_get_sibling_next(cnode)) {
    - if (!PURPLE_IS_META_CONTACT(cnode))
    - continue;
    - for (bnode = purple_blist_node_get_first_child(cnode);
    - bnode;
    - bnode = purple_blist_node_get_sibling_next(bnode)) {
    - if (!PURPLE_IS_BUDDY(bnode))
    - continue;
    - buddy = (PurpleBuddy *) bnode;
    - if (purple_buddy_get_account(buddy) == user->client_data) {
    - if (purple_strequal(gname, NM_ROOT_FOLDER_NAME))
    - gname = "";
    - folder = nm_find_folder(user, gname);
    - if (folder == NULL ||
    - !nm_folder_find_contact_by_display_id(folder, purple_buddy_get_name(buddy))) {
    - rem_list = g_slist_append(rem_list, buddy);
    - }
    - }
    - }
    - }
    - }
    -
    - g_slist_free_full(rem_list, (GDestroyNotify)purple_blist_remove_buddy);
    -}
    -
    -/* Add all of the contacts in the given folder to the Purple buddy list */
    -static void
    -_add_contacts_to_purple_blist(NMUser * user, NMFolder * folder)
    -{
    - NMUserRecord *user_record = NULL;
    - NMContact *contact = NULL;
    - PurpleBuddy *buddy = NULL;
    - PurpleGroup *group;
    - NMERR_T cnt = 0, i;
    - const char *name = NULL;
    - const char *fname = NULL;
    - int status = 0;
    -
    - /* If this is the root folder give it a name. Purple does not have the concept of
    - * a root folder.
    - */
    - fname = nm_folder_get_name(folder);
    - if (fname == NULL || *fname == '\0') {
    - fname = NM_ROOT_FOLDER_NAME;
    - }
    -
    - /* Does the Purple group exist already? */
    - group = purple_blist_find_group(fname);
    - if (group == NULL) {
    - group = purple_group_new(fname);
    - purple_blist_add_group(group, NULL);
    - }
    -
    - /* Get each contact for this folder */
    - cnt = nm_folder_get_contact_count(folder);
    - for (i = 0; i < cnt; i++) {
    - contact = nm_folder_get_contact(folder, i);
    - if (contact) {
    -
    - name = nm_contact_get_display_id(contact);
    - if (name) {
    -
    - buddy = purple_blist_find_buddy_in_group(user->client_data, name, group);
    - if (buddy == NULL) {
    - /* Add it to the purple buddy list */
    - buddy = purple_buddy_new(user->client_data,
    - name,
    - nm_contact_get_display_name(contact));
    -
    - purple_blist_add_buddy(buddy, NULL, group, NULL);
    - }
    -
    - /* Set the initial status for the buddy */
    - user_record = nm_contact_get_user_record(contact);
    - if (user_record) {
    - status = nm_user_record_get_status(user_record);
    - }
    - _update_buddy_status(user, buddy, status, time(0));
    -
    - /* Save the new buddy as part of the contact object */
    - nm_contact_set_data(contact, (gpointer) buddy);
    - }
    -
    - } else {
    - /* NULL contact. This should not happen, but
    - * let's break out of the loop.
    - */
    - break;
    - }
    - }
    -}
    -
    -/* Add all of the server side contacts to the Purple buddy list. */
    -static void
    -_add_purple_buddies(NMUser * user)
    -{
    - int cnt = 0, i;
    - NMFolder *root_folder = NULL;
    - NMFolder *folder = NULL;
    -
    - root_folder = nm_get_root_folder(user);
    - if (root_folder) {
    -
    - /* Add sub-folders and contacts to sub-folders...
    - * iterate through the sub-folders in reverse order because
    - * Purple adds the folders to the front -- so we want to add
    - * the first folder last
    - */
    - cnt = nm_folder_get_subfolder_count(root_folder);
    - for (i = cnt-1; i >= 0; i--) {
    - folder = nm_folder_get_subfolder(root_folder, i);
    - if (folder) {
    - _add_contacts_to_purple_blist(user, folder);
    - }
    - }
    -
    - /* Add contacts for the root folder */
    - _add_contacts_to_purple_blist(user, root_folder);
    - }
    -}
    -
    -static void
    -_sync_contact_list(NMUser *user)
    -{
    - /* Remove all buddies from the local list that are
    - * not in the server side list and add all buddies
    - * from the server side list that are not in
    - * the local list
    - */
    - _remove_purple_buddies(user);
    - _add_purple_buddies(user);
    - user->clist_synched = TRUE;
    -}
    -
    -static void
    -_sync_privacy_lists(NMUser *user)
    -{
    - GSList *node = NULL, *rem_list = NULL;
    - PurpleConnection *gc;
    - PurpleAccount *account;
    - const char *name, *dn;
    - NMUserRecord *user_record;
    -
    - if (user == NULL)
    - return;
    -
    - gc = purple_account_get_connection(user->client_data);
    - if (gc == NULL)
    - return;
    -
    - account = purple_connection_get_account(gc);
    -
    - /* Set the Purple privacy setting */
    - if (user->default_deny) {
    - if (user->allow_list == NULL) {
    - purple_account_set_privacy_type(account, PURPLE_ACCOUNT_PRIVACY_DENY_ALL);
    - } else {
    - purple_account_set_privacy_type(account, PURPLE_ACCOUNT_PRIVACY_ALLOW_USERS);
    - }
    - } else {
    - if (user->deny_list == NULL) {
    - purple_account_set_privacy_type(account, PURPLE_ACCOUNT_PRIVACY_ALLOW_ALL);
    - } else {
    - purple_account_set_privacy_type(account, PURPLE_ACCOUNT_PRIVACY_DENY_USERS);
    - }
    - }
    -
    - /* Add stuff */
    - for (node = user->allow_list; node; node = node->next) {
    - user_record = nm_find_user_record(user, (char *)node->data);
    - if (user_record)
    - name = nm_user_record_get_display_id(user_record);
    - else
    - name =(char *)node->data;
    -
    - if (!g_slist_find_custom(purple_account_privacy_get_permitted(account),
    - name, (GCompareFunc)purple_utf8_strcasecmp)) {
    - purple_account_privacy_permit_add(account, name , TRUE);
    - }
    - }
    -
    - for (node = user->deny_list; node; node = node->next) {
    - user_record = nm_find_user_record(user, (char *)node->data);
    - if (user_record)
    - name = nm_user_record_get_display_id(user_record);
    - else
    - name =(char *)node->data;
    -
    - if (!g_slist_find_custom(purple_account_privacy_get_denied(account),
    - name, (GCompareFunc)purple_utf8_strcasecmp)) {
    - purple_account_privacy_deny_add(account, name, TRUE);
    - }
    - }
    -
    -
    - /* Remove stuff */
    - for (node = purple_account_privacy_get_permitted(account); node; node = node->next) {
    - dn = nm_lookup_dn(user, (char *)node->data);
    - if (dn != NULL &&
    - !g_slist_find_custom(user->allow_list,
    - dn, (GCompareFunc)purple_utf8_strcasecmp)) {
    - rem_list = g_slist_append(rem_list, node->data);
    - }
    - }
    -
    - if (rem_list) {
    - for (node = rem_list; node; node = node->next) {
    - purple_account_privacy_permit_remove(account, (char *)node->data, TRUE);
    - }
    - g_slist_free(rem_list);
    - rem_list = NULL;
    - }
    -
    - for (node = purple_account_privacy_get_denied(account); node; node = node->next) {
    - dn = nm_lookup_dn(user, (char *)node->data);
    - if (dn != NULL &&
    - !g_slist_find_custom(user->deny_list,
    - dn, (GCompareFunc)purple_utf8_strcasecmp)) {
    - rem_list = g_slist_append(rem_list, node->data);
    - }
    - }
    -
    - if (rem_list) {
    - for (node = rem_list; node; node = node->next) {
    - purple_account_privacy_deny_remove(account, (char *)node->data, TRUE);
    - }
    - g_slist_free(rem_list);
    - }
    -}
    -
    - /* Map known property tags to user-friendly strings */
    -static const char *
    -_map_property_tag(const char *tag)
    -{
    - if (tag == NULL) return NULL;
    -
    - if (purple_strequal(tag, "telephoneNumber"))
    - return _("Telephone Number");
    - else if (purple_strequal(tag, "L"))
    - return _("Location");
    - else if (purple_strequal(tag, "OU"))
    - return _("Department");
    - else if (purple_strequal(tag, "personalTitle"))
    - return _("Personal Title");
    - else if (purple_strequal(tag, "Title"))
    - return _("Job Title");
    - else if (purple_strequal(tag, "mailstop"))
    - return _("Mailstop");
    - else if (purple_strequal(tag, "Internet EMail Address"))
    - return _("Email Address");
    - else
    - return tag;
    -}
    -
    -/* Display a dialog box showing the properties for the given user record */
    -static void
    -_show_info(PurpleConnection * gc, NMUserRecord * user_record, char * name)
    -{
    - PurpleNotifyUserInfo *user_info = purple_notify_user_info_new();
    - int count, i;
    - const char *tag, *value;
    -
    - tag = _("User ID");
    - value = nm_user_record_get_userid(user_record);
    - if (value) {
    - /* 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, tag, value);
    - }
    -
    - tag = _("Full name");
    - value = nm_user_record_get_full_name(user_record);
    - if (value) {
    - /* 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, tag, value);
    - }
    -
    - count = nm_user_record_get_property_count(user_record);
    - for (i = 0; i < count; i++) {
    - PurpleKeyValuePair *property = nm_user_record_get_property(user_record, i);
    - if (property) {
    - tag = _map_property_tag(property->key);
    - value = property->value;
    - if (tag && value) {
    - /* 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, tag, value);
    - }
    - purple_key_value_pair_free(property);
    - }
    - }
    -
    - purple_notify_userinfo(gc, name, user_info, NULL, NULL);
    - purple_notify_user_info_destroy(user_info);
    -
    - g_free(name);
    -}
    -
    -/* Send a join conference, the first item in the params list is the
    - * NMUser object and the second item is the conference to join.
    - * This callback is passed to purple_request_action when we ask the
    - * user if they want to join the conference.
    - */
    -static void
    -_join_conference_cb(GSList *params)
    -{
    - NMUser *user;
    - NMConference *conference;
    - NMERR_T rc = NM_OK;
    -
    - if (params == NULL || g_slist_length(params) != 2) {
    - return;
    - }
    -
    - user = g_slist_nth_data(params, 0);
    - conference = g_slist_nth_data(params, 1);
    -
    - if (user && conference) {
    - rc = nm_send_join_conference(user, conference,
    - _join_conf_resp_cb, conference);
    - _check_for_disconnect(user, rc);
    - }
    -
    - g_slist_free(params);
    -}
    -
    -/* Send a reject conference, the first item in the params list is the
    - * NMUser object and the second item is the conference to reject.
    - * This callback is passed to purple_request_action when we ask the
    - * user if they want to joing the conference.
    - */
    -static void
    -_reject_conference_cb(GSList *params)
    -{
    - NMUser *user;
    - NMConference *conference;
    - NMERR_T rc = NM_OK;
    -
    - if (params == NULL || g_slist_length(params) != 2) {
    - return;
    - }
    -
    - user = g_slist_nth_data(params, 0);
    - conference = g_slist_nth_data(params, 1);
    -
    - if (user && conference) {
    - rc = nm_send_reject_conference(user, conference, NULL, NULL);
    - _check_for_disconnect(user, rc);
    - }
    -
    - g_slist_free(params);
    -}
    -
    -static void
    -_initiate_conference_cb(PurpleBlistNode *node, gpointer ignored)
    -{
    - PurpleBuddy *buddy;
    - PurpleConnection *gc;
    -
    - NMUser *user;
    - const char *conf_name;
    - PurpleConversation *chat = NULL;
    - NMUserRecord *user_record;
    - NMConference *conference;
    -
    - g_return_if_fail(PURPLE_IS_BUDDY(node));
    -
    - buddy = (PurpleBuddy *) node;
    - gc = purple_account_get_connection(purple_buddy_get_account(buddy));
    -
    - user = purple_connection_get_protocol_data(gc);
    - if (user == NULL)
    - return;
    -
    - /* We should already have a userrecord for the buddy */
    - user_record = nm_find_user_record(user, purple_buddy_get_name(buddy));
    - if (user_record == NULL)
    - return;
    -
    - conf_name = _get_conference_name(++user->conference_count);
    - chat = purple_serv_got_joined_chat(gc, user->conference_count, conf_name);
    - if (chat) {
    -
    - conference = nm_create_conference(NULL);
    - nm_conference_set_data(conference, (gpointer) chat);
    - nm_send_create_conference(user, conference, _createconf_resp_send_invite, user_record);
    - nm_release_conference(conference);
    - }
    -}
    -
    -const char *
    -_get_conference_name(int id)
    -{
    - static char *name = NULL;
    -
    - g_free(name);
    -
    - name = g_strdup_printf(_("GroupWise Conference %d"), id);
    -
    - return name;
    -}
    -
    -static void
    -_show_privacy_locked_error(PurpleConnection *gc, NMUser *user)
    -{
    - char *err;
    -
    - err = g_strdup_printf(_("Unable to change server side privacy settings (%s)."),
    - nm_error_to_string(NMERR_ADMIN_LOCKED));
    - purple_notify_error(gc, NULL, err, NULL,
    - purple_request_cpar_from_connection(gc));
    - g_free(err);
    -}
    -
    -/*******************************************************************************
    - * Connect and recv callbacks
    - ******************************************************************************/
    -
    -static void
    -novell_ssl_recv_cb(GObject *stream, gpointer data)
    -{
    - PurpleConnection *gc = data;
    - NMUser *user;
    - NMERR_T rc;
    -
    - if (gc == NULL)
    - return;
    -
    - user = purple_connection_get_protocol_data(gc);
    - if (user == NULL)
    - return;
    -
    - rc = nm_process_new_data(user);
    - if (rc != NM_OK) {
    -
    - if (_is_disconnect_error(rc)) {
    -
    - purple_connection_error(gc,
    - PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
    - _("Error communicating with server. Closing connection."));
    - } else {
    - purple_debug_info("novell", "Error processing event or response (%d).", rc);
    - }
    - }
    -}
    -
    -static void
    -novell_login_callback(GObject *source_object, GAsyncResult *res, gpointer data)
    -{
    - GSocketClient *client = G_SOCKET_CLIENT(source_object);
    - PurpleConnection *gc = data;
    - GSocketConnection *sockconn;
    - NMUser *user;
    - NMConn *conn;
    - NMERR_T rc = 0;
    - const char *pwd = NULL;
    - gchar *my_addr = NULL;
    - char *ua = NULL;
    - GError *error = NULL;
    -
    - sockconn = g_socket_client_connect_to_host_finish(client, res, &error);
    - if (sockconn == NULL) {
    - if (g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
    - g_error_free(error);
    - } else {
    - purple_connection_take_error(gc, error);
    - }
    - return;
    - }
    -
    - user = purple_connection_get_protocol_data(gc);
    - if ((user == NULL) || (conn = user->conn) == NULL)
    - return;
    -
    - conn->stream = G_IO_STREAM(sockconn);
    - conn->input =
    - g_data_input_stream_new(g_io_stream_get_input_stream(conn->stream));
    - conn->output = g_object_ref(g_io_stream_get_output_stream(conn->stream));
    -
    - g_data_input_stream_set_byte_order(conn->input,
    - G_DATA_STREAM_BYTE_ORDER_LITTLE_ENDIAN);
    - g_data_input_stream_set_newline_type(conn->input,
    - G_DATA_STREAM_NEWLINE_TYPE_LF);
    -
    - my_addr = purple_network_get_my_ip_from_gio(sockconn);
    - pwd = purple_connection_get_password(gc);
    - ua = _user_agent_string();
    -
    - rc = nm_send_login(user, pwd, my_addr, ua, _login_resp_cb, NULL);
    - if (rc == NM_OK) {
    - GSource *source;
    - source = g_pollable_input_stream_create_source(
    - G_POLLABLE_INPUT_STREAM(conn->input), user->cancellable);
    - g_source_set_callback(source, (GSourceFunc)novell_ssl_recv_cb, gc,
    - NULL);
    - } else {
    - purple_connection_error(gc,
    - PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
    - _("Unable to connect"));
    - }
    -
    - g_free(ua);
    - g_free(my_addr);
    -}
    -
    -/*******************************************************************************
    - * Event callback and event handlers
    - ******************************************************************************/
    -
    -static void
    -_evt_receive_message(NMUser * user, NMEvent * event)
    -{
    - NMUserRecord *user_record = NULL;
    - NMContact *contact = NULL;
    - PurpleConversation *im;
    - NMConference *conference;
    - PurpleMessageFlags flags;
    - char *text = NULL;
    -
    - text = g_markup_escape_text(nm_event_get_text(event), -1);
    -
    - conference = nm_event_get_conference(event);
    - if (conference) {
    -
    - PurpleConversation *chat = nm_conference_get_data(conference);
    -
    - /* Is this a single person 'conversation' or a conference? */
    - if (chat == NULL && nm_conference_get_participant_count(conference) == 1) {
    -
    - user_record = nm_find_user_record(user, nm_event_get_source(event));
    - if (user_record) {
    - PurpleConversationManager *manager = NULL;
    -
    - flags = 0;
    - if(nm_event_get_type(event) == NMEVT_RECEIVE_AUTOREPLY) {
    - flags |= PURPLE_MESSAGE_AUTO_RESP;
    - }
    -
    - purple_serv_got_im(purple_account_get_connection(user->client_data),
    - nm_user_record_get_display_id(user_record),
    - text, flags,
    - nm_event_get_gmt(event));
    -
    - manager = purple_conversation_manager_get_default();
    - im = purple_conversation_manager_find_im(manager,
    - PURPLE_ACCOUNT(user->client_data),
    - nm_user_record_get_display_id(user_record));
    - if (im) {
    -
    - contact = nm_find_contact(user, nm_event_get_source(event));
    - if (contact) {
    -
    - purple_conversation_set_title(im,
    - nm_contact_get_display_name(contact));
    -
    -
    - } else {
    -
    - const char *name =
    - nm_user_record_get_full_name(user_record);
    -
    - if (name == NULL)
    - name = nm_user_record_get_userid(user_record);
    -
    - purple_conversation_set_title(im, name);
    - }
    -
    - }
    -
    - } else {
    - /* this should not happen, see the event code.
    - * the event code will get the contact details from
    - * the server if it does not have them before calling
    - * the event callback.
    - */
    - }
    -
    - } else if (chat) {
    -
    - /* get the contact for send if we have one */
    - NMContact *contact = nm_find_contact(user,
    - nm_event_get_source(event));
    -
    - /* get the user record for the sender */
    - user_record = nm_find_user_record(user, nm_event_get_source(event));
    - if (user_record) {
    - const char *name = nm_contact_get_display_name(contact);
    -
    - if (name == NULL) {
    - name = nm_user_record_get_full_name(user_record);
    - if (name == NULL)
    - name = nm_user_record_get_display_id(user_record);
    - }
    -
    - purple_serv_got_chat_in(purple_account_get_connection(user->client_data),
    - purple_chat_conversation_get_id(PURPLE_CHAT_CONVERSATION(chat)),
    - name, PURPLE_MESSAGE_RECV, text, nm_event_get_gmt(event));
    - }
    - }
    - }
    -
    - g_free(text);
    -}
    -
    -static void
    -_evt_conference_left(NMUser * user, NMEvent * event)
    -{
    - PurpleChatConversation *chat;
    - NMConference *conference;
    -
    - conference = nm_event_get_conference(event);
    - if (conference) {
    - chat = nm_conference_get_data(conference);
    - if (chat) {
    - NMUserRecord *ur = nm_find_user_record(user,
    - nm_event_get_source(event));
    -
    - if (ur)
    - purple_chat_conversation_remove_user(chat,
    - nm_user_record_get_display_id(ur),
    - NULL);
    - }
    - }
    -}
    -
    -static void
    -_evt_conference_invite_notify(NMUser * user, NMEvent * event)
    -{
    - PurpleConversation *gconv;
    - NMConference *conference;
    - NMUserRecord *user_record = NULL;
    - char *str = NULL;
    -
    - user_record = nm_find_user_record(user, nm_event_get_source(event));
    - conference = nm_event_get_conference(event);
    - if (user_record && conference) {
    - gconv = nm_conference_get_data(conference);
    - str = g_strdup_printf(_("%s has been invited to this conversation."),
    - nm_user_record_get_display_id(user_record));
    - purple_conversation_write_system_message(gconv, str, 0);
    - g_free(str);
    - }
    -}
    -
    -static void
    -_evt_conference_invite(NMUser * user, NMEvent * event)
    -{
    - NMUserRecord *ur;
    - PurpleConnection *gc;
    - GSList *params = NULL;
    - const char *title = NULL;
    - const char *secondary = NULL;
    - const char *name = NULL;
    - char *primary = NULL;
    - GDateTime *gmt = NULL;
    - gchar *gmt_str = NULL;
    -
    - ur = nm_find_user_record(user, nm_event_get_source(event));
    - if (ur)
    - name = nm_user_record_get_full_name(ur);
    -
    - if (name == NULL)
    - name = nm_event_get_source(event);
    -
    - gmt = g_date_time_new_from_unix_local(nm_event_get_gmt(event));
    - gmt_str = g_date_time_format(gmt, "%c");
    - title = _("Invitation to Conversation");
    - primary = g_strdup_printf(_("Invitation from: %s\n\nSent: %s"),
    - name, gmt_str);
    - secondary = _("Would you like to join the conversation?");
    -
    - /* Set up params list for the callbacks
    - * We need to send the NMUser object and
    - * the NMConference object to the callbacks
    - */
    - params = NULL;
    - params = g_slist_append(params, user);
    - params = g_slist_append(params, nm_event_get_conference(event));
    -
    - /* Prompt the user */
    - /* TODO: Would it be better to use purple_serv_got_chat_invite() here? */
    - gc = purple_account_get_connection(user->client_data);
    - purple_request_action(gc, title, primary, secondary,
    - PURPLE_DEFAULT_ACTION_NONE,
    - purple_request_cpar_from_connection(gc),
    - params, 2,
    - _("Yes"), G_CALLBACK(_join_conference_cb),
    - _("No"), G_CALLBACK(_reject_conference_cb));
    -
    - g_free(gmt_str);
    - g_date_time_unref(gmt);
    - g_free(primary);
    -}
    -
    -
    -static void
    -_evt_conference_joined(NMUser * user, NMEvent * event)
    -{
    - PurpleConversation *chat = NULL;
    - PurpleConnection *gc;
    - NMConference *conference = NULL;
    - NMUserRecord *ur = NULL;
    - const char *name;
    - const char *conf_name;
    -
    - gc = purple_account_get_connection(user->client_data);
    - if (gc == NULL)
    - return;
    -
    - conference = nm_event_get_conference(event);
    - if (conference) {
    - chat = nm_conference_get_data(conference);
    - if (nm_conference_get_participant_count(conference) == 2 && chat == NULL) {
    - ur = nm_conference_get_participant(conference, 0);
    - if (ur) {
    - conf_name = _get_conference_name(++user->conference_count);
    - chat =
    - purple_serv_got_joined_chat(gc, user->conference_count, conf_name);
    - if (chat) {
    -
    - nm_conference_set_data(conference, (gpointer) chat);
    -
    - name = nm_user_record_get_display_id(ur);
    - purple_chat_conversation_add_user(PURPLE_CHAT_CONVERSATION(chat), name, NULL,
    - PURPLE_CHAT_USER_NONE, TRUE);
    -
    - }
    - }
    - }
    -
    - if (chat != NULL) {
    - ur = nm_find_user_record(user, nm_event_get_source(event));
    - if (ur) {
    - name = nm_user_record_get_display_id(ur);
    - if (!purple_chat_conversation_has_user(PURPLE_CHAT_CONVERSATION(chat), name)) {
    - purple_chat_conversation_add_user(PURPLE_CHAT_CONVERSATION(chat), name, NULL,
    - PURPLE_CHAT_USER_NONE, TRUE);
    - }
    - }
    - }
    - }
    -}
    -
    -static void
    -_evt_status_change(NMUser * user, NMEvent * event)
    -{
    - PurpleBuddy *buddy = NULL;
    - GSList *buddies;
    - GSList *bnode;
    - NMUserRecord *user_record;
    - const char *display_id;
    - int status;
    -
    - user_record = nm_event_get_user_record(event);
    - if (user_record) {
    -
    - /* Retrieve new status */
    - status = nm_user_record_get_status(user_record);
    -
    - /* Update status for buddy in all folders */
    - display_id = nm_user_record_get_display_id(user_record);
    - buddies = purple_blist_find_buddies(user->client_data, display_id);
    - for (bnode = buddies; bnode; bnode = bnode->next) {
    - buddy = (PurpleBuddy *) bnode->data;
    - if (buddy) {
    - _update_buddy_status(user, buddy, status, nm_event_get_gmt(event));
    - }
    - }
    -
    - g_slist_free(buddies);
    -
    - }
    -}
    -
    -static void
    -_evt_user_disconnect(NMUser * user, NMEvent * event)
    -{
    - PurpleConnection *gc;
    - PurpleAccount *account = user->client_data;
    -
    - gc = purple_account_get_connection(account);
    - if (gc)
    - {
    - if (!purple_account_get_remember_password(account)) {
    - PurpleCredentialManager *manager = NULL;
    -
    - manager = purple_credential_manager_get_default();
    -
    - purple_credential_manager_clear_password_async(manager, account,
    - NULL, NULL, NULL);
    - }
    - purple_connection_error(gc,
    - PURPLE_CONNECTION_ERROR_NAME_IN_USE,
    - _("You have signed on from another location"));
    - }
    -}
    -
    -static void
    -_evt_user_typing(NMUser * user, NMEvent * event)
    -{
    - PurpleConnection *gc;
    - NMUserRecord *user_record = NULL;
    -
    - gc = purple_account_get_connection((PurpleAccount *) user->client_data);
    - if (gc) {
    - user_record = nm_find_user_record(user, nm_event_get_source(event));
    - if (user_record) {
    - purple_serv_got_typing(gc, nm_user_record_get_display_id(user_record),
    - 30, PURPLE_IM_TYPING);
    - }
    - }
    -}
    -
    -static void
    -_evt_user_not_typing(NMUser * user, NMEvent * event)
    -{
    - PurpleConnection *gc;
    - NMUserRecord *user_record;
    -
    - gc = purple_account_get_connection((PurpleAccount *) user->client_data);
    - if (gc) {
    - user_record = nm_find_user_record(user, nm_event_get_source(event));
    - if (user_record) {
    - purple_serv_got_typing_stopped(gc,
    - nm_user_record_get_display_id(user_record));
    - }
    - }
    -}
    -
    -static void
    -_evt_undeliverable_status(NMUser * user, NMEvent * event)
    -{
    - NMUserRecord *ur;
    - PurpleConversation *gconv;
    - char *str;
    -
    - ur = nm_find_user_record(user, nm_event_get_source(event));
    - if (ur) {
    - PurpleConversationManager *manager = NULL;
    -
    - manager = purple_conversation_manager_get_default();
    -
    - /* XXX - Should this be PURPLE_CONV_TYPE_IM? */
    - gconv = purple_conversation_manager_find(manager,
    - PURPLE_ACCOUNT(user->client_data),
    - nm_user_record_get_display_id(ur));
    - if (gconv) {
    - const char *name = nm_user_record_get_full_name(ur);
    -
    - if (name == NULL) {
    - name = nm_user_record_get_display_id(ur);
    - }
    - str = g_strdup_printf(_("%s appears to be offline and did not receive"
    - " the message that you just sent."), name);
    - purple_conversation_write_system_message(gconv, str, 0);
    - g_free(str);
    - }
    - }
    -}
    -
    -static void
    -_event_callback(NMUser * user, NMEvent * event)
    -{
    - if (user == NULL || event == NULL)
    - return;
    -
    - switch (nm_event_get_type(event)) {
    - case NMEVT_STATUS_CHANGE:
    - _evt_status_change(user, event);
    - break;
    - case NMEVT_RECEIVE_AUTOREPLY:
    - case NMEVT_RECEIVE_MESSAGE:
    - _evt_receive_message(user, event);
    - break;
    - case NMEVT_USER_DISCONNECT:
    - _evt_user_disconnect(user, event);
    - break;
    - case NMEVT_USER_TYPING:
    - _evt_user_typing(user, event);
    - break;
    - case NMEVT_USER_NOT_TYPING:
    - _evt_user_not_typing(user, event);
    - break;
    - case NMEVT_SERVER_DISCONNECT:
    - /* Nothing to do? */
    - break;
    - case NMEVT_INVALID_RECIPIENT:
    - break;
    - case NMEVT_UNDELIVERABLE_STATUS:
    - _evt_undeliverable_status(user, event);
    - break;
    - case NMEVT_CONFERENCE_INVITE_NOTIFY:
    - /* Someone else has been invited to join a
    - * conference that we are currently a part of
    - */
    - _evt_conference_invite_notify(user, event);
    - break;
    - case NMEVT_CONFERENCE_INVITE:
    - /* We have been invited to join a conference */
    - _evt_conference_invite(user, event);
    - break;
    - case NMEVT_CONFERENCE_JOINED:
    - /* Some one has joined a conference that we
    - * are a part of
    - */
    - _evt_conference_joined(user, event);
    - break;
    - case NMEVT_CONFERENCE_LEFT:
    - /* Someone else has left a conference that we
    - * are currently a part of
    - */
    - _evt_conference_left(user, event);
    - break;
    - default:
    - purple_debug_info("novell", "_event_callback(): unhandled event, %d",
    - nm_event_get_type(event));
    - break;
    - }
    -}
    -
    -/*******************************************************************************
    - * Protocol Ops
    - ******************************************************************************/
    -
    -static void
    -novell_login(G_GNUC_UNUSED PurpleProtocol *protocol, PurpleAccount *account) {
    - PurpleConnection *gc;
    - NMUser *user = NULL;
    - const char *server;
    - const char *name;
    - int port;
    - GError *error = NULL;
    -
    - if (account == NULL)
    - return;
    -
    - gc = purple_account_get_connection(account);
    - if (gc == NULL)
    - return;
    -
    - purple_connection_set_flags(gc, PURPLE_CONNECTION_FLAG_NO_IMAGES);
    -
    - server = purple_account_get_string(account, "server", NULL);
    - if (server == NULL || *server == '\0') {
    -
    - /* TODO: Would be nice to prompt if not set!
    - * purple_request_fields(gc, _("Server Address"),...);
    - */
    -
    - /* ...but for now just error out with a nice message. */
    - purple_connection_error(gc,
    - PURPLE_CONNECTION_ERROR_INVALID_SETTINGS,
    - _("Unable to connect to server. Please enter the "
    - "address of the server to which you wish to connect."));
    - return;
    - }
    -
    - port = purple_account_get_int(account, "port", DEFAULT_PORT);
    - name = purple_account_get_username(account);
    -
    - user = nm_initialize_user(name, server, port, account, _event_callback);
    - if (user && user->conn) {
    - /* save user */
    - purple_connection_set_protocol_data(gc, user);
    -
    - /* connect to the server */
    - user->conn->client = purple_gio_socket_client_new(account, &error);
    - if (user->conn->client == NULL) {
    - purple_connection_take_error(gc, error);
    - return;
    - }
    -
    - g_socket_client_set_tls(user->conn->client, TRUE);
    - g_socket_client_connect_to_host_async(
    - user->conn->client, user->conn->addr, user->conn->port,
    - user->cancellable, novell_login_callback, gc);
    - }
    -}
    -
    -static void
    -novell_close(G_GNUC_UNUSED PurpleProtocol *protocol, PurpleConnection * gc) {
    - NMUser *user;
    - NMConn *conn;
    -
    - if (gc == NULL)
    - return;
    -
    - user = purple_connection_get_protocol_data(gc);
    - if (user) {
    - conn = user->conn;
    - if (conn) {
    - nm_release_conn(conn);
    - }
    - nm_deinitialize_user(user);
    - }
    - purple_connection_set_protocol_data(gc, NULL);
    -}
    -
    -static int
    -novell_send_im(PurpleProtocolIM *im, PurpleConnection *gc, PurpleMessage *msg)
    -{
    - NMUserRecord *user_record = NULL;
    - NMConference *conf = NULL;
    - NMMessage *message;
    - NMUser *user;
    - const char *dn = NULL;
    - char *plain;
    - gboolean done = TRUE, created_conf = FALSE;
    - NMERR_T rc = NM_OK;
    - const gchar *name = purple_message_get_recipient(msg);
    -
    - if (gc == NULL || name == NULL || purple_message_is_empty(msg))
    - return 0;
    -
    - user = purple_connection_get_protocol_data(gc);
    - if (user == NULL)
    - return 0;
    -
    - /* Create a new message */
    - plain = purple_unescape_html(purple_message_get_contents(msg));
    - message = nm_create_message(plain);
    - g_free(plain);
    -
    - /* Need to get the DN for the buddy so we can look up the convo */
    - dn = nm_lookup_dn(user, name);
    -
    - /* Do we already know about the sender? */
    - user_record = nm_find_user_record(user, dn);
    - if (user_record) {
    -
    - /* Do we already have an instantiated conference? */
    - conf = nm_find_conversation(user, dn);
    - if (conf == NULL) {
    -
    - /* If not, create a blank conference */
    - conf = nm_create_conference(NULL);
    - created_conf = TRUE;
    -
    - nm_conference_add_participant(conf, user_record);
    - }
    -
    - nm_message_set_conference(message, conf);
    -
    - /* Make sure conference is instantiated */
    - if (!nm_conference_is_instantiated(conf)) {
    -
    - /* It is not, so send the createconf. We will
    - * have to finish sending the message when we
    - * get the response with the new conference guid.
    - */
    - rc = nm_send_create_conference(user, conf, _createconf_resp_send_msg, message);
    - _check_for_disconnect(user, rc);
    -
    - done = FALSE;
    - }
    -
    - } else {
    -
    - /* If we don't have details for the user, then we don't have
    - * a conference yet. So create one and send the getdetails
    - * to the server. We will have to finish sending the message
    - * when we get the response from the server.
    - */
    - conf = nm_create_conference(NULL);
    - created_conf = TRUE;
    -
    - nm_message_set_conference(message, conf);
    -
    - rc = nm_send_get_details(user, name, _get_details_resp_send_msg, message);
    - _check_for_disconnect(user, rc);
    -
    - done = FALSE;
    - }
    -
    - if (done) {
    -
    - /* Did we find everything we needed? */
    - rc = nm_send_message(user, message, _send_message_resp_cb);
    - _check_for_disconnect(user, rc);
    -
    - nm_release_message(message);
    - }
    -
    - if (created_conf && conf)
    - nm_release_conference(conf);
    -
    - return 1;
    -}
    -
    -static unsigned int
    -novell_send_typing(PurpleProtocolIM *im, PurpleConnection * gc, const char *name, PurpleIMTypingState state)
    -{
    - NMConference *conf = NULL;
    - NMUser *user;
    - const char *dn = NULL;
    - NMERR_T rc = NM_OK;
    -
    - if (gc == NULL || name == NULL)
    - return 0;
    -
    - user = purple_connection_get_protocol_data(gc);
    - if (user == NULL)
    - return 0;
    -
    - /* Need to get the DN for the buddy so we can look up the convo */
    - dn = nm_lookup_dn(user, name);
    - if (dn) {
    -
    - /* Now find the conference in our list */
    - conf = nm_find_conversation(user, dn);
    - if (conf) {
    -
    - rc = nm_send_typing(user, conf,
    - ((state == PURPLE_IM_TYPING) ? TRUE : FALSE), NULL);
    - _check_for_disconnect(user, rc);
    -
    - }
    -
    - }
    -
    - return 0;
    -}
    -
    -static void
    -novell_convo_closed(PurpleProtocolClient *client, PurpleConnection * gc,
    - const char *who)
    -{
    - NMUser *user;
    - NMConference *conf;
    - const char *dn;
    - NMERR_T rc = NM_OK;
    -
    - if (gc == NULL || who == NULL)
    - return;
    -
    - user = purple_connection_get_protocol_data(gc);
    - if (user && (dn = nm_lookup_dn(user, who))) {
    - conf = nm_find_conversation(user, dn);
    - if (conf) {
    - rc = nm_send_leave_conference(user, conf, NULL, NULL);
    - _check_for_disconnect(user, rc);
    - }
    - }
    -}
    -
    -static void
    -novell_chat_leave(PurpleProtocolChat *protocol_chat, PurpleConnection *gc,
    - gint id)
    -{
    - NMConference *conference;
    - NMUser *user;
    - PurpleChatConversation *chat;
    - GSList *cnode;
    - NMERR_T rc = NM_OK;
    -
    - if (gc == NULL)
    - return;
    -
    - user = purple_connection_get_protocol_data(gc);
    - if (user == NULL)
    - return;
    -
    - for (cnode = user->conferences; cnode != NULL; cnode = cnode->next) {
    - conference = cnode->data;
    - if (conference && (chat = nm_conference_get_data(conference))) {
    - if (purple_chat_conversation_get_id(chat) == id) {
    - rc = nm_send_leave_conference(user, conference, NULL, NULL);
    - _check_for_disconnect(user, rc);
    - break;
    - }
    - }
    - }
    -
    - purple_serv_got_chat_left(gc, id);
    -}
    -
    -static void
    -novell_chat_invite(PurpleProtocolChat *protocol_chat, PurpleConnection *gc,
    - gint id, const gchar *message, const gchar *who)
    -{
    - NMConference *conference;
    - NMUser *user;
    - PurpleChatConversation *chat;
    - GSList *cnode;
    - NMERR_T rc = NM_OK;
    - NMUserRecord *user_record = NULL;
    -
    - if (gc == NULL)
    - return;
    -
    - user = purple_connection_get_protocol_data(gc);
    - if (user == NULL)
    - return;
    -
    - user_record = nm_find_user_record(user, who);
    - if (user_record == NULL) {
    - rc = nm_send_get_details(user, who, _get_details_resp_send_invite, GINT_TO_POINTER(id));
    - _check_for_disconnect(user, rc);
    - return;
    - }
    -
    - for (cnode = user->conferences; cnode != NULL; cnode = cnode->next) {
    - conference = cnode->data;
    - if (conference && (chat = nm_conference_get_data(conference))) {
    - if (purple_chat_conversation_get_id(chat) == id) {
    - rc = nm_send_conference_invite(user, conference, user_record,
    - message, _sendinvite_resp_cb, NULL);
    - _check_for_disconnect(user, rc);
    - break;
    - }
    - }
    - }
    -}
    -
    -static gint
    -novell_chat_send(PurpleProtocolChat *protocol_chat, PurpleConnection *gc,
    - gint id, PurpleMessage *msg)
    -{
    - NMConference *conference;
    - PurpleConversation *chat;
    - PurpleConversationManager *manager = NULL;
    - GSList *cnode;
    - NMMessage *message;
    - NMUser *user;
    - NMERR_T rc = NM_OK;
    - const char *name;
    - char *str, *plain;
    -
    - if (gc == NULL || purple_message_is_empty(msg))
    - return -1;
    -
    - user = purple_connection_get_protocol_data(gc);
    - if (user == NULL)
    - return -1;
    -
    - plain = purple_unescape_html(purple_message_get_contents(msg));
    - message = nm_create_message(plain);
    - g_free(plain);
    -
    - for (cnode = user->conferences; cnode != NULL; cnode = cnode->next) {
    - conference = cnode->data;
    - if (conference && (chat = nm_conference_get_data(conference))) {
    - if (purple_chat_conversation_get_id(PURPLE_CHAT_CONVERSATION(chat)) == id) {
    -
    - nm_message_set_conference(message, conference);
    -
    - /* check to see if the conference is instatiated yet */
    - if (!nm_conference_is_instantiated(conference)) {
    - nm_message_add_ref(message);
    - nm_send_create_conference(user, conference, _createconf_resp_send_msg, message);
    - } else {
    - rc = nm_send_message(user, message, _send_message_resp_cb);
    - }
    -
    - nm_release_message(message);
    -
    - if (!_check_for_disconnect(user, rc)) {
    -
    - /* Use the account alias if it is set */
    - name = purple_account_get_private_alias(user->client_data);
    - if (name == NULL || *name == '\0') {
    -
    - /* If there is no account alias, try full name */
    - name = nm_user_record_get_full_name(user->user_record);
    - if (name == NULL || *name == '\0') {
    -
    - /* Fall back to the username that we are signed in with */
    - name = purple_account_get_username(user->client_data);
    - }
    - }
    -
    - purple_serv_got_chat_in(gc, id, name,
    - purple_message_get_flags(msg),
    - purple_message_get_contents(msg), time(NULL));
    - return 0;
    - } else
    - return -1;
    -
    - }
    - }
    - }
    -
    - manager = purple_conversation_manager_get_default();
    -
    - /* The conference was not found, must be closed */
    - chat = purple_conversation_manager_find_chat_by_id(manager,
    - purple_connection_get_account(gc),
    - id);
    - if (chat) {
    - str = g_strdup(_("This conference has been closed."
    - " No more messages can be sent."));
    - purple_conversation_write_system_message(chat, str, 0);
    - g_free(str);
    - }
    -
    - if (message)
    - nm_release_message(message);
    -
    - return -1;
    -}
    -
    -static void
    -novell_add_buddy(PurpleProtocolServer *protocol_server, PurpleConnection *gc,
    - PurpleBuddy *buddy, PurpleGroup * group, const gchar *message)
    -{
    - NMFolder *folder = NULL;
    - NMContact *contact;
    - NMUser *user;
    - NMERR_T rc = NM_OK;
    - const char *alias, *gname, *bname;
    -
    - if (gc == NULL || buddy == NULL || group == NULL)
    - return;
    -
    - user = (NMUser *) purple_connection_get_protocol_data(gc);
    - if (user == NULL)
    - return;
    -
    - /* If we haven't synched the contact list yet, ignore
    - * the add_buddy calls. Server side list is the master.
    - */
    - if (!user->clist_synched)
    - return;
    -
    - /* Don't re-add a buddy that is already on our contact list */
    - if (nm_find_user_record(user, purple_buddy_get_name(buddy)) != NULL)
    - return;
    -
    - contact = nm_create_contact();
    - nm_contact_set_dn(contact, purple_buddy_get_name(buddy));
    -
    - /* Remove the PurpleBuddy (we will add it back after adding it
    - * to the server side list). Save the alias if there is one.
    - */
    - alias = purple_buddy_get_alias(buddy);
    - bname = purple_buddy_get_name(buddy);
    - if (alias && !purple_strequal(alias, bname))
    - nm_contact_set_display_name(contact, alias);
    -
    - purple_blist_remove_buddy(buddy);
    - buddy = NULL;
    -
    - gname = purple_group_get_name(group);
    - if (purple_strequal(gname, NM_ROOT_FOLDER_NAME)) {
    - gname = "";
    - }
    -
    - folder = nm_find_folder(user, gname);
    - if (folder) {
    -
    - /* We have everything that we need, so send the createcontact */
    - rc = nm_send_create_contact(user, folder, contact,
    - _create_contact_resp_cb, contact);
    -
    - } else {
    -
    - /* Need to create the folder before we can add the contact */
    - rc = nm_send_create_folder(user, gname,
    - _create_folder_resp_add_contact, contact);
    - }
    -
    - _check_for_disconnect(user, rc);
    -
    -}
    -
    -static void
    -novell_remove_buddy(PurpleProtocolServer *protocol_server,
    - PurpleConnection *gc, PurpleBuddy *buddy,
    - PurpleGroup *group)
    -{
    - NMContact *contact;
    - NMFolder *folder;
    - NMUser *user;
    - const char *dn, *gname;
    - NMERR_T rc = NM_OK;
    -
    - if (gc == NULL || buddy == NULL || group == NULL)
    - return;
    -
    - user = purple_connection_get_protocol_data(gc);
    - if (user && (dn = nm_lookup_dn(user, purple_buddy_get_name(buddy)))) {
    - gname = purple_group_get_name(group);
    - if (purple_strequal(gname, NM_ROOT_FOLDER_NAME)) {
    - gname = "";
    - }
    - folder = nm_find_folder(user, gname);
    - if (folder) {
    - contact = nm_folder_find_contact(folder, dn);
    - if (contact) {
    -
    - /* Remove the buddy from the contact */
    - nm_contact_set_data(contact, NULL);
    -
    - /* Tell the server to remove the contact */
    - rc = nm_send_remove_contact(user, folder, contact,
    - _remove_contact_resp_cb, NULL);
    - _check_for_disconnect(user, rc);
    - }
    - }
    - }
    -}
    -
    -static void
    -novell_remove_group(PurpleProtocolServer *protocol_server,
    - PurpleConnection *gc, PurpleGroup *group)
    -{
    - NMUser *user;
    - NMERR_T rc = NM_OK;
    -
    - if (gc == NULL || group == NULL)
    - return;
    -
    - user = purple_connection_get_protocol_data(gc);
    - if (user) {
    - NMFolder *folder = nm_find_folder(user, purple_group_get_name(group));
    -
    - if (folder) {
    - rc = nm_send_remove_folder(user, folder,
    - _remove_folder_resp_cb, NULL);
    - _check_for_disconnect(user, rc);
    - }
    - }
    -}
    -
    -static void
    -novell_alias_buddy(PurpleProtocolServer *protocol_server, PurpleConnection *gc,
    - const gchar *name, const gchar *alias)
    -{
    - NMContact *contact;
    - NMUser *user;
    - GList *contacts = NULL;
    - GList *cnode = NULL;
    - const char *dn = NULL, *fname = NULL;
    - NMERR_T rc = NM_OK;
    -
    - if (gc == NULL || name == NULL || alias == NULL)
    - return;
    -
    - user = purple_connection_get_protocol_data(gc);
    - if (user && (dn = nm_lookup_dn(user, name))) {
    -
    - /* Alias all of instances of the contact */
    - contacts = nm_find_contacts(user, dn);
    - for (cnode = contacts; cnode != NULL; cnode = cnode->next) {
    - contact = (NMContact *) cnode->data;
    - if (contact) {
    - PurpleGroup *group = NULL;
    - PurpleBuddy *buddy;
    - NMFolder *folder;
    -
    - /* Alias the Purple buddy? */
    - folder = nm_find_folder_by_id(user,
    - nm_contact_get_parent_id(contact));
    - if (folder) {
    - fname = nm_folder_get_name(folder);
    - if (*fname == '\0') {
    - fname = NM_ROOT_FOLDER_NAME;
    - }
    - group = purple_blist_find_group(fname);
    - }
    -
    - if (group) {
    - const char *balias;
    - buddy = purple_blist_find_buddy_in_group(user->client_data,
    - name, group);
    - balias = buddy ? purple_buddy_get_local_alias(buddy) : NULL;
    - if (balias && !purple_strequal(balias, alias))
    - purple_buddy_set_local_alias(buddy, alias);
    - }
    -
    - /* Tell the server to alias the contact */
    - rc = nm_send_rename_contact(user, contact, alias,
    - _rename_contact_resp_cb, NULL);
    - _check_for_disconnect(user, rc);
    - }
    - }
    - if (contacts)
    - g_list_free(contacts);
    - }
    -}
    -
    -static void
    -novell_group_buddy(PurpleProtocolServer *protocol_server,
    - PurpleConnection *gc, const gchar *name,
    - const gchar *old_group_name, const gchar *new_group_name)
    -{
    - NMFolder *old_folder;
    - NMFolder *new_folder;
    - NMContact *contact;
    - NMUser *user;
    - const char *dn;
    - NMERR_T rc = NM_OK;
    -
    - if (gc == NULL || name == NULL ||
    - old_group_name == NULL || new_group_name == NULL)
    - return;
    -
    - user = purple_connection_get_protocol_data(gc);
    - if (user && (dn = nm_lookup_dn(user, name))) {
    -
    - /* Find the old folder */
    - if (purple_strequal(old_group_name, NM_ROOT_FOLDER_NAME)) {
    - old_folder = nm_get_root_folder(user);
    - if (nm_folder_find_contact(old_folder, dn) == NULL)
    - old_folder = nm_find_folder(user, old_group_name);
    - } else {
    - old_folder = nm_find_folder(user, old_group_name);
    - }
    -
    - if (old_folder && (contact = nm_folder_find_contact(old_folder, dn))) {
    -
    - /* Find the new folder */
    - new_folder = nm_find_folder(user, new_group_name);
    - if (new_folder == NULL) {
    - if (purple_strequal(new_group_name, NM_ROOT_FOLDER_NAME))
    - new_folder = nm_get_root_folder(user);
    - }
    -
    - if (new_folder) {
    -
    - /* Tell the server to move the contact to the new folder */
    - rc = nm_send_move_contact(user, contact, new_folder,
    - _move_contact_resp_cb, NULL);
    -
    - } else {
    -
    - nm_contact_add_ref(contact);
    -
    - /* Remove the old contact first */
    - nm_send_remove_contact(user, old_folder, contact,
    - _remove_contact_resp_cb, NULL);
    -
    - /* New folder does not exist yet, so create it */
    - rc = nm_send_create_folder(user, new_group_name,
    - _create_folder_resp_move_contact,
    - contact);
    - }
    -
    - _check_for_disconnect(user, rc);
    - }
    - }
    -}
    -
    -static void
    -novell_rename_group(PurpleProtocolServer *protocol_server,
    - PurpleConnection *gc, const gchar *old_name,
    - PurpleGroup *group, GList *moved_buddies)
    -{
    - NMERR_T rc = NM_OK;
    - NMFolder *folder;
    - NMUser *user;
    -
    - if (gc == NULL || old_name == NULL || group == NULL || moved_buddies == NULL) {
    - return;
    - }
    -
    - user = purple_connection_get_protocol_data(gc);
    - if (user) {
    - const char *gname = purple_group_get_name(group);
    - /* Does new folder exist already? */
    - if (nm_find_folder(user, gname)) {
    - /* purple_group_set_name() adds the buddies
    - * to the new group and removes the old group...
    - * so there is nothing more to do here.
    - */
    - return;
    - }
    -
    - if (purple_strequal(old_name, NM_ROOT_FOLDER_NAME)) {
    - /* Can't rename the root folder ... need to revisit this */
    - return;
    - }
    -
    - folder = nm_find_folder(user, old_name);
    - if (folder) {
    - rc = nm_send_rename_folder(user, folder, gname,
    - _rename_folder_resp_cb, NULL);
    - _check_for_disconnect(user, rc);
    - }
    - }
    -}
    -
    -static GList *
    -novell_protocol_get_account_options(PurpleProtocol *protocol) {
    - PurpleAccountOption *option;
    - GList *opts = NULL;
    -
    - option = purple_account_option_string_new(_("Server address"), "server",
    - NULL);
    - opts = g_list_append(opts, option);
    -
    - option = purple_account_option_int_new(_("Server port"), "port",
    - DEFAULT_PORT);
    - opts = g_list_append(opts, option);
    -
    - return opts;
    -}
    -
    -static void
    -novell_tooltip_text(PurpleProtocolClient *client, PurpleBuddy *buddy,
    - PurpleNotifyUserInfo *user_info, gboolean full)
    -{
    - NMUserRecord *user_record = NULL;
    - PurpleConnection *gc;
    - NMUser *user;
    - int status = 0;
    - const char *status_str = NULL;
    - const char *text = NULL;
    -
    - if (buddy == NULL)
    - return;
    -
    - gc = purple_account_get_connection(purple_buddy_get_account(buddy));
    - if (gc == NULL || (user = purple_connection_get_protocol_data(gc)) == NULL)
    - return;
    -
    - if (PURPLE_BUDDY_IS_ONLINE(buddy)) {
    - user_record = nm_find_user_record(user, purple_buddy_get_name(buddy));
    - if (user_record) {
    - status = nm_user_record_get_status(user_record);
    - text = nm_user_record_get_status_text(user_record);
    - /* No custom text, so default it ... */
    - switch (status) {
    - case NM_STATUS_AVAILABLE:
    - status_str = _("Available");
    - break;
    - case NM_STATUS_AWAY:
    - status_str = _("Away");
    - break;
    - case NM_STATUS_BUSY:
    - status_str = _("Busy");
    - break;
    - case NM_STATUS_AWAY_IDLE:
    - status_str = _("Idle");
    - break;
    - case NM_STATUS_OFFLINE:
    - status_str = _("Offline");
    - break;
    - default:
    - status_str = _("Unknown");
    - break;
    - }
    -
    - purple_notify_user_info_add_pair_plaintext(user_info, _("Status"), status_str);
    -
    - if (text) {
    - /* 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, _("Message"), text);
    - }
    - }
    - }
    -}
    -
    -static void
    -novell_set_idle(PurpleProtocolServer *protocol_server, PurpleConnection *gc,
    - gint time)
    -{
    - NMUser *user;
    - NMERR_T rc = NM_OK;
    - const char *id = NULL;
    - PurpleStatus *status = NULL;
    -
    - if (gc == NULL)
    - return;
    -
    - user = purple_connection_get_protocol_data(gc);
    - if (user == NULL)
    - return;
    -
    - status = purple_account_get_active_status(purple_connection_get_account(gc));
    - id = purple_status_get_id(status);
    -
    - /* Only go idle if active status is available */
    - if (purple_strequal(id, NOVELL_STATUS_TYPE_AVAILABLE)) {
    - if (time > 0) {
    - rc = nm_send_set_status(user, NM_STATUS_AWAY_IDLE, NULL, NULL, NULL, NULL);
    - } else {
    - rc = nm_send_set_status(user, NM_STATUS_AVAILABLE, NULL, NULL, NULL, NULL);
    - }
    - }
    -
    - _check_for_disconnect(user, rc);
    -}
    -
    -static void
    -novell_get_info(PurpleProtocolServer *protocol_server, PurpleConnection * gc,
    - const gchar *name)
    -{
    - NMUserRecord *user_record;
    - NMUser *user;
    - NMERR_T rc;
    -
    - if (gc == NULL || name == NULL)
    - return;
    -
    - user = purple_connection_get_protocol_data(gc);
    - if (user) {
    -
    - user_record = nm_find_user_record(user, name);
    - if (user_record) {
    - _show_info(gc, user_record, g_strdup(name));
    -
    - } else {
    - rc = nm_send_get_details(user, name,
    - _get_details_resp_show_info, g_strdup(name));
    -
    - _check_for_disconnect(user, rc);
    -
    - }
    -
    - }
    -}
    -
    -static char *
    -novell_status_text(PurpleProtocolClient *client, PurpleBuddy * buddy)
    -{
    - const char *text = NULL;
    - const char *dn = NULL;
    - PurpleAccount *account;
    -
    - account = buddy ? purple_buddy_get_account(buddy) : NULL;
    - if (buddy && account) {
    - PurpleConnection *gc = purple_account_get_connection(account);
    -
    - if (gc) {
    - NMUser *user = purple_connection_get_protocol_data(gc);
    -
    - if (user) {
    - dn = nm_lookup_dn(user, purple_buddy_get_name(buddy));
    - if (dn) {
    - NMUserRecord *user_record = nm_find_user_record(user, dn);
    -
    - if (user_record) {
    - text = nm_user_record_get_status_text(user_record);
    - if (text)
    - return g_strdup(text);
    - }
    - }
    - }
    - }
    - }
    -
    - return NULL;
    -}
    -
    -static GList *
    -novell_status_types(G_GNUC_UNUSED PurpleProtocol *protocol,
    - PurpleAccount *account)
    -{
    - GList *status_types = NULL;
    - PurpleStatusType *type;
    -
    - g_return_val_if_fail(account != NULL, NULL);
    -
    - type = purple_status_type_new_with_attrs(PURPLE_STATUS_AVAILABLE, NOVELL_STATUS_TYPE_AVAILABLE,
    - NULL, TRUE, TRUE, FALSE,
    - "message", _("Message"), purple_value_new(G_TYPE_STRING),
    - NULL);
    - status_types = g_list_append(status_types, type);
    -
    - type = purple_status_type_new_with_attrs(PURPLE_STATUS_AWAY, NOVELL_STATUS_TYPE_AWAY,
    - NULL, TRUE, TRUE, FALSE,
    - "message", _("Message"), purple_value_new(G_TYPE_STRING),
    - NULL);
    - status_types = g_list_append(status_types, type);
    -
    - type = purple_status_type_new_with_attrs(PURPLE_STATUS_UNAVAILABLE, NOVELL_STATUS_TYPE_BUSY,
    - _("Busy"), TRUE, TRUE, FALSE,
    - "message", _("Message"), purple_value_new(G_TYPE_STRING),
    - NULL);
    - status_types = g_list_append(status_types, type);
    -
    - type = purple_status_type_new_full(PURPLE_STATUS_INVISIBLE, NOVELL_STATUS_TYPE_APPEAR_OFFLINE,
    - NULL, TRUE, TRUE, FALSE);
    - status_types = g_list_append(status_types, type);
    -
    - type = purple_status_type_new_full(PURPLE_STATUS_OFFLINE, NULL, NULL, TRUE, TRUE, FALSE);
    - status_types = g_list_append(status_types, type);
    -
    - return status_types;
    -}
    -
    -static void
    -novell_set_status(PurpleProtocolServer *protocol_server,
    - PurpleAccount *account, PurpleStatus *status)
    -{
    - PurpleConnection *gc;
    - gboolean connected;
    - PurplePresence *presence;
    - PurpleStatusType *type;
    - PurpleStatusPrimitive primitive;
    - NMUser *user;
    - NMSTATUS_T novellstatus = NM_STATUS_AVAILABLE;
    - NMERR_T rc = NM_OK;
    - const char *msg = NULL;
    - char *text = NULL;
    -
    - connected = purple_account_is_connected(account);
    - presence = purple_status_get_presence(status);
    - type = purple_status_get_status_type(status);
    - primitive = purple_status_type_get_primitive(type);
    -
    - /*
    - * We don't have any independent statuses, so we don't need to
    - * do anything when a status is deactivated (because another
    - * status is about to be activated).
    - */
    - if (!purple_status_is_active(status))
    - return;
    -
    - if (!connected)
    - return;
    -
    - gc = purple_account_get_connection(account);
    - user = purple_connection_get_protocol_data(gc);
    - if (user == NULL)
    - return;
    -
    - if (primitive == PURPLE_STATUS_AVAILABLE) {
    - novellstatus = NM_STATUS_AVAILABLE;
    - } else if (primitive == PURPLE_STATUS_AWAY) {
    - novellstatus = NM_STATUS_AWAY;
    - } else if (primitive == PURPLE_STATUS_UNAVAILABLE) {
    - novellstatus = NM_STATUS_BUSY;
    - } else if (primitive == PURPLE_STATUS_INVISIBLE) {
    - novellstatus = NM_STATUS_OFFLINE;
    - } else if (purple_presence_is_idle(presence)) {
    - novellstatus = NM_STATUS_AWAY_IDLE;
    - } else {
    - novellstatus = NM_STATUS_AVAILABLE;
    - }
    -
    - if (primitive == PURPLE_STATUS_AWAY || primitive == PURPLE_STATUS_AVAILABLE ||
    - primitive == PURPLE_STATUS_UNAVAILABLE) {
    - msg = purple_status_get_attr_string(status, "message");
    - text = g_strdup(msg);
    -
    - if (primitive == PURPLE_STATUS_AVAILABLE)
    - msg = NULL; /* no auto replies for online status */
    -
    - /* Don't want newlines in status text */
    - purple_util_chrreplace(text, '\n', ' ');
    - }
    -
    - rc = nm_send_set_status(user, novellstatus, text, msg, NULL, NULL);
    - _check_for_disconnect(user, rc);
    -
    - g_free(text);
    -}
    -
    -static void
    -novell_add_permit(PurpleProtocolPrivacy *privacy, PurpleConnection *gc,
    - const char *who)
    -{
    - NMUser *user;
    - NMERR_T rc = NM_OK;
    - const char *name = who;
    -
    - if (gc == NULL || who == NULL)
    - return;
    -
    - user = purple_connection_get_protocol_data(gc);
    - if (user == NULL)
    - return;
    -
    - /* Remove first -- we will add it back in when we get
    - * the okay from the server
    - */
    - purple_account_privacy_permit_remove(purple_connection_get_account(gc), who, TRUE);
    -
    - if (nm_user_is_privacy_locked(user)) {
    - _show_privacy_locked_error(gc, user);
    - _sync_privacy_lists(user);
    - return;
    - }
    -
    - /* Work around for problem with un-typed, dotted contexts */
    - if (strchr(who, '.')) {
    - const char *dn = nm_lookup_dn(user, who);
    - if (dn == NULL) {
    - rc = nm_send_get_details(user, who,
    - _get_details_send_privacy_create,
    - GINT_TO_POINTER(TRUE));
    - _check_for_disconnect(user, rc);
    - return;
    - } else {
    - name = dn;
    - }
    - }
    -
    - rc = nm_send_create_privacy_item(user, name, TRUE,
    - _create_privacy_item_permit_resp_cb,
    - g_strdup(who));
    - _check_for_disconnect(user, rc);
    -}
    -
    -static void
    -novell_add_deny(PurpleProtocolPrivacy *privacy, PurpleConnection *gc,
    - const char *who)
    -{
    - NMUser *user;
    - NMERR_T rc = NM_OK;
    - const char *name = who;
    -
    - if (gc == NULL || who == NULL)
    - return;
    -
    - user = purple_connection_get_protocol_data(gc);
    - if (user == NULL)
    - return;
    -
    - /* Remove first -- we will add it back in when we get
    - * the okay from the server
    - */
    - purple_account_privacy_deny_remove(purple_connection_get_account(gc), who, TRUE);
    -
    - if (nm_user_is_privacy_locked(user)) {
    - _show_privacy_locked_error(gc, user);
    - _sync_privacy_lists(user);
    - return;
    - }
    -
    - /* Work around for problem with un-typed, dotted contexts */
    - if (strchr(who, '.')) {
    - const char *dn = nm_lookup_dn(user, who);
    - if (dn == NULL) {
    - rc = nm_send_get_details(user, who,
    - _get_details_send_privacy_create,
    - GINT_TO_POINTER(FALSE));
    - _check_for_disconnect(user, rc);
    - return;
    - } else {
    - name = dn;
    - }
    - }
    -
    - rc = nm_send_create_privacy_item(user, name, FALSE,
    - _create_privacy_item_deny_resp_cb,
    - g_strdup(who));
    - _check_for_disconnect(user, rc);
    -}
    -
    -static void
    -novell_remove_permit(PurpleProtocolPrivacy *privacy, PurpleConnection *gc,
    - const char *who)
    -{
    - NMUser *user;
    - NMERR_T rc = NM_OK;
    - const char *dn = NULL;
    -
    - if (gc == NULL || who == NULL)
    - return;
    -
    - user = purple_connection_get_protocol_data(gc);
    - if (user == NULL)
    - return;
    -
    - if (nm_user_is_privacy_locked(user)) {
    - _show_privacy_locked_error(gc, user);
    - _sync_privacy_lists(user);
    - return;
    - }
    -
    - dn = nm_lookup_dn(user, who);
    - if (dn == NULL)
    - dn = who;
    -
    - rc = nm_send_remove_privacy_item(user, dn, TRUE,
    - _remove_privacy_item_resp_cb,
    - g_strdup(who));
    - _check_for_disconnect(user, rc);
    -}
    -
    -static void
    -novell_remove_deny(PurpleProtocolPrivacy *privacy, PurpleConnection *gc,
    - const char *who)
    -{
    - NMUser *user;
    - NMERR_T rc = NM_OK;
    - const char *dn = NULL;
    -
    - if (gc == NULL || who == NULL)
    - return;
    -
    - user = purple_connection_get_protocol_data(gc);
    - if (user == NULL)
    - return;
    -
    - if (nm_user_is_privacy_locked(user)) {
    - _show_privacy_locked_error(gc, user);
    - _sync_privacy_lists(user);
    - return;
    - }
    -
    - dn = nm_lookup_dn(user, who);
    - if (dn == NULL)
    - dn = who;
    -
    - rc = nm_send_remove_privacy_item(user, dn, FALSE,
    - _remove_privacy_item_resp_cb,
    - g_strdup(who));
    - _check_for_disconnect(user, rc);
    -}
    -
    -static void
    -novell_set_permit_deny(PurpleProtocolPrivacy *privacy, PurpleConnection *gc)
    -{
    - NMERR_T rc = NM_OK;
    - const char *dn, *name = NULL;
    - NMUserRecord *user_record = NULL;
    - GSList *node = NULL, *copy = NULL;
    - NMUser *user;
    - int i, j, num_contacts, num_folders;
    - NMContact *contact;
    - NMFolder *folder = NULL;
    - PurpleAccount *account;
    -
    - if (gc == NULL)
    - return;
    -
    - account = purple_connection_get_account(gc);
    -
    - user = purple_connection_get_protocol_data(gc);
    - if (user == NULL)
    - return;
    -
    - if (user->privacy_synched == FALSE) {
    - _sync_privacy_lists(user);
    - user->privacy_synched = TRUE;
    - return;
    - }
    -
    - if (nm_user_is_privacy_locked(user)) {
    - _show_privacy_locked_error(gc, user);
    - _sync_privacy_lists(user);
    - return;
    - }
    -
    - switch (purple_account_get_privacy_type(account)) {
    -
    - case PURPLE_ACCOUNT_PRIVACY_ALLOW_ALL:
    - rc = nm_send_set_privacy_default(user, FALSE,
    - _set_privacy_default_resp_cb, NULL);
    - _check_for_disconnect(user, rc);
    -
    - /* clear server side deny list */
    - if (rc == NM_OK) {
    - copy = g_slist_copy(user->deny_list);
    - for (node = copy; node && node->data; node = node->next) {
    - rc = nm_send_remove_privacy_item(user, (const char *)node->data,
    - FALSE, NULL, NULL);
    - if (_check_for_disconnect(user, rc))
    - break;
    - }
    - g_slist_free(copy);
    - g_slist_free(user->deny_list);
    - user->deny_list = NULL;
    - }
    - break;
    -
    - case PURPLE_ACCOUNT_PRIVACY_DENY_ALL:
    - rc = nm_send_set_privacy_default(user, TRUE,
    - _set_privacy_default_resp_cb, NULL);
    - _check_for_disconnect(user, rc);
    -
    - /* clear server side allow list */
    - if (rc == NM_OK) {
    - copy = g_slist_copy(user->allow_list);
    - for (node = copy; node && node->data; node = node->next) {
    - rc = nm_send_remove_privacy_item(user, (const char *)node->data,
    - TRUE, NULL, NULL);
    - if (_check_for_disconnect(user, rc))
    - break;
    - }
    - g_slist_free(copy);
    - g_slist_free(user->allow_list);
    - user->allow_list = NULL;
    - }
    - break;
    -
    - case PURPLE_ACCOUNT_PRIVACY_ALLOW_USERS:
    -
    - rc = nm_send_set_privacy_default(user, TRUE,
    - _set_privacy_default_resp_cb, NULL);
    - _check_for_disconnect(user, rc);
    -
    - /* sync allow lists */
    - if (rc == NM_OK) {
    -
    - for (node = user->allow_list; node; node = node->next) {
    - user_record = nm_find_user_record(user, (char *)node->data);
    - if (user_record) {
    - name = nm_user_record_get_display_id(user_record);
    -
    - if (!g_slist_find_custom(purple_account_privacy_get_permitted(account),
    - name, (GCompareFunc)purple_utf8_strcasecmp)) {
    - purple_account_privacy_permit_add(account, name , TRUE);
    - }
    - }
    - }
    -
    - for (node = purple_account_privacy_get_permitted(account); node; node = node->next) {
    - dn = nm_lookup_dn(user, (char *)node->data);
    - if (dn) {
    -
    - if (!g_slist_find_custom(user->allow_list,
    - dn, (GCompareFunc)purple_utf8_strcasecmp)) {
    - rc = nm_send_create_privacy_item(user, dn, TRUE,
    - _create_privacy_item_deny_resp_cb,
    - g_strdup(dn));
    - _check_for_disconnect(user, rc);
    - }
    - } else {
    - purple_account_privacy_permit_remove(account, (char *)node->data, TRUE);
    - }
    - }
    - }
    - break;
    -
    - case PURPLE_ACCOUNT_PRIVACY_DENY_USERS:
    -
    - /* set to default allow */
    - rc = nm_send_set_privacy_default(user, FALSE,
    - _set_privacy_default_resp_cb, NULL);
    - _check_for_disconnect(user, rc);
    -
    - /* sync deny lists */
    - if (rc == NM_OK) {
    -
    - for (node = user->deny_list; node; node = node->next) {
    - user_record = nm_find_user_record(user, (char *)node->data);
    - if (user_record) {
    - name = nm_user_record_get_display_id(user_record);
    -
    - if (!g_slist_find_custom(purple_account_privacy_get_denied(account),
    - name, (GCompareFunc)purple_utf8_strcasecmp)) {
    - purple_account_privacy_deny_add(account, name , TRUE);
    - }
    - }
    - }
    -
    - for (node = purple_account_privacy_get_denied(account); node; node = node->next) {
    -
    - name = NULL;
    - dn = nm_lookup_dn(user, (char *)node->data);
    - if (dn) {
    - user_record = nm_find_user_record(user, dn);
    - name = nm_user_record_get_display_id(user_record);
    -
    - if (!g_slist_find_custom(user->deny_list,
    - dn, (GCompareFunc)purple_utf8_strcasecmp)) {
    - rc = nm_send_create_privacy_item(user, dn, FALSE,
    - _create_privacy_item_deny_resp_cb,
    - g_strdup(name));
    - _check_for_disconnect(user, rc);
    - }
    - } else {
    - purple_account_privacy_deny_remove(account, (char *)node->data, TRUE);
    - }
    - }
    -
    - }
    - break;
    -
    - case PURPLE_ACCOUNT_PRIVACY_ALLOW_BUDDYLIST:
    -
    - /* remove users from allow list that are not in buddy list */
    - copy = g_slist_copy(user->allow_list);
    - for (node = copy; node && node->data; node = node->next) {
    - if (!nm_find_contacts(user, node->data)) {
    - rc = nm_send_remove_privacy_item(user, (const char *)node->data,
    - TRUE, NULL, NULL);
    - if (_check_for_disconnect(user, rc))
    - return;
    - }
    - }
    - g_slist_free(copy);
    -
    - /* add all buddies to allow list */
    - num_contacts = nm_folder_get_contact_count(user->root_folder);
    - for (i = 0; i < num_contacts; i++) {
    - contact = nm_folder_get_contact(user->root_folder, i);
    - dn = nm_contact_get_dn(contact);
    - if (dn && !g_slist_find_custom(user->allow_list,
    - dn, (GCompareFunc)purple_utf8_strcasecmp))
    - {
    - rc = nm_send_create_privacy_item(user, dn, TRUE,
    - _create_privacy_item_deny_resp_cb,
    - g_strdup(dn));
    - if (_check_for_disconnect(user, rc))
    - return;
    - }
    -
    - }
    -
    - num_folders = nm_folder_get_subfolder_count(user->root_folder);
    - for (i = 0; i < num_folders; i++) {
    - folder = nm_folder_get_subfolder(user->root_folder, i);
    - num_contacts = nm_folder_get_contact_count(folder);
    - for (j = 0; j < num_contacts; j++) {
    - contact = nm_folder_get_contact(folder, j);
    - dn = nm_contact_get_dn(contact);
    - if (dn && !g_slist_find_custom(user->allow_list,
    - dn, (GCompareFunc)purple_utf8_strcasecmp))
    - {
    - rc = nm_send_create_privacy_item(user, dn, TRUE,
    - _create_privacy_item_deny_resp_cb,
    - g_strdup(dn));
    - if (_check_for_disconnect(user, rc))
    - return;
    - }
    - }
    - }
    -
    - /* set to default deny */
    - rc = nm_send_set_privacy_default(user, TRUE,
    - _set_privacy_default_resp_cb, NULL);
    - if (_check_for_disconnect(user, rc))
    - break;
    -
    - break;
    - }
    -}
    -
    -static GList *
    -novell_blist_node_menu(PurpleProtocolClient *client, PurpleBlistNode *node)
    -{
    - GList *list = NULL;
    - PurpleActionMenu *act;
    -
    - if(PURPLE_IS_BUDDY(node)) {
    - act = purple_action_menu_new(_("Initiate _Chat"),
    - G_CALLBACK(_initiate_conference_cb),
    - NULL, NULL);
    - list = g_list_append(list, act);
    - }
    -
    - return list;
    -}
    -
    -static void
    -novell_keepalive(PurpleProtocolServer *protocol_server, PurpleConnection *gc) {
    - NMUser *user;
    - NMERR_T rc = NM_OK;
    -
    - if (gc == NULL)
    - return;
    -
    - user = purple_connection_get_protocol_data(gc);
    - if (user == NULL)
    - return;
    -
    - rc = nm_send_keepalive(user, NULL, NULL);
    - _check_for_disconnect(user, rc);
    -}
    -
    -static const char *
    -novell_normalize(PurpleProtocolClient *client, PurpleAccount *account,
    - const char *who)
    -{
    - return purple_normalize_nocase(who);
    -}
    -
    -static gssize
    -novell_get_max_message_size(PurpleProtocolClient *client,
    - PurpleConversation *conv)
    -{
    - /* XXX: got from pidgin-otr - verify and document it */
    - return 1792;
    -}
    -
    -static void
    -novell_protocol_init(NovellProtocol *self)
    -{
    -}
    -
    -static void
    -novell_protocol_class_init(NovellProtocolClass *klass)
    -{
    - PurpleProtocolClass *protocol_class = PURPLE_PROTOCOL_CLASS(klass);
    -
    - protocol_class->login = novell_login;
    - protocol_class->close = novell_close;
    - protocol_class->status_types = novell_status_types;
    -
    - protocol_class->get_account_options = novell_protocol_get_account_options;
    -}
    -
    -static void
    -novell_protocol_class_finalize(G_GNUC_UNUSED NovellProtocolClass *klass)
    -{
    -}
    -
    -static void
    -novell_protocol_client_iface_init(PurpleProtocolClientInterface *client_iface)
    -{
    - client_iface->status_text = novell_status_text;
    - client_iface->tooltip_text = novell_tooltip_text;
    - client_iface->blist_node_menu = novell_blist_node_menu;
    - client_iface->convo_closed = novell_convo_closed;
    - client_iface->normalize = novell_normalize;
    - client_iface->get_max_message_size = novell_get_max_message_size;
    -}
    -
    -static void
    -novell_protocol_server_iface_init(PurpleProtocolServerInterface *server_iface)
    -{
    - server_iface->get_info = novell_get_info;
    - server_iface->set_status = novell_set_status;
    - server_iface->set_idle = novell_set_idle;
    - server_iface->add_buddy = novell_add_buddy;
    - server_iface->remove_buddy = novell_remove_buddy;
    - server_iface->keepalive = novell_keepalive;
    - server_iface->alias_buddy = novell_alias_buddy;
    - server_iface->group_buddy = novell_group_buddy;
    - server_iface->rename_group = novell_rename_group;
    - server_iface->remove_group = novell_remove_group;
    -}
    -
    -static void
    -novell_protocol_im_iface_init(PurpleProtocolIMInterface *im_iface)
    -{
    - im_iface->send = novell_send_im;
    - im_iface->send_typing = novell_send_typing;
    -}
    -
    -static void
    -novell_protocol_chat_iface_init(PurpleProtocolChatInterface *chat_iface)
    -{
    - chat_iface->invite = novell_chat_invite;
    - chat_iface->leave = novell_chat_leave;
    - chat_iface->send = novell_chat_send;
    -}
    -
    -static void
    -novell_protocol_privacy_iface_init(PurpleProtocolPrivacyInterface *privacy_iface)
    -{
    - privacy_iface->add_permit = novell_add_permit;
    - privacy_iface->add_deny = novell_add_deny;
    - privacy_iface->remove_permit = novell_remove_permit;
    - privacy_iface->remove_deny = novell_remove_deny;
    - privacy_iface->set_permit_deny = novell_set_permit_deny;
    -}
    -
    -G_DEFINE_DYNAMIC_TYPE_EXTENDED(
    - NovellProtocol,
    - novell_protocol,
    - PURPLE_TYPE_PROTOCOL,
    - G_TYPE_FLAG_FINAL,
    - G_IMPLEMENT_INTERFACE_DYNAMIC(PURPLE_TYPE_PROTOCOL_CLIENT,
    - novell_protocol_client_iface_init)
    - G_IMPLEMENT_INTERFACE_DYNAMIC(PURPLE_TYPE_PROTOCOL_SERVER,
    - novell_protocol_server_iface_init)
    - G_IMPLEMENT_INTERFACE_DYNAMIC(PURPLE_TYPE_PROTOCOL_IM,
    - novell_protocol_im_iface_init)
    - G_IMPLEMENT_INTERFACE_DYNAMIC(PURPLE_TYPE_PROTOCOL_CHAT,
    - novell_protocol_chat_iface_init)
    - G_IMPLEMENT_INTERFACE_DYNAMIC(PURPLE_TYPE_PROTOCOL_PRIVACY,
    - novell_protocol_privacy_iface_init))
    -
    -static PurpleProtocol *
    -novell_protocol_new(void) {
    - return PURPLE_PROTOCOL(g_object_new(
    - NOVELL_TYPE_PROTOCOL,
    - "id", "prpl-novell",
    - "name", "GroupWise",
    - "description", _("GroupWise is a messaging and collaboration "
    - "platform from Micro Focus."),
    - "icon-name", "im-novell",
    - "icon-resource-path", "/im/pidgin/libpurple/novell/icons",
    - NULL));
    -}
    -
    -static GPluginPluginInfo *
    -novell_query(GError **error)
    -{
    - return purple_plugin_info_new(
    - "id", "prpl-novell",
    - "name", "Novell GroupWise Protocol",
    - "version", DISPLAY_VERSION,
    - "category", N_("Protocol"),
    - "summary", N_("Novell GroupWise Messenger Protocol Plugin"),
    - "description", N_("Novell GroupWise Messenger Protocol Plugin"),
    - "website", PURPLE_WEBSITE,
    - "abi-version", PURPLE_ABI_VERSION,
    - "flags", PURPLE_PLUGIN_INFO_FLAGS_INTERNAL |
    - PURPLE_PLUGIN_INFO_FLAGS_AUTO_LOAD,
    - NULL
    - );
    -}
    -
    -static gboolean
    -novell_load(GPluginPlugin *plugin, GError **error)
    -{
    - PurpleProtocolManager *manager = purple_protocol_manager_get_default();
    -
    - novell_protocol_register_type(G_TYPE_MODULE(plugin));
    -
    - my_protocol = novell_protocol_new();
    - if(!purple_protocol_manager_register(manager, my_protocol, error)) {
    - g_clear_object(&my_protocol);
    -
    - return FALSE;
    - }
    -
    - return TRUE;
    -}
    -
    -static gboolean
    -novell_unload(GPluginPlugin *plugin, gboolean shutdown, GError **error)
    -{
    - PurpleProtocolManager *manager = purple_protocol_manager_get_default();
    -
    - if(!purple_protocol_manager_unregister(manager, my_protocol, error)) {
    - return FALSE;
    - }
    -
    - g_clear_object(&my_protocol);
    -
    - return TRUE;
    -}
    -
    -GPLUGIN_NATIVE_PLUGIN_DECLARE(novell)
    --- a/libpurple/protocols/novell/novell.h Fri Nov 11 19:39:24 2022 -0600
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,32 +0,0 @@
    -/* purple
    - *
    - * 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, write to the Free Software
    - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    - *
    - */
    -
    -#ifndef PURPLE_NOVELL_NOVELL_H
    -#define PURPLE_NOVELL_NOVELL_H
    -
    -#include <purple.h>
    -
    -#define NOVELL_TYPE_PROTOCOL (novell_protocol_get_type())
    -G_DECLARE_FINAL_TYPE(NovellProtocol, novell_protocol, NOVELL, PROTOCOL,
    - PurpleProtocol)
    -
    -#endif /* PURPLE_NOVELL_NOVELL_H */
    Binary file libpurple/protocols/novell/resources/icons/16x16/apps/im-novell.png has changed
    --- a/libpurple/protocols/novell/resources/icons/16x16/apps/scalable/im-novell.svg Fri Nov 11 19:39:24 2022 -0600
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,163 +0,0 @@
    -<?xml version="1.0" encoding="UTF-8" standalone="no"?>
    -<!-- Created with Inkscape (http://www.inkscape.org/) -->
    -<svg
    - xmlns:dc="http://purl.org/dc/elements/1.1/"
    - xmlns:cc="http://creativecommons.org/ns#"
    - xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
    - xmlns:svg="http://www.w3.org/2000/svg"
    - xmlns="http://www.w3.org/2000/svg"
    - xmlns:xlink="http://www.w3.org/1999/xlink"
    - xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
    - xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
    - width="16px"
    - height="16px"
    - id="svg4248"
    - sodipodi:version="0.32"
    - inkscape:version="0.46"
    - sodipodi:docbase="/home/hbons/GUI/Tango/Gaim Refresh/protocols/16/scalable"
    - sodipodi:docname="novell.svg"
    - inkscape:export-filename="/home/hbons/Bureaublad/Projecten/Pidgin/pidgin-mtn/pidgin/pixmaps/protocols/16/novell.png"
    - inkscape:export-xdpi="90"
    - inkscape:export-ydpi="90"
    - inkscape:output_extension="org.inkscape.output.svg.inkscape">
    - <defs
    - id="defs4250">
    - <linearGradient
    - inkscape:collect="always"
    - id="linearGradient8681">
    - <stop
    - style="stop-color:#b90000;stop-opacity:1"
    - offset="0"
    - id="stop8683" />
    - <stop
    - style="stop-color:#660000;stop-opacity:1"
    - offset="1"
    - id="stop8685" />
    - </linearGradient>
    - <linearGradient
    - inkscape:collect="always"
    - id="linearGradient8673">
    - <stop
    - style="stop-color:#ffffff;stop-opacity:1;"
    - offset="0"
    - id="stop8675" />
    - <stop
    - style="stop-color:#ffffff;stop-opacity:0;"
    - offset="1"
    - id="stop8677" />
    - </linearGradient>
    - <linearGradient
    - inkscape:collect="always"
    - id="linearGradient8657">
    - <stop
    - style="stop-color:#ffffff;stop-opacity:1;"
    - offset="0"
    - id="stop8659" />
    - <stop
    - style="stop-color:#ffffff;stop-opacity:0;"
    - offset="1"
    - id="stop8661" />
    - </linearGradient>
    - <inkscape:perspective
    - sodipodi:type="inkscape:persp3d"
    - inkscape:vp_x="0 : 8 : 1"
    - inkscape:vp_y="0 : 1000 : 0"
    - inkscape:vp_z="16 : 8 : 1"
    - inkscape:persp3d-origin="8 : 5.3333333 : 1"
    - id="perspective14" />
    - <linearGradient
    - inkscape:collect="always"
    - xlink:href="#linearGradient8657"
    - id="linearGradient8663"
    - x1="13.540643"
    - y1="4.1059771"
    - x2="13.540643"
    - y2="27.537933"
    - gradientUnits="userSpaceOnUse" />
    - <linearGradient
    - inkscape:collect="always"
    - xlink:href="#linearGradient8673"
    - id="linearGradient8679"
    - x1="17.568361"
    - y1="-2.2142286"
    - x2="17.568361"
    - y2="21.473934"
    - gradientUnits="userSpaceOnUse" />
    - <linearGradient
    - inkscape:collect="always"
    - xlink:href="#linearGradient8681"
    - id="linearGradient8687"
    - x1="7.2145119"
    - y1="6.08881"
    - x2="19.957991"
    - y2="21.372702"
    - gradientUnits="userSpaceOnUse" />
    - </defs>
    - <sodipodi:namedview
    - id="base"
    - pagecolor="#ffffff"
    - bordercolor="#666666"
    - borderopacity="1.0"
    - inkscape:pageopacity="0.0"
    - inkscape:pageshadow="2"
    - inkscape:zoom="23.791015"
    - inkscape:cx="21.551698"
    - inkscape:cy="9.5685566"
    - inkscape:current-layer="layer1"
    - showgrid="true"
    - inkscape:grid-bbox="true"
    - inkscape:document-units="px"
    - inkscape:window-width="1440"
    - inkscape:window-height="847"
    - inkscape:window-x="0"
    - inkscape:window-y="0"
    - inkscape:grid-points="true"
    - inkscape:snap-nodes="false"
    - inkscape:snap-bbox="true"
    - objecttolerance="10"
    - gridtolerance="10">
    - <inkscape:grid
    - type="xygrid"
    - id="grid7853"
    - visible="true"
    - enabled="true" />
    - </sodipodi:namedview>
    - <metadata
    - id="metadata4253">
    - <rdf:RDF>
    - <cc:Work
    - rdf:about="">
    - <dc:format>image/svg+xml</dc:format>
    - <dc:type
    - rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
    - </cc:Work>
    - </rdf:RDF>
    - </metadata>
    - <g
    - id="layer1"
    - inkscape:label="Layer 1"
    - inkscape:groupmode="layer">
    - <rect
    - style="fill:#ef1e1e;fill-opacity:1;stroke:url(#linearGradient8687);stroke-width:1.27949666999999989;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
    - id="rect1326"
    - width="18.090118"
    - height="19.004496"
    - x="3.4549403"
    - y="2.4977543"
    - ry="1.9604023"
    - rx="1.9604023"
    - transform="matrix(0.773903,0,0,0.789287,-2.173788,-1.471445)" />
    - <path
    - style="fill:url(#linearGradient8679);fill-opacity:1;stroke:url(#linearGradient8663);stroke-width:1.32302749000000008;stroke-miterlimit:4;stroke-opacity:1"
    - d="M 5.5,3.5 C 4.9023199,3.5 4.46875,3.9335697 4.46875,4.53125 L 4.46875,19.46875 C 4.46875,20.066429 4.9023192,20.499999 5.5,20.5 L 19.5,20.5 C 20.097679,20.5 20.53125,20.066429 20.53125,19.46875 L 20.53125,4.53125 C 20.53125,3.9335703 20.097679,3.5 19.5,3.5 L 5.5,3.5 z "
    - id="path2201"
    - transform="matrix(0.747082,0,0,0.764706,-1.838521,-1.176469)" />
    - <path
    - sodipodi:type="inkscape:offset"
    - inkscape:radius="0"
    - inkscape:original="M 4.03125 4 L 4 12 L 6 12 L 6 7.96875 L 11 13 L 11 5.03125 L 9 5.03125 L 9 8.96875 L 4.03125 4 z "
    - style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1;stroke-miterlimit:4;stroke-opacity:1"
    - id="path8697"
    - d="M 4.03125,4 L 4,12 L 6,12 L 6,7.96875 L 11,13 L 11,5.03125 L 9,5.03125 L 9,8.96875 L 4.03125,4 z" />
    - </g>
    -</svg>
    Binary file libpurple/protocols/novell/resources/icons/22x22/apps/im-novell.png has changed
    --- a/libpurple/protocols/novell/resources/icons/22x22/apps/scalable/im-novell.svg Fri Nov 11 19:39:24 2022 -0600
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,140 +0,0 @@
    -<?xml version="1.0" encoding="UTF-8" standalone="no"?>
    -<!-- Created with Inkscape (http://www.inkscape.org/) -->
    -<svg
    - xmlns:dc="http://purl.org/dc/elements/1.1/"
    - xmlns:cc="http://creativecommons.org/ns#"
    - xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
    - xmlns:svg="http://www.w3.org/2000/svg"
    - xmlns="http://www.w3.org/2000/svg"
    - xmlns:xlink="http://www.w3.org/1999/xlink"
    - xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
    - xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
    - width="24"
    - height="24"
    - id="svg2"
    - sodipodi:version="0.32"
    - inkscape:version="0.46"
    - version="1.0"
    - sodipodi:docbase="/home/hbons/Desktop/Gaim Refresh/protocols/22/scalable"
    - sodipodi:docname="novell.svg"
    - inkscape:export-filename="/home/hbons/Desktop/Gaim Refresh/protocols/22/groupwise.png"
    - inkscape:export-xdpi="90"
    - inkscape:export-ydpi="90"
    - inkscape:output_extension="org.inkscape.output.svg.inkscape">
    - <defs
    - id="defs4">
    - <linearGradient
    - inkscape:collect="always"
    - id="linearGradient2190">
    - <stop
    - style="stop-color:#d3d7cf;stop-opacity:1;"
    - offset="0"
    - id="stop2192" />
    - <stop
    - style="stop-color:#d3d7cf;stop-opacity:0;"
    - offset="1"
    - id="stop2194" />
    - </linearGradient>
    - <linearGradient
    - inkscape:collect="always"
    - id="linearGradient3150">
    - <stop
    - style="stop-color:#2e3436;stop-opacity:1;"
    - offset="0"
    - id="stop3152" />
    - <stop
    - style="stop-color:#2e3436;stop-opacity:0;"
    - offset="1"
    - id="stop3154" />
    - </linearGradient>
    - <radialGradient
    - inkscape:collect="always"
    - xlink:href="#linearGradient3150"
    - id="radialGradient3156"
    - cx="10.748654"
    - cy="10.457643"
    - fx="10.748654"
    - fy="10.457643"
    - r="6.6449099"
    - gradientTransform="matrix(-0.842757,5.698892e-16,-4.565819e-9,-0.35721,19.80716,14.19321)"
    - gradientUnits="userSpaceOnUse" />
    - <radialGradient
    - inkscape:collect="always"
    - xlink:href="#linearGradient2190"
    - id="radialGradient2196"
    - cx="4.3022962"
    - cy="7.5011024"
    - fx="4.3022962"
    - fy="7.5011024"
    - r="8.5"
    - gradientTransform="matrix(-2.567716,2.567716,-2.581797,-2.581795,54.64024,19.71367)"
    - gradientUnits="userSpaceOnUse" />
    - </defs>
    - <sodipodi:namedview
    - id="base"
    - pagecolor="#ffffff"
    - bordercolor="#666666"
    - borderopacity="1.0"
    - inkscape:pageopacity="0.0"
    - inkscape:pageshadow="2"
    - inkscape:zoom="18.194454"
    - inkscape:cx="19.83031"
    - inkscape:cy="13.86181"
    - inkscape:document-units="px"
    - inkscape:current-layer="layer1"
    - showgrid="true"
    - fill="#00ffff"
    - inkscape:window-width="1268"
    - inkscape:window-height="971"
    - inkscape:window-x="6"
    - inkscape:window-y="21" />
    - <metadata
    - id="metadata7">
    - <rdf:RDF>
    - <cc:Work
    - rdf:about="">
    - <dc:format>image/svg+xml</dc:format>
    - <dc:type
    - rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
    - </cc:Work>
    - </rdf:RDF>
    - </metadata>
    - <g
    - inkscape:label="Layer 1"
    - inkscape:groupmode="layer"
    - id="layer1">
    - <path
    - sodipodi:type="arc"
    - style="opacity:0.5;fill:url(#radialGradient3156);fill-opacity:1;stroke:none;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
    - id="path3140"
    - sodipodi:cx="10.748654"
    - sodipodi:cy="10.457643"
    - sodipodi:rx="6.6449099"
    - sodipodi:ry="2.3675451"
    - d="M 17.393564 10.457643 A 6.6449099 2.3675451 0 1 1 4.1037445,10.457643 A 6.6449099 2.3675451 0 1 1 17.393564 10.457643 z"
    - transform="matrix(1.655402,0,0,1.055946,-5.793349,9.457294)" />
    - <rect
    - style="opacity:1;fill:#eeeeec;fill-opacity:1;stroke:#888a85;stroke-width:0.99999988;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
    - id="rect1326"
    - width="18.090118"
    - height="19.004496"
    - x="3.4549403"
    - y="2.4977543"
    - ry="2.0372119"
    - rx="2.0372119" />
    - <path
    - sodipodi:type="inkscape:offset"
    - inkscape:radius="-0.99578816"
    - inkscape:original="M 5.5 2.5 C 4.3713846 2.5 3.46875 3.4026344 3.46875 4.53125 L 3.46875 19.46875 C 3.46875 20.597365 4.3713846 21.499999 5.5 21.5 L 19.5 21.5 C 20.628615 21.5 21.53125 20.597365 21.53125 19.46875 L 21.53125 4.53125 C 21.53125 3.4026346 20.628615 2.5 19.5 2.5 L 5.5 2.5 z "
    - xlink:href="#rect1326"
    - style="opacity:1;fill:url(#radialGradient2196);fill-opacity:1;stroke:#ffffff;stroke-width:1.00000048;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
    - id="path2201"
    - inkscape:href="#rect1326"
    - d="M 4,2.5 C 3.7105801,2.5 3.5,2.7105801 3.5,3 L 3.5,20.65625 C 3.5,20.94567 3.6855275,21.125 4,21.125 L 20.46875,21.125 C 20.783223,21.125 20.96875,20.945671 20.96875,20.65625 L 20.96875,3 C 20.96875,2.7105794 20.758171,2.5 20.46875,2.5 L 4,2.5 z " />
    - <path
    - style="fill:#cc0000;fill-opacity:1;stroke:none;stroke-width:1;stroke-miterlimit:4;stroke-opacity:1"
    - d="M 7.9770722,7 L 7.9770722,17.002529 L 10.015316,17.002529 L 9.9655567,11.206846 L 14.876959,15.95387 L 17,18 L 17,8.0143877 L 14.995675,8.0143877 L 14.995675,13.760417 L 9.1049377,8.0476192 L 7.9770722,7 z "
    - id="rect1317"
    - sodipodi:nodetypes="ccccccccccc" />
    - </g>
    -</svg>
    Binary file libpurple/protocols/novell/resources/icons/48x48/apps/im-novell.png has changed
    --- a/libpurple/protocols/novell/resources/icons/scalable/apps/im-novell.svg Fri Nov 11 19:39:24 2022 -0600
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,167 +0,0 @@
    -<?xml version="1.0" encoding="UTF-8" standalone="no"?>
    -<!-- Created with Inkscape (http://www.inkscape.org/) -->
    -<svg
    - xmlns:dc="http://purl.org/dc/elements/1.1/"
    - xmlns:cc="http://creativecommons.org/ns#"
    - xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
    - xmlns:svg="http://www.w3.org/2000/svg"
    - xmlns="http://www.w3.org/2000/svg"
    - xmlns:xlink="http://www.w3.org/1999/xlink"
    - xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
    - xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
    - width="48"
    - height="48"
    - id="svg2"
    - sodipodi:version="0.32"
    - inkscape:version="0.46"
    - version="1.0"
    - sodipodi:docbase="/home/hbons/Desktop/Gaim Refresh/protocols/scalable"
    - sodipodi:docname="novell.svg"
    - inkscape:export-filename="/home/hbons/Desktop/Gaim Refresh/protocols/48/groupwise.png"
    - inkscape:export-xdpi="90"
    - inkscape:export-ydpi="90"
    - inkscape:output_extension="org.inkscape.output.svg.inkscape">
    - <defs
    - id="defs4">
    - <linearGradient
    - inkscape:collect="always"
    - id="linearGradient2190">
    - <stop
    - style="stop-color:#d3d7cf;stop-opacity:1;"
    - offset="0"
    - id="stop2192" />
    - <stop
    - style="stop-color:#d3d7cf;stop-opacity:0;"
    - offset="1"
    - id="stop2194" />
    - </linearGradient>
    - <linearGradient
    - inkscape:collect="always"
    - id="linearGradient2238">
    - <stop
    - style="stop-color:#eeeeec;stop-opacity:1;"
    - offset="0"
    - id="stop2240" />
    - <stop
    - style="stop-color:#eeeeec;stop-opacity:0;"
    - offset="1"
    - id="stop2242" />
    - </linearGradient>
    - <linearGradient
    - inkscape:collect="always"
    - id="linearGradient3150">
    - <stop
    - style="stop-color:#2e3436;stop-opacity:1;"
    - offset="0"
    - id="stop3152" />
    - <stop
    - style="stop-color:#2e3436;stop-opacity:0;"
    - offset="1"
    - id="stop3154" />
    - </linearGradient>
    - <radialGradient
    - inkscape:collect="always"
    - xlink:href="#linearGradient3150"
    - id="radialGradient3156"
    - cx="10.748654"
    - cy="10.457643"
    - fx="10.748654"
    - fy="10.457643"
    - r="6.6449099"
    - gradientTransform="matrix(-0.842757,5.698892e-16,-4.565819e-9,-0.35721,19.80716,14.19321)"
    - gradientUnits="userSpaceOnUse" />
    - <linearGradient
    - inkscape:collect="always"
    - xlink:href="#linearGradient2238"
    - id="linearGradient2244"
    - x1="11.76915"
    - y1="3.4633243"
    - x2="11.76915"
    - y2="11.990735"
    - gradientUnits="userSpaceOnUse"
    - gradientTransform="matrix(1.888889,0,0,1.818182,2.77778,1.26862)" />
    - <radialGradient
    - inkscape:collect="always"
    - xlink:href="#linearGradient2190"
    - id="radialGradient2196"
    - cx="4.3022962"
    - cy="7.5011024"
    - fx="4.3022962"
    - fy="7.5011024"
    - r="8.5"
    - gradientTransform="matrix(-2.567716,2.567716,-2.581797,-2.581795,54.64024,19.71367)"
    - gradientUnits="userSpaceOnUse" />
    - </defs>
    - <sodipodi:namedview
    - id="base"
    - pagecolor="#ffffff"
    - bordercolor="#666666"
    - borderopacity="1.0"
    - inkscape:pageopacity="0.0"
    - inkscape:pageshadow="2"
    - inkscape:zoom="12.865422"
    - inkscape:cx="44.509337"
    - inkscape:cy="25.527163"
    - inkscape:document-units="px"
    - inkscape:current-layer="layer1"
    - showgrid="true"
    - fill="#00ffff"
    - inkscape:window-width="1268"
    - inkscape:window-height="971"
    - inkscape:window-x="6"
    - inkscape:window-y="21" />
    - <metadata
    - id="metadata7">
    - <rdf:RDF>
    - <cc:Work
    - rdf:about="">
    - <dc:format>image/svg+xml</dc:format>
    - <dc:type
    - rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
    - </cc:Work>
    - </rdf:RDF>
    - </metadata>
    - <g
    - inkscape:label="Layer 1"
    - inkscape:groupmode="layer"
    - id="layer1">
    - <path
    - sodipodi:type="arc"
    - style="opacity:0.5;fill:url(#radialGradient3156);fill-opacity:1;stroke:none;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
    - id="path3140"
    - sodipodi:cx="10.748654"
    - sodipodi:cy="10.457643"
    - sodipodi:rx="6.6449099"
    - sodipodi:ry="2.3675451"
    - d="M 17.393564 10.457643 A 6.6449099 2.3675451 0 1 1 4.1037445,10.457643 A 6.6449099 2.3675451 0 1 1 17.393564 10.457643 z"
    - transform="matrix(2.934577,0,0,2.111893,-7.042754,16.91046)" />
    - <rect
    - style="opacity:1;fill:#eeeeec;fill-opacity:1;stroke:#888a85;stroke-width:0.9999997;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
    - id="rect1326"
    - width="33.012436"
    - height="35.008015"
    - x="7.4937739"
    - y="5.4919853"
    - ry="4.0986137"
    - rx="4.0986137" />
    - <path
    - sodipodi:type="inkscape:offset"
    - inkscape:radius="-0.99578816"
    - inkscape:original="M 11.59375 5.5 C 9.323118 5.5 7.5 7.323118 7.5 9.59375 L 7.5 36.40625 C 7.5 38.676882 9.3231175 40.5 11.59375 40.5 L 36.40625 40.5 C 38.676882 40.5 40.5 38.676882 40.5 36.40625 L 40.5 9.59375 C 40.5 7.323118 38.676882 5.5 36.40625 5.5 L 11.59375 5.5 z "
    - xlink:href="#rect1326"
    - style="opacity:1;fill:url(#radialGradient2196);fill-opacity:1;stroke:#ffffff;stroke-width:1.00000048;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
    - id="path2201"
    - inkscape:href="#rect1326"
    - d="M 9.5,5.5 C 8.9264847,5.5 8.5,5.9264831 8.5,6.5 L 8.5,37.5 C 8.5,38.073517 8.9264835,38.499999 9.5,38.5 L 38.5,38.5 C 39.073514,38.5 39.5,38.073514 39.5,37.5 L 39.5,6.5 C 39.5,5.9264836 39.073514,5.5 38.5,5.5 L 9.5,5.5 z " />
    - <path
    - style="fill:#cc0000;fill-opacity:1;stroke:none;stroke-width:1;stroke-miterlimit:4;stroke-opacity:1"
    - d="M 16,13.995893 L 16,32.182309 L 19.840233,32.182309 L 19.746482,21.644702 L 29.000001,30.275655 L 33,33.995893 L 33,15.840234 L 29.223673,15.840234 L 29.223673,26.287561 L 18.124999,15.900655 L 16,13.995893 z "
    - id="rect1317"
    - sodipodi:nodetypes="ccccccccccc" />
    - <path
    - style="fill:url(#linearGradient2244);fill-opacity:1;stroke:none;stroke-width:1;stroke-miterlimit:4;stroke-opacity:1"
    - d="M 16,13.995893 L 16,32.182309 L 19.840234,32.182309 L 19.746482,21.644702 L 29.000001,30.275655 L 33,33.995893 L 33,15.840234 L 29.223673,15.840234 L 29.223673,26.287561 L 18.124999,15.900655 L 16,13.995893 z "
    - id="path2228"
    - sodipodi:nodetypes="ccccccccccc" />
    - </g>
    -</svg>
    --- a/libpurple/protocols/novell/resources/novell.gresource.xml Fri Nov 11 19:39:24 2022 -0600
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,11 +0,0 @@
    -<?xml version="1.0" encoding="UTF-8"?>
    -<gresources>
    - <gresource prefix="/im/pidgin/libpurple/novell">
    - <file>icons/16x16/apps/im-novell.png</file>
    - <file>icons/16x16/apps/scalable/im-novell.svg</file>
    - <file>icons/22x22/apps/im-novell.png</file>
    - <file>icons/22x22/apps/scalable/im-novell.svg</file>
    - <file>icons/48x48/apps/im-novell.png</file>
    - <file>icons/scalable/apps/im-novell.svg</file>
    - </gresource>
    -</gresources>
    --- a/meson.build Fri Nov 11 19:39:24 2022 -0600
    +++ b/meson.build Sat Nov 12 15:37:03 2022 -0600
    @@ -354,7 +354,7 @@
    DEFAULT_PRPLS = ['bonjour', 'demo', 'facebook', 'gg', 'irc', 'ircv3', 'jabber',
    - 'novell', 'sametime']
    + 'sametime']
    dynamic_list = get_option('dynamic-prpls').split(',')
    if dynamic_list == ['all']
    @@ -382,7 +382,6 @@
    DYNAMIC_IRC = DYNAMIC_PRPLS.contains('irc')
    DYNAMIC_IRCV3 = DYNAMIC_PRPLS.contains('ircv3')
    DYNAMIC_JABBER = DYNAMIC_PRPLS.contains('jabber')
    -DYNAMIC_NOVELL = DYNAMIC_PRPLS.contains('novell')
    DYNAMIC_SAMETIME = DYNAMIC_PRPLS.contains('sametime')
    conf.set('HAVE_SYS_UTSNAME_H',
    --- a/po/POTFILES.in Fri Nov 11 19:39:24 2022 -0600
    +++ b/po/POTFILES.in Sat Nov 12 15:37:03 2022 -0600
    @@ -160,17 +160,6 @@
    libpurple/protocols/jabber/usertune.c
    libpurple/protocols/jabber/xdata.c
    libpurple/protocols/jabber/xmpp.c
    -libpurple/protocols/novell/nmconference.c
    -libpurple/protocols/novell/nmconn.c
    -libpurple/protocols/novell/nmcontact.c
    -libpurple/protocols/novell/nmevent.c
    -libpurple/protocols/novell/nmfield.c
    -libpurple/protocols/novell/nmmessage.c
    -libpurple/protocols/novell/nmrequest.c
    -libpurple/protocols/novell/nmrtf.c
    -libpurple/protocols/novell/nmuser.c
    -libpurple/protocols/novell/nmuserrecord.c
    -libpurple/protocols/novell/novell.c
    libpurple/protocols/sametime/im_mime.c
    libpurple/protocols/sametime/sametime.c
    libpurple/protocols/sametime/tests/test_sametime_im_mime.c