1 
2 /*
3   Meanwhile - Unofficial Lotus Sametime Community Client Library
4   Copyright (C) 2004  Christopher (siege) O'Brien
5 
6   This library is free software; you can redistribute it and/or
7   modify it under the terms of the GNU Library General Public
8   License as published by the Free Software Foundation; either
9   version 2 of the License, or (at your option) any later version.
10 
11   This library is distributed in the hope that it will be useful,
12   but WITHOUT ANY WARRANTY; without even the implied warranty of
13   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14   Library General Public License for more details.
15 
16   You should have received a copy of the GNU Library General Public
17   License along with this library; if not, write to the Free
18   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19 */
20 
21 
22 #include <glib.h>
23 
24 #include "mw_channel.h"
25 #include "mw_common.h"
26 #include "mw_debug.h"
27 #include "mw_error.h"
28 #include "mw_message.h"
29 #include "mw_service.h"
30 #include "mw_session.h"
31 #include "mw_srvc_ft.h"
32 #include "mw_util.h"
33 
34 
35 #define PROTOCOL_TYPE  0x00000000
36 #define PROTOCOL_VER   0x00000001
37 
38 
39 /** send-on-channel type: FT transfer data */
40 #define msg_TRANSFER  0x0001
41 
42 
43 /** ack received transfer data */
44 #define msg_RECEIVED  0x0002
45 
46 
47 struct mwServiceFileTransfer {
48   struct mwService service;
49 
50   struct mwFileTransferHandler *handler;
51   GList *transfers;
52 };
53 
54 
55 struct mwFileTransfer {
56   struct mwServiceFileTransfer *service;
57 
58   struct mwChannel *channel;
59   struct mwIdBlock who;
60 
61   enum mwFileTransferState state;
62 
63   char *filename;
64   char *message;
65 
66   guint32 size;
67   guint32 remaining;
68 
69   struct mw_datum client_data;
70 };
71 
72 
73 /** momentarily places a mwLoginInfo into a mwIdBlock */
login_into_id(struct mwIdBlock * to,struct mwLoginInfo * from)74 static void login_into_id(struct mwIdBlock *to, struct mwLoginInfo *from) {
75   to->user = from->user_id;
76   to->community = from->community;
77 }
78 
79 
ft_state_str(enum mwFileTransferState state)80 static const char *ft_state_str(enum mwFileTransferState state) {
81   switch(state) {
82   case mwFileTransfer_NEW:
83     return "new";
84 
85   case mwFileTransfer_PENDING:
86     return "pending";
87 
88   case mwFileTransfer_OPEN:
89     return "open";
90 
91   case mwFileTransfer_CANCEL_LOCAL:
92     return "cancelled locally";
93 
94   case mwFileTransfer_CANCEL_REMOTE:
95     return "cancelled remotely";
96 
97   case mwFileTransfer_DONE:
98     return "done";
99 
100   case mwFileTransfer_ERROR:
101     return "error";
102 
103   case mwFileTransfer_UNKNOWN:
104   default:
105     return "UNKNOWN";
106   }
107 }
108 
109 
ft_state(struct mwFileTransfer * ft,enum mwFileTransferState state)110 static void ft_state(struct mwFileTransfer *ft,
111 		     enum mwFileTransferState state) {
112 
113   g_return_if_fail(ft != NULL);
114 
115   if(ft->state == state) return;
116 
117   g_info("setting ft (%s, %s) state: %s",
118 	 NSTR(ft->who.user), NSTR(ft->who.community),
119 	 ft_state_str(state));
120 
121   ft->state = state;
122 }
123 
124 
recv_channelCreate(struct mwServiceFileTransfer * srvc,struct mwChannel * chan,struct mwMsgChannelCreate * msg)125 static void recv_channelCreate(struct mwServiceFileTransfer *srvc,
126 			       struct mwChannel *chan,
127 			       struct mwMsgChannelCreate *msg) {
128 
129   struct mwFileTransferHandler *handler;
130   struct mwGetBuffer *b;
131 
132   char *fnm, *txt;
133   guint32 size, junk;
134   gboolean b_err;
135 
136   g_return_if_fail(srvc->handler != NULL);
137   handler = srvc->handler;
138 
139   b = mwGetBuffer_wrap(&msg->addtl);
140 
141   guint32_get(b, &junk); /* unknown */
142   mwString_get(b, &fnm); /* offered filename */
143   mwString_get(b, &txt); /* offering message */
144   guint32_get(b, &size); /* size of offered file */
145   guint32_get(b, &junk); /* unknown */
146   /* and we just skip an unknown guint16 at the end */
147 
148   b_err = mwGetBuffer_error(b);
149   mwGetBuffer_free(b);
150 
151   if(b_err) {
152     g_warning("bad/malformed addtl in File Transfer service");
153     mwChannel_destroy(chan, ERR_FAILURE, NULL);
154 
155   } else {
156     struct mwIdBlock idb;
157     struct mwFileTransfer *ft;
158 
159     login_into_id(&idb, mwChannel_getUser(chan));
160     ft = mwFileTransfer_new(srvc, &idb, txt, fnm, size);
161     ft->channel = chan;
162     ft_state(ft, mwFileTransfer_PENDING);
163 
164     mwChannel_setServiceData(chan, ft, NULL);
165 
166     if(handler->ft_offered)
167       handler->ft_offered(ft);
168   }
169 
170   g_free(fnm);
171   g_free(txt);
172 }
173 
174 
recv_channelAccept(struct mwServiceFileTransfer * srvc,struct mwChannel * chan,struct mwMsgChannelAccept * msg)175 static void recv_channelAccept(struct mwServiceFileTransfer *srvc,
176 			       struct mwChannel *chan,
177 			       struct mwMsgChannelAccept *msg) {
178 
179   struct mwFileTransferHandler *handler;
180   struct mwFileTransfer *ft;
181 
182   g_return_if_fail(srvc->handler != NULL);
183   handler = srvc->handler;
184 
185   ft = mwChannel_getServiceData(chan);
186   g_return_if_fail(ft != NULL);
187 
188   ft_state(ft, mwFileTransfer_OPEN);
189 
190   if(handler->ft_opened)
191     handler->ft_opened(ft);
192 }
193 
194 
recv_channelDestroy(struct mwServiceFileTransfer * srvc,struct mwChannel * chan,struct mwMsgChannelDestroy * msg)195 static void recv_channelDestroy(struct mwServiceFileTransfer *srvc,
196 				struct mwChannel *chan,
197 				struct mwMsgChannelDestroy *msg) {
198 
199   struct mwFileTransferHandler *handler;
200   struct mwFileTransfer *ft;
201   guint32 code;
202 
203   code = msg->reason;
204 
205   g_return_if_fail(srvc->handler != NULL);
206   handler = srvc->handler;
207 
208   ft = mwChannel_getServiceData(chan);
209   g_return_if_fail(ft != NULL);
210 
211   ft->channel = NULL;
212 
213   if(! mwFileTransfer_isDone(ft))
214     ft_state(ft, mwFileTransfer_CANCEL_REMOTE);
215 
216   mwFileTransfer_close(ft, code);
217 }
218 
219 
recv_TRANSFER(struct mwFileTransfer * ft,struct mwOpaque * data)220 static void recv_TRANSFER(struct mwFileTransfer *ft,
221 			  struct mwOpaque *data) {
222 
223   struct mwServiceFileTransfer *srvc;
224   struct mwFileTransferHandler *handler;
225 
226   srvc = ft->service;
227   handler = srvc->handler;
228 
229   g_return_if_fail(mwFileTransfer_isOpen(ft));
230 
231   if(data->len > ft->remaining) {
232     /* @todo handle error */
233 
234   } else {
235     ft->remaining -= data->len;
236 
237     if(! ft->remaining)
238       ft_state(ft, mwFileTransfer_DONE);
239 
240     if(handler->ft_recv)
241       handler->ft_recv(ft, data);
242   }
243 }
244 
245 
recv_RECEIVED(struct mwFileTransfer * ft,struct mwOpaque * data)246 static void recv_RECEIVED(struct mwFileTransfer *ft,
247 			  struct mwOpaque *data) {
248 
249   struct mwServiceFileTransfer *srvc;
250   struct mwFileTransferHandler *handler;
251 
252   srvc = ft->service;
253   handler = srvc->handler;
254 
255   if(! ft->remaining)
256     ft_state(ft, mwFileTransfer_DONE);
257 
258   if(handler->ft_ack)
259     handler->ft_ack(ft);
260 
261   if(! ft->remaining)
262     mwFileTransfer_close(ft, mwFileTransfer_SUCCESS);
263 }
264 
265 
recv(struct mwService * srvc,struct mwChannel * chan,guint16 type,struct mwOpaque * data)266 static void recv(struct mwService *srvc, struct mwChannel *chan,
267 		 guint16 type, struct mwOpaque *data) {
268 
269   struct mwFileTransfer *ft;
270 
271   ft = mwChannel_getServiceData(chan);
272   g_return_if_fail(ft != NULL);
273 
274   switch(type) {
275   case msg_TRANSFER:
276     recv_TRANSFER(ft, data);
277     break;
278 
279   case msg_RECEIVED:
280     recv_RECEIVED(ft, data);
281     break;
282 
283   default:
284     mw_mailme_opaque(data, "unknown message in ft service: 0x%04x", type);
285   }
286 }
287 
288 
clear(struct mwServiceFileTransfer * srvc)289 static void clear(struct mwServiceFileTransfer *srvc) {
290   struct mwFileTransferHandler *h;
291 
292   h = srvc->handler;
293   if(h && h->clear)
294     h->clear(srvc);
295   srvc->handler = NULL;
296 }
297 
298 
name(struct mwService * srvc)299 static const char *name(struct mwService *srvc) {
300   return "File Transfer";
301 }
302 
303 
desc(struct mwService * srvc)304 static const char *desc(struct mwService *srvc) {
305   return "Provides file transfer capabilities through the community server";
306 }
307 
308 
start(struct mwService * srvc)309 static void start(struct mwService *srvc) {
310   mwService_started(srvc);
311 }
312 
313 
stop(struct mwServiceFileTransfer * srvc)314 static void stop(struct mwServiceFileTransfer *srvc) {
315   while(srvc->transfers) {
316     mwFileTransfer_free(srvc->transfers->data);
317   }
318 
319   mwService_stopped(MW_SERVICE(srvc));
320 }
321 
322 
323 struct mwServiceFileTransfer *
mwServiceFileTransfer_new(struct mwSession * session,struct mwFileTransferHandler * handler)324 mwServiceFileTransfer_new(struct mwSession *session,
325 			  struct mwFileTransferHandler *handler) {
326 
327   struct mwServiceFileTransfer *srvc_ft;
328   struct mwService *srvc;
329 
330   g_return_val_if_fail(session != NULL, NULL);
331   g_return_val_if_fail(handler != NULL, NULL);
332 
333   srvc_ft = g_new0(struct mwServiceFileTransfer, 1);
334   srvc = MW_SERVICE(srvc_ft);
335 
336   mwService_init(srvc, session, mwService_FILE_TRANSFER);
337   srvc->recv_create = (mwService_funcRecvCreate) recv_channelCreate;
338   srvc->recv_accept = (mwService_funcRecvAccept) recv_channelAccept;
339   srvc->recv_destroy = (mwService_funcRecvDestroy) recv_channelDestroy;
340   srvc->recv = recv;
341   srvc->clear = (mwService_funcClear) clear;
342   srvc->get_name = name;
343   srvc->get_desc = desc;
344   srvc->start = start;
345   srvc->stop = (mwService_funcStop) stop;
346 
347   srvc_ft->handler = handler;
348 
349   return srvc_ft;
350 }
351 
352 
353 struct mwFileTransferHandler *
mwServiceFileTransfer_getHandler(struct mwServiceFileTransfer * srvc)354 mwServiceFileTransfer_getHandler(struct mwServiceFileTransfer *srvc) {
355   g_return_val_if_fail(srvc != NULL, NULL);
356   return srvc->handler;
357 }
358 
359 
360 const GList *
mwServiceFileTransfer_getTransfers(struct mwServiceFileTransfer * srvc)361 mwServiceFileTransfer_getTransfers(struct mwServiceFileTransfer *srvc) {
362   g_return_val_if_fail(srvc != NULL, NULL);
363   return srvc->transfers;
364 }
365 
366 
367 struct mwFileTransfer *
mwFileTransfer_new(struct mwServiceFileTransfer * srvc,const struct mwIdBlock * who,const char * msg,const char * filename,guint32 filesize)368 mwFileTransfer_new(struct mwServiceFileTransfer *srvc,
369 		   const struct mwIdBlock *who, const char *msg,
370 		   const char *filename, guint32 filesize) {
371 
372   struct mwFileTransfer *ft;
373 
374   g_return_val_if_fail(srvc != NULL, NULL);
375   g_return_val_if_fail(who != NULL, NULL);
376 
377   ft = g_new0(struct mwFileTransfer, 1);
378   ft->service = srvc;
379   mwIdBlock_clone(&ft->who, who);
380   ft->filename = g_strdup(filename);
381   ft->message = g_strdup(msg);
382   ft->size = ft->remaining = filesize;
383 
384   ft_state(ft, mwFileTransfer_NEW);
385 
386   /* stick a reference in the service */
387   srvc->transfers = g_list_prepend(srvc->transfers, ft);
388 
389   return ft;
390 }
391 
392 
393 struct mwServiceFileTransfer *
mwFileTransfer_getService(struct mwFileTransfer * ft)394 mwFileTransfer_getService(struct mwFileTransfer *ft) {
395   g_return_val_if_fail(ft != NULL, NULL);
396   return ft->service;
397 }
398 
399 
400 enum mwFileTransferState
mwFileTransfer_getState(struct mwFileTransfer * ft)401 mwFileTransfer_getState(struct mwFileTransfer *ft) {
402   g_return_val_if_fail(ft != NULL, mwFileTransfer_UNKNOWN);
403   return ft->state;
404 }
405 
406 
407 const struct mwIdBlock *
mwFileTransfer_getUser(struct mwFileTransfer * ft)408 mwFileTransfer_getUser(struct mwFileTransfer *ft) {
409   g_return_val_if_fail(ft != NULL, NULL);
410   return &ft->who;
411 }
412 
413 
414 const char *
mwFileTransfer_getMessage(struct mwFileTransfer * ft)415 mwFileTransfer_getMessage(struct mwFileTransfer *ft) {
416   g_return_val_if_fail(ft != NULL, NULL);
417   return ft->message;
418 }
419 
420 
421 const char *
mwFileTransfer_getFileName(struct mwFileTransfer * ft)422 mwFileTransfer_getFileName(struct mwFileTransfer *ft) {
423   g_return_val_if_fail(ft != NULL, NULL);
424   return ft->filename;
425 }
426 
427 
mwFileTransfer_getFileSize(struct mwFileTransfer * ft)428 guint32 mwFileTransfer_getFileSize(struct mwFileTransfer *ft) {
429   g_return_val_if_fail(ft != NULL, 0);
430   return ft->size;
431 }
432 
433 
mwFileTransfer_getRemaining(struct mwFileTransfer * ft)434 guint32 mwFileTransfer_getRemaining(struct mwFileTransfer *ft) {
435   g_return_val_if_fail(ft != NULL, 0);
436   return ft->remaining;
437 }
438 
439 
mwFileTransfer_accept(struct mwFileTransfer * ft)440 int mwFileTransfer_accept(struct mwFileTransfer *ft) {
441   struct mwServiceFileTransfer *srvc;
442   struct mwFileTransferHandler *handler;
443   int ret;
444 
445   g_return_val_if_fail(ft != NULL, -1);
446   g_return_val_if_fail(ft->channel != NULL, -1);
447   g_return_val_if_fail(mwFileTransfer_isPending(ft), -1);
448   g_return_val_if_fail(mwChannel_isIncoming(ft->channel), -1);
449   g_return_val_if_fail(mwChannel_isState(ft->channel, mwChannel_WAIT), -1);
450 
451   g_return_val_if_fail(ft->service != NULL, -1);
452   srvc = ft->service;
453 
454   g_return_val_if_fail(srvc->handler != NULL, -1);
455   handler = srvc->handler;
456 
457   ret = mwChannel_accept(ft->channel);
458 
459   if(ret) {
460     mwFileTransfer_close(ft, ERR_FAILURE);
461 
462   } else {
463     ft_state(ft, mwFileTransfer_OPEN);
464     if(handler->ft_opened)
465       handler->ft_opened(ft);
466   }
467 
468   return ret;
469 }
470 
471 
ft_create_chan(struct mwFileTransfer * ft)472 static void ft_create_chan(struct mwFileTransfer *ft) {
473   struct mwSession *s;
474   struct mwChannelSet *cs;
475   struct mwChannel *chan;
476   struct mwLoginInfo *login;
477   struct mwPutBuffer *b;
478 
479   /* we only should be calling this if there isn't a channel already
480      associated with the conversation */
481   g_return_if_fail(ft != NULL);
482   g_return_if_fail(mwFileTransfer_isNew(ft));
483   g_return_if_fail(ft->channel == NULL);
484 
485   s = mwService_getSession(MW_SERVICE(ft->service));
486   cs = mwSession_getChannels(s);
487 
488   chan = mwChannel_newOutgoing(cs);
489   mwChannel_setService(chan, MW_SERVICE(ft->service));
490   mwChannel_setProtoType(chan, PROTOCOL_TYPE);
491   mwChannel_setProtoVer(chan, PROTOCOL_VER);
492 
493   /* offer all known ciphers */
494   mwChannel_populateSupportedCipherInstances(chan);
495 
496   /* set the target */
497   login = mwChannel_getUser(chan);
498   login->user_id = g_strdup(ft->who.user);
499   login->community = g_strdup(ft->who.community);
500 
501   /* compose the addtl create */
502   b = mwPutBuffer_new();
503   guint32_put(b, 0x00);
504   mwString_put(b, ft->filename);
505   mwString_put(b, ft->message);
506   guint32_put(b, ft->size);
507   guint32_put(b, 0x00);
508   guint16_put(b, 0x00);
509 
510   mwPutBuffer_finalize(mwChannel_getAddtlCreate(chan), b);
511 
512   ft->channel = mwChannel_create(chan)? NULL: chan;
513   if(ft->channel) {
514     mwChannel_setServiceData(ft->channel, ft, NULL);
515   }
516 }
517 
518 
mwFileTransfer_offer(struct mwFileTransfer * ft)519 int mwFileTransfer_offer(struct mwFileTransfer *ft) {
520   struct mwServiceFileTransfer *srvc;
521   struct mwFileTransferHandler *handler;
522 
523   g_return_val_if_fail(ft != NULL, -1);
524   g_return_val_if_fail(ft->channel == NULL, -1);
525   g_return_val_if_fail(mwFileTransfer_isNew(ft), -1);
526 
527   g_return_val_if_fail(ft->service != NULL, -1);
528   srvc = ft->service;
529 
530   g_return_val_if_fail(srvc->handler != NULL, -1);
531   handler = srvc->handler;
532 
533   ft_create_chan(ft);
534   if(ft->channel) {
535     ft_state(ft, mwFileTransfer_PENDING);
536   } else {
537     ft_state(ft, mwFileTransfer_ERROR);
538     mwFileTransfer_close(ft, ERR_FAILURE);
539   }
540 
541   return 0;
542 }
543 
544 
mwFileTransfer_close(struct mwFileTransfer * ft,guint32 code)545 int mwFileTransfer_close(struct mwFileTransfer *ft, guint32 code) {
546   struct mwServiceFileTransfer *srvc;
547   struct mwFileTransferHandler *handler;
548   int ret = 0;
549 
550   g_return_val_if_fail(ft != NULL, -1);
551 
552   if(mwFileTransfer_isOpen(ft))
553     ft_state(ft, mwFileTransfer_CANCEL_LOCAL);
554 
555   if(ft->channel) {
556     ret = mwChannel_destroy(ft->channel, code, NULL);
557     ft->channel = NULL;
558   }
559 
560   srvc = ft->service;
561   g_return_val_if_fail(srvc != NULL, ret);
562 
563   handler = srvc->handler;
564   g_return_val_if_fail(handler != NULL, ret);
565 
566   if(handler->ft_closed)
567     handler->ft_closed(ft, code);
568 
569   return ret;
570 }
571 
572 
mwFileTransfer_free(struct mwFileTransfer * ft)573 void mwFileTransfer_free(struct mwFileTransfer *ft) {
574   struct mwServiceFileTransfer *srvc;
575 
576   if(! ft) return;
577 
578   srvc = ft->service;
579   if(srvc)
580     srvc->transfers = g_list_remove(srvc->transfers, ft);
581 
582   if(ft->channel) {
583     mwChannel_destroy(ft->channel, mwFileTransfer_SUCCESS, NULL);
584     ft->channel = NULL;
585   }
586 
587   mwFileTransfer_removeClientData(ft);
588 
589   mwIdBlock_clear(&ft->who);
590   g_free(ft->filename);
591   g_free(ft->message);
592   g_free(ft);
593 }
594 
595 
mwFileTransfer_send(struct mwFileTransfer * ft,struct mwOpaque * data)596 int mwFileTransfer_send(struct mwFileTransfer *ft,
597 			struct mwOpaque *data) {
598 
599   struct mwChannel *chan;
600   int ret;
601 
602   g_return_val_if_fail(ft != NULL, -1);
603   g_return_val_if_fail(mwFileTransfer_isOpen(ft), -1);
604   g_return_val_if_fail(ft->channel != NULL, -1);
605   chan = ft->channel;
606 
607   g_return_val_if_fail(mwChannel_isOutgoing(chan), -1);
608 
609   if(data->len > ft->remaining) {
610     /* @todo handle error */
611     return -1;
612   }
613 
614   ret = mwChannel_send(chan, msg_TRANSFER, data);
615   if(! ret) ft->remaining -= data->len;
616 
617   /* we're not done until we receive an ACK for the last piece of
618      outgoing data */
619 
620   return ret;
621 }
622 
623 
mwFileTransfer_ack(struct mwFileTransfer * ft)624 int mwFileTransfer_ack(struct mwFileTransfer *ft) {
625   struct mwChannel *chan;
626 
627   g_return_val_if_fail(ft != NULL, -1);
628 
629   chan = ft->channel;
630   g_return_val_if_fail(chan != NULL, -1);
631   g_return_val_if_fail(mwChannel_isIncoming(chan), -1);
632 
633   return mwChannel_sendEncrypted(chan, msg_RECEIVED, NULL, FALSE);
634 }
635 
636 
mwFileTransfer_setClientData(struct mwFileTransfer * ft,gpointer data,GDestroyNotify clean)637 void mwFileTransfer_setClientData(struct mwFileTransfer *ft,
638 				  gpointer data, GDestroyNotify clean) {
639   g_return_if_fail(ft != NULL);
640   mw_datum_set(&ft->client_data, data, clean);
641 }
642 
643 
mwFileTransfer_getClientData(struct mwFileTransfer * ft)644 gpointer mwFileTransfer_getClientData(struct mwFileTransfer *ft) {
645   g_return_val_if_fail(ft != NULL, NULL);
646   return mw_datum_get(&ft->client_data);
647 }
648 
649 
mwFileTransfer_removeClientData(struct mwFileTransfer * ft)650 void mwFileTransfer_removeClientData(struct mwFileTransfer *ft) {
651   g_return_if_fail(ft != NULL);
652   mw_datum_clear(&ft->client_data);
653 }
654 
655