view src/protocols/sametime/meanwhile/common.c @ 11943:0110fc7c6a8a

[gaim-migrate @ 14234] Bringing things up to date with the last Meanwhile release, 0.5.0 and the last gaim-meanwhile plugin release, 1.2.5 (which should be the last plugin release against oldstatus, if all goes well with HEAD and no major bugs crop up) It builds, so that's a start. The status bits that have been empty since the first import of the sametime stuff are still empty, but I'm going to try and fill those in tomorrow. I've decided to try and start using HEAD actively, to encourage me to get this freaking prpl fully functional. committer: Tailor Script <tailor@pidgin.im>
author Christopher O'Brien <siege@pidgin.im>
date Wed, 02 Nov 2005 03:39:03 +0000
parents 3ef77720e577
children 5b3368008513
line wrap: on
line source


/*
  Meanwhile - Unofficial Lotus Sametime Community Client Library
  Copyright (C) 2004  Christopher (siege) O'Brien
  
  This library is free software; you can redistribute it and/or
  modify it under the terms of the GNU Library General Public
  License as published by the Free Software Foundation; either
  version 2 of the License, or (at your option) any later version.
  
  This library 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
  Library General Public License for more details.
  
  You should have received a copy of the GNU Library General Public
  License along with this library; if not, write to the Free
  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/

#include <glib.h>
#include <string.h>

#include "mw_common.h"


/** @todo the *_get functions should make sure to clear their
    structures in the event of failure, to prevent memory leaks */


#define MW16_PUT(b, val) \
  *(b)++ = ((val) >> 0x08) & 0xff; \
  *(b)++ = (val) & 0xff;


#define MW16_GET(b, val) \
  val = (*(b)++ & 0xff) << 8; \
  val = val | (*(b)++ & 0xff);


#define MW32_PUT(b, val) \
  *(b)++ = ((val) >> 0x18) & 0xff; \
  *(b)++ = ((val) >> 0x10) & 0xff; \
  *(b)++ = ((val) >> 0x08) & 0xff; \
  *(b)++ = (val) & 0xff;


#define MW32_GET(b, val) \
  val = (*(b)++ & 0xff) << 0x18; \
  val = val | (*(b)++ & 0xff) << 0x10; \
  val = val | (*(b)++ & 0xff) << 0x08; \
  val = val | (*(b)++ & 0xff);


struct mwPutBuffer {
  char *buf;  /**< head of buffer */
  gsize len;  /**< length of buffer */

  char *ptr;  /**< offset to first unused byte */
  gsize rem;  /**< count of unused bytes remaining */
};


struct mwGetBuffer {
  char *buf;  /**< head of buffer */
  gsize len;  /**< length of buffer */

  char *ptr;  /**< offset to first unused byte */
  gsize rem;  /**< count of unused bytes remaining */

  gboolean wrap;   /**< TRUE to indicate buf shouldn't be freed */
  gboolean error;  /**< TRUE to indicate an error */
};


#define BUFFER_USED(buffer) \
  ((buffer)->len - (buffer)->rem)


/** ensure that there's at least enough space remaining in the put
    buffer to fit needed. */
static void ensure_buffer(struct mwPutBuffer *b, gsize needed) {
  if(b->rem < needed) {
    gsize len = b->len, use = BUFFER_USED(b);
    char *buf;

    /* newly created buffers are empty until written to, and then they
       have 1024 available */
    if(! len) len = 1024;

    /* double len until it's large enough to fit needed */
    while( (len - use) < needed ) len = len << 1;

    /* create the new buffer. if there was anything in the old buffer,
       copy it into the new buffer and free the old copy */
    buf = g_malloc(len);
    if(b->buf) {
      memcpy(buf, b->buf, use);
      g_free(b->buf);
    }

    /* put the new buffer into b */
    b->buf = buf;
    b->len = len;
    b->ptr = buf + use;
    b->rem = len - use;
  }
}


/** determine if there are at least needed bytes available in the
    buffer. sets the error flag if there's not at least needed bytes
    left in the buffer

    @returns true if there's enough data, false if not */
static gboolean check_buffer(struct mwGetBuffer *b, gsize needed) {
  if(! b->error)  b->error = (b->rem < needed);
  return ! b->error;
}


struct mwPutBuffer *mwPutBuffer_new() {
  return g_new0(struct mwPutBuffer, 1);
}


