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