Mercurial > pidgin
view src/protocols/sametime/meanwhile/srvc_ft.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 | 2ce8ec01a064 |
children |
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/glist.h> #include "mw_channel.h" #include "mw_common.h" #include "mw_debug.h" #include "mw_error.h" #include "mw_message.h" #include "mw_service.h" #include "mw_session.h" #include "mw_srvc_ft.h" #include "mw_util.h" #define PROTOCOL_TYPE 0x00000000 #define PROTOCOL_VER 0x00000001 /** send-on-channel type: FT transfer data */ #define msg_TRANSFER 0x0001 /** ack received transfer data */ #define msg_RECEIVED 0x0002 struct mwServiceFileTransfer { struct mwService service; struct mwFileTransferHandler *handler; GList *transfers; }; struct mwFileTransfer { struct mwServiceFileTransfer *service; struct mwChannel *channel; struct mwIdBlock who; enum mwFileTransferState state; char *filename; char *message; guint32 size; guint32 remaining; struct mw_datum client_data; }; /** momentarily places a mwLoginInfo into a mwIdBlock */ static void login_into_id(struct mwIdBlock *to, struct mwLoginInfo *from) { to->user = from->user_id; to->community = from->community; } static const char *ft_state_str(enum mwFileTransferState state) { switch(state) { case mwFileTransfer_NEW: return "new"; case mwFileTransfer_PENDING: return "pending"; case mwFileTransfer_OPEN: return "open"; case mwFileTransfer_CANCEL_LOCAL: return "cancelled locally"; case mwFileTransfer_CANCEL_REMOTE: return "cancelled remotely"; case mwFileTransfer_DONE: return "done"; case mwFileTransfer_ERROR: return "error"; case mwFileTransfer_UNKNOWN: default: return "UNKNOWN"; } } static void ft_state(struct mwFileTransfer *ft, enum mwFileTransferState state) { g_return_if_fail(ft != NULL); if(ft->state == state) return; g_info("setting ft (%s, %s) state: %s", NSTR(ft->who.user), NSTR(ft->who.community), ft_state_str(state)); ft->state = state; } static void recv_channelCreate(struct mwServiceFileTransfer *srvc, struct mwChannel *chan, struct mwMsgChannelCreate *msg) { struct mwFileTransferHandler *handler; struct mwGetBuffer *b; char *fnm, *txt; guint32 size, junk; gboolean b_err; g_return_if_fail(srvc->handler != NULL); handler = srvc->handler; b = mwGetBuffer_wrap(&msg->addtl); guint32_get(b, &junk); /* unknown */ mwString_get(b, &fnm); /* offered filename */ mwString_get(b, &txt); /* offering message */ guint32_get(b, &size); /* size of offered file */ guint32_get(b, &junk); /* unknown */ /* and we just skip an unknown guint16 at the end */ b_err = mwGetBuffer_error(b); mwGetBuffer_free(b); if(b_err) { g_warning("bad/malformed addtl in File Transfer service"); mwChannel_destroy(chan, ERR_FAILURE, NULL); } else { struct mwIdBlock idb; struct mwFileTransfer *ft; login_into_id(&idb, mwChannel_getUser(chan)); ft = mwFileTransfer_new(srvc, &idb, txt, fnm, size); ft->channel = chan; ft_state(ft, mwFileTransfer_PENDING); mwChannel_setServiceData(chan, ft, NULL); if(handler->ft_offered) handler->ft_offered(ft); } g_free(fnm); g_free(txt); } static void recv_channelAccept(struct mwServiceFileTransfer *srvc, struct mwChannel *chan, struct mwMsgChannelAccept *msg) { struct mwFileTransferHandler *handler; struct mwFileTransfer *ft; g_return_if_fail(srvc->handler != NULL); handler = srvc->handler; ft = mwChannel_getServiceData(chan); g_return_if_fail(ft != NULL); ft_state(ft, mwFileTransfer_OPEN); if(handler->ft_opened) handler->ft_opened(ft); } static void recv_channelDestroy(struct mwServiceFileTransfer *srvc, struct mwChannel *chan, struct mwMsgChannelDestroy *msg) { struct mwFileTransferHandler *handler; struct mwFileTransfer *ft; guint32 code; code = msg->reason; g_return_if_fail(srvc->handler != NULL); handler = srvc->handler; ft = mwChannel_getServiceData(chan); g_return_if_fail(ft != NULL); ft->channel = NULL; if(! mwFileTransfer_isDone(ft)) ft_state(ft, mwFileTransfer_CANCEL_REMOTE); mwFileTransfer_close(ft, code); } static void recv_TRANSFER(struct mwFileTransfer *ft, struct mwOpaque *data) { struct mwServiceFileTransfer *srvc; struct mwFileTransferHandler *handler; srvc = ft->service; handler = srvc->handler; g_return_if_fail(mwFileTransfer_isOpen(ft)); if(data->len > ft->remaining) { /* @todo handle error */ } else { ft->remaining -= data->len; if(! ft->remaining) ft_state(ft, mwFileTransfer_DONE); if(handler->ft_recv) handler->ft_recv(ft, data); } } static void recv_RECEIVED(struct mwFileTransfer *ft, struct mwOpaque *data) { struct mwServiceFileTransfer *srvc; struct mwFileTransferHandler *handler; srvc = ft->service; handler = srvc->handler; if(! ft->remaining) ft_state(ft, mwFileTransfer_DONE); if(handler->ft_ack) handler->ft_ack(ft); if(! ft->remaining) mwFileTransfer_close(ft, mwFileTransfer_SUCCESS); } static void recv(struct mwService *srvc, struct mwChannel *chan, guint16 type, struct mwOpaque *data) { struct mwFileTransfer *ft; ft = mwChannel_getServiceData(chan); g_return_if_fail(ft != NULL); switch(type) { case msg_TRANSFER: recv_TRANSFER(ft, data); break; case msg_RECEIVED: recv_RECEIVED(ft, data); break; default: mw_mailme_opaque(data, "unknown message in ft service: 0x%04x", type); } } static void clear(struct mwServiceFileTransfer *srvc) { struct mwFileTransferHandler *h; h = srvc->handler; if(h && h->clear) h->clear(srvc); srvc->handler = NULL; } static const char *name(struct mwService *srvc) { return "File Transfer"; } static const char *desc(struct mwService *srvc) { return "Provides file transfer capabilities through the community server"; } static void start(struct mwService *srvc) { mwService_started(srvc); } static void stop(struct mwServiceFileTransfer *srvc) { while(srvc->transfers) { mwFileTransfer_free(srvc->transfers->data); } mwService_stopped(MW_SERVICE(srvc)); } struct mwServiceFileTransfer * mwServiceFileTransfer_new(struct mwSession *session, struct mwFileTransferHandler *handler) { struct mwServiceFileTransfer *srvc_ft; struct mwService *srvc; g_return_val_if_fail(session != NULL, NULL); g_return_val_if_fail(handler != NULL, NULL); srvc_ft = g_new0(struct mwServiceFileTransfer, 1); srvc = MW_SERVICE(srvc_ft); mwService_init(srvc, session, mwService_FILE_TRANSFER); srvc->recv_create = (mwService_funcRecvCreate) recv_channelCreate; srvc->recv_accept = (mwService_funcRecvAccept) recv_channelAccept; srvc->recv_destroy = (mwService_funcRecvDestroy) recv_channelDestroy; srvc->recv = recv; srvc->clear = (mwService_funcClear) clear; srvc->get_name = name; srvc->get_desc = desc; srvc->start = start; srvc->stop = (mwService_funcStop) stop; srvc_ft->handler = handler; return srvc_ft; } struct mwFileTransferHandler * mwServiceFileTransfer_getHandler(struct mwServiceFileTransfer *srvc) { g_return_val_if_fail(srvc != NULL, NULL); return srvc->handler; } const GList * mwServiceFileTransfer_getTransfers(struct mwServiceFileTransfer *srvc) { g_return_val_if_fail(srvc != NULL, NULL); return srvc->transfers; } struct mwFileTransfer * mwFileTransfer_new(struct mwServiceFileTransfer *srvc, const struct mwIdBlock *who, const char *msg, const char *filename, guint32 filesize) { struct mwFileTransfer *ft; g_return_val_if_fail(srvc != NULL, NULL); g_return_val_if_fail(who != NULL, NULL); ft = g_new0(struct mwFileTransfer, 1); ft->service = srvc; mwIdBlock_clone(&ft->who, who); ft->filename = g_strdup(filename); ft->message = g_strdup(msg); ft->size = ft->remaining = filesize; ft_state(ft, mwFileTransfer_NEW); /* stick a reference in the service */ srvc->transfers = g_list_prepend(srvc->transfers, ft); return ft; } struct mwServiceFileTransfer * mwFileTransfer_getService(struct mwFileTransfer *ft) { g_return_val_if_fail(ft != NULL, NULL); return ft->service; } enum mwFileTransferState mwFileTransfer_getState(struct mwFileTransfer *ft) { g_return_val_if_fail(ft != NULL, mwFileTransfer_UNKNOWN); return ft->state; } const struct mwIdBlock * mwFileTransfer_getUser(struct mwFileTransfer *ft) { g_return_val_if_fail(ft != NULL, NULL); return &ft->who; } const char * mwFileTransfer_getMessage(struct mwFileTransfer *ft) { g_return_val_if_fail(ft != NULL, NULL); return ft->message; } const char * mwFileTransfer_getFileName(struct mwFileTransfer *ft) { g_return_val_if_fail(ft != NULL, NULL); return ft->filename; } guint32 mwFileTransfer_getFileSize(struct mwFileTransfer *ft) { g_return_val_if_fail(ft != NULL, 0); return ft->size; } guint32 mwFileTransfer_getRemaining(struct mwFileTransfer *ft) { g_return_val_if_fail(ft != NULL, 0); return ft->remaining; } int mwFileTransfer_accept(struct mwFileTransfer *ft) { struct mwServiceFileTransfer *srvc; struct mwFileTransferHandler *handler; int ret; g_return_val_if_fail(ft != NULL, -1); g_return_val_if_fail(ft->channel != NULL, -1); g_return_val_if_fail(mwFileTransfer_isPending(ft), -1); g_return_val_if_fail(mwChannel_isIncoming(ft->channel), -1); g_return_val_if_fail(mwChannel_isState(ft->channel, mwChannel_WAIT), -1); g_return_val_if_fail(ft->service != NULL, -1); srvc = ft->service; g_return_val_if_fail(srvc->handler != NULL, -1); handler = srvc->handler; ret = mwChannel_accept(ft->channel); if(ret) { mwFileTransfer_close(ft, ERR_FAILURE); } else { ft_state(ft, mwFileTransfer_OPEN); if(handler->ft_opened) handler->ft_opened(ft); } return ret; } static void ft_create_chan(struct mwFileTransfer *ft) { struct mwSession *s; struct mwChannelSet *cs; struct mwChannel *chan; struct mwLoginInfo *login; struct mwPutBuffer *b; /* we only should be calling this if there isn't a channel already associated with the conversation */ g_return_if_fail(ft != NULL); g_return_if_fail(mwFileTransfer_isNew(ft)); g_return_if_fail(ft->channel == NULL); s = mwService_getSession(MW_SERVICE(ft->service)); cs = mwSession_getChannels(s); chan = mwChannel_newOutgoing(cs); mwChannel_setService(chan, MW_SERVICE(ft->service)); mwChannel_setProtoType(chan, PROTOCOL_TYPE); mwChannel_setProtoVer(chan, PROTOCOL_VER); /* offer all known ciphers */ mwChannel_populateSupportedCipherInstances(chan); /* set the target */ login = mwChannel_getUser(chan); login->user_id = g_strdup(ft->who.user); login->community = g_strdup(ft->who.community); /* compose the addtl create */ b = mwPutBuffer_new(); guint32_put(b, 0x00); mwString_put(b, ft->filename); mwString_put(b, ft->message); guint32_put(b, ft->size); guint32_put(b, 0x00); guint16_put(b, 0x00); mwPutBuffer_finalize(mwChannel_getAddtlCreate(chan), b); ft->channel = mwChannel_create(chan)? NULL: chan; if(ft->channel) { mwChannel_setServiceData(ft->channel, ft, NULL); } } int mwFileTransfer_offer(struct mwFileTransfer *ft) { struct mwServiceFileTransfer *srvc; struct mwFileTransferHandler *handler; g_return_val_if_fail(ft != NULL, -1); g_return_val_if_fail(ft->channel == NULL, -1); g_return_val_if_fail(mwFileTransfer_isNew(ft), -1); g_return_val_if_fail(ft->service != NULL, -1); srvc = ft->service; g_return_val_if_fail(srvc->handler != NULL, -1); handler = srvc->handler; ft_create_chan(ft); if(ft->channel) { ft_state(ft, mwFileTransfer_PENDING); } else { ft_state(ft, mwFileTransfer_ERROR); mwFileTransfer_close(ft, ERR_FAILURE); } return 0; } int mwFileTransfer_close(struct mwFileTransfer *ft, guint32 code) { struct mwServiceFileTransfer *srvc; struct mwFileTransferHandler *handler; int ret = 0; g_return_val_if_fail(ft != NULL, -1); if(mwFileTransfer_isOpen(ft)) ft_state(ft, mwFileTransfer_CANCEL_LOCAL); if(ft->channel) { ret = mwChannel_destroy(ft->channel, code, NULL); ft->channel = NULL; } srvc = ft->service; g_return_val_if_fail(srvc != NULL, ret); handler = srvc->handler; g_return_val_if_fail(handler != NULL, ret); if(handler->ft_closed) handler->ft_closed(ft, code); return ret; } void mwFileTransfer_free(struct mwFileTransfer *ft) { struct mwServiceFileTransfer *srvc; if(! ft) return; srvc = ft->service; if(srvc) srvc->transfers = g_list_remove(srvc->transfers, ft); if(ft->channel) { mwChannel_destroy(ft->channel, mwFileTransfer_SUCCESS, NULL); ft->channel = NULL; } mwFileTransfer_removeClientData(ft); mwIdBlock_clear(&ft->who); g_free(ft->filename); g_free(ft->message); g_free(ft); } int mwFileTransfer_send(struct mwFileTransfer *ft, struct mwOpaque *data) { struct mwChannel *chan; int ret; g_return_val_if_fail(ft != NULL, -1); g_return_val_if_fail(mwFileTransfer_isOpen(ft), -1); g_return_val_if_fail(ft->channel != NULL, -1); chan = ft->channel; g_return_val_if_fail(mwChannel_isOutgoing(chan), -1); if(data->len > ft->remaining) { /* @todo handle error */ return -1; } ret = mwChannel_send(chan, msg_TRANSFER, data); if(! ret) ft->remaining -= data->len; /* we're not done until we receive an ACK for the last piece of outgoing data */ return ret; } int mwFileTransfer_ack(struct mwFileTransfer *ft) { struct mwChannel *chan; g_return_val_if_fail(ft != NULL, -1); chan = ft->channel; g_return_val_if_fail(chan != NULL, -1); g_return_val_if_fail(mwChannel_isIncoming(chan), -1); return mwChannel_sendEncrypted(chan, msg_RECEIVED, NULL, FALSE); } void mwFileTransfer_setClientData(struct mwFileTransfer *ft, gpointer data, GDestroyNotify clean) { g_return_if_fail(ft != NULL); mw_datum_set(&ft->client_data, data, clean); } gpointer mwFileTransfer_getClientData(struct mwFileTransfer *ft) { g_return_val_if_fail(ft != NULL, NULL); return mw_datum_get(&ft->client_data); } void mwFileTransfer_removeClientData(struct mwFileTransfer *ft) { g_return_if_fail(ft != NULL); mw_datum_clear(&ft->client_data); }