void mwPutBuffer_write(struct mwPutBuffer *b, gpointer data, gsize len) {
  g_return_if_fail(b != NULL);
  g_return_if_fail(data != NULL);

  if(! len) return;

  ensure_buffer(b, len);
  memcpy(b->ptr, data, len);
  b->ptr += len;
  b->rem -= len;
}


void mwPutBuffer_free(struct mwPutBuffer *b) {
  if(! b) return;
  g_free(b->buf);
  g_free(b);
}


void mwPutBuffer_finalize(struct mwOpaque *to, struct mwPutBuffer *from) {
  g_return_if_fail(to != NULL);
  g_return_if_fail(from != NULL);

  to->len = BUFFER_USED(from);
  to->data = from->buf;

  g_free(from);
}


struct mwGetBuffer *mwGetBuffer_new(struct mwOpaque *o) {
  struct mwGetBuffer *b = g_new0(struct mwGetBuffer, 1);

  if(o && o->len) {
    b->buf = b->ptr = g_memdup(o->data, o->len);
    b->len = b->rem = o->len;
  }

  return b;
}


struct mwGetBuffer *mwGetBuffer_wrap(const struct mwOpaque *o) {
  struct mwGetBuffer *b = g_new0(struct mwGetBuffer, 1);

  if(o && o->len) {
    b->buf = b->ptr = (char *) o->data;
    b->len = b->rem = o->len;
  }
  b->wrap = TRUE;

  return b;
}


gsize mwGetBuffer_read(struct mwGetBuffer *b, gpointer data, gsize len) {
  g_return_val_if_fail(b != NULL, 0);
  g_return_val_if_fail(data != NULL, 0);

  if(b->error) return 0;
  if(! len) return 0;

  if(b->rem < len)
    len = b->rem;

  memcpy(data, b->ptr, len);
  b->ptr += len;
  b->rem -= len;

  return len;
}


gsize mwGetBuffer_advance(struct mwGetBuffer *b, gsize len) {
  g_return_val_if_fail(b != NULL, 0);

  if(b->error) return 0;
  if(! len) return 0;

  if(b->rem < len)
    len = b->rem;

  b->ptr += len;
  b->rem -= len;

  return len;
}


void mwGetBuffer_reset(struct mwGetBuffer *b) {
  g_return_if_fail(b != NULL);

  b->rem = b->len;
  b->ptr = b->buf;
  b->error = FALSE;
}


gsize mwGetBuffer_remaining(struct mwGetBuffer *b) {
  g_return_val_if_fail(b != NULL, 0);
  return b->rem;
}


gboolean mwGetBuffer_error(struct mwGetBuffer *b) {
  g_return_val_if_fail(b != NULL, TRUE);
  return b->error;
}


void mwGetBuffer_free(struct mwGetBuffer *b) {
  if(! b) return;
  if(! b->wrap) g_free(b->buf);
  g_free(b);
}


#define guint16_buflen()  2


void guint16_put(struct mwPutBuffer *b, guint16 val) {
  g_return_if_fail(b != NULL);

  ensure_buffer(b, guint16_buflen());
  MW16_PUT(b->ptr, val);
  b->rem -= guint16_buflen();
}


void guint16_get(struct mwGetBuffer *b, guint16 *val) {
  g_return_if_fail(b != NULL);

  if(b->error) return;
  g_return_if_fail(check_buffer(b, guint16_buflen()));

  MW16_GET(b->ptr, *val);
  b->rem -= guint16_buflen();
}


guint16 guint16_peek(struct mwGetBuffer *b) {
  char *buf = b->buf;
  guint16 r = 0;
  
  if(b->rem >= guint16_buflen())
    MW16_GET(buf, r);

  return r;
}


#define guint32_buflen()  4


void guint32_put(struct mwPutBuffer *b, guint32 val) {
  g_return_if_fail(b != NULL);

  ensure_buffer(b, guint32_buflen());
  MW32_PUT(b->ptr, val);
  b->rem -= guint32_buflen();
}


void guint32_get(struct mwGetBuffer *b, guint32 *val) {
  g_return_if_fail(b != NULL);

  if(b->error) return;
  g_return_if_fail(check_buffer(b, guint32_buflen()));

  MW32_GET(b->ptr, *val);
  b->rem -= guint32_buflen();
}


