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