1 /*
2  *	binkleyforce -- unix FTN mailer project
3  *
4  *	Copyright (c) 1998-2000 Alexander Belkin, 2:5020/1398.11
5  *
6  *	This program is free software; you can redistribute it and/or modify
7  *	it under the terms of the GNU General Public License as published by
8  *	the Free Software Foundation; either version 2 of the License, or
9  *	(at your option) any later version.
10  *
11  *	$Id: prot_binkp.c,v 1.1.1.1 2004/09/09 09:52:38 kstepanenkov Exp $
12  */
13 
14 #include "includes.h"
15 #include "confread.h"
16 #include "logger.h"
17 #include "util.h"
18 #include "io.h"
19 #include "session.h"
20 #include "outbound.h"
21 #include "prot_common.h"
22 #include "prot_binkp.h"
23 
24 typedef enum {
25 	BPI_SendSysInfo,
26 	BPI_WaitADR,
27 	BPI_WaitPWD,
28 	BPI_Auth
29 } binkp_incoming_state;
30 
31 typedef enum {
32 	BPO_SendSysInfo,
33 	BPO_WaitNUL,
34 	BPO_SendPWD,
35 	BPO_WaitADR,
36 	BPO_Auth,
37 	BPO_WaitOK
38 } binkp_outgoing_state;
39 
40 #define GOTO(label,newrc)    { rc = (newrc); goto label; }
41 
binkp_process_NUL(s_binkp_sysinfo * remote_data,char * buffer)42 void binkp_process_NUL(s_binkp_sysinfo *remote_data, char *buffer)
43 {
44 	char *p, *q;
45 
46 	if( strncmp(buffer, "SYS ", 4) == 0 )
47 		strnxcpy(remote_data->systname, buffer+4, sizeof(remote_data->systname));
48 	else if( strncmp(buffer, "ZYZ ", 4) == 0 )
49 		strnxcpy(remote_data->sysop, buffer+4, sizeof(remote_data->sysop));
50 	else if( strncmp(buffer, "LOC ", 4) == 0 )
51 		strnxcpy(remote_data->location, buffer+4, sizeof(remote_data->location));
52 	else if( strncmp(buffer, "PHN ", 4) == 0 )
53 		strnxcpy(remote_data->phone, buffer+4, sizeof(remote_data->phone));
54 	else if( strncmp(buffer, "NDL ", 4) == 0 )
55 		strnxcpy(remote_data->flags, buffer+4, sizeof(remote_data->flags));
56 	else if( strncmp(buffer, "TIME ", 5) == 0 )
57 		strnxcpy(remote_data->timestr, buffer+5, sizeof(remote_data->timestr));
58 	else if( strncmp(buffer, "OPT ", 4) == 0 )
59 	{
60 		if( *remote_data->opt )
61 		{
62 			strnxcat(remote_data->opt, " ", sizeof(remote_data->opt));
63 			strnxcat(remote_data->opt, buffer+4, sizeof(remote_data->opt));
64 		}
65 		else
66 			strnxcpy(remote_data->opt, buffer+4, sizeof(remote_data->opt));
67 
68 		binkp_parse_options(remote_data, buffer+4);
69 	}
70 	else if( strncmp(buffer, "VER ", 4) == 0 )
71 	{
72 		/* <mailer> [<protocol>/<vermaj>.<vermin>] */
73 		if( (p = strchr(buffer+4, ' ')) )
74 		{
75 			strnxcpy(remote_data->progname, buffer+4,
76 					MIN(sizeof(remote_data->progname), p - (buffer+4) + 1));
77 			++p;
78 			if( (q = strchr(p, '/')) )
79 			{
80 				strnxcpy(remote_data->protname, p,
81 						MIN(sizeof(remote_data->protname), q - p + 1));
82 				sscanf(q+1, "%d.%d",
83 						&remote_data->majorver,
84 						&remote_data->minorver);
85 			}
86 		}
87 		else
88 			strnxcpy(remote_data->progname, buffer+4, sizeof(remote_data->progname));
89 	}
90 	else
91 		bf_log("BinkP got invalid NUL: \"%s\"", string_printable(buffer));
92 }
93 
binkp_process_ADR(s_binkp_sysinfo * remote_data,char * buffer)94 void binkp_process_ADR(s_binkp_sysinfo *remote_data, char *buffer)
95 {
96 	s_faddr addr;
97 	char *p, *q;
98 
99 	for( p = string_token(buffer, &q, NULL, 0); p;
100 	     p = string_token(NULL, &q, NULL, 0) )
101 	{
102 		if( ftn_addrparse(&addr, p, FALSE) )
103 			bf_log("BinkP got unparsable address \"%s\"", string_printable(p));
104 		else
105 			session_addrs_add(&remote_data->addrs, &remote_data->anum, addr);
106 	}
107 }
108 
binkp_outgoing(s_binkp_sysinfo * local_data,s_binkp_sysinfo * remote_data)109 int binkp_outgoing(s_binkp_sysinfo *local_data, s_binkp_sysinfo *remote_data)
110 {
111 	s_bpinfo bpi;
112 	binkp_outgoing_state binkp_state = BPO_SendSysInfo;
113 	int rc = HRC_OK;
114 	int recv_rc = 0;
115 	int send_rc = 0;
116 	bool send_ready = FALSE;
117 	bool recv_ready = FALSE;
118 
119 	binkp_init_bpinfo(&bpi);
120 
121 	while(1)
122 	{
123 		switch(binkp_state) {
124 		case BPO_SendSysInfo:
125 			binkp_queue_sysinfo(&bpi, local_data);
126 			binkp_state = BPO_WaitNUL;
127 			break;
128 
129 		case BPO_SendPWD:
130 			if( *local_data->passwd == '\0' )
131 			{
132 				binkp_queuemsg(&bpi, BPMSG_PWD, NULL, "-");
133 			}
134 			else if( remote_data->options & BINKP_OPT_MD5 )
135 			{
136 				char digest_bin[16];
137 				char digest_hex[33];
138 
139 				md5_cram_get(local_data->passwd, remote_data->challenge,
140 						remote_data->challenge_length, digest_bin);
141 
142 				/* Encode digest to the hex string */
143 				string_bin_to_hex(digest_hex, digest_bin, 16);
144 
145 				binkp_queuemsg(&bpi, BPMSG_PWD, "CRAM-MD5-", digest_hex);
146 			}
147 			else
148 				binkp_queuemsg(&bpi, BPMSG_PWD, NULL, local_data->passwd);
149 
150 			binkp_state = BPO_WaitADR;
151 			break;
152 
153 		case BPO_Auth:
154 			/* Set remote password same as local */
155 			strncpy(remote_data->passwd, local_data->passwd, BINKP_MAXPASSWD);
156 			remote_data->passwd[BINKP_MAXPASSWD] = '\0';
157 
158 			if( !remote_data->anum )
159 			{
160 				binkp_queuemsg(&bpi, BPMSG_BSY, NULL, "No addresses was presented");
161 				GOTO(Exit, HRC_BUSY);
162 			}
163 			else if( session_addrs_check_genuine(remote_data->addrs, remote_data->anum,
164 			                             state.node.addr) )
165 			{
166 				binkp_queuemsg(&bpi, BPMSG_ERR, NULL, "Sorry, you are not who I need");
167 				GOTO(Exit, HRC_NO_ADDRESS);
168 			}
169 			else if( session_addrs_check(remote_data->addrs, remote_data->anum,
170 			                             remote_data->passwd, NULL, 0) )
171 			{
172 				binkp_queuemsg(&bpi, BPMSG_ERR, NULL, "Security violation");
173 				GOTO(Exit, HRC_BAD_PASSWD);
174 			}
175 			else if( session_addrs_lock(remote_data->addrs, remote_data->anum) )
176 			{
177 				binkp_queuemsg(&bpi, BPMSG_BSY, NULL, "All addresses are busy");
178 				GOTO(Exit, HRC_BUSY);
179 			}
180 			binkp_state = BPO_WaitOK;
181 			break;
182 
183 		default:
184 			break;
185 		}
186 
187 		/*
188 		 * Receive/Send next data block
189 		 */
190 		send_ready = recv_ready = FALSE;
191 
192 		if( tty_select(&recv_ready, (bpi.opos || bpi.n_msgs) ?
193 		               &send_ready : NULL, bpi.timeout) < 0 )
194 			GOTO(Abort, HRC_OTHER_ERR);
195 
196 		recv_rc = BPMSG_NONE;
197 		send_rc = 0;
198 
199 		if( recv_ready && (recv_rc = binkp_recv(&bpi)) == BPMSG_EXIT )
200 			GOTO(Abort, HRC_OTHER_ERR);
201 
202 		if( send_ready && (send_rc = binkp_send(&bpi)) < 0 )
203 			GOTO(Abort, HRC_OTHER_ERR);
204 
205 		/*
206 		 * Handle received message
207 		 */
208 		switch(recv_rc) {
209 		case BPMSG_NONE:
210 			break;
211 
212 		case BPMSG_NUL:
213 			if( binkp_state == BPO_WaitNUL || binkp_state == BPO_WaitADR )
214 			{
215 				binkp_process_NUL(remote_data, bpi.ibuf+1);
216 				if( binkp_state == BPO_WaitNUL )
217 					binkp_state = BPO_SendPWD;
218 			}
219 			break;
220 
221 		case BPMSG_ADR:
222 			if( binkp_state == BPO_WaitADR )
223 			{
224 				binkp_process_ADR(remote_data, bpi.ibuf+1);
225 				binkp_state = BPO_Auth;
226 			}
227 			break;
228 
229 		case BPMSG_OK:
230 			if( binkp_state == BPO_WaitOK )
231 				GOTO(Exit, HRC_OK);
232 			break;
233 
234 		case BPMSG_ERR:
235 			bf_log("BinkP error: \"%s\"", string_printable(bpi.ibuf+1));
236 			GOTO(Abort, HRC_FATAL_ERR);
237 
238 		case BPMSG_BSY:
239 			bf_log("BinkP busy: \"%s\"", string_printable(bpi.ibuf+1));
240 			GOTO(Abort, HRC_TEMP_ERR);
241 		}
242 	}
243 
244 Exit:
245 	if( binkp_flush_queue(&bpi, bpi.timeout) && rc == HRC_OK )
246 		rc = HRC_OTHER_ERR;
247 
248 Abort:
249 	binkp_deinit_bpinfo(&bpi);
250 
251 	return rc;
252 }
253 
254 
binkp_auth_incoming(s_binkp_sysinfo * remote_data)255 int binkp_auth_incoming(s_binkp_sysinfo *remote_data)
256 {
257 	if( remote_data->challenge_length > 0
258 	 && strncmp(remote_data->passwd, "CRAM-MD5-", 9) == 0 )
259 	{
260 		return session_addrs_check(remote_data->addrs,
261 		                           remote_data->anum,
262 		                           remote_data->passwd + 9,
263 		                           remote_data->challenge,
264 		                           remote_data->challenge_length);
265 	}
266 
267 	return session_addrs_check(remote_data->addrs, remote_data->anum,
268 	                           remote_data->passwd, NULL, 0);
269 }
270 
binkp_incoming(s_binkp_sysinfo * local_data,s_binkp_sysinfo * remote_data)271 int binkp_incoming(s_binkp_sysinfo *local_data, s_binkp_sysinfo *remote_data)
272 {
273 	s_bpinfo bpi;
274 	binkp_incoming_state binkp_state = BPI_SendSysInfo;
275 	int rc = HRC_OK;
276 	int recv_rc = 0;
277 	int send_rc = 0;
278 	bool send_ready = FALSE;
279 	bool recv_ready = FALSE;
280 
281 	binkp_init_bpinfo(&bpi);
282 
283 	while(1)
284 	{
285 		switch(binkp_state) {
286 		case BPI_SendSysInfo:
287 			binkp_queue_sysinfo(&bpi, local_data);
288 			binkp_state = BPI_WaitADR;
289 			break;
290 
291 		case BPI_Auth:
292 			/* Set challenge string same as local */
293 			memcpy(remote_data->challenge, local_data->challenge,
294 					sizeof(remote_data->challenge));
295 			remote_data->challenge_length = local_data->challenge_length;
296 
297 			/* Do authorization */
298 			if( !remote_data->anum )
299 			{
300 				binkp_queuemsg(&bpi, BPMSG_BSY, NULL, "No addresses was presented");
301 				GOTO(Exit, HRC_BUSY);
302 			}
303 			else if( binkp_auth_incoming(remote_data) )
304 			{
305 				binkp_queuemsg(&bpi, BPMSG_ERR, NULL, "Security violation");
306 				GOTO(Exit, HRC_BAD_PASSWD);
307 			}
308 			else if( session_addrs_lock(remote_data->addrs, remote_data->anum) )
309 			{
310 				binkp_queuemsg(&bpi, BPMSG_BSY, NULL, "All addresses are busy");
311 				GOTO(Exit, HRC_BUSY);
312 			}
313 			else
314 			{
315 				binkp_queuemsg(&bpi, BPMSG_OK, NULL, NULL);
316 				GOTO(Exit, HRC_OK);
317 			}
318 			break;
319 
320 		default:
321 			break;
322 		}
323 
324 		/*
325 		 * Receive/Send next data block
326 		 */
327 		send_ready = recv_ready = FALSE;
328 
329 		if( tty_select(&recv_ready, (bpi.opos || bpi.n_msgs) ?
330 		               &send_ready : NULL, bpi.timeout) < 0 )
331 			GOTO(Abort, HRC_OTHER_ERR);
332 
333 		recv_rc = BPMSG_NONE;
334 		send_rc = 0;
335 
336 		if( recv_ready && (recv_rc = binkp_recv(&bpi)) == BPMSG_EXIT )
337 			GOTO(Abort, HRC_OTHER_ERR);
338 
339 		if( send_ready && (send_rc = binkp_send(&bpi)) < 0 )
340 			GOTO(Abort, HRC_OTHER_ERR);
341 
342 		/*
343 		 * Handle received message
344 		 */
345 		switch(recv_rc) {
346 		case BPMSG_NONE:
347 			break;
348 
349 		case BPMSG_NUL:
350 			if( binkp_state == BPI_WaitADR )
351 				binkp_process_NUL(remote_data, bpi.ibuf+1);
352 			break;
353 
354 		case BPMSG_ADR:
355 			if( binkp_state == BPI_WaitADR )
356 			{
357 				binkp_process_ADR(remote_data, bpi.ibuf+1);
358 				binkp_state = BPI_WaitPWD;
359 			}
360 			break;
361 
362 		case BPMSG_PWD:
363 			if( binkp_state == BPI_WaitPWD )
364 			{
365 				strnxcpy(remote_data->passwd, bpi.ibuf+1, sizeof(remote_data->passwd));
366 				binkp_state = BPI_Auth;
367 			}
368 			break;
369 
370 		case BPMSG_ERR:
371 			bf_log("BinkP error: \"%s\"", string_printable(bpi.ibuf+1));
372 			GOTO(Abort, HRC_FATAL_ERR);
373 
374 		case BPMSG_BSY:
375 			bf_log("BinkP busy: \"%s\"", string_printable(bpi.ibuf+1));
376 			GOTO(Abort, HRC_TEMP_ERR);
377 		}
378 	}
379 
380 Exit:
381 	if( binkp_flush_queue(&bpi, bpi.timeout) && rc == HRC_OK )
382 		rc = HRC_OTHER_ERR;
383 
384 Abort:
385 	binkp_deinit_bpinfo(&bpi);
386 
387 	return rc;
388 }
389 
binkp_transfer(s_protinfo * pi)390 int binkp_transfer(s_protinfo *pi)
391 {
392 	int  i, n, rc = PRC_NOERROR;
393 	bool recv_ready = FALSE;
394 	bool send_ready = FALSE;
395 	bool sent_EOB = FALSE;
396 	bool rcvd_EOB = FALSE;
397 	bool send_file = FALSE;
398 	bool recv_file = FALSE;
399 	bool wait_got = FALSE;
400 	bool nofiles = FALSE;
401 	int  recv_rc = 0;
402 	int  send_rc = 0;
403 	char *fname = NULL;
404 	size_t fsize = 0;
405 	time_t ftime = 0;
406 	size_t foffs = 0;
407 	s_bpinfo bpi;
408 
409 	binkp_init_bpinfo(&bpi);
410 
411 	while( !sent_EOB || !rcvd_EOB )
412 	{
413 		if( !send_file && !sent_EOB && bpi.opos == 0 && bpi.n_msgs == 0 )
414 		{
415 			if( !p_tx_fopen(pi) )
416 			{
417 				send_file = TRUE;
418 				binkp_queuemsgf(&bpi, BPMSG_FILE, "%s %ld %ld 0",
419 					pi->send->net_name, (long)pi->send->bytes_total,
420 					(long)pi->send->mod_time);
421 			}
422 			else
423 				/* No more files */
424 				nofiles = TRUE;
425 		}
426 
427 		if( send_file && bpi.opos == 0 && bpi.n_msgs == 0 )
428 		{
429 			if( (n = p_tx_readfile(bpi.obuf + BINKP_BLK_HDRSIZE,
430 			                       4096, pi)) < 0 )
431 			{
432 				p_tx_fclose(pi);
433 				send_file = FALSE;
434 			}
435 			else
436 			{
437 				binkp_puthdr(bpi.obuf, (unsigned)(n & 0x7fff));
438 				bpi.opos = n + BINKP_BLK_HDRSIZE;
439 				pi->send->bytes_sent += n;
440 				if( pi->send->eofseen )
441 				{
442 					wait_got = TRUE; send_file = FALSE;
443 					pi->send->status = FSTAT_WAITACK;
444 				}
445 			}
446 		}
447 
448 		if( nofiles && !wait_got && !sent_EOB )
449 		{
450 			sent_EOB = TRUE;
451 			binkp_queuemsg(&bpi, BPMSG_EOB, NULL, NULL);
452 		}
453 
454 		recv_ready = send_ready = FALSE;
455 		if( tty_select(&recv_ready, (bpi.opos || bpi.n_msgs) ?
456 		               &send_ready : NULL, bpi.timeout) < 0 )
457 			gotoexit(PRC_ERROR);
458 
459 		recv_rc = BPMSG_NONE;
460 		send_rc = 0;
461 
462 		if( recv_ready && (recv_rc = binkp_recv(&bpi)) == BPMSG_EXIT )
463 			gotoexit(PRC_ERROR);
464 		if( send_ready && (send_rc = binkp_send(&bpi)) < 0 )
465 			gotoexit(PRC_ERROR);
466 
467 		switch(recv_rc) {
468 		case BPMSG_NONE:
469 			break;
470 		case BPMSG_DATA: /* Got new data block */
471 			if( recv_file )
472 			{
473 				if( (n = p_rx_writefile(bpi.ibuf, bpi.isize, pi)) < 0 )
474 				{
475 					/* error writing file */
476 					if( n == -2 )
477 					{
478 						binkp_queuemsgf(&bpi, BPMSG_GOT, "%s %ld %ld",
479 							pi->recv->net_name, (long)pi->recv->bytes_total,
480 							(long)pi->recv->mod_time);
481 					}
482 					else
483 					{
484 						binkp_queuemsgf(&bpi, BPMSG_SKIP, "%s %ld %ld",
485 							pi->recv->net_name, (long)pi->recv->bytes_total,
486 							(long)pi->recv->mod_time);
487 					}
488 					recv_file = FALSE;
489 					p_rx_fclose(pi);
490 				}
491 				else
492 				{
493 					pi->recv->bytes_received += bpi.isize;
494 
495 					/* Was it the last data block? */
496 					if( pi->recv->bytes_received > pi->recv->bytes_total )
497 					{
498 						bf_log("binkp got too many data (%ld, %ld expected)",
499 							(long)pi->recv->bytes_received,
500 							(long)pi->recv->bytes_total);
501 
502 						recv_file = FALSE;
503 						pi->recv->status = FSTAT_REFUSED;
504 						(void)p_rx_fclose(pi);
505 
506 						binkp_queuemsgf(&bpi, BPMSG_SKIP, "%s %ld %ld",
507 							pi->recv->net_name, (long)pi->recv->bytes_total,
508 							(long)pi->recv->mod_time);
509 					}
510 					else if( pi->recv->bytes_received == pi->recv->bytes_total )
511 					{
512 						recv_file = FALSE;
513 						pi->recv->status = FSTAT_SUCCESS;
514 						if( !p_rx_fclose(pi) )
515 						{
516 							binkp_queuemsgf(&bpi, BPMSG_GOT, "%s %ld %ld",
517 								pi->recv->net_name, (long)pi->recv->bytes_total,
518 								(long)pi->recv->mod_time);
519 						}
520 						else
521 						{
522 							binkp_queuemsgf(&bpi, BPMSG_SKIP, "%s %ld %ld",
523 								pi->recv->net_name, (long)pi->recv->bytes_total,
524 								(long)pi->recv->mod_time);
525 						}
526 					}
527 				}
528 			}
529 #ifdef DEBUG
530 			else
531 				DEB((D_PROT, "ignore received data block"));
532 #endif
533 			break;
534 
535 		case BPMSG_FILE:
536 			if( recv_file )
537 				{ p_rx_fclose(pi); recv_file = FALSE; }
538 
539 			if( binkp_parsfinfo(bpi.ibuf+1, &fname,
540 			    &fsize, &ftime, &foffs) )
541 			{
542 				binkp_queuemsg(&bpi, BPMSG_ERR, "FILE: ", "unparsable arguments");
543 				goto FinishSession;
544 			}
545 
546 			if( pi->recv && !p_compfinfo(pi->recv, fname, fsize, ftime)
547 			 && pi->recv->bytes_skipped == foffs )
548 			{
549 				recv_file = TRUE;
550 				break;
551 			}
552 
553 			switch(p_rx_fopen(pi, fname, fsize, ftime, 0)) {
554 			case 0:
555 				if( pi->recv->bytes_skipped == 0 )
556 				{
557 					recv_file = TRUE;
558 					break;
559 				}
560 				binkp_queuemsgf(&bpi, BPMSG_GET, "%s %ld %ld %ld",
561 					pi->recv->net_name, (long)pi->recv->bytes_total,
562 					(long)pi->recv->mod_time,
563 					(long)pi->recv->bytes_skipped);
564 				break;
565 			case 1:
566 				/* SKIP (non-destructive) */
567 				binkp_queuemsgf(&bpi, BPMSG_SKIP, "%s %ld %ld",
568 					pi->recv->net_name, (long)pi->recv->bytes_total,
569 					(long)pi->recv->mod_time);
570 				break;
571 			case 2:
572 				/* SKIP (destructive) */
573 				binkp_queuemsgf(&bpi, BPMSG_GOT, "%s %ld %ld",
574 					pi->recv->net_name, (long)pi->recv->bytes_total,
575 					(long)pi->recv->mod_time);
576 				break;
577 			default:
578 				ASSERT_MSG();
579 			}
580 			break;
581 
582 		case BPMSG_EOB: /* End Of Batch */
583 			if( recv_file )
584 			{
585 				p_rx_fclose(pi);
586 				recv_file = FALSE;
587 			}
588 			rcvd_EOB = TRUE;
589 			break;
590 
591 		case BPMSG_GOT:
592 		case BPMSG_SKIP:
593 			if( binkp_parsfinfo(bpi.ibuf+1, &fname, &fsize, &ftime, NULL) == 0 )
594 			{
595 				if( send_file && !p_compfinfo(pi->send, fname, fsize, ftime) )
596 				{
597 					/* We got GOT/SKIP for current file! */
598 					if( recv_rc == BPMSG_GOT )
599 						pi->send->status = FSTAT_SKIPPED;
600 					else
601 						pi->send->status = FSTAT_REFUSED;
602 					p_tx_fclose(pi);
603 					send_file = FALSE;
604 					break;
605 				}
606 
607 				wait_got = FALSE;
608 
609 				for( i = 0; i < pi->n_sentfiles; i++ )
610 				{
611 					if( pi->sentfiles[i].status == FSTAT_WAITACK
612 					 && !p_compfinfo(&pi->sentfiles[i], fname, fsize, ftime) )
613 					{
614 						s_finfo *tmp = pi->send;
615 
616 						pi->send = &pi->sentfiles[i];
617 						if( recv_rc == BPMSG_GOT )
618 							pi->send->status = FSTAT_SUCCESS;
619 						else
620 							pi->send->status = FSTAT_REFUSED;
621 						p_tx_fclose(pi);
622 
623 						pi->send = tmp;
624 						break;
625 					}
626 					else if( pi->sentfiles[i].status == FSTAT_WAITACK )
627 						wait_got = TRUE;
628 				}
629 
630 				/*
631 				 * Check, maybe there are files still
632 				 * waiting for the GOT/SKIP acknwoledge
633 				 */
634 				while( i < pi->n_sentfiles && !wait_got )
635 				{
636 					if( pi->sentfiles[i].status == FSTAT_WAITACK )
637 						wait_got = TRUE;
638 					++i;
639 				}
640 			}
641 			break;
642 
643 		case BPMSG_ERR:
644 			bf_log("remote report error: \"%s\"", bpi.ibuf+1);
645 			gotoexit(PRC_ERROR);
646 			break;
647 
648 		case BPMSG_BSY:
649 			bf_log("remote busy error: \"%s\"", bpi.ibuf+1);
650 			gotoexit(PRC_ERROR);
651 			break;
652 
653 		case BPMSG_GET:
654 			if( binkp_parsfinfo(bpi.ibuf+1, &fname, &fsize, &ftime, &foffs) == 0 )
655 			{
656 				if( send_file && !p_compfinfo(pi->send, fname, fsize, ftime) )
657 				{
658 					if( fseek(pi->send->fp, foffs, SEEK_SET) == -1 )
659 					{
660 						bf_log("cannot send file from requested offset %ld", (long)foffs);
661 						p_tx_fclose(pi);
662 						send_file = FALSE;
663 					}
664 					else
665 					{
666 						bf_log("sending \"%s\" from %ld offset",
667 							pi->send->fname, (long)foffs);
668 						pi->send->bytes_skipped = foffs;
669 						pi->send->bytes_sent = foffs;
670 						binkp_queuemsgf(&bpi, BPMSG_FILE, "%s %ld %ld %ld",
671 							pi->send->net_name, (long)pi->send->bytes_total,
672 							(long)pi->send->mod_time, (long)foffs);
673 					}
674 				}
675 			}
676 			break;
677 
678 		default:
679 			bf_log("binkp got unhandled msg #%d", recv_rc);
680 			break;
681 		}
682 	} /* end of while( !sent_EOB || !rcvd_EOB ) */
683 
684 FinishSession:
685 	if( binkp_flush_queue(&bpi, bpi.timeout) && rc == PRC_NOERROR )
686 		rc = PRC_ERROR;
687 
688 exit:
689 	if( pi->send && pi->send->fp ) p_tx_fclose(pi);
690 	if( pi->recv && pi->recv->fp ) p_rx_fclose(pi);
691 
692 	binkp_deinit_bpinfo(&bpi);
693 
694 	DEB((D_PROT, "binkp: BINKP exit = %d", rc));
695 
696 	return rc;
697 }
698 
699