guint32 guint32_peek(struct mwGetBuffer *b) {
  char *buf = b->buf;
  guint32 r = 0;

  if(b->rem >= guint32_buflen())
    MW32_GET(buf, r);

  return r;
}


#define gboolean_buflen()  1


void gboolean_put(struct mwPutBuffer *b, gboolean val) {
  g_return_if_fail(b != NULL);

  ensure_buffer(b, gboolean_buflen());
  *(b->ptr) = !! val;
  b->ptr++;
  b->rem--;
}


void gboolean_get(struct mwGetBuffer *b, gboolean *val) {
  g_return_if_fail(b != NULL);

  if(b->error) return;
  g_return_if_fail(check_buffer(b, gboolean_buflen()));

  *val = !! *(b->ptr);
  b->ptr++;
  b->rem--;
}


gboolean gboolean_peek(struct mwGetBuffer *b) {
  gboolean v = FALSE;

  if(b->rem >= gboolean_buflen())
    v = !! *(b->ptr);

  return v;
}


gboolean mw_streq(const char *a, const char *b) {
  return (a == b) || (a && b && !strcmp(a, b));
}


void mwString_put(struct mwPutBuffer *b, const char *val) {
  gsize len = 0;

  g_return_if_fail(b != NULL);

  if(val) len = strlen(val);

  guint16_put(b, (guint16) len);

  if(len) {
    ensure_buffer(b, len);
    memcpy(b->ptr, val, len);
    b->ptr += len;
    b->rem -= len;
  }
}


void mwString_get(struct mwGetBuffer *b, char **val) {
  guint16 len = 0;

  g_return_if_fail(b != NULL);
  g_return_if_fail(val != NULL);

  *val = NULL;

  if(b->error) return;
  guint16_get(b, &len);

  g_return_if_fail(check_buffer(b, (gsize) len));

  if(len) {
    *val = g_malloc0(len + 1);
    memcpy(*val, b->ptr, len);
    b->ptr += len;
    b->rem -= len;
  }
}


void mwOpaque_put(struct mwPutBuffer *b, const struct mwOpaque *o) {
  gsize len;

  g_return_if_fail(b != NULL);

  if(! o) {
    guint32_put(b, 0x00);
    return;
  }

  len = o->len;
  if(len)
    g_return_if_fail(o->data != NULL);
  
  guint32_put(b, (guint32) len);

  if(len) {
    ensure_buffer(b, len);
    memcpy(b->ptr, o->data, len);
    b->ptr += len;
    b->rem -= len;
  }
}


void mwOpaque_get(struct mwGetBuffer *b, struct mwOpaque *o) {
  guint32 tmp = 0;

  g_return_if_fail(b != NULL);
  g_return_if_fail(o != NULL);

  o->len = 0;
  o->data = NULL;
  
  if(b->error) return;
  guint32_get(b, &tmp);

  g_return_if_fail(check_buffer(b, (gsize) tmp));

  o->len = (gsize) tmp;
  if(tmp > 0) {
    o->data = g_memdup(b->ptr, tmp);
    b->ptr += tmp;
    b->rem -= tmp;
  }
}


void mwOpaque_clear(struct mwOpaque *o) {
  if(! o) return;
  g_free(o->data);
  o->data = NULL;
  o->len = 0;
}


void mwOpaque_free(struct mwOpaque *o) {
  if(! o) return;
  g_free(o->data);
  g_free(o);
}


void mwOpaque_clone(struct mwOpaque *to, const struct mwOpaque *from) {
  g_return_if_fail(to != NULL);

  to->len = 0;
  to->data = NULL;

  if(from) {
    to->len = from->len;
    if(to->len)
      to->data = g_memdup(from->data, to->len);
  }
}


/* 8.2 Common Structures */
/* 8.2.1 Login Info block */


void mwLoginInfo_put(struct mwPutBuffer *b, const struct mwLoginInfo *login) {
  g_return_if_fail(b != NULL);
  g_return_if_fail(login != NULL);

  mwString_put(b, login->login_id);
  guint16_put(b, login->type);
  mwString_put(b, login->user_id);
  mwString_put(b, login->user_name);
  mwString_put(b, login->community);
  gboolean_put(b, login->full);

  if(login->full) {
    mwString_put(b, login->desc);
    guint32_put(b, login->ip_addr);
    mwString_put(b, login->server_id);
  }
}


