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