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 | } |