diff --git a/src/common.c b/src/common.c index c8f78ca..9a7c6b9 100644 --- a/src/common.c +++ b/src/common.c @@ -688,6 +688,9 @@ void mwUserStatus_get(struct mwGetBuffer *b, struct mwUserStatus *stat) { guint16_get(b, &stat->status); guint32_get(b, &stat->time); mwString_get(b, &stat->desc); + + // Quick'n ugly hack for recent Sametime clients + stat->time = 0; } diff --git a/src/common.c.status-timestamp-workaround b/src/common.c.status-timestamp-workaround new file mode 100644 index 0000000..c8f78ca --- /dev/null +++ b/src/common.c.status-timestamp-workaround @@ -0,0 +1,928 @@ + +/* + 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 +#include + +#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 { + guchar *buf; /**< head of buffer */ + gsize len; /**< length of buffer */ + + guchar *ptr; /**< offset to first unused byte */ + gsize rem; /**< count of unused bytes remaining */ +}; + + +struct mwGetBuffer { + guchar *buf; /**< head of buffer */ + gsize len; /**< length of buffer */ + + guchar *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); + guchar *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 = 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) { + guchar *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) { + guchar *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; +} + + +static 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 end_of_block; + + g_return_if_fail(b != NULL); + g_return_if_fail(idb != NULL); + + guint32_get(b, &end_of_block); + mwAwareIdBlock_get(b, &idb->id); + mwString_get(b, &idb->group); + gboolean_get(b, &idb->online); + + if(idb->online) { + mwString_get(b, &idb->alt_id); + mwUserStatus_get(b, &idb->status); + mwString_get(b, &idb->name); + } + + if(b->ptr < b->buf + end_of_block) { + mwGetBuffer_advance(b, b->buf + end_of_block - b->ptr); + } +} + + +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)); +} +