AmendHub

Download

jcs

/

subtext

/

fidopkt.c

 

(View History)

jcs   fidopkt: Crank scratch buffer size in fidopkt_encode_message Latest amendment: 484 on 2023-04-18

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