1 /*
2  * Copyright (c)2004 Cat's Eye Technologies.  All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  *
8  *   Redistributions of source code must retain the above copyright
9  *   notice, this list of conditions and the following disclaimer.
10  *
11  *   Redistributions in binary form must reproduce the above copyright
12  *   notice, this list of conditions and the following disclaimer in
13  *   the documentation and/or other materials provided with the
14  *   distribution.
15  *
16  *   Neither the name of Cat's Eye Technologies nor the names of its
17  *   contributors may be used to endorse or promote products derived
18  *   from this software without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
23  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
24  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
25  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
26  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
27  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
29  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
30  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
31  * OF THE POSSIBILITY OF SUCH DAMAGE.
32  */
33 
34 /*
35  * connection.c
36  * $Id: connection.c,v 1.20 2005/02/07 06:39:59 cpressey Exp $
37  * This code was derived in part from:
38  * $_DragonFly: src/test/caps/client.c,v 1.3 2004/03/31 20:27:34 dillon Exp $
39  * and is therefore also subject to the license conditions on that file.
40  */
41 
42 #include <stdarg.h>
43 #include <stdlib.h>
44 #include <string.h>
45 
46 #include <libaura/mem.h>
47 #include <libaura/buffer.h>
48 
49 #include "system.h"
50 #define	NEEDS_DFUI_STRUCTURE_DEFINITIONS
51 #include "dfui.h"
52 #undef	NEEDS_DFUI_STRUCTURE_DEFINITIONS
53 #include "encoding.h"
54 #include "dump.h"
55 
56 #include "conn_caps.h"
57 #include "conn_npipe.h"
58 #include "conn_tcp.h"
59 
60 struct dfui_connection *
61 dfui_connection_new(int transport, const char *rendezvous)
62 {
63 	struct dfui_connection *c = NULL;
64 
65 	if (
66 #ifdef HAS_CAPS
67 	    transport == DFUI_TRANSPORT_CAPS ||
68 #endif
69 #ifdef HAS_NPIPE
70 	    transport == DFUI_TRANSPORT_NPIPE ||
71 #endif
72 #ifdef HAS_TCP
73 	    transport == DFUI_TRANSPORT_TCP ||
74 #endif
75 	    0) {
76 		/* We're OK. */
77 	} else {
78 		return(NULL);
79 	}
80 
81 	if (dfui_debug_file == NULL) {
82 		dfui_debug_file = stderr;
83 	} else {
84 		setvbuf(dfui_debug_file, NULL, _IOLBF, 0);
85 	}
86 
87 	AURA_MALLOC(c, dfui_connection);
88 	c->rendezvous = aura_strdup(rendezvous);
89 	c->transport = transport;
90 	c->ebuf = aura_buffer_new(16384);
91 	c->is_connected = 0;
92 	c->t_data = NULL;
93 
94 	switch (transport) {
95 #ifdef HAS_CAPS
96 	case DFUI_TRANSPORT_CAPS:
97 		AURA_MALLOC(c->t_data, dfui_conn_caps);
98 		T_CAPS(c)->cid = 0;
99 		bzero(&T_CAPS(c)->msgid, sizeof(T_CAPS(c)->msgid));
100 
101 		/*
102 		 * XXX Ideally, this value should grow as needed.
103 		 * However, CAPS currently has a size limit of
104 		 * 128K internally.
105 		 */
106 		T_CAPS(c)->size = 128 * 1024;
107 		if ((T_CAPS(c)->buf = aura_malloc(T_CAPS(c)->size, "CAPS buffer")) == NULL) {
108 			AURA_FREE(T_CAPS(c), dfui_conn_caps);
109 			aura_free(c->rendezvous, "rendezvous string");
110 			AURA_FREE(c, dfui_connection);
111 			return(NULL);
112 		}
113 
114 		/*
115 		 * Set up dispatch functions.
116 		 */
117 		c->be_start = dfui_caps_be_start;
118 		c->be_stop = dfui_caps_be_stop;
119 		c->be_ll_exchange = dfui_caps_be_ll_exchange;
120 
121 		c->fe_connect = dfui_caps_fe_connect;
122 		c->fe_disconnect = dfui_caps_fe_disconnect;
123 		c->fe_ll_request = dfui_caps_fe_ll_request;
124 		break;
125 #endif /* HAS_CAPS */
126 
127 #ifdef HAS_NPIPE
128 	case DFUI_TRANSPORT_NPIPE:
129 		AURA_MALLOC(c->t_data, dfui_conn_npipe);
130 		T_NPIPE(c)->in_pipename = NULL;
131 		T_NPIPE(c)->out_pipename = NULL;
132 		T_NPIPE(c)->in = NULL;
133 		T_NPIPE(c)->out = NULL;
134 
135 		/*
136 		 * Set up dispatch functions.
137 		 */
138 		c->be_start = dfui_npipe_be_start;
139 		c->be_stop = dfui_npipe_be_stop;
140 		c->be_ll_exchange = dfui_npipe_be_ll_exchange;
141 
142 		c->fe_connect = dfui_npipe_fe_connect;
143 		c->fe_disconnect = dfui_npipe_fe_disconnect;
144 		c->fe_ll_request = dfui_npipe_fe_ll_request;
145 		break;
146 #endif /* HAS_NPIPE */
147 
148 #ifdef HAS_TCP
149 	case DFUI_TRANSPORT_TCP:
150                 AURA_MALLOC(c->t_data, dfui_conn_tcp);
151 		T_TCP(c)->listen_sd = -1;
152 		T_TCP(c)->connected_sd = -1;
153 		T_TCP(c)->is_connected = 0;
154 
155                 /*
156                  * Set up dispatch functions.
157                  */
158                 c->be_start = dfui_tcp_be_start;
159                 c->be_stop = dfui_tcp_be_stop;
160 		c->be_ll_exchange = dfui_tcp_be_ll_exchange;
161 
162                 c->fe_connect = dfui_tcp_fe_connect;
163                 c->fe_disconnect = dfui_tcp_fe_disconnect;
164 		c->fe_ll_request = dfui_tcp_fe_ll_request;
165 		break;
166 #endif /* HAS_TCP */
167 	}
168 
169 	return(c);
170 }
171 
172 void
173 dfui_connection_free(struct dfui_connection *c)
174 {
175 	if (c == NULL)
176 		return;
177 
178 	switch (c->transport) {
179 #ifdef HAS_CAPS
180 	case DFUI_TRANSPORT_CAPS:
181 		if (T_CAPS(c) != NULL) {
182 			if (T_CAPS(c)->buf != NULL)
183 				aura_free(T_CAPS(c)->buf, "CAPS buffer");
184 			AURA_FREE(T_CAPS(c), dfui_conn_caps);
185 		}
186 		break;
187 #endif
188 #ifdef HAS_NPIPE
189 	case DFUI_TRANSPORT_NPIPE:
190 		if (T_NPIPE(c) != NULL) {
191 			if (T_NPIPE(c)->in_pipename != NULL)
192 				aura_free(T_NPIPE(c)->in_pipename, "pipename");
193 			if (T_NPIPE(c)->out_pipename != NULL)
194 				aura_free(T_NPIPE(c)->out_pipename, "pipename");
195 			if (T_NPIPE(c)->in != NULL)
196 				fclose(T_NPIPE(c)->in);
197 			if (T_NPIPE(c)->out != NULL)
198 				fclose(T_NPIPE(c)->out);
199 			AURA_FREE(T_NPIPE(c), dfui_conn_npipe);
200 		}
201 		break;
202 #endif
203 #ifdef HAS_TCP
204 	case DFUI_TRANSPORT_TCP:
205 		if (T_TCP(c) != NULL) {
206 			/* XXX close sockets/files here */
207 			AURA_FREE(T_NPIPE(c), dfui_conn_tcp);
208 		}
209 		break;
210 #endif
211 	}
212 
213 	if (c->rendezvous != NULL)
214 		free(c->rendezvous);
215 	AURA_FREE(c, dfui_connection);
216 }
217 
218 /*
219  * VERY HIGH LEVEL
220  */
221 
222 /*
223  * Create and present a generic `dialog box'-type form for the user
224  * and return their response.  actions is a pipe-seperated list of
225  * actions to be put on the form (e.g. "OK|Cancel".)  The return
226  * value is the ordinal position of the action that was selected,
227  * starting at 1 for the first action.  A return value of 0 indicates
228  * that an error occurred.  A return value of -1 indicates that the
229  * front end aborted the communications.
230  */
231 int
232 dfui_be_present_dialog(struct dfui_connection *c, const char *title,
233 			const char *actions, const char *fmt, ...)
234 {
235 	struct dfui_form *f;
236 	struct dfui_response *r;
237 	va_list args;
238 	char *message;
239 	char action_id[256], action_name[256];
240 	size_t start, end, counter, i;
241 
242 	va_start(args, fmt);
243 	vasprintf(&message, fmt, args);
244 	va_end(args);
245 
246 	f = dfui_form_create("dialog", title, message, "", NULL);
247 
248 	free(message);
249 
250 	start = end = 0;
251 	while (actions[end] != '\0') {
252 		end = start;
253 		while (actions[end] != '|' && actions[end] != '\0')
254 			end++;
255 
256 		if ((end - start) >= 256)
257 			break;
258 		strncpy(action_name, &actions[start], end - start);
259 		action_name[end - start] = '\0';
260 		strcpy(action_id, action_name);
261 		for(i = 0; action_id[i] != '\0'; i++) {
262 			if (action_id[i] == ' ')
263 				action_id[i] = '_';
264 		}
265 		dfui_form_action_add(f, action_id,
266 		    dfui_info_new(action_name, "", ""));
267 
268 		start = end + 1;
269 	}
270 
271 	if (!dfui_be_present(c, f, &r)) {
272 		dfui_form_free(f);
273 		dfui_response_free(r);
274 		return(-1);
275 	}
276 
277 	strlcpy(action_name, dfui_response_get_action_id(r), 256);
278 	for(i = 0; action_name[i] != '\0'; i++) {
279 		if (action_name[i] == '_')
280 			action_name[i] = ' ';
281 	}
282 
283 	start = end = 0;
284 	counter = 1;
285 	while (actions[end] != '\0') {
286 		end = start;
287 		while (actions[end] != '|' && actions[end] != '\0')
288 			end++;
289 
290 		if ((end - start) >= 256)
291 			break;
292 		if (strlen(action_name) == (end - start) &&
293 		    strncmp(action_name, &actions[start], end - start) == 0) {
294 			break;
295 		}
296 		counter++;
297 
298 		start = end + 1;
299 	}
300 
301 	dfui_form_free(f);
302 	dfui_response_free(r);
303 
304 	return(counter);
305 }
306 
307 /******** BACKEND ********/
308 
309 /*
310  * Connect to the frontend.
311  */
312 dfui_err_t
313 dfui_be_start(struct dfui_connection *c)
314 {
315 	if (c->is_connected) {
316 		return(DFUI_FAILURE);
317 	} else if (c->be_start(c)) {
318 		c->is_connected = 1;
319 		return(DFUI_SUCCESS);
320 	} else {
321 		return(DFUI_FAILURE);
322 	}
323 }
324 
325 /*
326  * Tell the frontend that we're done and disconnect from it.
327  */
328 dfui_err_t
329 dfui_be_stop(struct dfui_connection *c)
330 {
331 	if (!c->is_connected) {
332 		return(DFUI_SUCCESS);
333 	} else if (c->be_stop(c)) {
334 		c->is_connected = 0;
335 		return(DFUI_SUCCESS);
336 	} else {
337 		return(DFUI_FAILURE);
338 	}
339 }
340 
341 /*
342  * Present a form to the user.  This call is synchronous;
343  * it does not return until the user has selected an action.
344  */
345 dfui_err_t
346 dfui_be_present(struct dfui_connection *c,
347 		struct dfui_form *f, struct dfui_response **r)
348 {
349 	struct aura_buffer *e;
350 
351 	e = aura_buffer_new(16384);
352 	dfui_encode_form(e, f);
353 
354 	c->be_ll_exchange(c, DFUI_BE_MSG_PRESENT, aura_buffer_buf(e));
355 
356 	aura_buffer_free(e);
357 
358 	/* check for ABORT reply */
359 	if (aura_buffer_buf(c->ebuf)[0] == DFUI_FE_MSG_ABORT) {
360 		return(DFUI_FAILURE);
361 	}
362 
363 	/*
364 	 * Now we've got the response; so decode it.
365 	 */
366 
367 	e = aura_buffer_new(16384);
368 	aura_buffer_set(e, aura_buffer_buf(c->ebuf) + 1, aura_buffer_len(c->ebuf) - 1);
369 	*r = dfui_decode_response(e);
370 	aura_buffer_free(e);
371 
372 	return(DFUI_SUCCESS);
373 }
374 
375 /*
376  * Begin showing a progress bar to the user.
377  * This function is asynchronous; it returns immediately.
378  * The assumption is that the backend will make subsequent
379  * calls to dfui_be_progress_update() frequently, and in
380  * them, check to see if the user cancelled.
381  */
382 dfui_err_t
383 dfui_be_progress_begin(struct dfui_connection *c, struct dfui_progress *pr)
384 {
385 	struct aura_buffer *e;
386 
387 	e = aura_buffer_new(16384);
388 	dfui_encode_progress(e, pr);
389 
390 	c->be_ll_exchange(c, DFUI_BE_MSG_PROG_BEGIN, aura_buffer_buf(e));
391 	aura_buffer_free(e);
392 
393 	/* response might have been be READY or ABORT */
394 	if (aura_buffer_buf(c->ebuf)[0] == DFUI_FE_MSG_ABORT) {
395 		return(DFUI_FAILURE);
396 	} else {
397 		return(DFUI_SUCCESS);
398 	}
399 }
400 
401 dfui_err_t
402 dfui_be_progress_update(struct dfui_connection *c,
403 			struct dfui_progress *pr, int *cancelled)
404 {
405 	struct aura_buffer *e;
406 
407 	e = aura_buffer_new(16384);
408 	dfui_encode_progress(e, pr);
409 
410 	c->be_ll_exchange(c, DFUI_BE_MSG_PROG_UPDATE, aura_buffer_buf(e));
411 	aura_buffer_free(e);
412 
413 	/* response might have been READY, CANCEL, or ABORT */
414 
415 	*cancelled = 0;
416 	if (aura_buffer_buf(c->ebuf)[0] == DFUI_FE_MSG_CANCEL) {
417 		*cancelled = 1;
418 	}
419 	if (aura_buffer_buf(c->ebuf)[0] == DFUI_FE_MSG_ABORT) {
420 		return(DFUI_FAILURE);
421 	} else {
422 		return(DFUI_SUCCESS);
423 	}
424 }
425 
426 dfui_err_t
427 dfui_be_progress_end(struct dfui_connection *c)
428 {
429 	c->be_ll_exchange(c, DFUI_BE_MSG_PROG_END, "");
430 
431 	/* response might have been be READY or ABORT */
432 	if (aura_buffer_buf(c->ebuf)[0] == DFUI_FE_MSG_ABORT) {
433 		return(DFUI_FAILURE);
434 	} else {
435 		return(DFUI_SUCCESS);
436 	}
437 }
438 
439 dfui_err_t
440 dfui_be_set_global_setting(struct dfui_connection *c,
441 			   const char *key, const char *value,
442 			   int *cancelled)
443 {
444 	struct aura_buffer *e;
445 	struct dfui_property *p;
446 
447 	e = aura_buffer_new(16384);
448 	p = dfui_property_new(key, value);
449 	dfui_encode_property(e, p);
450 	c->be_ll_exchange(c, DFUI_BE_MSG_SET_GLOBAL, aura_buffer_buf(e));
451 	aura_buffer_free(e);
452 	dfui_property_free(p);
453 
454 	/* response might have been READY, CANCEL, or ABORT */
455 
456 	*cancelled = 0;
457 	if (aura_buffer_buf(c->ebuf)[0] == DFUI_FE_MSG_CANCEL) {
458 		*cancelled = 1;
459 	}
460 	if (aura_buffer_buf(c->ebuf)[0] == DFUI_FE_MSG_ABORT) {
461 		return(DFUI_FAILURE);
462 	} else {
463 		return(DFUI_SUCCESS);
464 	}
465 }
466 
467 /******** FRONTEND ********/
468 
469 dfui_err_t
470 dfui_fe_connect(struct dfui_connection *c)
471 {
472 	return(c->fe_connect(c));
473 }
474 
475 dfui_err_t
476 dfui_fe_disconnect(struct dfui_connection *c)
477 {
478 	dfui_debug("DISCONNECTING<<>>\n");
479 	return(c->fe_disconnect(c));
480 }
481 
482 /*
483  * Receive a message from the backend.  This call is synchronous;
484  * it does not return until a message comes in from the backend.
485  * After this call, the message type is available in *msgtype,
486  * and the message itself (if any) is available in *payload, ready
487  * to be casted to its real type (as per *msgtype).
488  */
489 dfui_err_t
490 dfui_fe_receive(struct dfui_connection *c, char *msgtype, void **payload)
491 {
492 	struct aura_buffer *e;
493 
494 	c->fe_ll_request(c, DFUI_FE_MSG_READY, "");
495 	*msgtype = aura_buffer_buf(c->ebuf)[0];
496 	switch (*msgtype) {
497 	case DFUI_BE_MSG_PRESENT:
498 		e = aura_buffer_new(16384);
499 		aura_buffer_set(e, aura_buffer_buf(c->ebuf) + 1, aura_buffer_len(c->ebuf) - 1);
500 		*payload = dfui_decode_form(e);
501 		aura_buffer_free(e);
502 		return(DFUI_SUCCESS);
503 
504 	case DFUI_BE_MSG_PROG_BEGIN:
505 		e = aura_buffer_new(16384);
506 		aura_buffer_set(e, aura_buffer_buf(c->ebuf) + 1, aura_buffer_len(c->ebuf) - 1);
507 		*payload = dfui_decode_progress(e);
508 		aura_buffer_free(e);
509 		return(DFUI_SUCCESS);
510 
511 	case DFUI_BE_MSG_PROG_UPDATE:
512 		e = aura_buffer_new(16384);
513 		aura_buffer_set(e, aura_buffer_buf(c->ebuf) + 1, aura_buffer_len(c->ebuf) - 1);
514 		*payload = dfui_decode_progress(e);
515 		aura_buffer_free(e);
516 		return(DFUI_SUCCESS);
517 
518 	case DFUI_BE_MSG_PROG_END:
519 		*payload = NULL;
520 		return(DFUI_SUCCESS);
521 
522 	case DFUI_BE_MSG_SET_GLOBAL:
523 		e = aura_buffer_new(16384);
524 		aura_buffer_set(e, aura_buffer_buf(c->ebuf) + 1, aura_buffer_len(c->ebuf) - 1);
525 		*payload = dfui_decode_property(e);
526 		aura_buffer_free(e);
527 		return(DFUI_SUCCESS);
528 
529 	case DFUI_BE_MSG_STOP:
530 		*payload = NULL;
531 		return(DFUI_SUCCESS);
532 
533 	default:
534 		/* XXX ??? */
535 		return(DFUI_FAILURE);
536 	}
537 }
538 
539 /*
540  * Wrapper function for dfui_fe_receive for binding generators which
541  * seem to (understandably) have problems wrapping void *'s themselves.
542  */
543 struct dfui_payload *
544 dfui_fe_receive_payload(struct dfui_connection *c)
545 {
546 	char msgtype;
547 	void *v;
548 	struct dfui_payload *payload;
549 
550 	if (!dfui_fe_receive(c, &msgtype, &v)) {
551 		return(NULL);
552 	}
553 
554 	AURA_MALLOC(payload, dfui_payload);
555 
556 	payload->msgtype = msgtype;
557 	payload->form = NULL;
558 	payload->progress = NULL;
559 
560 	switch (msgtype) {
561 	case DFUI_BE_MSG_PRESENT:
562 		payload->form = v;
563 		break;
564 
565 	case DFUI_BE_MSG_PROG_BEGIN:
566 	case DFUI_BE_MSG_PROG_UPDATE:
567 		payload->progress = v;
568 		break;
569 
570 	case DFUI_BE_MSG_SET_GLOBAL:
571 		payload->global_setting = v;
572 		break;
573 
574 	case DFUI_BE_MSG_PROG_END:
575 	case DFUI_BE_MSG_STOP:
576 		break;
577 	}
578 
579 	return(payload);
580 }
581 
582 char
583 dfui_payload_get_msg_type(const struct dfui_payload *p)
584 {
585 	if (p == NULL)
586 		return(' ');
587 	return(p->msgtype);
588 }
589 
590 struct dfui_form *
591 dfui_payload_get_form(const struct dfui_payload *p)
592 {
593 	if (p == NULL)
594 		return(NULL);
595 	return(p->form);
596 }
597 
598 struct dfui_progress *
599 dfui_payload_get_progress(const struct dfui_payload *p)
600 {
601 	if (p == NULL)
602 		return(NULL);
603 	return(p->progress);
604 }
605 
606 void
607 dfui_payload_free(struct dfui_payload *p)
608 {
609 	if (p == NULL)
610 		return;
611 	if (p->form != NULL)
612 		dfui_form_free(p->form);
613 	if (p->progress != NULL)
614 		dfui_progress_free(p->progress);
615 	AURA_FREE(p, dfui_payload);
616 }
617 
618 /*
619  * Submit the result of a form to the backend.
620  */
621 dfui_err_t
622 dfui_fe_submit(struct dfui_connection *c, struct dfui_response *r)
623 {
624 	struct aura_buffer *e;
625 	dfui_err_t request_error;
626 
627 	e = aura_buffer_new(16384);
628 	dfui_encode_response(e, r);
629 
630 	dfui_debug("ENCODE<<%s>>\n", aura_buffer_buf(e));
631 	request_error = c->fe_ll_request(c, DFUI_FE_MSG_SUBMIT,
632 	    aura_buffer_buf(e));
633 	/* XXX we should check for READY from the backend? */
634 	aura_buffer_free(e);
635 
636 	return(request_error);
637 }
638 
639 dfui_err_t
640 dfui_fe_progress_continue(struct dfui_connection *c)
641 {
642 	c->fe_ll_request(c, DFUI_FE_MSG_CONTINUE, "");
643 	return(DFUI_SUCCESS);
644 }
645 
646 dfui_err_t
647 dfui_fe_progress_cancel(struct dfui_connection *c)
648 {
649 	c->fe_ll_request(c, DFUI_FE_MSG_CANCEL, "");
650 	return(DFUI_SUCCESS);
651 }
652 
653 dfui_err_t
654 dfui_fe_confirm_set_global(struct dfui_connection *c)
655 {
656 	c->fe_ll_request(c, DFUI_FE_MSG_CONTINUE, "");
657 	return(DFUI_SUCCESS);
658 }
659 
660 dfui_err_t
661 dfui_fe_cancel_set_global(struct dfui_connection *c)
662 {
663 	c->fe_ll_request(c, DFUI_FE_MSG_CANCEL, "");
664 	return(DFUI_SUCCESS);
665 }
666 
667 dfui_err_t
668 dfui_fe_confirm_stop(struct dfui_connection *c)
669 {
670 	c->fe_ll_request(c, DFUI_FE_MSG_CONTINUE, "");
671 	return(DFUI_SUCCESS);
672 }
673 
674 /*
675  * Abort the backend.
676  * Note that you still must call dfui_fe_disconnect after this.
677  */
678 dfui_err_t
679 dfui_fe_abort(struct dfui_connection *c)
680 {
681 	c->fe_ll_request(c, DFUI_FE_MSG_ABORT, "");
682 	return(DFUI_SUCCESS);
683 }
684