AmendHub

Download

jcs

/

subtext

/

fidopkt.c

 

(View History)

jcs   fidopkt: Tweak log messages Latest amendment: 420 on 2023-03-15

1 /*
2 * FTN packet parser
3 * http://wiki.synchro.net/ref:fidonet_packets
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 <stdlib.h>
22 #include <string.h>
23 #include <time.h>
24
25 #include "fidopkt.h"
26 #include "logger.h"
27 #include "util.h"
28 #include "zip.h" /* for GET_U* */
29
30 /* #define FIDOPKT_DEBUG */
31
32 #define FIDOPKT_ORIGNODE 0
33 #define FIDOPKT_DESTNODE 2
34 #define FIDOPKT_YEAR 4
35 #define FIDOPKT_MONTH 6
36 #define FIDOPKT_DAY 8
37 #define FIDOPKT_HOUR 10
38 #define FIDOPKT_MINUTE 12
39 #define FIDOPKT_SECOND 14
40
41 #define FIDOPKT_BAUD 16
42 #define FIDOPKT_PKTTYPE 18
43
44 #define FIDOPKT_ORIGNET 20
45 #define FIDOPKT_DESTNET 22
46 #define FIDOPKT_PRODCODELOW 24
47 #define FIDOPKT_REVMAJOR 25
48 #define FIDOPKT_PASSWORD 26
49
50 #define FIDOPKT_QORIGZONE 34
51 #define FIDOPKT_QDESTZONE 36
52
53 #define FIDOPKT_AUXNET 38
54 #define FIDOPKT_CWVALIDCOPY 40
55 #define FIDOPKT_PRODCODEHIGH 42
56 #define FIDOPKT_REVMINOR 43
57 #define FIDOPKT_CAPABILWORD 44
58
59 #define FIDOPKT_ORIGZONE 46
60 #define FIDOPKT_DESTZONE 48
61 #define FIDOPKT_ORIGPOINT 50
62 #define FIDOPKT_DESTPOINT 52
63 #define FIDOPKT_PRODDATA 54
64
65 #define FIDOPKT_HEADER_SIZE 58
66
67 #define FIDOPKT_MSG_PKTTYPE 0
68 #define FIDOPKT_MSG_ORIGNODE 2
69 #define FIDOPKT_MSG_DESTNODE 4
70 #define FIDOPKT_MSG_ORIGNET 6
71 #define FIDOPKT_MSG_DESTNET 8
72 #define FIDOPKT_MSG_ATTR 10
73 #define FIDOPKT_MSG_COST 12
74
75 #define FIDOPKT_MSG_HEADER_SIZE 14
76
77 static const char *months[] = {
78 "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct",
79 "Nov", "Dec",
80 };
81
82 size_t fidopkt_read_until(char **buf, size_t *buf_len, char delim,
83 size_t max_len, char *ret, bool terminate);
84 bool fidopkt_parse_msgid(struct fidopkt_message *msg);
85
86 size_t
87 fidopkt_read_until(char **buf, size_t *buf_len, char delim,
88 size_t max_len, char *ret, bool terminate)
89 {
90 char ch;
91 size_t retpos = 0;
92 bool found = false;
93
94 if (*buf_len < max_len)
95 max_len = *buf_len;
96
97 while (max_len != 0) {
98 ch = *(*buf)++;
99 ret[retpos++] = (ch == delim ? '\0' : ch);
100 max_len--;
101 (*buf_len)--;
102 if (ch == delim || ch == '\0') {
103 found = true;
104 break;
105 }
106 }
107 if (terminate || found)
108 ret[retpos - 1] = '\0';
109
110 return retpos;
111 }
112
113 bool
114 fidopkt_parse_address(char *str, struct fidopkt_address *address)
115 {
116 /* 21:1/137.1 */
117 if (sscanf(str, "%u:%u/%u.%u", &address->zone, &address->net,
118 &address->node, &address->point) == 4)
119 ;
120 /* 21:1/137 */
121 else if (sscanf(str, "%u:%u/%u", &address->zone, &address->net,
122 &address->node) == 3)
123 address->point = 0;
124 else {
125 address->zone = address->net = address->node = address->point = 0;
126 return false;
127 }
128
129 return true;
130 }
131
132 bool
133 fidopkt_parse_msgid(struct fidopkt_message *msg)
134 {
135 static char garbage[50];
136 unsigned short zone, net, node, point = 0;
137 unsigned long id;
138
139 /* 37246.fsxnet_fsx_gen@21:1/137.1 28583dba */
140 if (sscanf(msg->msgid_orig, "%49[^@]@%u:%u/%u.%u %lx",
141 &garbage, &zone, &net, &node, &point, &id) == 6)
142 goto found;
143
144 /* 37246.fsxnet_fsx_gen@21:1/137 28583dba */
145 if (sscanf(msg->msgid_orig, "%49[^@]@%u:%u/%u %lx",
146 &garbage, &zone, &net, &node, &id) == 5)
147 goto found;
148
149 /* 21:3/110.10@fsxnet 05996db9 */
150 if (sscanf(msg->msgid_orig, "%u:%u/%u.%u@%49s %lx",
151 &zone, &net, &node, &point, &garbage, &id) == 6)
152 goto found;
153
154 /* 21:3/110@fsxnet 05996db9 */
155 if (sscanf(msg->msgid_orig, "%u:%u/%u@%49s %lx",
156 &zone, &net, &node, &garbage, &id) == 5)
157 goto found;
158
159 /* 21:3/102.1 63f277aa */
160 if (sscanf(msg->msgid_orig, "%u:%u/%u.%u %lx",
161 &zone, &net, &node, &point, &id) == 5)
162 goto found;
163
164 /* 21:3/102 63f277aa */
165 if (sscanf(msg->msgid_orig, "%u:%u/%u %lx",
166 &zone, &net, &node, &id) == 4)
167 goto found;
168
169 return false;
170
171 found:
172 msg->msgid.id = id;
173 msg->msgid.zone = zone;
174 msg->msgid.net = net;
175 msg->msgid.node = node;
176 msg->msgid.point = point;
177
178 #ifdef FIDOPKT_DEBUG
179 logger_printf("[fidopkt] %s -> zone:%u net:%u node:%u point:%u id:%lu",
180 msg->msgid_orig, msg->msgid.zone, msg->msgid.net,
181 msg->msgid.node, msg->msgid.point, msg->msgid.id);
182 #endif
183 return true;
184 }
185
186 struct fidopkt_message *
187 fidopkt_parse_message(char *packet_filename, struct fidopkt_header *header,
188 char **bufp, size_t *lenp)
189 {
190 static char line[100];
191 #ifdef FIDOPKT_DEBUG
192 static char datetime[100], tz[50], ct[50];
193 char *lastbreak, was;
194 #endif
195 char *buf;
196 struct fidopkt_message *ret;
197 struct tm tm = { 0 };
198 struct fidopkt_address orig_address, dest_address;
199 size_t body_len, len, llen, msg_size;
200 long tzoff = 0;
201 char dmon[6];
202 short dday, dyear, dhour, dmin, dsec, dutc, attr, n;
203 bool tear;
204
205 #ifdef FIDOPKT_DEBUG
206 logger_printf("[fidopkt] Parsing %s ==========", packet_filename);
207 #endif
208
209 buf = *bufp;
210 len = *lenp;
211
212 if (header == NULL) {
213 if (len < FIDOPKT_HEADER_SIZE) {
214 logger_printf("[fidopkt] Packet too small (%ld)", len);
215 return;
216 }
217
218 if (GET_U16(buf + FIDOPKT_PKTTYPE) != 2) {
219 logger_printf("[fidopkt] Not a FidoNet packet");
220 return;
221 }
222
223 if (GET_U16(buf + FIDOPKT_BAUD) == 2) {
224 logger_printf("[fidopkt] 5d format not supported");
225 return;
226 }
227
228 if (buf[FIDOPKT_CWVALIDCOPY] != buf[FIDOPKT_CAPABILWORD + 1] ||
229 buf[FIDOPKT_CWVALIDCOPY + 1] != buf[FIDOPKT_CAPABILWORD]) {
230 logger_printf("[fidopkt] Not in 4d format");
231 return;
232 }
233
234 if (GET_U16(buf + FIDOPKT_ORIGZONE) != 0) {
235 orig_address.zone = GET_U16(buf + FIDOPKT_ORIGZONE);
236 dest_address.zone = GET_U16(buf + FIDOPKT_DESTZONE);
237 } else if (GET_U16(buf + FIDOPKT_QORIGZONE) != 0) {
238 orig_address.zone = GET_U16(buf + FIDOPKT_QORIGZONE);
239 dest_address.zone = GET_U16(buf + FIDOPKT_QDESTZONE);
240 } else {
241 orig_address.zone = 0;
242 dest_address.zone = 0;
243 }
244
245 orig_address.net = GET_U16(buf + FIDOPKT_ORIGNET);
246 orig_address.node = GET_U16(buf + FIDOPKT_ORIGNODE);
247 orig_address.point = GET_U16(buf + FIDOPKT_ORIGPOINT);
248 dest_address.net = GET_U16(buf + FIDOPKT_DESTNET);
249 dest_address.node = GET_U16(buf + FIDOPKT_DESTNODE);
250 dest_address.point = GET_U16(buf + FIDOPKT_DESTPOINT);
251
252 buf += FIDOPKT_HEADER_SIZE;
253 len -= FIDOPKT_HEADER_SIZE;
254 }
255
256 if (len < FIDOPKT_MSG_HEADER_SIZE) {
257 logger_printf("[fidopkt] Message header too short");
258 return;
259 }
260
261 if (GET_U16(buf + FIDOPKT_MSG_PKTTYPE) != 2) {
262 logger_printf("[fidopkt] Header packet type != 2");
263 return;
264 }
265
266 attr = GET_U16(buf + FIDOPKT_MSG_ATTR);
267
268 buf += FIDOPKT_MSG_HEADER_SIZE;
269 len -= FIDOPKT_MSG_HEADER_SIZE;
270
271 ret = xmalloczero(sizeof(struct fidopkt_message));
272 if (ret == NULL) {
273 logger_printf("[fidopkt] malloc(%lu) failed",
274 sizeof(struct fidopkt_message));
275 goto parse_fail;
276 }
277
278 if (header == NULL) {
279 ret->header.orig = orig_address;
280 ret->header.dest = dest_address;
281 } else {
282 memcpy(&ret->header, header, sizeof(ret->header));
283 }
284 ret->attr = attr;
285
286 fidopkt_read_until(&buf, &len, '\0', sizeof(line), line, true);
287 if (len == 0) {
288 logger_printf("[fidopkt] Short read");
289 goto parse_fail;
290 }
291 if (sscanf(line, "%d %5s %d %d:%d:%d", &dday, (char *)&dmon, &dyear,
292 &dhour, &dmin, &dsec) != 6) {
293 logger_printf("[fidopkt] Couldn't parse date \"%s\"", line);
294 goto parse_fail;
295 }
296
297 memset(&tm, 0, sizeof(tm));
298 tm.tm_sec = dsec;
299 tm.tm_min = dmin;
300 tm.tm_hour = dhour;
301 tm.tm_mday = dday;
302 tm.tm_year = 100 + dyear;
303 tm.tm_mon = -1;
304 for (n = 0; n < nitems(months); n++) {
305 if (strcmp(months[n], dmon) == 0) {
306 tm.tm_mon = n;
307 break;
308 }
309 }
310 if (tm.tm_mon == -1) {
311 logger_printf("[fidopkt] Couldn't parse month \"%s\"", dmon);
312 goto parse_fail;
313 }
314 #ifdef FIDOPKT_DEBUG
315 strlcpy(datetime, line, sizeof(datetime));
316 #endif
317
318 fidopkt_read_until(&buf, &len, '\0', sizeof(ret->to), ret->to, true);
319 if (len == 0) {
320 logger_printf("[fidopkt] Short read during \"to\"");
321 goto parse_fail;
322 }
323 fidopkt_read_until(&buf, &len, '\0', sizeof(ret->from), ret->from, true);
324 if (len == 0) {
325 logger_printf("[fidopkt] Short read during \"from\"");
326 goto parse_fail;
327 }
328 fidopkt_read_until(&buf, &len, '\0', sizeof(ret->subject), ret->subject,
329 true);
330 if (len == 0) {
331 logger_printf("[fidopkt] Short read during \"subject\"");
332 goto parse_fail;
333 }
334
335 /* body will be terminated by a null, packet with two nulls */
336 for (msg_size = 0; msg_size < len - 1; msg_size++) {
337 if (buf[msg_size] == '\0') {
338 msg_size++;
339 break;
340 }
341 }
342
343 len -= msg_size;
344
345 ret->body = xmalloc(msg_size);
346 if (ret->body == NULL) {
347 logger_printf("[fidopkt] malloc(%lu) failed", msg_size);
348 goto parse_fail;
349 }
350
351 body_len = 0;
352 tear = false;
353 while (msg_size) {
354 llen = fidopkt_read_until(&buf, &msg_size, '\r', sizeof(line), line,
355 false);
356
357 if (body_len == 0 && strncmp(line, "AREA:", 5) == 0) {
358 line[llen] = '\0';
359 strlcpy(ret->area, line + 5, sizeof(ret->area));
360 continue;
361 }
362
363 /*
364 * We are not terminating long lines now, so don't use unbounded
365 * string comparisons.
366 */
367 if (memcmp(line, "--- ", 4) == 0 || memcmp(line, "---\0", 4) == 0) {
368 /* tear line */
369 tear = true;
370 } else if (line[0] == 0x01) {
371 /* kludge line */
372 line[llen] = '\0';
373 if (sscanf(line + 1, "TZUTC: %d", &dutc) == 1) {
374 tzoff = ((long)dutc * 60 * 60) / 100;
375 #ifdef FIDOPKT_DEBUG
376 strlcpy(tz, line + 1 + 7, sizeof(tz));
377 #endif
378 } else if (strncmp(line + 1, "MSGID: ", 7) == 0) {
379 /*
380 * FTS-0009.001 says: ^AMSGID: origaddr serialno
381 * serialno is an 8 char hexadecimal string unique to the
382 * sending node
383 */
384 strlcpy(ret->msgid_orig, line + 1 + 7,
385 sizeof(ret->msgid_orig));
386 } else if (strncmp(line + 1, "REPLY: ", 7) == 0)
387 strlcpy(ret->reply, line + 1 + 7, sizeof(ret->reply));
388 } else if (tear && strncmp(line, " * Origin: ", 11) == 0) {
389 line[llen] = '\0';
390 strlcpy(ret->origin, line + 11, sizeof(ret->origin));
391 } else if (!tear) {
392 if (line[0] == ' ' && line[1] == ' ')
393 continue;
394 if (body_len == 0 && (line[0] == '\n' || line[0] == '\r'))
395 continue;
396 if (line[llen - 1] == '\0') {
397 memcpy(ret->body + body_len, line, llen - 1);
398 body_len += llen - 1;
399 ret->body[body_len++] = '\r';
400 ret->body[body_len++] = '\n';
401 } else {
402 /* long line */
403 memcpy(ret->body + body_len, line, llen);
404 body_len += llen;
405 }
406 }
407 }
408
409 /* trim trailing newlines */
410 while (body_len > 1) {
411 if (ret->body[body_len - 1] == '\n' &&
412 ret->body[body_len - 2] == '\r')
413 body_len -= 2;
414 else if (ret->body[body_len - 1] == '\r')
415 body_len -= 1;
416 else
417 break;
418 }
419 ret->body[body_len] = '\0';
420 ret->body_len = body_len;
421
422 ret->time = mktime(&tm);
423 if (ret->time == -1) {
424 logger_printf("[fidopkt] Parsed date but mktime failed");
425 goto parse_fail;
426 }
427 /* make time UTC */
428 ret->time -= tzoff;
429
430 if (ret->msgid_orig[0] == '\0') {
431 /* no msgid, invent a stable one */
432 snprintf(ret->msgid_orig, sizeof(ret->msgid_orig),
433 "nomsgid@%u:%u/%u.%u %08lx",
434 ret->header.orig.zone, ret->header.orig.net,
435 ret->header.orig.node, ret->header.orig.point,
436 ret->time);
437 }
438
439 if (!fidopkt_parse_msgid(ret)) {
440 logger_printf("[fidopkt] Failed parsing msgid \"%s\"",
441 ret->msgid_orig);
442 goto parse_fail;
443 }
444
445 #ifdef FIDOPKT_DEBUG
446 logger_printf("[fidopkt] ID: %u:%u/%u.%u %08lx (%s)", ret->msgid.zone,
447 ret->msgid.net, ret->msgid.node, ret->msgid.point, ret->msgid.id,
448 ret->msgid_orig);
449 logger_printf("[fidopkt] Orig Node: %u:%u/%u.%u",
450 ret->header.orig.zone, ret->header.orig.net, ret->header.orig.node,
451 ret->header.orig.point);
452 logger_printf("[fidopkt] Dest Node: %u:%u/%u.%u",
453 ret->header.dest.zone, ret->header.dest.net, ret->header.dest.node,
454 ret->header.dest.point);
455 logger_printf("[fidopkt] Origin: %s", ret->origin);
456 logger_printf("[fidopkt] Reply To: %s", ret->reply);
457 logger_printf("[fidopkt] Area: %s", ret->area);
458 logger_printf("[fidopkt] Attr: 0x%x", ret->attr);
459 logger_printf("[fidopkt] From: %s", ret->from);
460 logger_printf("[fidopkt] To: %s", ret->to);
461 logger_printf("[fidopkt] Subject: %s", ret->subject);
462
463 llen = strlcpy(ct, ctime(&ret->time), sizeof(ct));
464 ct[llen - 1] = '\0';
465 logger_printf("[fidopkt] Date: %s UTC (%s %s%s) (%ul)", ct, datetime,
466 (tzoff >= 0 ? "+" : ""), tz, ret->time);
467 logger_printf("[fidopkt] Message:");
468
469 lastbreak = ret->body;
470 for (n = 0; n <= ret->body_len; n++) {
471 if (ret->body[n] == '\n' || n == ret->body_len) {
472 was = ret->body[n];
473 ret->body[n] = '\0';
474 logger_printf("[fidopkt] %s", lastbreak);
475 ret->body[n] = was;
476 lastbreak = ret->body + n + 1;
477 }
478 }
479 #endif
480
481 /* packets are terminated by two nulls */
482 if (len == 2 && buf[0] == '\0') {
483 buf++;
484 len--;
485 }
486 if (len == 1 && buf[0] == '\0') {
487 buf++;
488 len--;
489 }
490
491 *bufp = buf;
492 *lenp = len;
493 return ret;
494
495 parse_fail:
496 fidopkt_message_free(&ret);
497 return NULL;
498 }
499
500 size_t
501 fidopkt_encode_message(struct fidopkt_message *msg, char **ret_buf,
502 char *pkt_password, short tzoff)
503 {
504 static char scratch[30];
505 struct tm *tm;
506 char *buf;
507 size_t len, off, buf_size, subject_len, n;
508
509 subject_len = strlen(msg->subject);
510
511 buf_size = FIDOPKT_HEADER_SIZE + FIDOPKT_MSG_HEADER_SIZE +
512 subject_len + msg->body_len + 512;
513 buf = xmalloczero(buf_size);
514
515 tm = localtime(&msg->time);
516
517 PUT_U16(buf + FIDOPKT_ORIGZONE, msg->header.orig.zone);
518 PUT_U16(buf + FIDOPKT_QORIGZONE, msg->header.orig.zone);
519 PUT_U16(buf + FIDOPKT_ORIGNET, msg->header.orig.net);
520 PUT_U16(buf + FIDOPKT_ORIGNODE, msg->header.orig.node);
521 memcpy(buf + FIDOPKT_PASSWORD, pkt_password, strlen(pkt_password));
522
523 PUT_U16(buf + FIDOPKT_DESTZONE, msg->header.dest.zone);
524 PUT_U16(buf + FIDOPKT_QDESTZONE, msg->header.dest.zone);
525 PUT_U16(buf + FIDOPKT_DESTNET, msg->header.dest.net);
526 PUT_U16(buf + FIDOPKT_DESTNODE, msg->header.dest.node);
527
528 PUT_U32(buf + FIDOPKT_PRODDATA, 'SUBT');
529
530 PUT_U16(buf + FIDOPKT_YEAR, tm->tm_year + 1900);
531 PUT_U16(buf + FIDOPKT_MONTH, tm->tm_mon + 1);
532 PUT_U16(buf + FIDOPKT_DAY, tm->tm_mday);
533 PUT_U16(buf + FIDOPKT_HOUR, tm->tm_hour);
534 PUT_U16(buf + FIDOPKT_MINUTE, tm->tm_min);
535 PUT_U16(buf + FIDOPKT_SECOND, tm->tm_sec);
536
537 PUT_U16(buf + FIDOPKT_PKTTYPE, 2);
538
539 buf[FIDOPKT_CWVALIDCOPY + 1] = 1;
540 buf[FIDOPKT_CAPABILWORD] = 1;
541
542 buf[FIDOPKT_PRODCODELOW] = 0;
543 buf[FIDOPKT_PRODCODEHIGH] = 0xfe; /* FTA-1005.003 */
544
545 PUT_U16(buf + FIDOPKT_HEADER_SIZE + FIDOPKT_MSG_PKTTYPE, 2);
546
547 PUT_U16(buf + FIDOPKT_HEADER_SIZE + FIDOPKT_MSG_DESTNET,
548 msg->header.dest.net);
549 PUT_U16(buf + FIDOPKT_HEADER_SIZE + FIDOPKT_MSG_DESTNODE,
550 msg->header.dest.node);
551
552 PUT_U16(buf + FIDOPKT_HEADER_SIZE + FIDOPKT_MSG_ORIGNET,
553 msg->header.orig.net);
554 PUT_U16(buf + FIDOPKT_HEADER_SIZE + FIDOPKT_MSG_ORIGNODE,
555 msg->header.orig.node);
556
557 PUT_U16(buf + FIDOPKT_HEADER_SIZE + FIDOPKT_MSG_ATTR, msg->attr);
558
559 off = FIDOPKT_HEADER_SIZE + FIDOPKT_MSG_HEADER_SIZE;
560
561 /* date, null-terminated, with two spaces between year and hour */
562 if (!grow_to_fit(&buf, &buf_size, off, 21, 21))
563 goto fail;
564 off += strftime(buf + off, 20, "%d %b %y %H:%M:%S", tm);
565 off++;
566
567 /* to, null-terminated */
568 len = strlen(msg->to);
569 if (!grow_to_fit(&buf, &buf_size, off, len + 1, len + 1))
570 goto fail;
571 memcpy(buf + off, msg->to, len);
572 off += len + 1;
573
574 /* from, null-terminated */
575 len = strlen(msg->from);
576 if (!grow_to_fit(&buf, &buf_size, off, len + 1, len + 1))
577 goto fail;
578 memcpy(buf + off, msg->from, len);
579 off += len + 1;
580
581 /* subject, null-terminated */
582 if (!grow_to_fit(&buf, &buf_size, off, subject_len + 1,
583 subject_len + 1))
584 goto fail;
585 memcpy(buf + off, msg->subject, subject_len);
586 off += subject_len + 1;
587
588 /* optional area (non-kludge) \r-terminated, no space after colon */
589 if (msg->area[0] != '\0') {
590 len = snprintf(scratch, sizeof(scratch), "AREA:%s\r", msg->area);
591 if (len > sizeof(scratch))
592 goto fail;
593 if (!grow_to_fit(&buf, &buf_size, off, len, len))
594 goto fail;
595 memcpy(buf + off, scratch, len);
596 off += len;
597 }
598
599 if (msg->area[0] == '\0') {
600 /* kludge line: INTL to from, \r-terminated, but not on net mail */
601 len = snprintf(scratch, sizeof(scratch),
602 "%cINTL %d:%d/%d %d:%d/%d\r",
603 0x1, msg->header.dest.zone, msg->header.dest.net,
604 msg->header.dest.node, msg->header.orig.zone,
605 msg->header.orig.net, msg->header.orig.node);
606 if (len > sizeof(scratch))
607 goto fail;
608 if (!grow_to_fit(&buf, &buf_size, off, len, len))
609 goto fail;
610 memcpy(buf + off, scratch, len);
611 off += len;
612 }
613
614 /* kludge line: TZOFF tzoff, \r-terminated */
615 len = snprintf(scratch, sizeof(scratch), "%cTZUTC: %+05d\r",
616 0x1, tzoff);
617 if (len > sizeof(scratch))
618 goto fail;
619 if (!grow_to_fit(&buf, &buf_size, off, len, len))
620 goto fail;
621 memcpy(buf + off, scratch, len);
622 off += len;
623
624 /* kludge line: MSGID zone:net/node id, \r-terminated */
625 len = snprintf(scratch, sizeof(scratch), "%cMSGID: %d:%d/%d %08lx\r",
626 0x1, msg->msgid.zone, msg->msgid.net, msg->msgid.node, msg->msgid.id);
627 if (len > sizeof(scratch))
628 goto fail;
629 if (!grow_to_fit(&buf, &buf_size, off, len, len))
630 goto fail;
631 memcpy(buf + off, scratch, len);
632 off += len;
633
634 /* kludge line: REPLY orig-msgid, \r-terminated */
635 if (msg->reply[0] != '\0') {
636 len = snprintf(scratch, sizeof(scratch), "%cREPLY: %s\r",
637 0x1, msg->reply);
638 if (len > sizeof(scratch))
639 goto fail;
640 if (!grow_to_fit(&buf, &buf_size, off, len, len))
641 goto fail;
642 memcpy(buf + off, scratch, len);
643 off += len;
644 }
645
646 /* kludge line: PID spam, \r-terminated */
647 len = snprintf(scratch, sizeof(scratch), "%cPID: Subtext %s\r",
648 0x1, get_version(false));
649 if (len > sizeof(scratch))
650 goto fail;
651 if (!grow_to_fit(&buf, &buf_size, off, len, len))
652 goto fail;
653 memcpy(buf + off, scratch, len);
654 off += len;
655
656 if (!grow_to_fit(&buf, &buf_size, off, msg->body_len + 7,
657 msg->body_len + 7))
658 goto fail;
659 /*
660 * FidoNet messages can only have \r line-breaks and we probably used
661 * \r\n during the message editor, so strip out \n.
662 */
663 for (n = 0; n < msg->body_len; n++) {
664 if (n == msg->body_len - 1 && msg->body[n] == '\r')
665 /* supress trailing \r, we'll add two next */
666 continue;
667
668 if (msg->body[n] == '\n') {
669 if (msg->body[n - 1] == '\r')
670 continue;
671 buf[off++] = '\r';
672 } else
673 buf[off++] = msg->body[n];
674 }
675 /* add tear line */
676 off += sprintf(buf + off, "\r\r--- \r");
677
678 if (msg->origin[0]) {
679 len = 11 + strlen(msg->origin) + 2;
680
681 if (!grow_to_fit(&buf, &buf_size, off, len, len))
682 goto fail;
683 off += sprintf(buf + off, " * Origin: %s\r", msg->origin);
684 }
685
686 /* packets are terminated by two nulls */
687 if (!grow_to_fit(&buf, &buf_size, off, 2, 2))
688 goto fail;
689 buf[off++] = '\0';
690 buf[off++] = '\0';
691
692 *ret_buf = buf;
693 return off;
694
695 fail:
696 if (buf != NULL)
697 xfree(&buf);
698 return 0;
699 }
700
701 void
702 fidopkt_message_free(struct fidopkt_message **msgp)
703 {
704 struct fidopkt_message *msg = (struct fidopkt_message *)*msgp;
705
706 if (msg->body)
707 xfree(&msg->body);
708 xfree(&msg);
709 *msgp = NULL;
710 }