void mwLoginInfo_get(struct mwGetBuffer *b, struct mwLoginInfo *login) {
  g_return_if_fail(b != NULL);
  g_return_if_fail(login != NULL);

  if(b->error) return;

  mwString_get(b, &login->login_id);
  guint16_get(b, &login->type);
  mwString_get(b, &login->user_id);
  mwString_get(b, &login->user_name);
  mwString_get(b, &login->community);
  gboolean_get(b, &login->full);
  
  if(login->full) {
    mwString_get(b, &login->desc);
    guint32_get(b, &login->ip_addr);
    mwString_get(b, &login->server_id);
  }
}


void mwLoginInfo_clear(struct mwLoginInfo *login) {
  if(! login) return;

  g_free(login->login_id);
  g_free(login->user_id);
  g_free(login->user_name);
  g_free(login->community);
  g_free(login->desc);
  g_free(login->server_id);

  memset(login, 0x00, sizeof(struct mwLoginInfo));
}


void mwLoginInfo_clone(struct mwLoginInfo *to,
		       const struct mwLoginInfo *from) {

  g_return_if_fail(to != NULL);
  g_return_if_fail(from != NULL);

  to->login_id= g_strdup(from->login_id);
  to->type = from->type;
  to->user_id = g_strdup(from->user_id);
  to->user_name = g_strdup(from->user_name);
  to->community = g_strdup(from->community);

  if( (to->full = from->full) ) {
    to->desc = g_strdup(from->desc);
    to->ip_addr = from->ip_addr;
    to->server_id = g_strdup(from->server_id);
  }
}


/* 8.2.2 Private Info Block */


void mwUserItem_put(struct mwPutBuffer *b, const struct mwUserItem *user) {
  g_return_if_fail(b != NULL);
  g_return_if_fail(user != NULL);

  gboolean_put(b, user->full);
  mwString_put(b, user->id);
  mwString_put(b, user->community);
  
  if(user->full)
    mwString_put(b, user->name);
}


void mwUserItem_get(struct mwGetBuffer *b, struct mwUserItem *user) {
  g_return_if_fail(b != NULL);
  g_return_if_fail(user != NULL);

  if(b->error) return;

  gboolean_get(b, &user->full);
  mwString_get(b, &user->id);
  mwString_get(b, &user->community);

  if(user->full)
    mwString_get(b, &user->name);
}


void mwUserItem_clear(struct mwUserItem *user) {
  if(! user) return;

  g_free(user->id);
  g_free(user->community);
  g_free(user->name);

  memset(user, 0x00, sizeof(struct mwUserItem));
}


void mwUserItem_clone(struct mwUserItem *to,
		      const struct mwUserItem *from) {

  g_return_if_fail(to != NULL);
  g_return_if_fail(from != NULL);

  to->full = from->full;
  to->id = g_strdup(from->id);
  to->community = g_strdup(from->community);
  to->name = (to->full)? g_strdup(from->name): NULL;
}


void mwPrivacyInfo_put(struct mwPutBuffer *b,
		       const struct mwPrivacyInfo *info) {
  guint32 c;

  g_return_if_fail(b != NULL);
  g_return_if_fail(info != NULL);

  gboolean_put(b, info->deny);
  guint32_put(b, info->count);

  for(c = info->count; c--; ) mwUserItem_put(b, info->users + c);
}


void mwPrivacyInfo_get(struct mwGetBuffer *b, struct mwPrivacyInfo *info) {
  g_return_if_fail(b != NULL);
  g_return_if_fail(info != NULL);

  if(b->error) return;

  gboolean_get(b, &info->deny);
  guint32_get(b, &info->count);

  if(info->count) {
    guint32 c = info->count;
    info->users = g_new0(struct mwUserItem, c);
    while(c--) mwUserItem_get(b, info->users + c);
  }
}


void mwPrivacyInfo_clone(struct mwPrivacyInfo *to,
			 const struct mwPrivacyInfo *from) {

  guint32 c;

  g_return_if_fail(to != NULL);
  g_return_if_fail(from != NULL);

  to->deny = from->deny;
  c = to->count = from->count;

  to->users = g_new0(struct mwUserItem, c);
  while(c--) mwUserItem_clone(to->users+c, from->users+c);
}


