Commit | Line | Data |
---|---|---|
bebb6839 C |
1 | char *protv = "C-Kermit Protocol Module 5A(052), 23 Nov 92"; /* -*-C-*- */ |
2 | ||
3 | /* C K C P R O -- C-Kermit Protocol Module, in Wart preprocessor notation. */ | |
4 | /* | |
5 | Author: Frank da Cruz (fdc@columbia.edu, FDCCU@CUVMA.BITNET), | |
6 | Columbia University Center for Computing Activities. | |
7 | First released January 1985. | |
8 | Copyright (C) 1985, 1992, Trustees of Columbia University in the City of New | |
9 | York. Permission is granted to any individual or institution to use this | |
10 | software as long as it is not sold for profit. This copyright notice must be | |
11 | retained. This software may not be included in commercial products without | |
12 | written permission of Columbia University. | |
13 | */ | |
14 | #include "ckcdeb.h" | |
15 | #include "ckcasc.h" | |
16 | #include "ckcker.h" | |
17 | /* | |
18 | Note -- This file may also be preprocessed by the Unix Lex program, but | |
19 | you must indent the above #include statements before using Lex, and then | |
20 | restore them to the left margin in the resulting C program before compilation. | |
21 | Also, the invocation of the "wart()" function below must be replaced by an | |
22 | invocation of the "yylex()" function. It might also be necessary to remove | |
23 | comments in the %%...%% section. | |
24 | */ | |
25 | ||
26 | /* State definitions for Wart (or Lex) */ | |
27 | %states ipkt rfile rattr rdata ssinit ssfile ssattr ssdata sseof sseot | |
28 | %states serve generic get rgen | |
29 | ||
30 | /* External C-Kermit variable declarations */ | |
31 | extern char *versio, *srvtxt, *cmarg, *cmarg2, **cmlist; | |
32 | extern char filnam[], ttname[]; | |
33 | extern CHAR sstate, *rpar(), encbuf[], *srvptr, *data; | |
34 | extern int timint, rtimo, nfils, hcflg, xflg, flow, mdmtyp, network; | |
35 | extern int cxseen, czseen, server, srvdis, local, displa, bctu, bctr, bctl; | |
36 | extern int quiet, tsecs, parity, backgrd, nakstate, atcapu, wslotn, winlo; | |
37 | extern int wslots, success, xitsta, rprintf, discard, cdtimo, keep, fdispla; | |
38 | extern int timef; | |
39 | extern long speed, ffc; | |
40 | extern char *DIRCMD, *DIRCM2, *DELCMD, *TYPCMD, *SPACMD, *SPACM2, *WHOCMD; | |
41 | extern CHAR *rdatap; | |
42 | extern struct zattr iattr; | |
43 | #ifdef DYNAMIC | |
44 | extern CHAR *srvcmd; | |
45 | #else | |
46 | extern CHAR srvcmd[]; | |
47 | #endif /* DYNAMIC */ | |
48 | ||
49 | #ifndef NOSPL | |
50 | extern int cmdlvl; | |
51 | #else | |
52 | extern int tlevel; | |
53 | #endif /* NOSPL */ | |
54 | ||
55 | #ifdef NOMSEND | |
56 | extern int sndsrc; | |
57 | #endif /* NOMSEND */ | |
58 | ||
59 | /* Flags for the ENABLE and DISABLE commands */ | |
60 | extern int | |
61 | en_cwd, en_del, en_dir, en_fin, en_get, en_bye, | |
62 | en_hos, en_sen, en_spa, en_set, en_typ, en_who; | |
63 | ||
64 | /* Global variables declared here */ | |
65 | ||
66 | int what = W_NOTHING; /* What we're doing */ | |
67 | ||
68 | /* Local variables */ | |
69 | ||
70 | static char vstate = 0; /* Saved State */ | |
71 | static char vcmd = 0; /* Saved Command */ | |
72 | ||
73 | static int x; /* General-purpose integer */ | |
74 | static char *s; /* General-purpose string pointer */ | |
75 | ||
76 | /* Macros - Note, BEGIN is predefined by Wart (and Lex) as "state = ", */ | |
77 | /* BEGIN is NOT a GOTO! */ | |
78 | #define TINIT if (tinit() < 0) return(-9) | |
79 | #define SERVE TINIT; nakstate = 1; what = W_NOTHING; BEGIN serve | |
80 | #define RESUME if (server) { SERVE; } else { sleep(2); return(0); } | |
81 | #define QUIT x=quiet; quiet=1; clsif(); clsof(1); tsecs=gtimer(); quiet=x; \ | |
82 | return(1) | |
83 | ||
84 | %% | |
85 | /* | |
86 | Protocol entry points, one for each start state (sstate). | |
87 | The lowercase letters are internal "inputs" from the user interface. | |
88 | */ | |
89 | ||
90 | s { TINIT; /* Do Send command */ | |
91 | if (sinit()) BEGIN ssinit; | |
92 | else RESUME; } | |
93 | ||
94 | v { TINIT; nakstate = 1; BEGIN get; } /* Receive */ | |
95 | r { TINIT; vstate = get; vcmd = 0; sipkt('I'); BEGIN ipkt; } /* Get */ | |
96 | c { TINIT; vstate = rgen; vcmd = 'C'; sipkt('I'); BEGIN ipkt; } /* Host */ | |
97 | k { TINIT; vstate = rgen; vcmd = 'K'; sipkt('I'); BEGIN ipkt; } /* Kermit */ | |
98 | g { TINIT; vstate = rgen; vcmd = 'G'; sipkt('I'); BEGIN ipkt; } /* Generic */ | |
99 | ||
100 | x { sleep(1); SERVE; } /* Be a Server */ | |
101 | ||
102 | a { if (!data) TINIT; /* "ABEND" -- Tell other side. */ | |
103 | errpkt((CHAR *)"User cancelled"); | |
104 | success = 0; | |
105 | return(0); } /* Return from protocol. */ | |
106 | \f | |
107 | /* | |
108 | Dynamic states: <current-states>input-character { action } | |
109 | nakstate != 0 means we're in a receiving state, in which we send ACKs & NAKs. | |
110 | */ | |
111 | <rgen,get,serve>S { /* Receive Send-Init packet. */ | |
112 | if (state == serve && !en_sen) { /* Not allowed if in server mode */ | |
113 | errpkt((CHAR *)"SEND disabled"); /* and SEND is disabled. */ | |
114 | SERVE; | |
115 | } else { /* OK to go ahead. */ | |
116 | nakstate = 1; /* Can send NAKs from here. */ | |
117 | rinit(rdatap); /* Set parameters */ | |
118 | bctu = bctr; /* Switch to agreed-upon block check */ | |
119 | bctl = (bctu == 4) ? 2 : bctu; /* Set block-check length */ | |
120 | what = W_RECV; /* Remember we're receiving */ | |
121 | resetc(); /* Reset counters */ | |
122 | rtimer(); /* Reset timer */ | |
123 | BEGIN rfile; /* Go into receive-file state */ | |
124 | } | |
125 | } | |
126 | ||
127 | /* States in which we get replies back from commands sent to a server. */ | |
128 | /* Complicated because direction of protocol changes, packet number */ | |
129 | /* stays at zero through I-G-S sequence, and complicated even more by */ | |
130 | /* sliding windows buffer allocation. */ | |
131 | ||
132 | <ipkt>Y { /* Get ack for I-packet */ | |
133 | spar(rdatap); /* Set parameters */ | |
134 | #ifdef COMMENT | |
135 | getsbuf(winlo = 0); /* Set window-low back to zero */ | |
136 | #else | |
137 | winlo = 0; | |
138 | #endif /* COMMENT */ | |
139 | if (vcmd) { /* If sending a generic command */ | |
140 | scmd(vcmd,(CHAR *)cmarg); /* Do that */ | |
141 | vcmd = 0; /* and then un-remember it. */ | |
142 | } else if (vstate == get) srinit(); /* If sending GET command, do that. */ | |
143 | rtimer(); /* Reset the elapsed seconds timer. */ | |
144 | winlo = 0; /* Window back to 0, again. */ | |
145 | nakstate = 1; /* Can send NAKs from here. */ | |
146 | BEGIN vstate; /* Switch to desired state */ | |
147 | } | |
148 | ||
149 | <ipkt>E { /* Ignore Error reply to I packet */ | |
150 | #ifdef COMMENT | |
151 | getsbuf(winlo = 0); /* Set window-low back to zero */ | |
152 | #else | |
153 | winlo = 0; | |
154 | #endif /* COMMENT */ | |
155 | if (vcmd) { /* In case other Kermit doesn't */ | |
156 | scmd(vcmd,(CHAR *)cmarg); /* understand I-packets. */ | |
157 | vcmd = 0; /* Otherwise act as above... */ | |
158 | } else if (vstate == get) srinit(); | |
159 | winlo = 0; /* Back to packet 0 again. */ | |
160 | freerpkt(winlo); /* Discard the Error packet. */ | |
161 | nakstate = 1; /* Can send NAKs from here. */ | |
162 | BEGIN vstate; | |
163 | } | |
164 | ||
165 | <get>Y { /* Resend of previous I-pkt ACK, same seq number! */ | |
166 | srinit(); | |
167 | } | |
168 | ||
169 | /* States in which we're being a server */ | |
170 | ||
171 | <serve>I { /* Get I-packet */ | |
172 | spar(rdatap); /* Set parameters from it */ | |
173 | ack1(rpar()); /* Respond with our own parameters */ | |
174 | pktinit(); /* Reinitialize packet numbers */ | |
175 | } | |
176 | ||
177 | <serve>R { /* Get Receive-Init (GET) */ | |
178 | debug(F100,"<serve>R","",0); | |
179 | if (!en_get) { /* Only if not disabled! */ | |
180 | errpkt((CHAR *)"GET disabled"); | |
181 | SERVE; | |
182 | } else { /* OK to go ahead. */ | |
183 | srvptr = srvcmd; /* Point to server command buffer */ | |
184 | decode(rdatap,putsrv,0); /* Decode the GET command into it */ | |
185 | /* Accept multiple filespecs */ | |
186 | cmarg2 = ""; /* Don't use cmarg2 */ | |
187 | cmarg = ""; /* Don't use cmarg */ | |
188 | #ifndef NOMSEND /* New way. */ | |
189 | nfils = fnparse((char *)srvcmd); /* Use cmlist instead */ | |
190 | #else | |
191 | nfils = 0 - zxpand((char *)srvcmd); | |
192 | #endif /* NOMSEND */ | |
193 | nakstate = 0; /* Now I'm the sender! */ | |
194 | if (sinit()) { /* Send Send-Init */ | |
195 | timint = chktimo(rtimo,timef); /* Switch to per-packet timer */ | |
196 | BEGIN ssinit; /* If successful, switch state */ | |
197 | } else { SERVE; } /* Else back to server command wait */ | |
198 | } | |
199 | } | |
200 | ||
201 | <serve>G { /* Generic server command */ | |
202 | srvptr = srvcmd; /* Point to command buffer */ | |
203 | decode(rdatap,putsrv,0); /* Decode packet data into it */ | |
204 | putsrv('\0'); /* Insert a couple nulls */ | |
205 | putsrv('\0'); /* for termination */ | |
206 | if (srvcmd[0]) { | |
207 | sstate = srvcmd[0]; /* Set requested start state */ | |
208 | nakstate = 0; /* Now I'm the sender. */ | |
209 | what = W_REMO; /* Doing a REMOTE command. */ | |
210 | if (timint < 1) | |
211 | timint = chktimo(rtimo,timef); /* Switch to per-packet timer */ | |
212 | BEGIN generic; /* Switch to generic command state */ | |
213 | } else { | |
214 | errpkt((CHAR *)"Badly formed server command"); /* report error */ | |
215 | SERVE; /* & go back to server command wait */ | |
216 | } | |
217 | } | |
218 | ||
219 | <serve>C { /* Receive Host command */ | |
220 | if (!en_hos) { | |
221 | errpkt((CHAR *)"REMOTE HOST disabled"); | |
222 | SERVE; | |
223 | } else { | |
224 | srvptr = srvcmd; /* Point to command buffer */ | |
225 | decode(rdatap,putsrv,0); /* Decode command packet into it */ | |
226 | putsrv('\0'); /* Null-terminate */ | |
227 | nakstate = 0; /* Now sending, not receiving */ | |
228 | if (syscmd((char *)srvcmd,"")) { /* Try to execute the command */ | |
229 | what = W_REMO; /* Doing a REMOTE command. */ | |
230 | if (timint < 1) | |
231 | timint = chktimo(rtimo,timef); /* Switch to per-packet timer */ | |
232 | BEGIN ssinit; /* If OK, send back its output */ | |
233 | } else { /* Otherwise */ | |
234 | errpkt((CHAR *)"Can't do system command"); /* report error */ | |
235 | SERVE; /* & go back to server command wait */ | |
236 | } | |
237 | } | |
238 | } | |
239 | ||
240 | <serve>q { /* User typed Ctrl-C... */ | |
241 | if (!en_fin) { | |
242 | errpkt((CHAR *)"QUIT disabled"); | |
243 | SERVE; | |
244 | } else { | |
245 | success = 0; QUIT; | |
246 | } | |
247 | } | |
248 | ||
249 | <serve>N { /* Server got a NAK in command-wait */ | |
250 | errpkt((CHAR *)"Did you say RECEIVE instead of GET?"); | |
251 | SERVE; | |
252 | } | |
253 | ||
254 | <serve>. { /* Any other command in this state */ | |
255 | errpkt((CHAR *)"Unimplemented server function"); /* we don't know about */ | |
256 | SERVE; /* back to server command wait */ | |
257 | } | |
258 | ||
259 | <generic>C { /* Got REMOTE CWD command */ | |
260 | if (!en_cwd) { | |
261 | errpkt((CHAR *)"REMOTE CD disabled"); | |
262 | SERVE; | |
263 | } else { | |
264 | if (!cwd((char *)(srvcmd+1))) errpkt((CHAR *)"Can't change directory"); | |
265 | SERVE; /* Back to server command wait */ | |
266 | } | |
267 | } | |
268 | ||
269 | <generic>D { /* REMOTE DIRECTORY command */ | |
270 | char *n2; | |
271 | if (!en_dir) { /* If DIR is disabled, */ | |
272 | errpkt((CHAR *)"REMOTE DIRECTORY disabled"); /* refuse. */ | |
273 | SERVE; | |
274 | } else { /* DIR is enabled. */ | |
275 | if (!en_cwd) { /* But if CWD is disabled */ | |
276 | zstrip((char *)(srvcmd+2),&n2); /* and they included a pathname, */ | |
277 | if (strcmp((char *)(srvcmd+2),n2)) { /* refuse. */ | |
278 | errpkt((CHAR *)"Access denied"); | |
279 | SERVE; /* Remember, this is not a goto! */ | |
280 | } | |
281 | } | |
282 | if (state == generic) { /* It's OK to go ahead. */ | |
283 | n2 = (*(srvcmd+2)) ? DIRCMD : DIRCM2; | |
284 | if (syscmd(n2,(char *)(srvcmd+2))) /* If it can be done */ | |
285 | BEGIN ssinit; /* send the results back */ | |
286 | else { /* otherwise */ | |
287 | errpkt((CHAR *)"Can't list directory"); /* report failure, */ | |
288 | SERVE; /* return to server command wait */ | |
289 | } | |
290 | } | |
291 | } | |
292 | } | |
293 | ||
294 | <generic>E { /* REMOTE DELETE (Erase) command */ | |
295 | char *n2; | |
296 | if (!en_del) { | |
297 | errpkt((CHAR *)"REMOTE DELETE disabled"); | |
298 | SERVE; | |
299 | } else { | |
300 | if (!en_cwd) { /* But if CWD is disabled */ | |
301 | zstrip((char *)(srvcmd+2),&n2); /* and they included a pathname, */ | |
302 | if (strcmp((char *)(srvcmd+2),n2)) { /* refuse. */ | |
303 | errpkt((CHAR *)"Access denied"); | |
304 | SERVE; /* Remember, this is not a goto! */ | |
305 | } | |
306 | } | |
307 | if (state == generic) { /* It's OK to go ahead. */ | |
308 | if (syscmd(DELCMD,(char *)(srvcmd+2))) /* Try to do it */ | |
309 | BEGIN ssinit; /* If OK send results back */ | |
310 | else { /* otherwise */ | |
311 | errpkt((CHAR *)"Can't remove file"); /* report failure */ | |
312 | SERVE; /* & return to server command wait */ | |
313 | } | |
314 | } | |
315 | } | |
316 | } | |
317 | ||
318 | <generic>F { /* FINISH */ | |
319 | if (!en_fin) { | |
320 | errpkt((CHAR *)"FINISH disabled"); | |
321 | SERVE; | |
322 | } else { | |
323 | ack(); /* Acknowledge */ | |
324 | screen(SCR_TC,0,0l,""); /* Display */ | |
325 | return(0); /* Done */ | |
326 | } | |
327 | } | |
328 | ||
329 | <generic>L { /* BYE (LOGOUT) */ | |
330 | if (!en_bye) { | |
331 | errpkt((CHAR *)"BYE disabled"); | |
332 | SERVE; | |
333 | } else { | |
334 | ack(); /* Acknowledge */ | |
335 | ttres(); /* Reset the terminal */ | |
336 | screen(SCR_TC,0,0l,""); /* Display */ | |
337 | doclean(); /* Clean up files, etc */ | |
338 | return(zkself()); /* Try to log self out */ | |
339 | } | |
340 | } | |
341 | ||
342 | <generic>H { /* REMOTE HELP */ | |
343 | if (sndhlp()) BEGIN ssinit; /* Try to send it */ | |
344 | else { /* If not ok, */ | |
345 | errpkt((CHAR *)"Can't send help"); /* send error message instead */ | |
346 | SERVE; /* and return to server command wait */ | |
347 | } | |
348 | } | |
349 | ||
350 | <generic>S { /* REMOTE SET */ | |
351 | if (!en_set) { | |
352 | errpkt((CHAR *)"REMOTE SET disabled"); | |
353 | SERVE; | |
354 | } else { | |
355 | if (remset((char *)(srvcmd+1))) /* Try to do what they ask */ | |
356 | ack(); /* If OK, then acknowledge */ | |
357 | else /* Otherwise */ | |
358 | errpkt((CHAR *)"Unknown REMOTE SET parameter"); /* give error msg */ | |
359 | SERVE; /* Return to server command wait */ | |
360 | } | |
361 | } | |
362 | ||
363 | <generic>T { /* REMOTE TYPE */ | |
364 | char *n2; | |
365 | if (!en_typ) { | |
366 | errpkt((CHAR *)"REMOTE TYPE disabled"); | |
367 | SERVE; | |
368 | } else { | |
369 | if (!en_cwd) { /* But if CWD is disabled */ | |
370 | zstrip((char *)(srvcmd+2),&n2); /* and they included a pathname, */ | |
371 | if (strcmp((char *)(srvcmd+2),n2)) { /* refuse. */ | |
372 | errpkt((CHAR *)"Access denied"); | |
373 | SERVE; /* Remember, this is not a goto! */ | |
374 | } | |
375 | } | |
376 | if (state == generic) { /* It's OK to go ahead. */ | |
377 | if (syscmd(TYPCMD,(char *)(srvcmd+2))) /* Try */ | |
378 | BEGIN ssinit; /* OK */ | |
379 | else { /* not OK */ | |
380 | errpkt((CHAR *)"Can't type file"); /* give error message */ | |
381 | SERVE; /* wait for next server command */ | |
382 | } | |
383 | } | |
384 | } | |
385 | } | |
386 | ||
387 | <generic>U { /* REMOTE SPACE */ | |
388 | if (!en_spa) { | |
389 | errpkt((CHAR *)"REMOTE SPACE disabled"); | |
390 | SERVE; | |
391 | } else { | |
392 | x = *(srvcmd+1); /* Get area to check */ | |
393 | x = ((x == '\0') || (x == SP) | |
394 | #ifdef OS2 | |
395 | || (x == '!') | |
396 | #endif /* OS2 */ | |
397 | ); | |
398 | if (!x && !en_cwd) { /* If CWD disabled and they gave */ | |
399 | errpkt((CHAR *)"Access denied"); /* a non-default area, */ | |
400 | SERVE; /* refuse. */ | |
401 | } else { | |
402 | #ifdef OS2 | |
403 | _PROTOTYP(int sndspace,(int)); | |
404 | if (sndspace(x ? toupper(srvcmd[2]) : 0)) | |
405 | BEGIN ssinit; /* Try to send it */ | |
406 | else { /* If not ok, */ | |
407 | errpkt((CHAR *)"Can't send space"); /* send error message */ | |
408 | SERVE; /* and return to server command wait */ | |
409 | } | |
410 | #else | |
411 | x = (x ? syscmd(SPACMD,"") : syscmd(SPACM2,(char *)(srvcmd+2))); | |
412 | if (x) { /* If we got the info */ | |
413 | BEGIN ssinit; /* send it */ | |
414 | } else { /* otherwise */ | |
415 | errpkt((CHAR *)"Can't check space"); /* send error message */ | |
416 | SERVE; /* and await next server command */ | |
417 | } | |
418 | #endif /* OS2 */ | |
419 | } | |
420 | } | |
421 | } | |
422 | ||
423 | <generic>W { /* REMOTE WHO */ | |
424 | if (!en_who) { | |
425 | errpkt((CHAR *)"REMOTE WHO disabled"); | |
426 | SERVE; | |
427 | } else { | |
428 | if (syscmd(WHOCMD,(char *)(srvcmd+2))) /* The now-familiar scenario. */ | |
429 | BEGIN ssinit; | |
430 | else { | |
431 | errpkt((CHAR *)"Can't do who command"); | |
432 | SERVE; | |
433 | } | |
434 | } | |
435 | } | |
436 | ||
437 | <generic>q { | |
438 | if (!en_fin) { /* Ctrl-C typed */ | |
439 | errpkt((CHAR *)"QUIT disabled"); | |
440 | SERVE; | |
441 | } else { | |
442 | success = 0; QUIT; | |
443 | } | |
444 | } | |
445 | ||
446 | <generic>. { /* Anything else in this state... */ | |
447 | errpkt((CHAR *)"Unimplemented REMOTE command"); /* Complain */ | |
448 | SERVE; /* and return to server command wait */ | |
449 | } | |
450 | ||
451 | <rgen>Y { /* Short-Form reply */ | |
452 | decode(rdatap,puttrm,0); /* in ACK Data field */ | |
453 | if (rdatap && *rdatap) conoll(""); /* Maybe add a CRLF */ | |
454 | RESUME; | |
455 | } | |
456 | ||
457 | <rgen,rfile>F { /* File header */ | |
458 | xflg = 0; /* Not screen data */ | |
459 | if (!rcvfil(filnam)) { /* Figure out local filename */ | |
460 | errpkt((CHAR *)"Can't transform filename"); /* Trouble */ | |
461 | RESUME; | |
462 | } else { /* OK to receive */ | |
463 | encstr((CHAR *)filnam); /* Encode the name */ | |
464 | ack1((CHAR *)(encbuf+7)); /* Send it back in ACK */ | |
465 | initattr(&iattr); /* Clear file attribute structure */ | |
466 | if (window(wslotn) < 0) { /* Allocate negotiated window slots */ | |
467 | errpkt((CHAR *)"Can't open window"); | |
468 | RESUME; | |
469 | } | |
470 | BEGIN rattr; /* Now expect Attribute packets */ | |
471 | } | |
472 | } | |
473 | ||
474 | <rgen,rfile>X { /* X-packet instead of file header */ | |
475 | xflg = 1; /* Screen data */ | |
476 | ack(); /* Acknowledge the X-packet */ | |
477 | initattr(&iattr); /* Initialize attribute structure */ | |
478 | if (window(wslotn) < 0) { /* allocate negotiated window slots */ | |
479 | errpkt((CHAR *)"Can't open window"); | |
480 | RESUME; | |
481 | } | |
482 | what = W_REMO; /* we're doing a REMOTE command */ | |
483 | BEGIN rattr; /* Expect Attribute packets */ | |
484 | } | |
485 | ||
486 | <rattr>A { /* Attribute packet */ | |
487 | if (discard) { /* If SET FILE COLLISION DISCARD */ | |
488 | ack1((CHAR *)"N"); /* refuse it */ | |
489 | screen(SCR_ST,ST_REFU,0L,"file collision setting"); | |
490 | } else if (gattr(rdatap,&iattr) == 0) { /* Read into attribute structure */ | |
491 | ack(); /* If OK, acknowledge */ | |
492 | } else { /* Otherwise */ | |
493 | ack1((CHAR *)iattr.reply.val); /* refuse to accept the file */ | |
494 | screen(SCR_ST,ST_REFU,0L,getreason(iattr.reply.val)); /* give reason */ | |
495 | } | |
496 | } | |
497 | ||
498 | <rattr>D { /* First data packet */ | |
499 | if (discard) { /* if we're discarding the file */ | |
500 | ack1((CHAR *)"X"); /* just ack the data like this. */ | |
501 | BEGIN rdata; /* and wait for more data packets. */ | |
502 | } else { /* Not discarding. */ | |
503 | if (xflg) /* If screen data */ | |
504 | x = opent(&iattr); /* "open" the screen */ | |
505 | else /* otherwise */ | |
506 | x = opena(filnam,&iattr); /* open the file, with attributes */ | |
507 | if (x) { /* If file was opened ok */ | |
508 | if (decode(rdatap,putfil,1) < 0) { /* decode first data packet */ | |
509 | errpkt((CHAR *)"Error writing data"); | |
510 | RESUME; | |
511 | } | |
512 | ack(); /* acknowledge it */ | |
513 | BEGIN rdata; /* and switch to receive-data state */ | |
514 | } else { /* otherwise */ | |
515 | errpkt((CHAR *)"Can't open file"); /* send error message */ | |
516 | RESUME; /* and quit. */ | |
517 | } | |
518 | } | |
519 | } | |
520 | ||
521 | <rfile>B { /* EOT, no more files */ | |
522 | ack(); /* Acknowledge */ | |
523 | tsecs = gtimer(); /* Get timing for statistics */ | |
524 | reot(); /* Do EOT things */ | |
525 | RESUME; /* and quit */ | |
526 | } | |
527 | ||
528 | <rdata>D { /* Data packet */ | |
529 | if (cxseen || discard) /* If file interrupt */ | |
530 | ack1((CHAR *)"X"); /* put "X" in ACK */ | |
531 | else if (czseen) /* If file-group interrupt */ | |
532 | ack1((CHAR *)"Z"); /* put "Z" in ACK */ | |
533 | else if (decode(rdatap,putfil,1) < 0) { /* Normal case, decode to file */ | |
534 | errpkt((CHAR *)"Error writing data"); /* If failure, */ | |
535 | clsof(!keep); /* Close & keep/discard the file */ | |
536 | RESUME; /* Send ACK only after data */ | |
537 | } else ack(); /* written to file OK. */ | |
538 | } | |
539 | ||
540 | <rattr>Z { /* EOF immediately after A-Packet. */ | |
541 | if (xflg) /* Zero-length file. If screen data */ | |
542 | x = opent(&iattr); /* "open" the screen */ | |
543 | else /* otherwise */ | |
544 | x = opena(filnam,&iattr); /* open the file, with attributes. */ | |
545 | if (!x || reof(filnam, &iattr) < 0) { /* Now close & dispose of the file */ | |
546 | errpkt((CHAR *)"Can't create file"); /* If problem, send error msg */ | |
547 | RESUME; /* and quit */ | |
548 | } else { /* otherwise */ | |
549 | ack(); /* acknowledge the EOF packet */ | |
550 | BEGIN rfile; /* and await another file */ | |
551 | } | |
552 | } | |
553 | ||
554 | <rdata>Z { /* End Of File (EOF) Packet */ | |
555 | /* wslots = 1; */ /* Window size back to 1 */ | |
556 | #ifndef COHERENT | |
557 | /* | |
558 | Coherent compiler blows up on this switch() statement. | |
559 | */ | |
560 | x = reof(filnam, &iattr); /* Handle the EOF packet */ | |
561 | switch (x) { /* reof() sets the success flag */ | |
562 | case -3: /* If problem, send error msg */ | |
563 | errpkt((CHAR *)"Can't print file"); /* Fatal */ | |
564 | RESUME; | |
565 | break; | |
566 | case -2: | |
567 | errpkt((CHAR *)"Can't mail file"); /* Fatal */ | |
568 | RESUME; | |
569 | break; | |
570 | case 2: | |
571 | case 3: | |
572 | screen(SCR_EM,0,0l,"Can't delete temp file"); /* Not fatal */ | |
573 | RESUME; | |
574 | break; | |
575 | default: | |
576 | if (x < 0) { /* Fatal */ | |
577 | errpkt((CHAR *)"Can't close file"); | |
578 | RESUME; | |
579 | } else { /* Success */ | |
580 | ack(); /* Acknowledge the EOF packet */ | |
581 | BEGIN rfile; /* and await another file */ | |
582 | } | |
583 | } | |
584 | #else | |
585 | if (reof(filnam, &iattr) < 0) { /* Close and dispose of the file */ | |
586 | errpkt((CHAR *)"Error at end of file"); | |
587 | RESUME; | |
588 | } else { /* reof() sets success flag */ | |
589 | ack(); | |
590 | BEGIN rfile; | |
591 | } | |
592 | #endif /* COHERENT */ | |
593 | } | |
594 | ||
595 | <ssinit>Y { /* ACK for Send-Init */ | |
596 | spar(rdatap); /* set parameters from it */ | |
597 | bctu = bctr; /* switch to agreed-upon block check */ | |
598 | bctl = (bctu == 4) ? 2 : bctu; /* Set block-check length */ | |
599 | what = W_SEND; /* Remember we're sending */ | |
600 | x = sfile(xflg); /* Send X or F header packet */ | |
601 | if (x) { /* If the packet was sent OK */ | |
602 | resetc(); /* reset per-transaction counters */ | |
603 | rtimer(); /* reset timers */ | |
604 | BEGIN ssfile; /* and switch to receive-file state */ | |
605 | } else { /* otherwise send error msg & quit */ | |
606 | s = xflg ? "Can't execute command" : "Can't open file"; | |
607 | errpkt((CHAR *)s); | |
608 | RESUME; | |
609 | } | |
610 | } | |
611 | ||
612 | /* | |
613 | These states are necessary to handle the case where we get a server command | |
614 | packet (R, G, or C) reply with an S packet, but the client retransmits the | |
615 | command packet. The input() function doesn't catch this because the packet | |
616 | number is still zero. | |
617 | */ | |
618 | <ssinit>R { /* R packet was retransmitted. */ | |
619 | xsinit(); /* Resend packet 0 */ | |
620 | } | |
621 | ||
622 | <ssinit>G { /* Same deal if G packet comes again */ | |
623 | xsinit(); | |
624 | } | |
625 | ||
626 | <ssinit>C { /* Same deal if C packet comes again */ | |
627 | xsinit(); | |
628 | } | |
629 | ||
630 | <ssfile>Y { /* ACK for F packet */ | |
631 | srvptr = srvcmd; /* Point to string buffer */ | |
632 | decode(rdatap,putsrv,0); /* Decode data field, if any */ | |
633 | putsrv('\0'); /* Terminate with null */ | |
634 | ffc = 0L; /* Reset file byte counter */ | |
635 | if (*srvcmd) { /* If remote name was recorded */ | |
636 | if (fdispla == XYFD_C) screen(SCR_AN,0,0L,(char *)srvcmd); | |
637 | tlog(F110," stored as",(char *) srvcmd,0L); /* Transaction log. */ | |
638 | } | |
639 | if (atcapu) { /* If attributes are to be used */ | |
640 | if (sattr(xflg) < 0) { /* set and send them */ | |
641 | errpkt((CHAR *)"Can't send attributes"); /* if problem, say so */ | |
642 | RESUME; /* and quit */ | |
643 | } else BEGIN ssattr; /* if ok, switch to attribute state */ | |
644 | } else { | |
645 | if (window(wslotn) < 0) { | |
646 | errpkt((CHAR *)"Can't open window"); | |
647 | RESUME; | |
648 | } | |
649 | if (sdata() < 0) { /* No attributes, send data */ | |
650 | clsif(); /* If not ok, close input file, */ | |
651 | window(1); /* put window size back to 1, */ | |
652 | seof((CHAR *)""); /* send EOF packet, */ | |
653 | BEGIN sseof; /* and switch to EOF state. */ | |
654 | } else BEGIN ssdata; /* All ok, switch to send-data state */ | |
655 | } | |
656 | } | |
657 | ||
658 | <ssattr>Y { /* Got ACK to A packet */ | |
659 | ffc = 0L; /* Reset file byte counter */ | |
660 | if (rsattr(rdatap) < 0) { /* Was the file refused? */ | |
661 | discard = 1; /* Set the discard flag */ | |
662 | clsif(); /* Close the file */ | |
663 | sxeof((CHAR *)"D"); /* send EOF with "discard" code */ | |
664 | BEGIN sseof; /* switch to send-EOF state */ | |
665 | } else { | |
666 | if (window(wslotn) < 0) { /* Allocate negotiated window slots */ | |
667 | errpkt((CHAR *)"Can't open window"); | |
668 | RESUME; | |
669 | } | |
670 | if (sdata() < 0) { /* File accepted, send data */ | |
671 | clsif(); /* If problem, close input file */ | |
672 | window(1); /* Window size back to 1... */ | |
673 | seof((CHAR *)""); /* send EOF packet */ | |
674 | BEGIN sseof; /* and switch to send-EOF state. */ | |
675 | } else { /* All ok, enter send-data state. */ | |
676 | BEGIN ssdata; | |
677 | } | |
678 | } | |
679 | } | |
680 | ||
681 | <ssdata>Y { /* Got ACK to Data packet */ | |
682 | canned(rdatap); /* Check if file transfer cancelled */ | |
683 | if (sdata() < 0) { /* Try to send next data */ | |
684 | clsif(); /* If no more data, close file */ | |
685 | window(1); /* Window size back to 1... */ | |
686 | if (cxseen || czseen) /* If interrupted */ | |
687 | seof((CHAR *)"D"); /* send special EOF packet */ | |
688 | else seof((CHAR *)""); /* Otherwise regular EOF packet */ | |
689 | BEGIN sseof; /* And enter send-eof state */ | |
690 | } | |
691 | } | |
692 | ||
693 | <sseof>Y { /* Got ACK to EOF */ | |
694 | success = (cxseen == 0 && czseen == 0); /* Set this for IF command */ | |
695 | cxseen = 0; /* This goes back to zero. */ | |
696 | if (gnfile() > 0) { /* Any more files to send? */ | |
697 | if (sfile(xflg)) /* Yes, try to send next file header */ | |
698 | BEGIN ssfile; /* if ok, enter send-file state */ | |
699 | else { /* otherwise */ | |
700 | errpkt((CHAR *)"Can't open file"); /* send error message */ | |
701 | RESUME; /* and quit */ | |
702 | } | |
703 | } else { /* No next file */ | |
704 | tsecs = gtimer(); /* get statistics timers */ | |
705 | seot(); /* send EOT packet */ | |
706 | BEGIN sseot; /* enter send-eot state */ | |
707 | } | |
708 | } | |
709 | ||
710 | <sseot>Y { /* Got ACK to EOT */ | |
711 | RESUME; /* All done, just quit */ | |
712 | } | |
713 | ||
714 | E { /* Got Error packet, in any state */ | |
715 | ermsg((char *)rdatap); /* Issue message. */ | |
716 | success = 0; /* For IF SUCCESS/FAIL. */ | |
717 | debug(F101,"ckcpro.w sstate at E pkt","",sstate); | |
718 | x = quiet; quiet = 1; /* Close files silently, */ | |
719 | clsif(); clsof(1); /* discarding any output file. */ | |
720 | tsecs = gtimer(); /* Get timers */ | |
721 | quiet = x; /* restore quiet state */ | |
722 | /* | |
723 | If we are executing commands from a command file or macro, let the command | |
724 | file or macro decide whether to exit, based on SET { TAKE, MACRO } ERROR. | |
725 | */ | |
726 | if ( | |
727 | #ifndef NOSPL | |
728 | cmdlvl == 0 | |
729 | #else | |
730 | tlevel < 0 | |
731 | #endif /* NOSPL */ | |
732 | ) | |
733 | if (backgrd && !server) | |
734 | fatal("Protocol error"); | |
735 | xitsta |= what; /* Save this for doexit(). */ | |
736 | RESUME; | |
737 | } | |
738 | ||
739 | q { QUIT; } /* Ctrl-C interrupt during packets. */ | |
740 | ||
741 | . { /* Anything not accounted for above */ | |
742 | errpkt((CHAR *)"Unexpected packet type"); /* Give error message */ | |
743 | xitsta |= what; /* Save this for doexit(). */ | |
744 | RESUME; /* and quit */ | |
745 | } | |
746 | %% | |
747 | \f | |
748 | /* P R O T O -- Protocol entry function */ | |
749 | ||
750 | VOID | |
751 | proto() { | |
752 | ||
753 | int x; | |
754 | long lx; | |
755 | ||
756 | /* Set up the communication line for file transfer. */ | |
757 | ||
758 | if (local && (speed < 0L) && (network == 0)) { | |
759 | screen(SCR_EM,0,0l,"Sorry, you must 'set speed' first"); | |
760 | return; | |
761 | } | |
762 | x = -1; | |
763 | if (ttopen(ttname,&x,mdmtyp,cdtimo) < 0) { | |
764 | debug(F111,"failed: proto ttopen local",ttname,local); | |
765 | screen(SCR_EM,0,0l,"Can't open line"); | |
766 | return; | |
767 | } | |
768 | if (x > -1) local = x; | |
769 | debug(F111,"proto ttopen local",ttname,local); | |
770 | ||
771 | lx = (local && !network) ? speed : -1; | |
772 | if (ttpkt(lx,flow,parity) < 0) { /* Put line in packet mode, */ | |
773 | screen(SCR_EM,0,0l,"Can't condition line"); | |
774 | return; | |
775 | } | |
776 | if (!local) connoi(); /* No console interrupts if remote */ | |
777 | ||
778 | if (sstate == 'x') { /* If entering server mode, */ | |
779 | server = 1; /* set flag, */ | |
780 | debug(F101,"server backgrd","",backgrd); | |
781 | debug(F101,"server quiet","",quiet); | |
782 | if (!quiet && !backgrd) { | |
783 | debug(F100,"SHOULD NOT SEE THIS IF IN BACKGROUND!","",0); | |
784 | if (!local) { /* and issue appropriate message. */ | |
785 | conoll(srvtxt); | |
786 | conoll("KERMIT READY TO SERVE..."); | |
787 | } else { | |
788 | conol("Entering server mode on "); | |
789 | conoll(ttname); | |
790 | conoll("Type Ctrl-C to quit."); | |
791 | if (srvdis) intmsg(-1L); | |
792 | } | |
793 | } | |
794 | } else server = 0; | |
795 | #ifdef VMS | |
796 | if (!quiet && !backgrd) /* So message doesn't overwrite prompt */ | |
797 | conoll(""); | |
798 | if (local) conres(); /* So Ctrl-C will work */ | |
799 | #endif /* VMS */ | |
800 | /* | |
801 | If in remote mode, not shushed, not in background, and at top command level, | |
802 | issue a helpful message telling what to do... | |
803 | */ | |
804 | if (!local && !quiet && !backgrd && | |
805 | #ifndef NOSPL | |
806 | cmdlvl == 0 | |
807 | #else | |
808 | tlevel < 0 | |
809 | #endif /* NOSPL */ | |
810 | ) { | |
811 | if (sstate == 'v') { | |
812 | conoll("Return to your local Kermit and give a SEND command."); | |
813 | conoll(""); | |
814 | conoll("KERMIT READY TO RECEIVE..."); | |
815 | } else if (sstate == 's') { | |
816 | conoll("Return to your local Kermit and give a RECEIVE command."); | |
817 | conoll(""); | |
818 | conoll("KERMIT READY TO SEND..."); | |
819 | } else if ( sstate == 'g' || sstate == 'r' || sstate == 'c' ) { | |
820 | conoll("Return to your local Kermit and give a SERVER command."); | |
821 | conoll(""); | |
822 | conoll((sstate == 'r') ? | |
823 | "KERMIT READY TO GET..." : | |
824 | "KERMIT READY TO SEND SERVER COMMAND..."); | |
825 | } | |
826 | } | |
827 | sleep(1); | |
828 | /* | |
829 | The 'wart()' function is generated by the wart program. It gets a | |
830 | character from the input() routine and then based on that character and | |
831 | the current state, selects the appropriate action, according to the state | |
832 | table above, which is transformed by the wart program into a big case | |
833 | statement. The function is active for one transaction. | |
834 | */ | |
835 | wart(); /* Enter the state table switcher. */ | |
836 | ||
837 | if (server) { /* Back from packet protocol. */ | |
838 | if (!quiet && !backgrd) { /* Give appropriate message */ | |
839 | conoll(""); | |
840 | conoll("C-Kermit server done"); | |
841 | } | |
842 | } | |
843 | /* | |
844 | Note: the following is necessary in case we have just done a remote-mode | |
845 | file transfer, in which case the controlling terminal modes have been | |
846 | changed by ttpkt(). In particular, special characters like Ctrl-C and | |
847 | Ctrl-\ might have been turned off (see ttpkt). So this call to ttres() is | |
848 | essential. | |
849 | */ | |
850 | #ifndef OS2 | |
851 | if (!local) | |
852 | #endif /* OS2 */ | |
853 | ttres(); /* Reset the communication device */ | |
854 | screen(SCR_TC,0,0l,""); /* Transaction complete */ | |
855 | server = 0; /* Not a server any more */ | |
856 | } |