AmendHub

Download

jcs

/

subtext

/

fidopkt.c

 

(View History)

jcs   fidopkt: Fix return value when aborting parse early Latest amendment: 572 on 2023-12-02

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, val;
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 NULL;
217 }
218
219 if ((val = GET_U16(buf + FIDOPKT_PKTTYPE)) != 2) {
220 logger_printf("[fidopkt] Not a FidoNet packet (0x%x)", val);
221 return NULL;
222 }
223
224 if (GET_U16(buf + FIDOPKT_BAUD) == 2) {
225 logger_printf("[fidopkt] 5d format not supported");
226 return NULL;
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 NULL;
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 NULL;
260 }
261
262 if ((val = GET_U16(buf + FIDOPKT_MSG_PKTTYPE)) != 2) {
263 logger_printf("[fidopkt] Header packet type != 2 (0x%x)", val);
264 return NULL;
265 }
266
267 attr = GET_U16(buf + FIDOPKT_MSG_ATTR);
268
269 /* prefer message header values over packet header values */
270 if (GET_U16(buf + FIDOPKT_MSG_ORIGNODE) != 0) {
271 orig_address.node = GET_U16(buf + FIDOPKT_MSG_ORIGNODE);
272 dest_address.node = GET_U16(buf + FIDOPKT_MSG_DESTNODE);
273 }
274 if (GET_U16(buf + FIDOPKT_MSG_ORIGNET) != 0) {
275 orig_address.net = GET_U16(buf + FIDOPKT_MSG_ORIGNET);
276 dest_address.net = GET_U16(buf + FIDOPKT_MSG_DESTNET);
277 }
278
279 buf += FIDOPKT_MSG_HEADER_SIZE;
280 len -= FIDOPKT_MSG_HEADER_SIZE;
281
282 ret = xmalloczero(sizeof(struct fidopkt_message));
283 if (ret == NULL) {
284 logger_printf("[fidopkt] malloc(%lu) failed",
285 sizeof(struct fidopkt_message));
286 goto parse_fail;
287 }
288
289 if (header == NULL) {
290 ret->header.orig = orig_address;
291 ret->header.dest = dest_address;
292 } else {
293 memcpy(&ret->header, header, sizeof(ret->header));
294 }
295 ret->attr = attr;
296
297 fidopkt_read_until(&buf, &len, '\0', sizeof(line), line, true);
298 if (len == 0) {
299 logger_printf("[fidopkt] Short read");
300 goto parse_fail;
301 }
302 if (sscanf(line, "%d %5s %d %d:%d:%d", &dday, (char *)&dmon, &dyear,
303 &dhour, &dmin, &dsec) != 6) {
304 logger_printf("[fidopkt] Couldn't parse date \"%s\"", line);
305 goto parse_fail;
306 }
307
308 memset(&tm, 0, sizeof(tm));
309 tm.tm_sec = dsec;
310 tm.tm_min = dmin;
311 tm.tm_hour = dhour;
312 tm.tm_mday = dday;
313 tm.tm_year = 100 + dyear;
314 tm.tm_mon = -1;
315 for (n = 0; n < nitems(months); n++) {
316 if (strcmp(months[n], dmon) == 0) {
317 tm.tm_mon = n;
318 break;
319 }
320 }
321 if (tm.tm_mon == -1) {
322 logger_printf("[fidopkt] Couldn't parse month \"%s\"", dmon);
323 goto parse_fail;
324 }
325 #ifdef FIDOPKT_DEBUG
326 strlcpy(datetime, line, sizeof(datetime));
327 #endif
328
329 fidopkt_read_until(&buf, &len, '\0', sizeof(ret->to), ret->to, true);
330 if (len == 0) {
331 logger_printf("[fidopkt] Short read during \"to\"");
332 goto parse_fail;
333 }
334 fidopkt_read_until(&buf, &len, '\0', sizeof(ret->from), ret->from, true);
335 if (len == 0) {
336 logger_printf("[fidopkt] Short read during \"from\"");
337 goto parse_fail;
338 }
339 fidopkt_read_until(&buf, &len, '\0', sizeof(ret->subject), ret->subject,
340 true);
341 if (len == 0) {
342 logger_printf("[fidopkt] Short read during \"subject\"");
343 goto parse_fail;
344 }
345
346 /* body will be terminated by a null, packet with two nulls */
347 for (msg_size = 0; msg_size < len - 1; msg_size++) {
348 if (buf[msg_size] == '\0') {
349 msg_size++;
350 break;
351 }
352 }
353
354 len -= msg_size;
355
356 msg_size_limited = msg_size;
357 if (db->config.ftn_max_tossed_message_size != 0 &&
358 msg_size > db->config.ftn_max_tossed_message_size) {
359 msg_size_limited = db->config.ftn_max_tossed_message_size;
360 logger_printf("[fidopkt] message size %ld > limit %ld, truncating",
361 msg_size, msg_size_limited);
362 }
363
364 ret->body = xmalloc(msg_size_limited);
365 if (ret->body == NULL) {
366 logger_printf("[fidopkt] malloc(%lu) failed", msg_size_limited);
367 goto parse_fail;
368 }
369
370 body_len = 0;
371 tear = false;
372 while (msg_size) {
373 llen = fidopkt_read_until(&buf, &msg_size, '\r', sizeof(line),
374 line, false);
375
376 if (body_len == 0 && strncmp(line, "AREA:", 5) == 0) {
377 line[llen] = '\0';
378 strlcpy(ret->area, line + 5, sizeof(ret->area));
379 continue;
380 }
381
382 /*
383 * We are not terminating long lines now, so don't use unbounded
384 * string comparisons.
385 */
386 if (memcmp(line, "--- ", 4) == 0 || memcmp(line, "---\0", 4) == 0) {
387 /* tear line */
388 tear = true;
389 } else if (line[0] == 0x01) {
390 /* kludge line */
391 line[llen] = '\0';
392 if (sscanf(line + 1, "TZUTC: %d", &dutc) == 1) {
393 tzoff = ((long)dutc * 60 * 60) / 100;
394 #ifdef FIDOPKT_DEBUG
395 strlcpy(tz, line + 1 + 7, sizeof(tz));
396 #endif
397 } else if (strncmp(line + 1, "MSGID: ", 7) == 0) {
398 /*
399 * FTS-0009.001 says: ^AMSGID: origaddr serialno
400 * serialno is an 8 char hexadecimal string unique to the
401 * sending node
402 */
403 strlcpy(ret->msgid_orig, line + 1 + 7,
404 sizeof(ret->msgid_orig));
405 } else if (strncmp(line + 1, "REPLY: ", 7) == 0)
406 strlcpy(ret->reply, line + 1 + 7, sizeof(ret->reply));
407 } else if (tear && strncmp(line, " * Origin: ", 11) == 0) {
408 line[llen] = '\0';
409 strlcpy(ret->origin, line + 11, sizeof(ret->origin));
410 } else if (!tear) {
411 if (line[0] == ' ' && line[1] == ' ')
412 continue;
413 if (body_len == 0 && (line[0] == '\n' || line[0] == '\r'))
414 continue;
415 if (llen + body_len > msg_size_limited)
416 continue;
417 if (line[llen - 1] == '\0') {
418 memcpy(ret->body + body_len, line, llen - 1);
419 body_len += llen - 1;
420 ret->body[body_len++] = '\r';
421 ret->body[body_len++] = '\n';
422 } else {
423 /* long line */
424 memcpy(ret->body + body_len, line, llen);
425 body_len += llen;
426 }
427 }
428 }
429
430 /* trim trailing newlines */
431 while (body_len > 1) {
432 if (ret->body[body_len - 1] == '\n' &&
433 ret->body[body_len - 2] == '\r')
434 body_len -= 2;
435 else if (ret->body[body_len - 1] == '\r')
436 body_len -= 1;
437 else
438 break;
439 }
440 ret->body[body_len] = '\0';
441 ret->body_len = body_len;
442
443 ret->time = mktime(&tm);
444 if (ret->time == -1) {
445 logger_printf("[fidopkt] Parsed date but mktime failed");
446 goto parse_fail;
447 }
448 /* make time UTC */
449 ret->time -= tzoff;
450
451 if (ret->msgid_orig[0] == '\0') {
452 /* no msgid, invent a stable one */
453 snprintf(ret->msgid_orig, sizeof(ret->msgid_orig),
454 "nomsgid@%u:%u/%u.%u %08lx",
455 ret->header.orig.zone, ret->header.orig.net,
456 ret->header.orig.node, ret->header.orig.point,
457 ret->time);
458 }
459
460 if (!fidopkt_parse_msgid(ret)) {
461 logger_printf("[fidopkt] Failed parsing msgid \"%s\"",
462 ret->msgid_orig);
463 goto parse_fail;
464 }
465
466 #ifdef FIDOPKT_DEBUG
467 logger_printf("[fidopkt] ID: %u:%u/%u.%u %08lx (%s)", ret->msgid.zone,
468 ret->msgid.net, ret->msgid.node, ret->msgid.point, ret->msgid.id,
469 ret->msgid_orig);
470 logger_printf("[fidopkt] Orig Node: %u:%u/%u.%u",
471 ret->header.orig.zone, ret->header.orig.net, ret->header.orig.node,
472 ret->header.orig.point);
473 logger_printf("[fidopkt] Dest Node: %u:%u/%u.%u",
474 ret->header.dest.zone, ret->header.dest.net, ret->header.dest.node,
475 ret->header.dest.point);
476 logger_printf("[fidopkt] Origin: %s", ret->origin);
477 logger_printf("[fidopkt] Reply To: %s", ret->reply);
478 logger_printf("[fidopkt] Area: %s", ret->area);
479 logger_printf("[fidopkt] Attr: 0x%x", ret->attr);
480 logger_printf("[fidopkt] From: %s", ret->from);
481 logger_printf("[fidopkt] To: %s", ret->to);
482 logger_printf("[fidopkt] Subject: %s", ret->subject);
483
484 llen = strlcpy(ct, ctime(&ret->time), sizeof(ct));
485 ct[llen - 1] = '\0';
486 logger_printf("[fidopkt] Date: %s UTC (%s %s%s) (%ul)", ct, datetime,
487 (tzoff >= 0 ? "+" : ""), tz, ret->time);
488 logger_printf("[fidopkt] Message:");
489
490 lastbreak = ret->body;
491 for (n = 0; n <= ret->body_len; n++) {
492 if (ret->body[n] == '\n' || n == ret->body_len) {
493 was = ret->body[n];
494 ret->body[n] = '\0';
495 logger_printf("[fidopkt] %s", lastbreak);
496 ret->body[n] = was;
497 lastbreak = ret->body + n + 1;
498 }
499 }
500 #endif
501
502 /* packets are terminated by two nulls */
503 if (len == 2 && buf[0] == '\0') {
504 buf++;
505 len--;
506 }
507 if (len == 1 && buf[0] == '\0') {
508 buf++;
509 len--;
510 }
511
512 *bufp = buf;
513 *lenp = len;
514 return ret;
515
516 parse_fail:
517 fidopkt_message_free(&ret);
518 return NULL;
519 }
520
521 size_t
522 fidopkt_encode_message(struct fidopkt_message *msg, char **ret_buf,
523 char *pkt_password, short tzoff)
524 {
525 static char scratch[64];
526 struct tm *tm;
527 char *buf, *vers, *versdot;
528 size_t len, off, buf_size, subject_len, n;
529 short versnum;
530
531 subject_len = strlen(msg->subject);
532
533 buf_size = FIDOPKT_HEADER_SIZE + FIDOPKT_MSG_HEADER_SIZE +
534 subject_len + msg->body_len + 512;
535 buf = xmalloczero(buf_size);
536 if (buf == NULL) {
537 logger_printf("[fidopkt] Failed allocating %ld", buf_size);
538 return 0;
539 }
540
541 tm = localtime(&msg->time);
542
543 PUT_U16(buf + FIDOPKT_ORIGZONE, msg->header.orig.zone);
544 PUT_U16(buf + FIDOPKT_QORIGZONE, msg->header.orig.zone);
545 PUT_U16(buf + FIDOPKT_ORIGNET, msg->header.orig.net);
546 PUT_U16(buf + FIDOPKT_ORIGNODE, msg->header.orig.node);
547 memcpy(buf + FIDOPKT_PASSWORD, pkt_password, strlen(pkt_password));
548
549 PUT_U16(buf + FIDOPKT_DESTZONE, msg->header.dest.zone);
550 PUT_U16(buf + FIDOPKT_QDESTZONE, msg->header.dest.zone);
551 PUT_U16(buf + FIDOPKT_DESTNET, msg->header.dest.net);
552 PUT_U16(buf + FIDOPKT_DESTNODE, msg->header.dest.node);
553
554 PUT_U32(buf + FIDOPKT_PRODDATA, 'SUBT');
555
556 PUT_U16(buf + FIDOPKT_YEAR, tm->tm_year + 1900);
557 PUT_U16(buf + FIDOPKT_MONTH, tm->tm_mon + 1);
558 PUT_U16(buf + FIDOPKT_DAY, tm->tm_mday);
559 PUT_U16(buf + FIDOPKT_HOUR, tm->tm_hour);
560 PUT_U16(buf + FIDOPKT_MINUTE, tm->tm_min);
561 PUT_U16(buf + FIDOPKT_SECOND, tm->tm_sec);
562
563 PUT_U16(buf + FIDOPKT_PKTTYPE, 2);
564
565 buf[FIDOPKT_CWVALIDCOPY + 1] = 1;
566 buf[FIDOPKT_CAPABILWORD] = 1;
567
568 vers = get_version(false);
569 versdot = strchr(vers, '.');
570 if (versdot) {
571 versdot[0] = '\0';
572 versnum = atoi(vers);
573 buf[FIDOPKT_REVMAJOR] = versnum;
574 }
575
576 buf[FIDOPKT_PRODCODELOW] = 0xfe; /* FTA-1005.003 */
577 buf[FIDOPKT_PRODCODEHIGH] = 0; /* until we get a product code */
578
579 PUT_U16(buf + FIDOPKT_HEADER_SIZE + FIDOPKT_MSG_PKTTYPE, 2);
580
581 PUT_U16(buf + FIDOPKT_HEADER_SIZE + FIDOPKT_MSG_DESTNET,
582 msg->header.dest.net);
583 PUT_U16(buf + FIDOPKT_HEADER_SIZE + FIDOPKT_MSG_DESTNODE,
584 msg->header.dest.node);
585
586 PUT_U16(buf + FIDOPKT_HEADER_SIZE + FIDOPKT_MSG_ORIGNET,
587 msg->header.orig.net);
588 PUT_U16(buf + FIDOPKT_HEADER_SIZE + FIDOPKT_MSG_ORIGNODE,
589 msg->header.orig.node);
590
591 PUT_U16(buf + FIDOPKT_HEADER_SIZE + FIDOPKT_MSG_ATTR, msg->attr);
592
593 off = FIDOPKT_HEADER_SIZE + FIDOPKT_MSG_HEADER_SIZE;
594
595 /* date, null-terminated, with two spaces between year and hour */
596 if (!grow_to_fit(&buf, &buf_size, off, 21, 21))
597 goto fail;
598 off += strftime(buf + off, 20, "%d %b %y %H:%M:%S", tm);
599 off++;
600
601 /* to, null-terminated */
602 len = strlen(msg->to);
603 if (!grow_to_fit(&buf, &buf_size, off, len + 1, len + 1))
604 goto fail;
605 memcpy(buf + off, msg->to, len);
606 off += len + 1;
607
608 /* from, null-terminated */
609 len = strlen(msg->from);
610 if (!grow_to_fit(&buf, &buf_size, off, len + 1, len + 1))
611 goto fail;
612 memcpy(buf + off, msg->from, len);
613 off += len + 1;
614
615 /* subject, null-terminated */
616 if (!grow_to_fit(&buf, &buf_size, off, subject_len + 1,
617 subject_len + 1))
618 goto fail;
619 memcpy(buf + off, msg->subject, subject_len);
620 off += subject_len + 1;
621
622 /* optional area (non-kludge) \r-terminated, no space after colon */
623 if (msg->area[0] != '\0') {
624 len = snprintf(scratch, sizeof(scratch), "AREA:%s\r", msg->area);
625 if (len > sizeof(scratch))
626 goto fail;
627 if (!grow_to_fit(&buf, &buf_size, off, len, len))
628 goto fail;
629 memcpy(buf + off, scratch, len);
630 off += len;
631 }
632
633 if (msg->area[0] == '\0') {
634 /* kludge line: INTL to from, \r-terminated, but not on net mail */
635 len = snprintf(scratch, sizeof(scratch),
636 "%cINTL %d:%d/%d %d:%d/%d\r",
637 0x1, msg->header.dest.zone, msg->header.dest.net,
638 msg->header.dest.node, msg->header.orig.zone,
639 msg->header.orig.net, msg->header.orig.node);
640 if (len > sizeof(scratch))
641 goto fail;
642 if (!grow_to_fit(&buf, &buf_size, off, len, len))
643 goto fail;
644 memcpy(buf + off, scratch, len);
645 off += len;
646 }
647
648 /* kludge line: TZOFF tzoff, \r-terminated */
649 len = snprintf(scratch, sizeof(scratch), "%cTZUTC: %+05d\r",
650 0x1, tzoff);
651 if (len > sizeof(scratch))
652 goto fail;
653 if (!grow_to_fit(&buf, &buf_size, off, len, len))
654 goto fail;
655 memcpy(buf + off, scratch, len);
656 off += len;
657
658 /* kludge line: MSGID zone:net/node id, \r-terminated */
659 len = snprintf(scratch, sizeof(scratch), "%cMSGID: %d:%d/%d %08lx\r",
660 0x1, msg->msgid.zone, msg->msgid.net, msg->msgid.node, msg->msgid.id);
661 if (len > sizeof(scratch))
662 goto fail;
663 if (!grow_to_fit(&buf, &buf_size, off, len, len))
664 goto fail;
665 memcpy(buf + off, scratch, len);
666 off += len;
667
668 /* kludge line: REPLY orig-msgid, \r-terminated */
669 if (msg->reply[0] != '\0') {
670 len = snprintf(scratch, sizeof(scratch), "%cREPLY: %s\r",
671 0x1, msg->reply);
672 if (len > sizeof(scratch))
673 goto fail;
674 if (!grow_to_fit(&buf, &buf_size, off, len, len))
675 goto fail;
676 memcpy(buf + off, scratch, len);
677 off += len;
678 }
679
680 /* kludge line: PID spam, \r-terminated */
681 len = snprintf(scratch, sizeof(scratch), "%cPID: Subtext %s\r",
682 0x1, get_version(false));
683 if (len > sizeof(scratch))
684 goto fail;
685 if (!grow_to_fit(&buf, &buf_size, off, len, len))
686 goto fail;
687 memcpy(buf + off, scratch, len);
688 off += len;
689
690 if (!grow_to_fit(&buf, &buf_size, off, msg->body_len + 7,
691 msg->body_len + 7))
692 goto fail;
693
694 /*
695 * FidoNet messages can only have \r line-breaks and we probably used
696 * \r\n during the message editor, so strip out \n.
697 */
698 for (n = 0; n < msg->body_len; n++) {
699 if (n == msg->body_len - 1 && msg->body[n] == '\r')
700 /* supress trailing \r, we'll add two next */
701 continue;
702
703 if (msg->body[n] == '\0')
704 break;
705
706 if (msg->body[n] == '\n') {
707 if (msg->body[n - 1] == '\r')
708 continue;
709 buf[off++] = '\r';
710 } else
711 buf[off++] = msg->body[n];
712 }
713
714 off += sprintf(buf + off, "\r");
715
716 if (msg->origin[0]) {
717 /* add tear line */
718 off += sprintf(buf + off, "\r--- \r");
719
720 /* " * Origin: " */
721 len = 11 + strlen(msg->origin) + 2;
722
723 if (!grow_to_fit(&buf, &buf_size, off, len, len))
724 goto fail;
725 off += sprintf(buf + off, " * Origin: %s\r", msg->origin);
726 }
727
728 /*
729 * Messages are terminated by a null, and the whole packet is
730 * terminated by two nulls.
731 */
732 if (!grow_to_fit(&buf, &buf_size, off, 3, 3))
733 goto fail;
734 buf[off++] = '\0';
735 buf[off++] = '\0';
736 buf[off++] = '\0';
737
738 *ret_buf = buf;
739 return off;
740
741 fail:
742 logger_printf("[fidopkt] Failed growing buf of size %ld", buf_size);
743 if (buf != NULL)
744 xfree(&buf);
745 return 0;
746 }
747
748 void
749 fidopkt_message_free(struct fidopkt_message **msgp)
750 {
751 struct fidopkt_message *msg = (struct fidopkt_message *)*msgp;
752
753 if (msg->body)
754 xfree(&msg->body);
755 xfree(&msg);
756 *msgp = NULL;
757 }