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