AmendHub

Download

jcs

/

subtext

/

fidopkt.c

 

(View History)

jcs   fidopkt: Fix some off-by-ones Latest amendment: 638 on 2026-09-22

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