1 /******************************************************************
2  * common protocols' file management
3  ******************************************************************/
4 /*
5  * $Id: protfm.c,v 1.18 2005/08/22 17:16:05 mitry Exp $
6  *
7  * $Log: protfm.c,v $
8  * Revision 1.18  2005/08/22 17:16:05  mitry
9  * Removed useless static function
10  *
11  * Revision 1.17  2005/08/16 14:49:01  mitry
12  * Replaced strncpy() with xstrcpy()
13  *
14  * Revision 1.16  2005/08/12 15:36:19  mitry
15  * Changed gmtoff()
16  *
17  * Revision 1.15  2005/05/17 18:17:42  mitry
18  * Removed system basename() usage.
19  * Added qbasename() implementation.
20  *
21  * Revision 1.14  2005/05/16 20:32:42  mitry
22  * Changed code a bit
23  *
24  * Revision 1.13  2005/05/06 20:37:38  mitry
25  * Fixed dangerous syntax BUG :)
26  *
27  * Revision 1.12  2005/05/05 19:20:09  mitry
28  * Changed rxopen() and rxclose() a bit
29  *
30  * Revision 1.11  2005/04/08 18:12:31  mitry
31  * check_cps() sets tty_gothup to HUP_CPS
32  *
33  * Revision 1.10  2005/04/07 13:05:11  mitry
34  * Added check of nullable tosend arg
35  *
36  * Revision 1.9  2005/03/31 19:40:38  mitry
37  * Update function prototypes and it's duplication
38  *
39  * Revision 1.8  2005/03/28 17:02:52  mitry
40  * Pre non-blocking i/o update. Mostly non working.
41  *
42  * Revision 1.7  2005/02/23 21:47:47  mitry
43  * tty_online and tty_gothup logic
44  *
45  * Revision 1.6  2005/02/22 13:56:53  mitry
46  * Removed warning about difference in signedness
47  *
48  * Revision 1.5  2005/02/21 16:33:42  mitry
49  * Changed tty_hangedup to tty_online
50  *
51  */
52 
53 #include "headers.h"
54 
55 #ifdef HAVE_UTIME_H
56 #include <utime.h>
57 #endif
58 
59 #include <fnmatch.h>
60 #include "hydra.h"
61 #include "ls_zmodem.h"
62 #include "binkp.h"
63 #include "qipc.h"
64 #include "tty.h"
65 
66 /*  Common protocols' vars */
67 FILE   *txfd=NULL,     *rxfd=NULL;
68 volatile long    txpos,          rxpos;
69 word    txblklen,       rxblklen;
70 byte   *txbuf,         *rxbuf;
71 /*
72 word    txretries,      rxretries;
73 long    txsyncid,       rxsyncid;
74 dword   txoptions,      rxoptions;
75 */
76 unsigned effbaud=DEFAULT_SPEED;
77 byte    *rxbufptr;
78 int     txstate,        rxstate;
79 byte    *rxbufmax;
80 long    txstart; /*,        rxstart; */
81 word    txmaxblklen;
82 word    timeout;
83 byte    txlastc;
84 
85 #define CHAT_BUF 16384
86 static unsigned char qrcv_buf[MSG_BUFFER]={0};
87 static unsigned char qsnd_buf[CHAT_BUF]={0};
88 static unsigned char ubuf[CHAT_BUF];
89 static char hellostr[MAX_STRING];
90 static unsigned short qsndbuflen=0;
91 
92 static char weskipstr[]="recd: %s, 0 bytes, 0 cps [%sskipped]";
93 static char wesusstr[]="recd: %s, 0 bytes, 0 cps [%ssuspended]";
94 
sifname(char * s)95 static int sifname(char *s)
96 {
97 	static char ALLOWED_CHARS[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz";
98 	static int CHARS = sizeof(ALLOWED_CHARS) / sizeof(char);
99 	int i;
100 	for(i = 0; i < CHARS; i++) if(*s < ALLOWED_CHARS[i]) { *s = ALLOWED_CHARS[i]; break; }
101 	if(i == CHARS) return 1;
102 	return 0;
103 }
104 
estimatedtime(off_t size,int cps,unsigned long baud)105 static char *estimatedtime(off_t size, int cps, unsigned long baud)
106 {
107 	static char et[16];
108 	int h,m,s;
109 	if (cps < 1) cps = baud / 10;
110 	if (!cps) cps = 1;
111 	s = size / cps;
112 	if (s < 1) s = 1;
113 	h = s / 3600; s %= 3600;
114 	m = s / 60;   s %= 60;
115 	snprintf(et,16,"%02d:%02d:%02d",h,m,s);
116 	return et;
117 }
118 
rxopen(char * name,time_t rtime,off_t rsize,FILE ** f)119 int rxopen(char *name, time_t rtime, off_t rsize, FILE **f)
120 {
121 	struct stat sb;
122 	slist_t *i;
123 	char p[MAX_PATH + 5], bn[MAX_PATH + 5];
124 	int prevcps = (recvf.start&&(time(NULL)-recvf.start>2))?recvf.cps:effbaud/10,rc;
125 
126 	if(!name || !*name) return FOP_ERROR;
127 	xstrcpy(bn, qbasename(name), MAX_PATH);
128 	mapname((char*)bn, cfgs(CFG_MAPIN), MAX_PATH);
129  	recvf.start=time(NULL);
130 	xfree(recvf.fname);
131  	recvf.fname=xstrdup(bn);
132 	recvf.mtime=rtime-gmtoff(recvf.start);
133 	recvf.ftot=rsize;
134 	if(recvf.toff+rsize > recvf.ttot) recvf.ttot+=rsize;
135 	recvf.nf++;if(recvf.nf>recvf.allf) recvf.allf++;
136 	IFPerl(if((rc=perl_on_recv())!=FOP_OK)return rc);
137 	if(whattype(name)==IS_PKT&&(rsize==60||!rsize)&&cfgi(CFG_KILLBADPKT))return FOP_SKIP;
138 	rc=skipiftic;skipiftic=0;
139 	if(rc&&istic(bn)&&cfgi(CFG_AUTOTICSKIP)) {
140 		write_log(rc==FOP_SKIP?weskipstr:wesusstr,recvf.fname,"auto");
141 		return rc;
142 	}
143 	for(i=cfgsl(CFG_AUTOSKIP);i;i=i->next)
144 		if(!xfnmatch(i->str, bn, FNM_PATHNAME)) {
145 			write_log(weskipstr,recvf.fname,"");
146 			skipiftic=FOP_SKIP;
147 			return FOP_SKIP;
148 		}
149 	for(i=cfgsl(CFG_AUTOSUSPEND);i;i=i->next)
150 		if(!xfnmatch(i->str, bn, FNM_PATHNAME)) {
151 			write_log(wesusstr,recvf.fname,"");
152 			skipiftic=FOP_SUSPEND;
153 			return FOP_SUSPEND;
154 		}
155 
156 	snprintf(p, MAX_PATH, "%s/tmp/", cfgs(CFG_INBOUND));
157 	if(stat(p, &sb))
158 		if(mkdirs(p) && errno!=EEXIST) {
159 			write_log("can't make directory %s: %s", p, strerror(errno));
160 			write_log(wesusstr,recvf.fname,"");
161 			skipiftic=FOP_SUSPEND;
162 			return FOP_SUSPEND;
163 		}
164 	snprintf(p, MAX_PATH, "%s/%s", ccs, bn);
165 
166 	if(!stat(p, &sb) && sb.st_size==rsize) {
167 		write_log(weskipstr,recvf.fname,"");
168 		skipiftic=FOP_SKIP;
169 		return FOP_SKIP;
170 	}
171 
172 	snprintf(p, MAX_PATH, "%s/tmp/%s", ccs, bn);
173 
174 	if(!stat(p, &sb)) {
175 		if(sb.st_size<rsize && sb.st_mtime==recvf.mtime) {
176 			*f=fopen(p, "ab");
177 			if(!*f) {
178 				write_log("can't open file %s for writing: %s", p,strerror(errno));
179 				write_log(wesusstr,recvf.fname,"");
180 				skipiftic=FOP_SUSPEND;
181 				return FOP_SUSPEND;
182 			}
183 			recvf.foff = recvf.soff = ftello( *f );
184 			if(cfgi(CFG_ESTIMATEDTIME)) {
185 				write_log("start recv: %s, %lu bytes (from %lu), estimated time %s",
186 					recvf.fname, (long) rsize, (long) recvf.soff, estimatedtime(rsize-recvf.soff,prevcps,effbaud));
187 			}
188 			return FOP_CONT;
189 		}
190 	}
191 
192 	*f=fopen(p, "wb");
193 	if(!*f) {
194 		write_log("can't open file %s for writing: %s", p,strerror(errno));
195 		write_log(wesusstr,recvf.fname,"");
196 		skipiftic=FOP_SUSPEND;
197 		return FOP_SUSPEND;
198 	}
199 	recvf.foff=recvf.soff=0;
200 	if(cfgi(CFG_ESTIMATEDTIME)) {
201 		write_log("start recv: %s, %lu bytes, estimated time %s",
202 			recvf.fname, (long) rsize, estimatedtime(rsize,prevcps,effbaud));
203 	}
204 	return FOP_OK;
205 }
206 
rxclose(FILE ** f,int what)207 int rxclose(FILE **f, int what)
208 {
209 	long cps=time(NULL)-recvf.start;
210 	int rc,overwrite;
211 	char *ss, p[MAX_PATH+5], p2[MAX_PATH+5];
212 	struct utimbuf ut;struct stat sb;
213 	slist_t *i;
214 
215 	if(!f || !*f) return FOP_ERROR;
216 	recvf.toff+=recvf.foff;recvf.stot+=recvf.soff;
217 	*p2=0;
218 	if(!cps) cps=1;cps=(recvf.foff-recvf.soff)/cps;
219 	IFPerl(if((ss=perl_end_recv(what))) {
220 		if(!*ss)what=FOP_SKIP;
221 		else xstrcpy(p2,ss,MAX_PATH);});
222 	switch(what) {
223 		case FOP_SUSPEND: ss="suspended";break;
224 		case FOP_SKIP: ss="skipped";break;
225 		case FOP_ERROR: ss="error";break;
226 		case FOP_OK: ss="ok";break;
227 		default: ss="";
228 	}
229 	if(recvf.soff)
230 		write_log("rcvd: %s, %lu bytes (from %lu), %ld cps [%s]",
231 			recvf.fname, (long) recvf.foff, (long) recvf.soff, cps, ss);
232 	else
233 		write_log("rcvd: %s, %lu bytes, %ld cps [%s]",
234 			recvf.fname, (long) recvf.foff, cps, ss);
235 	fclose(*f);*f=NULL;
236 	snprintf(p, MAX_PATH, "%s/tmp/%s", cfgs(CFG_INBOUND), recvf.fname);
237 	if(*p2) {
238 		if(*p2!='/'&&*p2=='.')	{
239 			ss=xstrdup(p2);
240 			snprintf(p2,MAX_PATH,"%s/%s",cfgs(CFG_INBOUND),ss);
241 			xfree(ss);
242 		}
243 	} else snprintf(p2, MAX_PATH, "%s/%s", cfgs(CFG_INBOUND), recvf.fname);
244 	ut.actime=ut.modtime=recvf.mtime;
245 	recvf.foff=0;
246 	switch(what) {
247 	case FOP_SKIP:
248 		lunlink(p);
249 		break;
250 	case FOP_SUSPEND:
251 	case FOP_ERROR:
252 		if(whattype(p)==IS_PKT&&cfgi(CFG_KILLBADPKT))lunlink(p);
253 		    else utime(p,&ut);
254 		break;
255 	case FOP_OK:
256 		rc=receive_callback?receive_callback(p):0;
257 		if(rc) lunlink(p);
258 		else {
259 			ss=p2+strlen(p2)-1;overwrite=0;
260 			for(i=cfgsl(CFG_ALWAYSOVERWRITE);i;i=i->next)
261 			    if(!xfnmatch(i->str,recvf.fname,FNM_PATHNAME))
262 				overwrite=1;
263 			while(!overwrite&&!stat(p2, &sb)&&p2[0]) {
264 				if(sifname(ss)) {
265 					ss--;
266 					while('.' == *ss && ss >= p2) ss--;
267 					if(ss < p2) {
268 						write_log("can't find suitable name for %s: leaving in temporary directory",p);
269 						p2[0] = '\x00';
270 					}
271 				}
272 			}
273 			if(p2[0]) {
274 				if(overwrite)lunlink(p2);
275 				if(rename(p, p2)) {
276 					write_log("can't rename %s to %s: %s",p,p2,strerror(errno));
277 				} else {
278 					utime(p2,&ut);chmod(p2,cfgi(CFG_DEFPERM));
279 				}
280 			}
281 		}
282 		break;
283 	}
284 	if(what==FOP_SKIP||what==FOP_SUSPEND)skipiftic=what;
285 	recvf.start=0;recvf.ftot=0;
286 	rxstatus=0;
287 	return what;
288 }
289 
txopen(char * tosend,char * sendas)290 FILE *txopen(char *tosend, char *sendas)
291 {
292 	FILE *f;
293 	struct stat sb;
294 	int prevcps = (sendf.start&&(time(NULL)-sendf.start>2))?sendf.cps:effbaud/10;
295 
296 	if ( !tosend )
297 	    return NULL;
298 
299 	if(stat(tosend, &sb)) {
300 		write_log("can't find file %s", tosend);
301 		return NULL;
302 	}
303 	if(whattype(sendas)==IS_PKT&&sb.st_size==60)return NULL;
304 	xfree(sendf.fname);
305  	sendf.fname=xstrdup(sendas);
306 	sendf.ftot=sb.st_size;
307 	sendf.foff=sendf.soff=0;
308 	sendf.start=time(NULL);
309 	sendf.mtime=sb.st_mtime+gmtoff(sendf.start);
310 	if(sendf.toff+sb.st_size > sendf.ttot) sendf.ttot+=sb.st_size;
311 	sendf.nf++;if(sendf.nf>sendf.allf) sendf.allf++;
312 	IFPerl({char *p=perl_on_send(tosend);if(p&&!*p)return NULL;
313 		if(p){xfree(sendf.fname);sendf.fname=xstrdup(p);}});
314 	f=fopen(tosend, "rb");
315 	if(!f) {
316 		write_log("can't open file %s for reading: %s", tosend,strerror(errno));
317 		return NULL;
318 	}
319 	if(cfgi(CFG_ESTIMATEDTIME)) {
320 		write_log("start send: %s, %lu bytes, estimated time %s",
321 			sendf.fname, (long) sendf.ftot, estimatedtime(sendf.ftot,prevcps,effbaud));
322 	}
323 	return f;
324 }
325 
txclose(FILE ** f,int what)326 int txclose(FILE **f, int what)
327 {
328 	long cps=time(NULL)-sendf.start;
329 	char *ss;
330 
331 	if(!f || !*f) return FOP_ERROR;
332 	sendf.toff+=sendf.foff;sendf.stot+=sendf.soff;
333 
334 	if(!cps) cps=1;cps=(sendf.foff-sendf.soff)/cps;
335 	IFPerl(perl_end_send(what));
336 	switch(what) {
337 		case FOP_SUSPEND: ss="suspended";break;
338 		case FOP_SKIP: ss="skipped";break;
339 		case FOP_ERROR: ss="error";break;
340 		case FOP_OK: ss="ok";break;
341 		default: ss="";
342 	}
343 	if(sendf.soff)write_log("sent: %s, %lu bytes (from %lu), %ld cps [%s]",
344 		sendf.fname, (long) sendf.foff, (long) sendf.soff, cps, ss);
345 	    else write_log("sent: %s, %lu bytes, %ld cps [%s]",
346 		sendf.fname, (long) sendf.foff, cps, ss);
347 	sendf.foff=0;sendf.ftot=0;
348 	sendf.start=0;
349 	fclose(*f);*f=NULL;
350 	return what;
351 }
352 
chatinit(int prot)353 void chatinit(int prot)
354 {
355 	xstrcpy(hellostr,"\05\05\05",MAX_STRING);
356 	strtr(cfgs(CFG_CHATHALLOSTR),'|','\n');strtr(ccs,'$','\a');
357 	snprintf(hellostr+3,MAX_STRING-7,ccs,rnode->sysop);
358 	recode_to_remote(hellostr);
359 	xstrcat(hellostr,"\05\05\05",MAX_STRING);
360 	chattimer=0;
361 	chatlg=0;
362 	qsndbuflen=0;
363 	*qsnd_buf=0;
364 	switch(prot) {
365 		case P_ZEDZAP:
366 		case P_DIRZAP:
367 		case P_ZMODEM:
368 			chatprot=P_ZMODEM;
369 			break;
370 		case P_HYDRA:
371 #ifdef HYDRA8K16K
372 		case P_HYDRA4:
373 		case P_HYDRA8:
374 		case P_HYDRA16:
375 #endif/*HYDRA8K16K*/
376 			chatprot=P_HYDRA;
377 			break;
378 		case P_JANUS:
379 			chatprot=P_JANUS;
380 			break;
381 #ifdef WITH_BINKP
382 		case 0:
383 			if(bink)chatprot=P_BINKP;
384 #endif
385 	}
386 }
387 
c_devfree(void)388 int c_devfree(void)
389 {
390 	int rc=0;
391 	switch(chatprot) {
392 		case P_ZMODEM:
393 			rc=z_devfree();
394 			break;
395 		case P_HYDRA:
396 			rc=hydra_devfree();
397 			break;
398 #ifdef WITH_BINKP
399 		case P_BINKP:
400 			rc=binkp_devfree();
401 #endif
402 	}
403 	return rc;
404 }
405 
c_devsend(unsigned char * str,unsigned len)406 int c_devsend(unsigned char *str,unsigned len)
407 {
408 	int rc=0;
409 	switch(chatprot) {
410 		case P_ZMODEM:
411 			rc=z_devsend(str,len-1);
412 			break;
413 		case P_HYDRA:
414 			rc=hydra_devsend("CON",str,len);
415 			break;
416 #ifdef WITH_BINKP
417 		case P_BINKP:
418 			rc=binkp_devsend(str,len);
419 #endif
420 	}
421 	return rc;
422 }
423 
chatsend(unsigned char * str)424 int chatsend(unsigned char *str)
425 {
426 	if(!str||!*str)return 0;
427 	if(!c_devfree())return 1;
428 	if(chattimer<2) {
429 		c_devsend((unsigned char*)hellostr,strlen(hellostr)+1);
430 		chatlg=chatlog_init(rnode->sysop,&rnode->addrs->addr,0);
431 		qchat("");
432 	} else if(*str!=5) {
433 		xstrcpy( (char*) ubuf, (char*) str, CHAT_BUF);
434 		recode_to_remote((char*)ubuf);
435 		if(!c_devsend(ubuf,strlen((char*)ubuf)))return 1;
436 		if(chatlg)chatlog_write((char*)str,0);
437 	} else write_log("Chat already opened!");
438 	chattimer=time(NULL)+TIM_CHAT;
439 	return 0;
440 }
441 
c_devrecv(unsigned char * data,unsigned len)442 void c_devrecv(unsigned char *data,unsigned len)
443 {
444 	int i;
445 	if(!data||!*data||!len)return;
446 	data[len]=0;
447 	if(chattimer<2) {
448 		char *p;
449 		if(len>5&&data[1]!=5&&(p=strstr((char*)data," * "))&&(strstr(p+3,"lose")||strstr(p+3,"has chat")))return;
450 		c_devsend((unsigned char*)hellostr,strlen(hellostr));
451 		chatlg=chatlog_init(rnode->sysop,&rnode->addrs->addr,1);
452 	}
453 	recode_to_local((char*)data);
454 	if(data[strlen((char*)data)-1]==5)data[strlen((char*)data)-1]='\n';
455 	for(len=i=0;len<=strlen((char*)data);len++)if(data[len]!=5)data[i++]=data[len];
456 	chattimer=time(NULL)+TIM_CHAT;
457 	if(chatlg)chatlog_write((char*)data,1);
458 	qchat((char*)data);
459 }
460 
getevt(void)461 void getevt(void)
462 {
463 	int i;
464 	while(qrecvpkt((char*)qrcv_buf)) {
465 		switch(qrcv_buf[2]) {
466 			case QR_SKIP:
467 				rxstatus=RX_SKIP;
468 				break;
469 			case QR_REFUSE:
470 				rxstatus=RX_SUSPEND;
471 				break;
472 			case QR_HANGUP:
473 				tty_gothup = HUP_OPERATOR;
474 				break;
475 			case QR_CHAT:
476 				if(qrcv_buf[3]) {
477 					xstrcpy((char*)(qsnd_buf+qsndbuflen),(char*)(qrcv_buf+3),CHAT_BUF-qsndbuflen);
478 					qsndbuflen+=strlen((char*)(qrcv_buf+3));
479 					if(qsndbuflen>CHAT_BUF-128)qsndbuflen=CHAT_BUF-128;
480 				    } else {
481 					i=chatprot;chatprot=-1;
482 					chatsend(qsnd_buf);
483 					if(chatlg)chatlog_done();
484 					chatlg=0;chatprot=i;
485 					xstrcat((char*)qsnd_buf,"\n * Chat closed\n",CHAT_BUF);
486 					chatsend(qsnd_buf);
487 					if(chattimer>1)qlcerase();
488 					qsndbuflen=0;
489 					chattimer=1;
490 				}
491 				break;
492 		}
493 		qsnd_buf[qsndbuflen]=0;
494 	}
495 	if(qsndbuflen>0)if(!chatsend(qsnd_buf))qsndbuflen=0;
496 }
497 
check_cps(void)498 void check_cps(void)
499 {
500 	int cpsdelay=cfgi(CFG_MINCPSDELAY),ncps=rnode->realspeed/1000,r=cfgi(CFG_REALMINCPS);
501 	if(!(sendf.cps=time(NULL)-sendf.start))sendf.cps=1;
502 	    else sendf.cps=(sendf.foff-sendf.soff)/sendf.cps;
503 	if(!(recvf.cps=time(NULL)-recvf.start))recvf.cps=1;
504 	    else recvf.cps=(recvf.foff-recvf.soff)/recvf.cps;
505 	if(sendf.start&&cfgi(CFG_MINCPSOUT)>0&&(time(NULL)-sendf.start)>cpsdelay&&sendf.cps<(r?cci:cci*ncps)) {
506 		write_log("mincpsout=%d reached, aborting session",r?cci:cci*ncps);
507 		tty_gothup = HUP_CPS;
508 	}
509 	if(recvf.start&&cfgi(CFG_MINCPSIN)>0&&(time(NULL)-recvf.start)>cpsdelay&&recvf.cps<(r?cci:cci*ncps)) {
510 		write_log("mincpsin=%d reached, aborting session",r?cci:cci*ncps);
511 		tty_gothup = HUP_CPS;
512 	}
513 	getevt();
514 }
515