void mwPrivacyInfo_clear(struct mwPrivacyInfo *info) {
  struct mwUserItem *u;
  guint32 c;

  g_return_if_fail(info != NULL);

  u = info->users;
  c = info->count;

  while(c--) mwUserItem_clear(u + c);
  g_free(u);

  info->count = 0;
  info->users = NULL;
}


/* 8.2.3 User Status Block */


void mwUserStatus_put(struct mwPutBuffer *b,
		      const struct mwUserStatus *stat) {

  g_return_if_fail(b != NULL);
  g_return_if_fail(stat != NULL);

  guint16_put(b, stat->status);
  guint32_put(b, stat->time);
  mwString_put(b, stat->desc);
}


void mwUserStatus_get(struct mwGetBuffer *b, struct mwUserStatus *stat) {
  g_return_if_fail(b != NULL);
  g_return_if_fail(stat != NULL);

  if(b->error) return;

  guint16_get(b, &stat->status);
  guint32_get(b, &stat->time);
  mwString_get(b, &stat->desc);
}


void mwUserStatus_clear(struct mwUserStatus *stat) {
  if(! stat) return;
  g_free(stat->desc);
  memset(stat, 0x00, sizeof(struct mwUserStatus));
}


void mwUserStatus_clone(struct mwUserStatus *to,
			const struct mwUserStatus *from) {

  g_return_if_fail(to != NULL);
  g_return_if_fail(from != NULL);

  to->status = from->status;
  to->time = from->time;
  to->desc = g_strdup(from->desc);
}


/* 8.2.4 ID Block */


void mwIdBlock_put(struct mwPutBuffer *b, const struct mwIdBlock *id) {
  g_return_if_fail(b != NULL);
  g_return_if_fail(id != NULL);

  mwString_put(b, id->user);
  mwString_put(b, id->community);
}


void mwIdBlock_get(struct mwGetBuffer *b, struct mwIdBlock *id) {
  g_return_if_fail(b != NULL);
  g_return_if_fail(id != NULL);

  if(b->error) return;

  mwString_get(b, &id->user);
  mwString_get(b, &id->community);
}


void mwIdBlock_clear(struct mwIdBlock *id) {
  if(! id) return;

  g_free(id->user);
  id->user = NULL;

  g_free(id->community);
  id->community = NULL;
}


void mwIdBlock_clone(struct mwIdBlock *to, const struct mwIdBlock *from) {
  g_return_if_fail(to != NULL);
  g_return_if_fail(from != NULL);

  to->user = g_strdup(from->user);
  to->community = g_strdup(from->community);
}


guint mwIdBlock_hash(const struct mwIdBlock *idb) {
  return (idb)? g_str_hash(idb->user): 0;
}


gboolean mwIdBlock_equal(const struct mwIdBlock *a,
			 const struct mwIdBlock *b) {

  g_return_val_if_fail(a != NULL, FALSE);
  g_return_val_if_fail(b != NULL, FALSE);

  return ( mw_streq(a->user, b->user) &&
	   mw_streq(a->community, b->community) );
}


/* 8.2.5 Encryption Block */

/** @todo I think this can be put into cipher */

void mwEncryptItem_put(struct mwPutBuffer *b,
		       const struct mwEncryptItem *ei) {

  g_return_if_fail(b != NULL);
  g_return_if_fail(ei != NULL);
  
  guint16_put(b, ei->id);
  mwOpaque_put(b, &ei->info);

}


void mwEncryptItem_get(struct mwGetBuffer *b, struct mwEncryptItem *ei) {
  g_return_if_fail(b != NULL);
  g_return_if_fail(ei != NULL);

  if(b->error) return;

  guint16_get(b, &ei->id);
  mwOpaque_get(b, &ei->info);
}


void mwEncryptItem_clear(struct mwEncryptItem *ei) {
  if(! ei) return;
  ei->id = 0x0000;
  mwOpaque_clear(&ei->info);
}


void mwEncryptItem_free(struct mwEncryptItem *ei) {
  mwEncryptItem_clear(ei);
  g_free(ei);
}


/* 8.4.2.1 Awareness ID Block */


/** @todo move this into srvc_aware */

void mwAwareIdBlock_put(struct mwPutBuffer *b,
			const struct mwAwareIdBlock *idb) {

  g_return_if_fail(b != NULL);
  g_return_if_fail(idb != NULL);

  guint16_put(b, idb->type);
  mwString_put(b, idb->user);
  mwString_put(b, idb->community);
}


