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