/* 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)); }