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_npipe.h"
57 #include "conn_tcp.h"
58 
59 struct dfui_connection *
60 dfui_connection_new(int transport, const char *rendezvous)
61 {
62 	struct dfui_connection *c = NULL;
63 
64 	if (
65 #ifdef HAS_NPIPE
66 	    transport == DFUI_TRANSPORT_NPIPE ||
67 #endif
68 #ifdef HAS_TCP
69 	    transport == DFUI_TRANSPORT_TCP ||
70 #endif
71 	    0) {
72 		/* We're OK. */
73 	} else {
74 		return(NULL);
75 	}
76 
77 	if (dfui_debug_file == NULL) {
78 		dfui_debug_file = stderr;
79 	} else {
80 		setvbuf(dfui_debug_file, NULL, _IOLBF, 0);
81 	}
82 
83 	AURA_MALLOC(c, dfui_connection);
84 	c->rendezvous = aura_strdup(rendezvous);
85 	c->transport = transport;
86 	c->ebuf = aura_buffer_new(16384);
87 	c->is_connected = 0;
88 	c->t_data = NULL;
89 
90 	switch (transport) {
91 #ifdef HAS_NPIPE
92 	case DFUI_TRANSPORT_NPIPE:
93 		AURA_MALLOC(c->t_data, dfui_conn_npipe);
94 		T_NPIPE(c)->in_pipename = NULL;
95 		T_NPIPE(c)->out_pipename = NULL;
96 		T_NPIPE(c)->in = NULL;
97 		T_NPIPE(c)->out = NULL;
98 
99 		/*
100 		 * Set up dispatch functions.
101 		 */
102 		c->be_start = dfui_npipe_be_start;
103 		c->be_stop = dfui_npipe_be_stop;
104 		c->be_ll_exchange = dfui_npipe_be_ll_exchange;
105 
106 		c->fe_connect = dfui_npipe_fe_connect;
107 		c->fe_disconnect = dfui_npipe_fe_disconnect;
108 		c->fe_ll_request = dfui_npipe_fe_ll_request;
109 		break;
110 #endif /* HAS_NPIPE */
111 
112 #ifdef HAS_TCP
113 	case DFUI_TRANSPORT_TCP:
114                 AURA_MALLOC(c->t_data, dfui_conn_tcp);
115 		T_TCP(c)->listen_sd = -1;
116 		T_TCP(c)->connected_sd = -1;
117 		T_TCP(c)->is_connected = 0;
118 
119                 /*
120                  * Set up dispatch functions.
121                  */
122                 c->be_start = dfui_tcp_be_start;
123                 c->be_stop = dfui_tcp_be_stop;
124 		c->be_ll_exchange = dfui_tcp_be_ll_exchange;
125 
126                 c->fe_connect = dfui_tcp_fe_connect;
127                 c->fe_disconnect = dfui_tcp_fe_disconnect;
128 		c->fe_ll_request = dfui_tcp_fe_ll_request;
129 		break;
130 #endif /* HAS_TCP */
131 	}
132 
133 	return(c);
134 }
135 
136 void
137 dfui_connection_free(struct dfui_connection *c)
138 {
139 	if (c == NULL)
140 		return;
141 
142 	switch (c->transport) {
143 #ifdef HAS_NPIPE
144 	case DFUI_TRANSPORT_NPIPE:
145 		if (T_NPIPE(c) != NULL) {
146 			if (T_NPIPE(c)->in_pipename != NULL)
147 				aura_free(T_NPIPE(c)->in_pipename, "pipename");
148 			if (T_NPIPE(c)->out_pipename != NULL)
149 				aura_free(T_NPIPE(c)->out_pipename, "pipename");
150 			if (T_NPIPE(c)->in != NULL)
151 				fclose(T_NPIPE(c)->in);
152 			if (T_NPIPE(c)->out != NULL)
153 				fclose(T_NPIPE(c)->out);
154 			AURA_FREE(T_NPIPE(c), dfui_conn_npipe);
155 		}
156 		break;
157 #endif
158 #ifdef HAS_TCP
159 	case DFUI_TRANSPORT_TCP:
160 		if (T_TCP(c) != NULL) {
161 			/* XXX close sockets/files here */
162 			AURA_FREE(T_NPIPE(c), dfui_conn_tcp);
163 		}
164 		break;
165 #endif
166 	}
167 
168 	if (c->rendezvous != NULL)
169 		free(c->rendezvous);
170 	AURA_FREE(c, dfui_connection);
171 }
172 
173 /*
174  * VERY HIGH LEVEL
175  */
176 
177 /*
178  * Create and present a generic `dialog box'-type form for the user
179  * and return their response.  actions is a pipe-seperated list of
180  * actions to be put on the form (e.g. "OK|Cancel".)  The return
181  * value is the ordinal position of the action that was selected,
182  * starting at 1 for the first action.  A return value of 0 indicates
183  * that an error occurred.  A return value of -1 indicates that the
184  * front end aborted the communications.
185  */
186 int
187 dfui_be_present_dialog(struct dfui_connection *c, const char *title,
188 			const char *actions, const char *fmt, ...)
189 {
190 	struct dfui_form *f;
191 	struct dfui_response *r;
192 	va_list args;
193 	char *message;
194 	char action_id[256], action_name[256];
195 	size_t start, end, counter, i;
196 
197 	va_start(args, fmt);
198 	vasprintf(&message, fmt, args);
199 	va_end(args);
200 
201 	f = dfui_form_create("dialog", title, message, "", NULL);
202 
203 	free(message);
204 
205 	start = end = 0;
206 	while (actions[end] != '\0') {
207 		end = start;
208 		while (actions[end] != '|' && actions[end] != '\0')
209 			end++;
210 
211 		if ((end - start) >= 256)
212 			break;
213 		strncpy(action_name, &actions[start], end - start);
214 		action_name[end - start] = '\0';
215 		strcpy(action_id, action_name);
216 		for(i = 0; action_id[i] != '\0'; i++) {
217 			if (action_id[i] == ' ')
218 				action_id[i] = '_';
219 		}
220 		dfui_form_action_add(f, action_id,
221 		    dfui_info_new(action_name, "", ""));
222 
223 		start = end + 1;
224 	}
225 
226 	if (!dfui_be_present(c, f, &r)) {
227 		dfui_form_free(f);
228 		dfui_response_free(r);
229 		return(-1);
230 	}
231 
232 	strlcpy(action_name, dfui_response_get_action_id(r), 256);
233 	for(i = 0; action_name[i] != '\0'; i++) {
234 		if (action_name[i] == '_')
235 			action_name[i] = ' ';
236 	}
237 
238 	start = end = 0;
239 	counter = 1;
240 	while (actions[end] != '\0') {
241 		end = start;
242 		while (actions[end] != '|' && actions[end] != '\0')
243 			end++;
244 
245 		if ((end - start) >= 256)
246 			break;
247 		if (strlen(action_name) == (end - start) &&
248 		    strncmp(action_name, &actions[start], end - start) == 0) {
249 			break;
250 		}
251 		counter++;
252 
253 		start = end + 1;
254 	}
255 
256 	dfui_form_free(f);
257 	dfui_response_free(r);
258 
259 	return(counter);
260 }
261 
262 /******** BACKEND ********/
263 
264 /*
265  * Connect to the frontend.
266  */
267 dfui_err_t
268 dfui_be_start(struct dfui_connection *c)
269 {
270 	if (c->is_connected) {
271 		return(DFUI_FAILURE);
272 	} else if (c->be_start(c)) {
273 		c->is_connected = 1;
274 		return(DFUI_SUCCESS);
275 	} else {
276 		return(DFUI_FAILURE);
277 	}
278 }
279 
280 /*
281  * Tell the frontend that we're done and disconnect from it.
282  */
283 dfui_err_t
284 dfui_be_stop(struct dfui_connection *c)
285 {
286 	if (!c->is_connected) {
287 		return(DFUI_SUCCESS);
288 	} else if (c->be_stop(c)) {
289 		c->is_connected = 0;
290 		return(DFUI_SUCCESS);
291 	} else {
292 		return(DFUI_FAILURE);
293 	}
294 }
295 
296 /*
297  * Present a form to the user.  This call is synchronous;
298  * it does not return until the user has selected an action.
299  */
300 dfui_err_t
301 dfui_be_present(struct dfui_connection *c,
302 		struct dfui_form *f, struct dfui_response **r)
303 {
304 	struct aura_buffer *e;
305 
306 	e = aura_buffer_new(16384);
307 	dfui_encode_form(e, f);
308 
309 	c->be_ll_exchange(c, DFUI_BE_MSG_PRESENT, aura_buffer_buf(e));
310 
311 	aura_buffer_free(e);
312 
313 	/* check for ABORT reply */
314 	if (aura_buffer_buf(c->ebuf)[0] == DFUI_FE_MSG_ABORT) {
315 		return(DFUI_FAILURE);
316 	}
317 
318 	/*
319 	 * Now we've got the response; so decode it.
320 	 */
321 
322 	e = aura_buffer_new(16384);
323 	aura_buffer_set(e, aura_buffer_buf(c->ebuf) + 1, aura_buffer_len(c->ebuf) - 1);
324 	*r = dfui_decode_response(e);
325 	aura_buffer_free(e);
326 
327 	return(DFUI_SUCCESS);
328 }
329 
330 /*
331  * Begin showing a progress bar to the user.
332  * This function is asynchronous; it returns immediately.
333  * The assumption is that the backend will make subsequent
334  * calls to dfui_be_progress_update() frequently, and in
335  * them, check to see if the user cancelled.
336  */
337 dfui_err_t
338 dfui_be_progress_begin(struct dfui_connection *c, struct dfui_progress *pr)
339 {
340 	struct aura_buffer *e;
341 
342 	e = aura_buffer_new(16384);
343 	dfui_encode_progress(e, pr);
344 
345 	c->be_ll_exchange(c, DFUI_BE_MSG_PROG_BEGIN, aura_buffer_buf(e));
346 	aura_buffer_free(e);
347 
348 	/* response might have been be READY or ABORT */
349 	if (aura_buffer_buf(c->ebuf)[0] == DFUI_FE_MSG_ABORT) {
350 		return(DFUI_FAILURE);
351 	} else {
352 		return(DFUI_SUCCESS);
353 	}
354 }
355 
356 dfui_err_t
357 dfui_be_progress_update(struct dfui_connection *c,
358 			struct dfui_progress *pr, int *cancelled)
359 {
360 	struct aura_buffer *e;
361 
362 	e = aura_buffer_new(16384);
363 	dfui_encode_progress(e, pr);
364 
365 	c->be_ll_exchange(c, DFUI_BE_MSG_PROG_UPDATE, aura_buffer_buf(e));
366 	aura_buffer_free(e);
367 
368 	/* response might have been READY, CANCEL, or ABORT */
369 
370 	*cancelled = 0;
371 	if (aura_buffer_buf(c->ebuf)[0] == DFUI_FE_MSG_CANCEL) {
372 		*cancelled = 1;
373 	}
374 	if (aura_buffer_buf(c->ebuf)[0] == DFUI_FE_MSG_ABORT) {
375 		return(DFUI_FAILURE);
376 	} else {
377 		return(DFUI_SUCCESS);
378 	}
379 }
380 
381 dfui_err_t
382 dfui_be_progress_end(struct dfui_connection *c)
383 {
384 	c->be_ll_exchange(c, DFUI_BE_MSG_PROG_END, "");
385 
386 	/* response might have been be READY or ABORT */
387 	if (aura_buffer_buf(c->ebuf)[0] == DFUI_FE_MSG_ABORT) {
388 		return(DFUI_FAILURE);
389 	} else {
390 		return(DFUI_SUCCESS);
391 	}
392 }
393 
394 dfui_err_t
395 dfui_be_set_global_setting(struct dfui_connection *c,
396 			   const char *key, const char *value,
397 			   int *cancelled)
398 {
399 	struct aura_buffer *e;
400 	struct dfui_property *p;
401 
402 	e = aura_buffer_new(16384);
403 	p = dfui_property_new(key, value);
404 	dfui_encode_property(e, p);
405 	c->be_ll_exchange(c, DFUI_BE_MSG_SET_GLOBAL, aura_buffer_buf(e));
406 	aura_buffer_free(e);
407 	dfui_property_free(p);
408 
409 	/* response might have been READY, CANCEL, or ABORT */
410 
411 	*cancelled = 0;
412 	if (aura_buffer_buf(c->ebuf)[0] == DFUI_FE_MSG_CANCEL) {
413 		*cancelled = 1;
414 	}
415 	if (aura_buffer_buf(c->ebuf)[0] == DFUI_FE_MSG_ABORT) {
416 		return(DFUI_FAILURE);
417 	} else {
418 		return(DFUI_SUCCESS);
419 	}
420 }
421 
422 /******** FRONTEND ********/
423 
424 dfui_err_t
425 dfui_fe_connect(struct dfui_connection *c)
426 {
427 	return(c->fe_connect(c));
428 }
429 
430 dfui_err_t
431 dfui_fe_disconnect(struct dfui_connection *c)
432 {
433 	dfui_debug("DISCONNECTING<<>>\n");
434 	return(c->fe_disconnect(c));
435 }
436 
437 /*
438  * Receive a message from the backend.  This call is synchronous;
439  * it does not return until a message comes in from the backend.
440  * After this call, the message type is available in *msgtype,
441  * and the message itself (if any) is available in *payload, ready
442  * to be casted to its real type (as per *msgtype).
443  */
444 dfui_err_t
445 dfui_fe_receive(struct dfui_connection *c, char *msgtype, void **payload)
446 {
447 	struct aura_buffer *e;
448 
449 	c->fe_ll_request(c, DFUI_FE_MSG_READY, "");
450 	*msgtype = aura_buffer_buf(c->ebuf)[0];
451 	switch (*msgtype) {
452 	case DFUI_BE_MSG_PRESENT:
453 		e = aura_buffer_new(16384);
454 		aura_buffer_set(e, aura_buffer_buf(c->ebuf) + 1, aura_buffer_len(c->ebuf) - 1);
455 		*payload = dfui_decode_form(e);
456 		aura_buffer_free(e);
457 		return(DFUI_SUCCESS);
458 
459 	case DFUI_BE_MSG_PROG_BEGIN:
460 		e = aura_buffer_new(16384);
461 		aura_buffer_set(e, aura_buffer_buf(c->ebuf) + 1, aura_buffer_len(c->ebuf) - 1);
462 		*payload = dfui_decode_progress(e);
463 		aura_buffer_free(e);
464 		return(DFUI_SUCCESS);
465 
466 	case DFUI_BE_MSG_PROG_UPDATE:
467 		e = aura_buffer_new(16384);
468 		aura_buffer_set(e, aura_buffer_buf(c->ebuf) + 1, aura_buffer_len(c->ebuf) - 1);
469 		*payload = dfui_decode_progress(e);
470 		aura_buffer_free(e);
471 		return(DFUI_SUCCESS);
472 
473 	case DFUI_BE_MSG_PROG_END:
474 		*payload = NULL;
475 		return(DFUI_SUCCESS);
476 
477 	case DFUI_BE_MSG_SET_GLOBAL:
478 		e = aura_buffer_new(16384);
479 		aura_buffer_set(e, aura_buffer_buf(c->ebuf) + 1, aura_buffer_len(c->ebuf) - 1);
480 		*payload = dfui_decode_property(e);
481 		aura_buffer_free(e);
482 		return(DFUI_SUCCESS);
483 
484 	case DFUI_BE_MSG_STOP:
485 		*payload = NULL;
486 		return(DFUI_SUCCESS);
487 
488 	default:
489 		/* XXX ??? */
490 		return(DFUI_FAILURE);
491 	}
492 }
493 
494 /*
495  * Wrapper function for dfui_fe_receive for binding generators which
496  * seem to (understandably) have problems wrapping void *'s themselves.
497  */
498 struct dfui_payload *
499 dfui_fe_receive_payload(struct dfui_connection *c)
500 {
501 	char msgtype;
502 	void *v;
503 	struct dfui_payload *payload;
504 
505 	if (!dfui_fe_receive(c, &msgtype, &v)) {
506 		return(NULL);
507 	}
508 
509 	AURA_MALLOC(payload, dfui_payload);
510 
511 	payload->msgtype = msgtype;
512 	payload->form = NULL;
513 	payload->progress = NULL;
514 
515 	switch (msgtype) {
516 	case DFUI_BE_MSG_PRESENT:
517 		payload->form = v;
518 		break;
519 
520 	case DFUI_BE_MSG_PROG_BEGIN:
521 	case DFUI_BE_MSG_PROG_UPDATE:
522 		payload->progress = v;
523 		break;
524 
525 	case DFUI_BE_MSG_SET_GLOBAL:
526 		payload->global_setting = v;
527 		break;
528 
529 	case DFUI_BE_MSG_PROG_END:
530 	case DFUI_BE_MSG_STOP:
531 		break;
532 	}
533 
534 	return(payload);
535 }
536 
537 char
538 dfui_payload_get_msg_type(const struct dfui_payload *p)
539 {
540 	if (p == NULL)
541 		return(' ');
542 	return(p->msgtype);
543 }
544 
545 struct dfui_form *
546 dfui_payload_get_form(const struct dfui_payload *p)
547 {
548 	if (p == NULL)
549 		return(NULL);
550 	return(p->form);
551 }
552 
553 struct dfui_progress *
554 dfui_payload_get_progress(const struct dfui_payload *p)
555 {
556 	if (p == NULL)
557 		return(NULL);
558 	return(p->progress);
559 }
560 
561 void
562 dfui_payload_free(struct dfui_payload *p)
563 {
564 	if (p == NULL)
565 		return;
566 	if (p->form != NULL)
567 		dfui_form_free(p->form);
568 	if (p->progress != NULL)
569 		dfui_progress_free(p->progress);
570 	AURA_FREE(p, dfui_payload);
571 }
572 
573 /*
574  * Submit the result of a form to the backend.
575  */
576 dfui_err_t
577 dfui_fe_submit(struct dfui_connection *c, struct dfui_response *r)
578 {
579 	struct aura_buffer *e;
580 	dfui_err_t request_error;
581 
582 	e = aura_buffer_new(16384);
583 	dfui_encode_response(e, r);
584 
585 	dfui_debug("ENCODE<<%s>>\n", aura_buffer_buf(e));
586 	request_error = c->fe_ll_request(c, DFUI_FE_MSG_SUBMIT,
587 	    aura_buffer_buf(e));
588 	/* XXX we should check for READY from the backend? */
589 	aura_buffer_free(e);
590 
591 	return(request_error);
592 }
593 
594 dfui_err_t
595 dfui_fe_progress_continue(struct dfui_connection *c)
596 {
597 	c->fe_ll_request(c, DFUI_FE_MSG_CONTINUE, "");
598 	return(DFUI_SUCCESS);
599 }
600 
601 dfui_err_t
602 dfui_fe_progress_cancel(struct dfui_connection *c)
603 {
604 	c->fe_ll_request(c, DFUI_FE_MSG_CANCEL, "");
605 	return(DFUI_SUCCESS);
606 }
607 
608 dfui_err_t
609 dfui_fe_confirm_set_global(struct dfui_connection *c)
610 {
611 	c->fe_ll_request(c, DFUI_FE_MSG_CONTINUE, "");
612 	return(DFUI_SUCCESS);
613 }
614 
615 dfui_err_t
616 dfui_fe_cancel_set_global(struct dfui_connection *c)
617 {
618 	c->fe_ll_request(c, DFUI_FE_MSG_CANCEL, "");
619 	return(DFUI_SUCCESS);
620 }
621 
622 dfui_err_t
623 dfui_fe_confirm_stop(struct dfui_connection *c)
624 {
625 	c->fe_ll_request(c, DFUI_FE_MSG_CONTINUE, "");
626 	return(DFUI_SUCCESS);
627 }
628 
629 /*
630  * Abort the backend.
631  * Note that you still must call dfui_fe_disconnect after this.
632  */
633 dfui_err_t
634 dfui_fe_abort(struct dfui_connection *c)
635 {
636 	c->fe_ll_request(c, DFUI_FE_MSG_ABORT, "");
637 	return(DFUI_SUCCESS);
638 }
639