AmendHub

Download

jcs

/

subtext

/

binkp.c

 

(View History)

jcs   binkp: Use common binkp: dir, move bad zips to bad subdir Latest amendment: 456 on 2023-03-27

1 /*
2 * FTN Binkp
3 * https://www.ritlabs.com/binkp/
4 *
5 * Copyright (c) 2023 joshua stein <jcs@jcs.org>
6 *
7 * Permission to use, copy, modify, and distribute this software for any
8 * purpose with or without fee is hereby granted, provided that the above
9 * copyright notice and this permission notice appear in all copies.
10 *
11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18 */
19
20 #include <stdio.h>
21 #include <string.h>
22 #include "binkp.h"
23 #include "board.h"
24 #include "fidopkt.h"
25 #include "logger.h"
26 #include "mail.h"
27 #include "subtext.h"
28 #include "zip.h"
29
30 /* #define BINKP_DEBUG */
31
32 bool binkp_ready = false;
33 time_t binkp_next_poll = 0;
34
35 static struct fidopkt_address binkp_our_address = { 0 };
36 static struct binkp_connection *binkpc = NULL;
37 static Str255 binkp_inbox_dir, binkp_done_dir, binkp_outbox_dir,
38 binkp_bad_dir;
39 static short binkp_fidopkt_skipped, binkp_fidopkt_imported;
40 static bool binkp_temp_fail = false;
41 struct uthread *binkp_thread = NULL;
42
43 void binkp_mkdir(Str255 dir_str, char *suffix);
44 bool binkp_connect(void);
45 void binkp_free(void);
46 size_t binkp_send_frame(short command, char *data, size_t data_size);
47 bool binkp_read_frame(void);
48 bool binkp_login(void);
49 void binkp_ingest(void);
50 bool binkp_zip_decider(char *filename, size_t size);
51 void binkp_fidopkt_processor(char *filename, unsigned char *data,
52 size_t size);
53 size_t binkp_buffer_file(Str255 path, char **data);
54 void binkp_toss_inbox(void);
55 void binkp_deliver_outbox(void);
56
57 void
58 binkp_init(void)
59 {
60 Str255 base_dir;
61
62 if (db->config.binkp_hostname[0] == '\0')
63 return;
64
65 if (!fidopkt_parse_address(db->config.ftn_node_addr,
66 &binkp_our_address)) {
67 logger_printf("[binkp] Failed parsing local FTN node address "
68 "\"%s\", fix in settings", db->config.ftn_node_addr);
69 return;
70 }
71
72 binkp_mkdir(base_dir, NULL);
73 binkp_mkdir(binkp_inbox_dir, "inbox");
74 binkp_mkdir(binkp_done_dir, "processed");
75 binkp_mkdir(binkp_outbox_dir, "outbox");
76 binkp_mkdir(binkp_bad_dir, "bad");
77
78 binkp_ready = true;
79 }
80
81 void
82 binkp_mkdir(Str255 dir_str, char *suffix)
83 {
84 short error;
85 long id;
86
87 if (getpath(db->bile->vrefnum, db->bile->filename, dir_str, false) != 0)
88 panic("getpath failed on %s", PtoCstr(db->bile->filename));
89 PtoCstr(dir_str);
90 strlcat((char *)dir_str, ":binkp", sizeof(Str255));
91 if (suffix != NULL) {
92 strlcat((char *)dir_str, ":", sizeof(Str255));
93 strlcat((char *)dir_str, suffix, sizeof(Str255));
94 }
95 CtoPstr(dir_str);
96
97 if (!FIsDir(dir_str)) {
98 error = DirCreate(db->bile->vrefnum, 0, dir_str, &id);
99 if (error)
100 panic("Failed creating %s: %d", PtoCstr(dir_str), error);
101 }
102 }
103
104 void
105 binkp_atexit(void)
106 {
107 if (binkpc != NULL)
108 binkp_free();
109 }
110
111 void
112 binkp_poll(void)
113 {
114 char filename[16];
115 size_t n, pkt_buf_size;
116 char *pkt_buf;
117 unsigned long started = Time, elapsed;
118
119 binkp_connect();
120 if (!binkpc) {
121 binkp_next_poll = Time + (60 * 5);
122 goto done;
123 }
124
125 if (!binkp_login())
126 goto done;
127
128 while (binkpc != NULL && !binkpc->done_receiving) {
129 binkp_read_frame();
130 uthread_yield();
131 }
132
133 if (binkpc)
134 binkp_deliver_outbox();
135
136 if (binkpc)
137 _TCPClose(&binkpc->tcp_send_iopb, binkpc->tcp_stream, nil, nil,
138 false);
139
140 elapsed = Time - started;
141 logger_printf("[binkp] Finished polling in %ld sec%s", elapsed,
142 elapsed == 1 ? "" : "s");
143
144 binkp_next_poll = Time + db->config.binkp_interval_seconds;
145
146 done:
147 if (binkpc != NULL)
148 binkp_free();
149
150 binkp_toss_inbox();
151 }
152
153 bool
154 binkp_connect(void)
155 {
156 char ip_s[16];
157 char *hostname;
158 ip_addr local_ip, host_ip;
159 tcp_port local_port;
160 short error;
161
162 if (_TCPInit() != noErr)
163 panic("Failed initializing MacTCP");
164
165 if (binkpc != NULL)
166 binkp_free();
167
168 binkpc = xmalloczero(sizeof(struct binkp_connection));
169 if (binkpc == NULL) {
170 logger_printf("[binkp] Failed allocating connection of size %ld",
171 sizeof(struct binkp_connection));
172 goto error;
173 }
174 binkpc->buf_size = 1024;
175 binkpc->buf = xmalloc(binkpc->buf_size);
176 if (binkpc->buf == NULL) {
177 logger_printf("[binkp] Failed allocating connection buffer of "
178 "size %ld", binkpc->buf_size);
179 goto error;
180 }
181 binkpc->tcp_buf_size = (4 * 1500) + binkpc->buf_size;
182 binkpc->tcp_buf = xmalloc(binkpc->tcp_buf_size);
183 if (binkpc->tcp_buf == NULL) {
184 logger_printf("[binkp] Failed allocating TCP buffer of "
185 "size %ld", binkpc->tcp_buf_size);
186 goto error;
187 }
188
189 hostname = db->config.binkp_hostname;
190 error = DNSResolveName(&hostname, &host_ip,
191 uthread_yield);
192 if (error) {
193 logger_printf("[binkp] Failed resolving binkp host %s: %d",
194 db->config.binkp_hostname, error);
195 goto error;
196 }
197
198 error = _TCPCreate(&binkpc->tcp_send_iopb, &binkpc->tcp_stream,
199 (Ptr)binkpc->tcp_buf, binkpc->tcp_buf_size, nil, nil, nil, false);
200 if (error) {
201 logger_printf("[binkp] Failed creating TCP stream: %d", error);
202 goto error;
203 }
204
205 long2ip(host_ip, (char *)&ip_s);
206
207 logger_printf("[binkp] Connecting to %s (%s) port %d",
208 db->config.binkp_hostname, ip_s, db->config.binkp_port);
209
210 error = _TCPActiveOpen(&binkpc->tcp_send_iopb, binkpc->tcp_stream,
211 host_ip, db->config.binkp_port, &local_ip, &local_port, nil, nil,
212 true);
213 while (!error && binkpc->tcp_send_iopb.ioResult > 0)
214 uthread_yield();
215 if (error || binkpc->tcp_send_iopb.ioResult != 0) {
216 logger_printf("[binkp] Failed connecting to %s (%s) port %d: %d",
217 db->config.binkp_hostname, ip_s, db->config.binkp_port,
218 binkpc->tcp_send_iopb.ioResult);
219 goto error;
220 }
221
222 return true;
223
224 error:
225 binkp_free();
226 return false;
227 }
228
229 void
230 binkp_free(void)
231 {
232 if (binkpc == NULL)
233 return;
234
235 if (binkpc->tcp_stream)
236 _TCPRelease(&binkpc->tcp_send_iopb, binkpc->tcp_stream, nil, nil,
237 false);
238
239 if (binkpc->tcp_buf != NULL)
240 xfree(&binkpc->tcp_buf);
241 if (binkpc->buf != NULL)
242 xfree(&binkpc->buf);
243
244 xfree(&binkpc);
245 }
246
247 size_t
248 binkp_send_frame(short command, char *data, size_t data_size)
249 {
250 u_int16_t flen;
251 short error;
252
253 if (binkpc == NULL)
254 return 0;
255
256 flen = data_size + 2;
257 if (command != BINKP_DATA)
258 flen++;
259 if (flen > binkpc->buf_size) {
260 logger_printf("[binkp] Frame too large (%u), can't send", flen);
261 return 0;
262 }
263
264 while (binkpc->tcp_send_iopb.ioResult > 0)
265 /* previous _TCPSend has not completed yet */
266 uthread_yield();
267
268 if (command == BINKP_DATA) {
269 binkpc->buf[0] = (data_size >> 8) & 0xff;
270 binkpc->buf[1] = data_size & 0xff;
271 flen = data_size + 2;
272 memcpy(binkpc->buf + 2, data, data_size);
273 } else {
274 binkpc->buf[0] = (((data_size + 1) | 0x8000) >> 8) & 0xff;
275 binkpc->buf[1] = (data_size + 1) & 0xff;
276 binkpc->buf[2] = command;
277 flen = data_size + 3;
278 memcpy(binkpc->buf + 3, data, data_size);
279 /* not sent, just for debugging */
280 binkpc->buf[3 + data_size] = '\0';
281 }
282
283 memset(&binkpc->tcp_wds, 0, sizeof(binkpc->tcp_wds));
284 binkpc->tcp_wds[0].ptr = (Ptr)binkpc->buf;
285 binkpc->tcp_wds[0].length = flen;
286
287 #ifdef BINKP_DEBUG
288 switch (command) {
289 case BINKP_COMMAND_M_PWD:
290 logger_printf("[binkp] Sending password command of size %ld",
291 data_size);
292 break;
293 case BINKP_COMMAND_M_FILE:
294 logger_printf("[binkp] Sending new file: %s", binkpc->buf + 3);
295 break;
296 case BINKP_COMMAND_M_EOB:
297 logger_printf("[binkp] Sending EOB");
298 break;
299 case BINKP_COMMAND_M_GOT:
300 logger_printf("[binkp] Sending GOT for %s",
301 binkpc->cur_incoming_file.filename);
302 break;
303 case BINKP_DATA:
304 logger_printf("[binkp] Sending data of size %ld", data_size);
305 break;
306 default:
307 logger_printf("[binkp] Sending command %d of size %ld: %s",
308 command, data_size, binkpc->buf + 3);
309 }
310 #endif
311
312 error = _TCPSend(&binkpc->tcp_send_iopb, binkpc->tcp_stream,
313 binkpc->tcp_wds, nil, nil, false);
314 if (error) {
315 logger_printf("[binkp] TCPSend of %d failed: %d",
316 binkpc->tcp_wds[0].length, error);
317 return 0;
318 }
319
320 uthread_yield();
321
322 return binkpc->tcp_wds[0].length;
323 }
324
325 bool
326 binkp_read_frame(void)
327 {
328 char tmp[128];
329 size_t len, off, frame_data_read;
330 unsigned short rlen;
331 short error;
332 Ptr read_dest;
333
334 if (binkpc == NULL)
335 return false;
336
337 error = _TCPStatus(&binkpc->tcp_read_iopb, binkpc->tcp_stream,
338 &binkpc->tcp_status_pb, nil, nil, false);
339 if (error)
340 return false;
341
342 if (binkpc->tcp_status_pb.amtUnreadData < 2)
343 return false;
344
345 rlen = 2;
346 error = _TCPRcv(&binkpc->tcp_read_iopb, binkpc->tcp_stream,
347 (Ptr)&binkpc->cur_frame, &rlen, nil, nil, false);
348 if (error)
349 return false;
350
351 if (binkpc->cur_frame.data_size == 0) {
352 logger_printf("[binkp] Received bogus frame, no data_size");
353 return false;
354 }
355
356 binkpc->cur_frame.type =
357 (binkpc->cur_frame.data_size & (1 << 15)) == 0 ? BINKP_TYPE_DATA :
358 BINKP_TYPE_COMMAND;
359 binkpc->cur_frame.data_size &= 0x7fff;
360
361 frame_data_read = 0;
362 while (frame_data_read < binkpc->cur_frame.data_size) {
363 error = _TCPStatus(&binkpc->tcp_read_iopb, binkpc->tcp_stream,
364 &binkpc->tcp_status_pb, nil, nil, false);
365 if (error)
366 goto failed_read;
367
368 if (binkpc->tcp_status_pb.amtUnreadData == 0) {
369 uthread_yield();
370 continue;
371 }
372
373 if (binkpc->cur_frame.type == BINKP_TYPE_COMMAND) {
374 if (frame_data_read >= binkpc->buf_size) {
375 /*
376 * Frame is too big but we can't overwrite buf since we
377 * need the start of the frame, so just read into a junk
378 * buffer and discard it
379 */
380 read_dest = tmp;
381 rlen = MIN(binkpc->cur_frame.data_size - frame_data_read,
382 sizeof(tmp));
383 } else {
384 read_dest = binkpc->buf + frame_data_read;
385 rlen = MIN(binkpc->buf_size - frame_data_read,
386 binkpc->cur_frame.data_size - frame_data_read);
387 }
388 } else {
389 read_dest = binkpc->buf;
390 rlen = MIN(binkpc->cur_frame.data_size - frame_data_read,
391 binkpc->buf_size);
392 }
393
394 rlen = MIN(rlen, binkpc->tcp_status_pb.amtUnreadData);
395
396 error = _TCPRcv(&binkpc->tcp_read_iopb, binkpc->tcp_stream,
397 read_dest, &rlen, nil, nil, false);
398 if (error)
399 goto failed_read;
400
401 frame_data_read += rlen;
402
403 if (binkpc->cur_frame.type == BINKP_TYPE_DATA) {
404 binkpc->cur_incoming_file.data_read += rlen;
405 #ifdef BINKP_DEBUG
406 logger_printf("[binkp] Read %d TCP chunk (%ld / %d)",
407 rlen, frame_data_read, binkpc->cur_frame.data_size);
408 #endif
409
410 if (binkpc->cur_incoming_file.frefnum == 0)
411 panic("binkpc: no frefnum for data, bogus state");
412 len = rlen;
413 error = FSWrite(binkpc->cur_incoming_file.frefnum, &len,
414 binkpc->buf);
415 if (error) {
416 logger_printf("[binkp] Error writing %u to %s: %d",
417 rlen, PtoCstr(binkpc->cur_incoming_file.pfilename),
418 error);
419 CtoPstr(binkpc->cur_incoming_file.pfilename);
420 binkpc->done_receiving = binkpc->done_sending = true;
421 goto failed_read;
422 }
423 }
424 }
425
426 if (frame_data_read > binkpc->cur_frame.data_size)
427 panic("binkp data overread");
428
429 if (binkpc->cur_frame.type == BINKP_TYPE_COMMAND) {
430 binkpc->cur_frame.command_id = binkpc->buf[0];
431 if (frame_data_read < binkpc->buf_size)
432 binkpc->buf[frame_data_read] = '\0';
433 else
434 binkpc->buf[binkpc->buf_size - 1] = '\0';
435
436 #ifdef BINKP_DEBUG
437 logger_printf("[binkp] Read command 0x%x frame [%ld]: %s",
438 binkpc->cur_frame.command_id,
439 frame_data_read, binkpc->buf + (frame_data_read > 0 ? 1 : 0));
440 #endif
441
442 switch (binkpc->cur_frame.command_id) {
443 case BINKP_COMMAND_M_NUL:
444 if (strncmp(binkpc->buf + 1, "SYS ", 4) == 0)
445 logger_printf("[binkp] Connected to %s",
446 binkpc->buf + 1 + 4);
447 else if (strncmp(binkpc->buf + 1, "TIME ", 5) == 0) {
448 /*
449 * TODO: try to parse timezone and pass to fidopkt, so
450 * any packets without a TZUTC line can use the hub's
451 * timezone.
452 */
453 }
454 break;
455 case BINKP_COMMAND_M_FILE:
456 if (binkpc->cur_incoming_file.filename[0]) {
457 logger_printf("[binkp] Received M_FILE but not done "
458 "with file %s!", binkpc->cur_incoming_file.filename);
459 binkpc->cur_incoming_file.filename[0] = '\0';
460 if (binkpc->cur_incoming_file.frefnum > 0) {
461 FSClose(binkpc->cur_incoming_file.frefnum);
462 binkpc->cur_incoming_file.frefnum = 0;
463 }
464 }
465
466 if (sscanf(binkpc->buf + 1, "%128s %lu %lu %lu",
467 &binkpc->cur_incoming_file.filename,
468 &binkpc->cur_incoming_file.size,
469 &binkpc->cur_incoming_file.mtime, &off) == 4) {
470 logger_printf("[binkp] Receiving file \"%s\" size %ld",
471 binkpc->cur_incoming_file.filename,
472 binkpc->cur_incoming_file.size);
473 if (off != 0)
474 logger_printf("[binkp] Non-zero file fetch offset not "
475 "supported");
476 binkpc->cur_incoming_file.data_read = 0;
477
478 PtoCstr(binkp_inbox_dir);
479 snprintf((char *)binkpc->cur_incoming_file.pfilename,
480 sizeof(binkpc->cur_incoming_file.pfilename),
481 "%s:%s", binkp_inbox_dir,
482 binkpc->cur_incoming_file.filename);
483 CtoPstr(binkp_inbox_dir);
484 CtoPstr(binkpc->cur_incoming_file.pfilename);
485
486 error = Create(binkpc->cur_incoming_file.pfilename, 0,
487 SUBTEXT_CREATOR, 'BINK');
488 if (error == dupFNErr) {
489 FSDelete(binkpc->cur_incoming_file.pfilename, 0);
490 error = Create(binkpc->cur_incoming_file.pfilename, 0,
491 SUBTEXT_CREATOR, 'BINK');
492 }
493 if (error) {
494 warn("failed creating %s: %d",
495 PtoCstr(binkpc->cur_incoming_file.pfilename), error);
496 CtoPstr(binkpc->cur_incoming_file.pfilename);
497 goto failed_read;
498 }
499
500 error = FSOpen(binkpc->cur_incoming_file.pfilename, 0,
501 &binkpc->cur_incoming_file.frefnum);
502 if (error) {
503 warn("failed opening %s: %d",
504 PtoCstr(binkpc->cur_incoming_file.pfilename), error);
505 CtoPstr(binkpc->cur_incoming_file.pfilename);
506 goto failed_read;
507 }
508
509 len = binkpc->cur_incoming_file.size;
510 error = Allocate(binkpc->cur_incoming_file.frefnum, &len);
511 if (error) {
512 warn("failed setting %s to size %ld: %d",
513 PtoCstr(binkpc->cur_incoming_file.pfilename),
514 binkpc->cur_incoming_file.size, error);
515 CtoPstr(binkpc->cur_incoming_file.pfilename);
516 }
517 } else {
518 logger_printf("[binkp] Failed parsing M_FILE: %s",
519 binkpc->buf + 1);
520 }
521 break;
522 case BINKP_COMMAND_M_EOB:
523 binkpc->done_receiving = true;
524 break;
525 case BINKP_COMMAND_M_ERR:
526 logger_printf("[binkp] Error from remote: %s",
527 binkpc->buf + 1);
528 binkpc->done_receiving = binkpc->done_sending = true;
529 break;
530 case BINKP_COMMAND_M_BSY:
531 logger_printf("[binkp] Remote is busy: %s", binkpc->buf + 1);
532 binkpc->done_receiving = binkpc->done_sending = true;
533 break;
534 }
535 } else {
536 #ifdef BINKP_DEBUG
537 logger_printf("[binkp] Read %d data frame of file (%ld / %ld)",
538 binkpc->cur_frame.data_size,
539 binkpc->cur_incoming_file.data_read,
540 binkpc->cur_incoming_file.size);
541 #endif
542 if (binkpc->cur_incoming_file.data_read ==
543 binkpc->cur_incoming_file.size) {
544 #ifdef BINKP_DEBUG
545 logger_printf("[binkp] Done reading file %s (%ld)",
546 binkpc->cur_incoming_file.filename,
547 binkpc->cur_incoming_file.size);
548 #endif
549 FSClose(binkpc->cur_incoming_file.frefnum);
550 binkpc->cur_incoming_file.frefnum = 0;
551
552 len = snprintf(tmp, sizeof(tmp), "%s %lu %lu",
553 binkpc->cur_incoming_file.filename,
554 binkpc->cur_incoming_file.size,
555 binkpc->cur_incoming_file.mtime);
556 if (!binkp_send_frame(BINKP_COMMAND_M_GOT, tmp, len))
557 logger_printf("[binkp] Failed sending M_GOT %s",
558 binkpc->cur_incoming_file.filename);
559
560 binkpc->cur_incoming_file.filename[0] = '\0';
561 }
562 }
563
564 return true;
565
566 failed_read:
567 if (binkpc->cur_incoming_file.frefnum > 0) {
568 FSClose(binkpc->cur_incoming_file.frefnum);
569 binkpc->cur_incoming_file.frefnum = 0;
570 }
571 binkpc->done_receiving = binkpc->done_sending = true;
572 return false;
573 }
574
575 bool
576 binkp_login(void)
577 {
578 char command[50];
579 size_t len;
580
581 while (binkpc != NULL && binkp_read_frame())
582 ;
583
584 len = snprintf(command, sizeof(command), "SYS %s", db->config.name);
585 if (!binkp_send_frame(BINKP_COMMAND_M_NUL, command, len))
586 return false;
587
588 len = snprintf(command, sizeof(command), "LOC %s", db->config.location);
589 if (!binkp_send_frame(BINKP_COMMAND_M_NUL, command, len))
590 return false;
591
592 len = snprintf(command, sizeof(command), "NDL 14400,TCP,BINKP");
593 if (!binkp_send_frame(BINKP_COMMAND_M_NUL, command, len))
594 return false;
595
596 len = snprintf(command, sizeof(command), "VER Subtext/%s binkp/1.0",
597 get_version(false));
598 if (!binkp_send_frame(BINKP_COMMAND_M_NUL, command, len))
599 return false;
600
601 if (!binkp_send_frame(BINKP_COMMAND_M_ADR,
602 db->config.ftn_node_addr, strlen(db->config.ftn_node_addr)))
603 return false;
604
605 if (!binkp_send_frame(BINKP_COMMAND_M_PWD, db->config.binkp_password,
606 strlen(db->config.binkp_password)))
607 return false;
608
609 while (binkpc) {
610 if (!binkp_read_frame()) {
611 uthread_yield();
612 continue;
613 }
614
615 if (binkpc->cur_frame.type != BINKP_TYPE_COMMAND)
616 continue;
617
618 if (binkpc->cur_frame.command_id == BINKP_COMMAND_M_ERR) {
619 logger_printf("[binkp] Login as %s failed, disconnecting",
620 db->config.ftn_node_addr);
621 binkp_free();
622 return false;
623 }
624
625 if (binkpc->cur_frame.command_id == BINKP_COMMAND_M_OK) {
626 logger_printf("[binkp] Logged in successfully as %s",
627 db->config.ftn_node_addr);
628 return true;
629 }
630 }
631
632 return false;
633 }
634
635 void
636 binkp_toss_inbox(void)
637 {
638 CInfoPBRec cipbr = { 0 };
639 HFileInfo *fpb = (HFileInfo *)&cipbr;
640 DirInfo *dpb = (DirInfo *)&cipbr;
641 CMovePBRec cmpbr = { 0 };
642 Str255 file_name_c, path, done_path;
643 short dir_id, error, zret;
644 unsigned long started = Time, elapsed;
645 size_t data_size;
646 char *data;
647 bool bad;
648
649 binkp_fidopkt_skipped = 0;
650 binkp_fidopkt_imported = 0;
651 binkp_temp_fail = false;
652
653 fpb->ioVRefNum = 0;
654 fpb->ioNamePtr = (StringPtr)&binkp_inbox_dir;
655 error = PBGetCatInfo(&cipbr, false);
656 if (error) {
657 logger_printf("[binkp] PBGetCatInfo on binkp dir failed: %d",
658 error);
659 return;
660 }
661 dir_id = dpb->ioDrDirID;
662
663 fpb->ioNamePtr = (StringPtr)&file_name_c;
664 for (;;) {
665 file_name_c[0] = 0;
666 fpb->ioDirID = dir_id;
667 /*
668 * Keep requesting the first item in the directory since we're
669 * deleting or moving each file as we process it
670 */
671 fpb->ioFDirIndex = 1;
672
673 error = PBGetCatInfo(&cipbr, false);
674 if (error)
675 break;
676
677 PtoCstr(file_name_c);
678
679 PtoCstr(binkp_inbox_dir);
680 snprintf((char *)path, sizeof(path), "%s:%s",
681 binkp_inbox_dir, file_name_c);
682 logger_printf("[binkp] Tossing file %s", (char *)path);
683 CtoPstr(path);
684 CtoPstr(binkp_inbox_dir);
685
686 binkp_fidopkt_skipped = 0;
687 binkp_fidopkt_imported = 0;
688 bad = false;
689
690 if (zip_is_zip_file(path)) {
691 zret = zip_read_file(path, binkp_zip_decider,
692 binkp_fidopkt_processor);
693 if (zret == ZIP_NO_MEMORY)
694 binkp_temp_fail = true;
695 else if (zret != ZIP_OK) {
696 bad = true;
697 logger_printf("[binkp] Failed processing ZIP file %s: %d",
698 file_name_c, zret);
699 }
700 } else if (strstr((char *)file_name_c, ".pkt")) {
701 data_size = binkp_buffer_file(path, &data);
702 if (data_size == 0)
703 binkp_temp_fail = true;
704 else {
705 binkp_fidopkt_processor((char *)file_name_c,
706 (unsigned char *)data, data_size);
707 xfree(&data);
708 }
709 }
710
711 if (binkp_temp_fail) {
712 logger_printf("[binkp] Temporary failure during tossing, "
713 "aborting");
714 binkp_temp_fail = false;
715 return;
716 }
717
718 logger_printf("[binkp] %s: tossed %d, skipped %d",
719 (char *)file_name_c, binkp_fidopkt_imported,
720 binkp_fidopkt_skipped);
721
722 if (db->config.binkp_delete_done && !bad) {
723 if ((error = FSDelete(path, 0)) != 0) {
724 logger_printf("[binkp] Failed deleting inbox file %s: %d",
725 PtoCstr(path), error);
726 break;
727 }
728 } else {
729 if (bad) {
730 PtoCstr(binkp_bad_dir);
731 snprintf((char *)done_path, sizeof(done_path),
732 "%s:%s", binkp_bad_dir, file_name_c);
733 CtoPstr(done_path);
734 CtoPstr(binkp_bad_dir);
735 } else {
736 PtoCstr(binkp_done_dir);
737 snprintf((char *)done_path, sizeof(done_path),
738 "%s:%s", binkp_done_dir, file_name_c);
739 CtoPstr(done_path);
740 CtoPstr(binkp_done_dir);
741 }
742
743 try_rename:
744 cmpbr.ioNamePtr = path;
745 if (bad)
746 cmpbr.ioNewName = binkp_bad_dir;
747 else
748 cmpbr.ioNewName = binkp_done_dir;
749 cmpbr.ioDirID = 0;
750 cmpbr.ioNewDirID = 0;
751 cmpbr.ioVRefNum = 0;
752 error = PBCatMove(&cmpbr, false);
753 if (error == dupFNErr) {
754 if ((error = FSDelete(done_path, 0)) != 0) {
755 logger_printf("[binkp] Failed deleting %s: %d",
756 PtoCstr(done_path), error);
757 break;
758 }
759 goto try_rename;
760 } else if (error) {
761 logger_printf("[binkp] Failed moving %s to %s: %d",
762 PtoCstr(path), PtoCstr(done_path), error);
763 CtoPstr(done_path);
764 break;
765 }
766 }
767 }
768
769 if (binkp_fidopkt_skipped == 0 && binkp_fidopkt_imported == 0)
770 return;
771
772 elapsed = Time - started;
773 logger_printf("[binkp] Done tossing in %ld sec%s", elapsed,
774 elapsed == 1 ? "" : "s");
775 }
776
777 bool
778 binkp_zip_decider(char *filename, size_t size)
779 {
780 size_t flen = strlen(filename);
781
782 if (strcmp(filename + flen - 4, ".pkt") == 0)
783 return true;
784
785 return false;
786 }
787
788 void
789 binkp_fidopkt_processor(char *filename, unsigned char *data, size_t size)
790 {
791 struct fidopkt_message *msg = NULL;
792 struct fidopkt_header *header = NULL;
793 struct board *board = NULL;
794 short n, ret;
795
796 uthread_yield();
797
798 while (size != 0 && (msg = fidopkt_parse_message(filename, header,
799 (char **)&data, &size)) != NULL) {
800 if (msg->time)
801 /* convert to local time zone */
802 msg->time +=
803 (((long)db->config.timezone_utcoff * 60 * 60) / 100);
804 else
805 msg->time = Time;
806
807 if (msg->area[0]) {
808 for (n = 0; n < db->nboards; n++) {
809 if (db->boards[n].ftn_area[0] &&
810 strcasecmp(msg->area, db->boards[n].ftn_area) == 0) {
811 board = &db->boards[n];
812 break;
813 }
814 }
815
816 if (!board) {
817 #ifdef BINKP_DEBUG
818 logger_printf("[fidopkt] No local board for %s, skipping",
819 msg->area);
820 #endif
821 binkp_fidopkt_skipped++;
822 goto next_message;
823 }
824
825 ret = board_toss_ftn_message(board, msg);
826 } else
827 ret = mail_toss_ftn_message(msg);
828
829 if (ret == -1) {
830 binkp_temp_fail = true;
831 fidopkt_message_free(&msg);
832 break;
833 }
834 if (ret == 0)
835 binkp_fidopkt_skipped++;
836 else
837 binkp_fidopkt_imported++;
838
839 next_message:
840 if (header == NULL && size > 0) {
841 header = xmalloc(sizeof(msg->header));
842 if (header == NULL) {
843 fidopkt_message_free(&msg);
844 binkp_temp_fail = true;
845 break;
846 }
847 memcpy(header, &msg->header, sizeof(struct fidopkt_header));
848 }
849 fidopkt_message_free(&msg);
850 }
851
852 if (header)
853 xfree(&header);
854 }
855
856 size_t
857 binkp_buffer_file(Str255 path, char **data)
858 {
859 struct stat sb;
860 long count;
861 short error, frefnum;
862
863 if (FStat(path, &sb) != 0)
864 return 0;
865
866 *data = xmalloc(sb.st_size);
867 if (*data == NULL)
868 return 0;
869
870 error = FSOpen(path, 0, &frefnum);
871 if (error) {
872 xfree(data);
873 return 0;
874 }
875
876 count = sb.st_size;
877 error = FSRead(frefnum, &count, *data);
878 FSClose(frefnum);
879 if (error && error != eofErr) {
880 xfree(data);
881 return 0;
882 }
883
884 return count;
885 }
886
887 void
888 binkp_deliver_outbox(void)
889 {
890 char command[50];
891 CInfoPBRec cipbr = { 0 };
892 HFileInfo *fpb = (HFileInfo *)&cipbr;
893 DirInfo *dpb = (DirInfo *)&cipbr;
894 CMovePBRec cmpbr = { 0 };
895 Str255 file_name_c, path;
896 struct stat sb;
897 unsigned long started = Time, elapsed;
898 size_t len, data_size, fsize_left;
899 short dir_id, error, frefnum;
900 char *data = NULL;
901
902 fpb->ioVRefNum = 0;
903 fpb->ioNamePtr = (StringPtr)&binkp_outbox_dir;
904 error = PBGetCatInfo(&cipbr, false);
905 if (error) {
906 logger_printf("[binkp] PBGetCatInfo on binkp outbox failed: %d",
907 error);
908 return;
909 }
910 dir_id = dpb->ioDrDirID;
911
912 fpb->ioNamePtr = (StringPtr)&file_name_c;
913 for (;;) {
914 file_name_c[0] = 0;
915 fpb->ioDirID = dir_id;
916 /*
917 * Keep requesting the first item in the directory since we're
918 * deleting each file as we process it
919 */
920 fpb->ioFDirIndex = 1;
921
922 error = PBGetCatInfo(&cipbr, false);
923 if (error)
924 break;
925
926 if (data == NULL) {
927 data = xmalloc(binkpc->buf_size);
928 if (data == NULL) {
929 logger_printf("[binkp] Failed allocating outbound buffer "
930 "%lu", binkpc->buf_size);
931 return;
932 }
933 }
934
935 PtoCstr(file_name_c);
936
937 PtoCstr(binkp_outbox_dir);
938 snprintf((char *)path, sizeof(path), "%s:%s",
939 binkp_outbox_dir, file_name_c);
940 CtoPstr(path);
941 CtoPstr(binkp_outbox_dir);
942
943 FStat(path, &sb);
944
945 logger_printf("[binkp] Sending file %s size %lu", file_name_c,
946 sb.st_size);
947
948 /* just send 1 as unix mtime */
949 len = snprintf(command, sizeof(command), "%s %lu 1 0", file_name_c,
950 sb.st_size);
951 if (!binkp_send_frame(BINKP_COMMAND_M_FILE, command, len))
952 goto done;
953
954 error = FSOpen(path, 0, &frefnum);
955 if (error) {
956 warn("binkp: failed opening %s: %d", PtoCstr(path), error);
957 goto done;
958 }
959
960 for (fsize_left = sb.st_size; fsize_left != 0; ) {
961 /* leave 2 bytes for size */
962 len = MIN(fsize_left, binkpc->buf_size - 2);
963
964 error = FSRead(frefnum, &len, data);
965 if (error && error != eofErr) {
966 warn("binkp: error reading from %s: %d", PtoCstr(path),
967 error);
968 goto done;
969 }
970
971 if (!binkp_send_frame(BINKP_DATA, data, len))
972 goto done;
973
974 fsize_left -= len;
975 }
976
977 FSClose(frefnum);
978 frefnum = 0;
979
980 /* wait for frame */
981 while (binkpc != NULL && !binkp_read_frame()) {
982 uthread_yield();
983 continue;
984 }
985
986 if (!binkpc) {
987 logger_printf("[binkp] Connection lost waiting for M_GOT");
988 goto done;
989 }
990
991 if (binkpc->cur_frame.type != BINKP_TYPE_COMMAND ||
992 binkpc->cur_frame.command_id != BINKP_COMMAND_M_GOT) {
993 logger_printf("[binkp] Received unexpected response to "
994 "sending file");
995 goto done;
996 }
997
998 logger_printf("[binkp] Successfully sent file %s", file_name_c);
999
1000 error = FSDelete(path, 0);
1001 if (error)
1002 /* this must be fatal so we don't keep trying this file */
1003 panic("[binkp] Failed deleting outbox file %s: %d",
1004 PtoCstr(path), error);
1005 }
1006
1007 if (!binkp_send_frame(BINKP_COMMAND_M_EOB, "", 0))
1008 logger_printf("[binkp] Failed sending M_EOB");
1009
1010 if (binkpc)
1011 binkpc->done_sending = true;
1012
1013 done:
1014 if (data)
1015 xfree(&data);
1016 if (frefnum)
1017 FSClose(frefnum);
1018 }
1019
1020 bool
1021 binkp_packets_in_outbox(void)
1022 {
1023 CInfoPBRec cipbr = { 0 };
1024 HFileInfo *fpb = (HFileInfo *)&cipbr;
1025 DirInfo *dpb = (DirInfo *)&cipbr;
1026 Str255 file_name_c, path;
1027 short dir_id, error;
1028
1029 fpb->ioVRefNum = 0;
1030 fpb->ioNamePtr = (StringPtr)&binkp_outbox_dir;
1031 error = PBGetCatInfo(&cipbr, false);
1032 if (error) {
1033 logger_printf("[binkp] PBGetCatInfo on binkp outbox failed: %d",
1034 error);
1035 return;
1036 }
1037 dir_id = dpb->ioDrDirID;
1038
1039 fpb->ioNamePtr = (StringPtr)&file_name_c;
1040 file_name_c[0] = 0;
1041 fpb->ioDirID = dir_id;
1042 fpb->ioFDirIndex = 1;
1043
1044 error = PBGetCatInfo(&cipbr, false);
1045 if (error)
1046 return false;
1047
1048 return true;
1049 }
1050
1051 bool
1052 binkp_scan_message(struct fidopkt_message *msg)
1053 {
1054 Str255 path;
1055 char filename[32];
1056 char *buf = NULL;
1057 short error, frefnum;
1058 size_t size, asize;
1059 bool ret = false;
1060
1061 size = fidopkt_encode_message(msg, &buf,
1062 db->config.ftn_hub_pkt_password, db->config.timezone_utcoff);
1063 if (size == 0)
1064 return false;
1065
1066 snprintf(filename, sizeof(filename), "%c%07lx.pkt",
1067 msg->area[0] ? 'b' : 'm', msg->msgid.id & 0x0fffffff);
1068
1069 PtoCstr(binkp_outbox_dir);
1070 snprintf((char *)path, sizeof(path), "%s:%s", binkp_outbox_dir,
1071 filename);
1072 CtoPstr(binkp_outbox_dir);
1073 CtoPstr(path);
1074
1075 error = Create(path, 0, SUBTEXT_CREATOR, FTN_SPOOL_PACKET_TYPE);
1076 if (error == dupFNErr) {
1077 warn("binkp: collision saving packet to %s", PtoCstr(path));
1078 goto done;
1079 }
1080 if (error) {
1081 warn("binkp: failed creating %s: %d", PtoCstr(path), error);
1082 goto done;
1083 }
1084
1085 error = FSOpen(path, 0, &frefnum);
1086 if (error) {
1087 warn("binkp: failed opening newly created %s: %d", PtoCstr(path),
1088 error);
1089 goto done;
1090 }
1091
1092 asize = size;
1093 error = Allocate(frefnum, &asize);
1094 if (error) {
1095 warn("binkp: failed setting %s to size %ld: %d", PtoCstr(path),
1096 size, error);
1097 goto done;
1098 }
1099
1100 error = FSWrite(frefnum, &size, buf);
1101 if (error) {
1102 logger_printf("[binkp] Error writing %lu to %s: %d",
1103 size, PtoCstr(path), error);
1104 goto done;
1105 }
1106
1107 ret = true;
1108
1109 done:
1110 if (frefnum)
1111 FSClose(frefnum);
1112 xfree(&buf);
1113 return ret;
1114 }