void mwAwareIdBlock_get(struct mwGetBuffer *b, struct mwAwareIdBlock *idb) {
  g_return_if_fail(b != NULL);
  g_return_if_fail(idb != NULL);

  if(b->error) return;

  guint16_get(b, &idb->type);
  mwString_get(b, &idb->user);
  mwString_get(b, &idb->community);
}


void mwAwareIdBlock_clone(struct mwAwareIdBlock *to,
			  const struct mwAwareIdBlock *from) {

  g_return_if_fail(to != NULL);
  g_return_if_fail(from != NULL);

  to->type = from->type;
  to->user = g_strdup(from->user);
  to->community = g_strdup(from->community);
}


void mwAwareIdBlock_clear(struct mwAwareIdBlock *idb) {
  if(! idb) return;
  g_free(idb->user);
  g_free(idb->community);
  memset(idb, 0x00, sizeof(struct mwAwareIdBlock));
}


guint mwAwareIdBlock_hash(const struct mwAwareIdBlock *a) {
  return (a)? g_str_hash(a->user): 0;
}


gboolean mwAwareIdBlock_equal(const struct mwAwareIdBlock *a,
			      const struct mwAwareIdBlock *b) {

  g_return_val_if_fail(a != NULL, FALSE);
  g_return_val_if_fail(b != NULL, FALSE);
  
  return ( (a->type == b->type) &&
	   mw_streq(a->user, b->user) &&
	   mw_streq(a->community, b->community) );
}


/* 8.4.2.4 Snapshot */

void mwAwareSnapshot_get(struct mwGetBuffer *b, struct mwAwareSnapshot *idb) {
  guint32 junk;
  char *empty = NULL;

  g_return_if_fail(b != NULL);
  g_return_if_fail(idb != NULL);

  guint32_get(b, &junk);
  mwAwareIdBlock_get(b, &idb->id);
  mwString_get(b, &idb->group);
  gboolean_get(b, &idb->online);

  g_free(empty);

  if(idb->online) {
    mwString_get(b, &idb->alt_id);
    mwUserStatus_get(b, &idb->status);
    mwString_get(b, &idb->name);
  }
}


void mwAwareSnapshot_clone(struct mwAwareSnapshot *to,
			   const struct mwAwareSnapshot *from) {

  g_return_if_fail(to != NULL);
  g_return_if_fail(from != NULL);

  mwAwareIdBlock_clone(&to->id, &from->id);
  if( (to->online = from->online) ) {
    to->alt_id = g_strdup(from->alt_id);
    mwUserStatus_clone(&to->status, &from->status);
    to->name = g_strdup(from->name);
    to->group = g_strdup(from->group);
  }
}


void mwAwareSnapshot_clear(struct mwAwareSnapshot *idb) {
  if(! idb) return;
  mwAwareIdBlock_clear(&idb->id);
  mwUserStatus_clear(&idb->status);
  g_free(idb->alt_id);
  g_free(idb->name);
  g_free(idb->group);
  memset(idb, 0x00, sizeof(struct mwAwareSnapshot));
}


const char *mwLoginType_getName(enum mwLoginType type) {
  switch(type) {
  case mwLogin_LIB:
    return "Lotus Binary Library";

  case mwLogin_JAVA_WEB:
    return "Lotus Java Client Applet";

  case mwLogin_BINARY:
    return "Lotus Sametime Connect";

  case mwLogin_JAVA_APP:
    return "Lotus Java Client Application";

  case mwLogin_LINKS:
    return "Lotus Sametime Links";

  case mwLogin_NOTES_6_5:
  case mwLogin_NOTES_7_0:
    return "Lotus Notes Client";

  case mwLogin_ICT:
  case mwLogin_ICT_1_7_8_2:
    return "IBM Community Tools";

  case mwLogin_NOTESBUDDY:
  case mwLogin_NOTESBUDDY_4_15:
    return "Alphaworks NotesBuddy";

  case mwLogin_SANITY:
    return "Sanity";

  case mwLogin_ST_PERL:
    return "ST-Send-Message";

  case mwLogin_PMR_ALERT:
    return "PMR Alert";

  case mwLogin_TRILLIAN:
  case mwLogin_TRILLIAN_IBM:
    return "Trillian";

  case mwLogin_MEANWHILE:
    return "Meanwhile";

  default:
    return NULL;
  }
}