/* * FTN packet parser * http://wiki.synchro.net/ref:fidonet_packets * * Copyright (c) 2023 joshua stein * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include "fidopkt.h" #include "db.h" #include "logger.h" #include "util.h" #include "zip.h" /* for GET_U* */ /* #define FIDOPKT_DEBUG */ #define FIDOPKT_ORIGNODE 0 #define FIDOPKT_DESTNODE 2 #define FIDOPKT_YEAR 4 #define FIDOPKT_MONTH 6 #define FIDOPKT_DAY 8 #define FIDOPKT_HOUR 10 #define FIDOPKT_MINUTE 12 #define FIDOPKT_SECOND 14 #define FIDOPKT_BAUD 16 #define FIDOPKT_PKTTYPE 18 #define FIDOPKT_ORIGNET 20 #define FIDOPKT_DESTNET 22 #define FIDOPKT_PRODCODELOW 24 #define FIDOPKT_REVMAJOR 25 #define FIDOPKT_PASSWORD 26 #define FIDOPKT_QORIGZONE 34 #define FIDOPKT_QDESTZONE 36 #define FIDOPKT_AUXNET 38 #define FIDOPKT_CWVALIDCOPY 40 #define FIDOPKT_PRODCODEHIGH 42 #define FIDOPKT_REVMINOR 43 #define FIDOPKT_CAPABILWORD 44 #define FIDOPKT_ORIGZONE 46 #define FIDOPKT_DESTZONE 48 #define FIDOPKT_ORIGPOINT 50 #define FIDOPKT_DESTPOINT 52 #define FIDOPKT_PRODDATA 54 #define FIDOPKT_HEADER_SIZE 58 #define FIDOPKT_MSG_PKTTYPE 0 #define FIDOPKT_MSG_ORIGNODE 2 #define FIDOPKT_MSG_DESTNODE 4 #define FIDOPKT_MSG_ORIGNET 6 #define FIDOPKT_MSG_DESTNET 8 #define FIDOPKT_MSG_ATTR 10 #define FIDOPKT_MSG_COST 12 #define FIDOPKT_MSG_HEADER_SIZE 14 static const char *months[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", }; size_t fidopkt_read_until(char **buf, size_t *buf_len, char delim, size_t max_len, char *ret, bool terminate); bool fidopkt_parse_msgid(struct fidopkt_message *msg); size_t fidopkt_read_until(char **buf, size_t *buf_len, char delim, size_t max_len, char *ret, bool terminate) { char ch; size_t retpos = 0; bool found = false; if (*buf_len < max_len) max_len = *buf_len; while (max_len != 0) { ch = *(*buf)++; ret[retpos++] = (ch == delim ? '\0' : ch); max_len--; (*buf_len)--; if (ch == delim || ch == '\0') { found = true; break; } } if (terminate || found) ret[retpos - 1] = '\0'; return retpos; } bool fidopkt_parse_address(char *str, struct fidopkt_address *address) { /* 21:1/137.1 */ if (sscanf(str, "%u:%u/%u.%u", &address->zone, &address->net, &address->node, &address->point) == 4) ; /* 21:1/137 */ else if (sscanf(str, "%u:%u/%u", &address->zone, &address->net, &address->node) == 3) address->point = 0; else { address->zone = address->net = address->node = address->point = 0; return false; } return true; } bool fidopkt_parse_msgid(struct fidopkt_message *msg) { static char garbage[50]; unsigned short zone, net, node, point = 0; unsigned long id; /* 37246.fsxnet_fsx_gen@21:1/137.1 28583dba */ if (sscanf(msg->msgid_orig, "%49[^@]@%u:%u/%u.%u %lx", &garbage, &zone, &net, &node, &point, &id) == 6) goto found; /* 37246.fsxnet_fsx_gen@21:1/137 28583dba */ if (sscanf(msg->msgid_orig, "%49[^@]@%u:%u/%u %lx", &garbage, &zone, &net, &node, &id) == 5) goto found; /* 21:3/110.10@fsxnet 05996db9 */ if (sscanf(msg->msgid_orig, "%u:%u/%u.%u@%49s %lx", &zone, &net, &node, &point, &garbage, &id) == 6) goto found; /* 21:3/110@fsxnet 05996db9 */ if (sscanf(msg->msgid_orig, "%u:%u/%u@%49s %lx", &zone, &net, &node, &garbage, &id) == 5) goto found; /* 21:3/102.1 63f277aa */ if (sscanf(msg->msgid_orig, "%u:%u/%u.%u %lx", &zone, &net, &node, &point, &id) == 5) goto found; /* 21:3/102 63f277aa */ if (sscanf(msg->msgid_orig, "%u:%u/%u %lx", &zone, &net, &node, &id) == 4) goto found; return false; found: msg->msgid.id = id; msg->msgid.zone = zone; msg->msgid.net = net; msg->msgid.node = node; msg->msgid.point = point; #ifdef FIDOPKT_DEBUG logger_printf("[fidopkt] %s -> zone:%u net:%u node:%u point:%u id:%lu", msg->msgid_orig, msg->msgid.zone, msg->msgid.net, msg->msgid.node, msg->msgid.point, msg->msgid.id); #endif return true; } struct fidopkt_message * fidopkt_parse_message(char *packet_filename, struct fidopkt_header *header, char **bufp, size_t *lenp) { static char line[100]; #ifdef FIDOPKT_DEBUG static char datetime[100], tz[50], ct[50]; char *lastbreak, was; #endif char *buf; struct fidopkt_message *ret; struct tm tm = { 0 }; struct fidopkt_address orig_address, dest_address; size_t body_len, len, llen, msg_size, msg_size_limited; long tzoff = 0; char dmon[6]; short dday, dyear, dhour, dmin, dsec, dutc, attr, n, val; bool tear; #ifdef FIDOPKT_DEBUG logger_printf("[fidopkt] Parsing %s ==========", packet_filename); #endif buf = *bufp; len = *lenp; if (header == NULL) { if (len < FIDOPKT_HEADER_SIZE) { logger_printf("[fidopkt] Packet too small (%ld)", len); return NULL; } if ((val = GET_U16(buf + FIDOPKT_PKTTYPE)) != 2) { logger_printf("[fidopkt] Not a FidoNet packet (0x%x)", val); return NULL; } if (GET_U16(buf + FIDOPKT_BAUD) == 2) { logger_printf("[fidopkt] 5d format not supported"); return NULL; } if (buf[FIDOPKT_CWVALIDCOPY] != buf[FIDOPKT_CAPABILWORD + 1] || buf[FIDOPKT_CWVALIDCOPY + 1] != buf[FIDOPKT_CAPABILWORD]) { logger_printf("[fidopkt] Not in 4d format"); return NULL; } if (GET_U16(buf + FIDOPKT_ORIGZONE) != 0) { orig_address.zone = GET_U16(buf + FIDOPKT_ORIGZONE); dest_address.zone = GET_U16(buf + FIDOPKT_DESTZONE); } else if (GET_U16(buf + FIDOPKT_QORIGZONE) != 0) { orig_address.zone = GET_U16(buf + FIDOPKT_QORIGZONE); dest_address.zone = GET_U16(buf + FIDOPKT_QDESTZONE); } else { orig_address.zone = 0; dest_address.zone = 0; } orig_address.net = GET_U16(buf + FIDOPKT_ORIGNET); orig_address.node = GET_U16(buf + FIDOPKT_ORIGNODE); orig_address.point = GET_U16(buf + FIDOPKT_ORIGPOINT); dest_address.net = GET_U16(buf + FIDOPKT_DESTNET); dest_address.node = GET_U16(buf + FIDOPKT_DESTNODE); dest_address.point = GET_U16(buf + FIDOPKT_DESTPOINT); buf += FIDOPKT_HEADER_SIZE; len -= FIDOPKT_HEADER_SIZE; } if (len < FIDOPKT_MSG_HEADER_SIZE) { logger_printf("[fidopkt] Message header too short"); return NULL; } if ((val = GET_U16(buf + FIDOPKT_MSG_PKTTYPE)) != 2) { logger_printf("[fidopkt] Header packet type != 2 (0x%x)", val); return NULL; } attr = GET_U16(buf + FIDOPKT_MSG_ATTR); /* prefer message header values over packet header values */ if (GET_U16(buf + FIDOPKT_MSG_ORIGNODE) != 0) { orig_address.node = GET_U16(buf + FIDOPKT_MSG_ORIGNODE); dest_address.node = GET_U16(buf + FIDOPKT_MSG_DESTNODE); } if (GET_U16(buf + FIDOPKT_MSG_ORIGNET) != 0) { orig_address.net = GET_U16(buf + FIDOPKT_MSG_ORIGNET); dest_address.net = GET_U16(buf + FIDOPKT_MSG_DESTNET); } buf += FIDOPKT_MSG_HEADER_SIZE; len -= FIDOPKT_MSG_HEADER_SIZE; ret = xmalloczero(sizeof(struct fidopkt_message)); if (ret == NULL) { logger_printf("[fidopkt] malloc(%lu) failed", sizeof(struct fidopkt_message)); goto parse_fail; } if (header == NULL) { ret->header.orig = orig_address; ret->header.dest = dest_address; } else { memcpy(&ret->header, header, sizeof(ret->header)); } ret->attr = attr; fidopkt_read_until(&buf, &len, '\0', sizeof(line), line, true); if (len == 0) { logger_printf("[fidopkt] Short read"); goto parse_fail; } if (sscanf(line, "%d %5s %d %d:%d:%d", &dday, (char *)&dmon, &dyear, &dhour, &dmin, &dsec) != 6) { logger_printf("[fidopkt] Couldn't parse date \"%s\"", line); goto parse_fail; } memset(&tm, 0, sizeof(tm)); tm.tm_sec = dsec; tm.tm_min = dmin; tm.tm_hour = dhour; tm.tm_mday = dday; tm.tm_year = 100 + dyear; tm.tm_mon = -1; for (n = 0; n < nitems(months); n++) { if (strcmp(months[n], dmon) == 0) { tm.tm_mon = n; break; } } if (tm.tm_mon == -1) { logger_printf("[fidopkt] Couldn't parse month \"%s\"", dmon); goto parse_fail; } #ifdef FIDOPKT_DEBUG strlcpy(datetime, line, sizeof(datetime)); #endif fidopkt_read_until(&buf, &len, '\0', sizeof(ret->to), ret->to, true); if (len == 0) { logger_printf("[fidopkt] Short read during \"to\""); goto parse_fail; } fidopkt_read_until(&buf, &len, '\0', sizeof(ret->from), ret->from, true); if (len == 0) { logger_printf("[fidopkt] Short read during \"from\""); goto parse_fail; } fidopkt_read_until(&buf, &len, '\0', sizeof(ret->subject), ret->subject, true); if (len == 0) { logger_printf("[fidopkt] Short read during \"subject\""); goto parse_fail; } /* body will be terminated by a null, packet with two nulls */ for (msg_size = 0; msg_size < len - 1; msg_size++) { if (buf[msg_size] == '\0') { msg_size++; break; } } len -= msg_size; msg_size_limited = msg_size; if (db->config.ftn_max_tossed_message_size != 0 && msg_size > db->config.ftn_max_tossed_message_size) { msg_size_limited = db->config.ftn_max_tossed_message_size; logger_printf("[fidopkt] message size %ld > limit %ld, truncating", msg_size, msg_size_limited); } ret->body = xmalloc(msg_size_limited); if (ret->body == NULL) { logger_printf("[fidopkt] malloc(%lu) failed", msg_size_limited); goto parse_fail; } body_len = 0; tear = false; while (msg_size) { llen = fidopkt_read_until(&buf, &msg_size, '\r', sizeof(line), line, false); if (body_len == 0 && strncmp(line, "AREA:", 5) == 0) { line[llen] = '\0'; strlcpy(ret->area, line + 5, sizeof(ret->area)); continue; } /* * We are not terminating long lines now, so don't use unbounded * string comparisons. */ if (memcmp(line, "--- ", 4) == 0 || memcmp(line, "---\0", 4) == 0) { /* tear line */ tear = true; } else if (line[0] == 0x01) { /* kludge line */ line[llen] = '\0'; if (sscanf(line + 1, "TZUTC: %d", &dutc) == 1) { tzoff = ((long)dutc * 60 * 60) / 100; #ifdef FIDOPKT_DEBUG strlcpy(tz, line + 1 + 7, sizeof(tz)); #endif } else if (strncmp(line + 1, "MSGID: ", 7) == 0) { /* * FTS-0009.001 says: ^AMSGID: origaddr serialno * serialno is an 8 char hexadecimal string unique to the * sending node */ strlcpy(ret->msgid_orig, line + 1 + 7, sizeof(ret->msgid_orig)); } else if (strncmp(line + 1, "REPLY: ", 7) == 0) strlcpy(ret->reply, line + 1 + 7, sizeof(ret->reply)); } else if (tear && strncmp(line, " * Origin: ", 11) == 0) { line[llen] = '\0'; strlcpy(ret->origin, line + 11, sizeof(ret->origin)); } else if (!tear) { if (line[0] == ' ' && line[1] == ' ') continue; if (body_len == 0 && (line[0] == '\n' || line[0] == '\r')) continue; if (llen + body_len > msg_size_limited) continue; if (line[llen - 1] == '\0') { memcpy(ret->body + body_len, line, llen - 1); body_len += llen - 1; ret->body[body_len++] = '\r'; ret->body[body_len++] = '\n'; } else { /* long line */ memcpy(ret->body + body_len, line, llen); body_len += llen; } } } /* trim trailing newlines */ while (body_len > 1) { if (ret->body[body_len - 1] == '\n' && ret->body[body_len - 2] == '\r') body_len -= 2; else if (ret->body[body_len - 1] == '\r') body_len -= 1; else break; } ret->body[body_len] = '\0'; ret->body_len = body_len; ret->time = mktime(&tm); if (ret->time == -1) { logger_printf("[fidopkt] Parsed date but mktime failed"); goto parse_fail; } /* make time UTC */ ret->time -= tzoff; if (ret->msgid_orig[0] == '\0') { /* no msgid, invent a stable one */ snprintf(ret->msgid_orig, sizeof(ret->msgid_orig), "nomsgid@%u:%u/%u.%u %08lx", ret->header.orig.zone, ret->header.orig.net, ret->header.orig.node, ret->header.orig.point, ret->time); } if (!fidopkt_parse_msgid(ret)) { logger_printf("[fidopkt] Failed parsing msgid \"%s\"", ret->msgid_orig); goto parse_fail; } #ifdef FIDOPKT_DEBUG logger_printf("[fidopkt] ID: %u:%u/%u.%u %08lx (%s)", ret->msgid.zone, ret->msgid.net, ret->msgid.node, ret->msgid.point, ret->msgid.id, ret->msgid_orig); logger_printf("[fidopkt] Orig Node: %u:%u/%u.%u", ret->header.orig.zone, ret->header.orig.net, ret->header.orig.node, ret->header.orig.point); logger_printf("[fidopkt] Dest Node: %u:%u/%u.%u", ret->header.dest.zone, ret->header.dest.net, ret->header.dest.node, ret->header.dest.point); logger_printf("[fidopkt] Origin: %s", ret->origin); logger_printf("[fidopkt] Reply To: %s", ret->reply); logger_printf("[fidopkt] Area: %s", ret->area); logger_printf("[fidopkt] Attr: 0x%x", ret->attr); logger_printf("[fidopkt] From: %s", ret->from); logger_printf("[fidopkt] To: %s", ret->to); logger_printf("[fidopkt] Subject: %s", ret->subject); llen = strlcpy(ct, ctime(&ret->time), sizeof(ct)); ct[llen - 1] = '\0'; logger_printf("[fidopkt] Date: %s UTC (%s %s%s) (%ul)", ct, datetime, (tzoff >= 0 ? "+" : ""), tz, ret->time); logger_printf("[fidopkt] Message:"); lastbreak = ret->body; for (n = 0; n <= ret->body_len; n++) { if (ret->body[n] == '\n' || n == ret->body_len) { was = ret->body[n]; ret->body[n] = '\0'; logger_printf("[fidopkt] %s", lastbreak); ret->body[n] = was; lastbreak = ret->body + n + 1; } } #endif /* packets are terminated by two nulls */ if (len == 2 && buf[0] == '\0') { buf++; len--; } if (len == 1 && buf[0] == '\0') { buf++; len--; } *bufp = buf; *lenp = len; return ret; parse_fail: fidopkt_message_free(&ret); return NULL; } size_t fidopkt_encode_message(struct fidopkt_message *msg, char **ret_buf, char *pkt_password, short tzoff) { static char scratch[64]; struct tm *tm; char *buf, *vers, *versdot; size_t len, off, buf_size, subject_len, n; short versnum; subject_len = strlen(msg->subject); buf_size = FIDOPKT_HEADER_SIZE + FIDOPKT_MSG_HEADER_SIZE + subject_len + msg->body_len + 512; buf = xmalloczero(buf_size); if (buf == NULL) { logger_printf("[fidopkt] Failed allocating %ld", buf_size); return 0; } tm = localtime(&msg->time); PUT_U16(buf + FIDOPKT_ORIGZONE, msg->header.orig.zone); PUT_U16(buf + FIDOPKT_QORIGZONE, msg->header.orig.zone); PUT_U16(buf + FIDOPKT_ORIGNET, msg->header.orig.net); PUT_U16(buf + FIDOPKT_ORIGNODE, msg->header.orig.node); memcpy(buf + FIDOPKT_PASSWORD, pkt_password, strlen(pkt_password)); PUT_U16(buf + FIDOPKT_DESTZONE, msg->header.dest.zone); PUT_U16(buf + FIDOPKT_QDESTZONE, msg->header.dest.zone); PUT_U16(buf + FIDOPKT_DESTNET, msg->header.dest.net); PUT_U16(buf + FIDOPKT_DESTNODE, msg->header.dest.node); PUT_U32(buf + FIDOPKT_PRODDATA, 'SUBT'); PUT_U16(buf + FIDOPKT_YEAR, tm->tm_year + 1900); PUT_U16(buf + FIDOPKT_MONTH, tm->tm_mon + 1); PUT_U16(buf + FIDOPKT_DAY, tm->tm_mday); PUT_U16(buf + FIDOPKT_HOUR, tm->tm_hour); PUT_U16(buf + FIDOPKT_MINUTE, tm->tm_min); PUT_U16(buf + FIDOPKT_SECOND, tm->tm_sec); PUT_U16(buf + FIDOPKT_PKTTYPE, 2); buf[FIDOPKT_CWVALIDCOPY + 1] = 1; buf[FIDOPKT_CAPABILWORD] = 1; vers = get_version(false); versdot = strchr(vers, '.'); if (versdot) { versdot[0] = '\0'; versnum = atoi(vers); buf[FIDOPKT_REVMAJOR] = versnum; } buf[FIDOPKT_PRODCODELOW] = 0xfe; /* FTA-1005.003 */ buf[FIDOPKT_PRODCODEHIGH] = 0; /* until we get a product code */ PUT_U16(buf + FIDOPKT_HEADER_SIZE + FIDOPKT_MSG_PKTTYPE, 2); PUT_U16(buf + FIDOPKT_HEADER_SIZE + FIDOPKT_MSG_DESTNET, msg->header.dest.net); PUT_U16(buf + FIDOPKT_HEADER_SIZE + FIDOPKT_MSG_DESTNODE, msg->header.dest.node); PUT_U16(buf + FIDOPKT_HEADER_SIZE + FIDOPKT_MSG_ORIGNET, msg->header.orig.net); PUT_U16(buf + FIDOPKT_HEADER_SIZE + FIDOPKT_MSG_ORIGNODE, msg->header.orig.node); PUT_U16(buf + FIDOPKT_HEADER_SIZE + FIDOPKT_MSG_ATTR, msg->attr); off = FIDOPKT_HEADER_SIZE + FIDOPKT_MSG_HEADER_SIZE; /* date, null-terminated, with two spaces between year and hour */ if (!grow_to_fit(&buf, &buf_size, off, 21, 21)) goto fail; off += strftime(buf + off, 20, "%d %b %y %H:%M:%S", tm); off++; /* to, null-terminated */ len = strlen(msg->to); if (!grow_to_fit(&buf, &buf_size, off, len + 1, len + 1)) goto fail; memcpy(buf + off, msg->to, len); off += len + 1; /* from, null-terminated */ len = strlen(msg->from); if (!grow_to_fit(&buf, &buf_size, off, len + 1, len + 1)) goto fail; memcpy(buf + off, msg->from, len); off += len + 1; /* subject, null-terminated */ if (!grow_to_fit(&buf, &buf_size, off, subject_len + 1, subject_len + 1)) goto fail; memcpy(buf + off, msg->subject, subject_len); off += subject_len + 1; /* optional area (non-kludge) \r-terminated, no space after colon */ if (msg->area[0] != '\0') { len = snprintf(scratch, sizeof(scratch), "AREA:%s\r", msg->area); if (len > sizeof(scratch)) goto fail; if (!grow_to_fit(&buf, &buf_size, off, len, len)) goto fail; memcpy(buf + off, scratch, len); off += len; } if (msg->area[0] == '\0') { /* kludge line: INTL to from, \r-terminated, but not on net mail */ len = snprintf(scratch, sizeof(scratch), "%cINTL %d:%d/%d %d:%d/%d\r", 0x1, msg->header.dest.zone, msg->header.dest.net, msg->header.dest.node, msg->header.orig.zone, msg->header.orig.net, msg->header.orig.node); if (len > sizeof(scratch)) goto fail; if (!grow_to_fit(&buf, &buf_size, off, len, len)) goto fail; memcpy(buf + off, scratch, len); off += len; } /* kludge line: TZOFF tzoff, \r-terminated */ len = snprintf(scratch, sizeof(scratch), "%cTZUTC: %+05d\r", 0x1, tzoff); if (len > sizeof(scratch)) goto fail; if (!grow_to_fit(&buf, &buf_size, off, len, len)) goto fail; memcpy(buf + off, scratch, len); off += len; /* kludge line: MSGID zone:net/node id, \r-terminated */ len = snprintf(scratch, sizeof(scratch), "%cMSGID: %d:%d/%d %08lx\r", 0x1, msg->msgid.zone, msg->msgid.net, msg->msgid.node, msg->msgid.id); if (len > sizeof(scratch)) goto fail; if (!grow_to_fit(&buf, &buf_size, off, len, len)) goto fail; memcpy(buf + off, scratch, len); off += len; /* kludge line: REPLY orig-msgid, \r-terminated */ if (msg->reply[0] != '\0') { len = snprintf(scratch, sizeof(scratch), "%cREPLY: %s\r", 0x1, msg->reply); if (len > sizeof(scratch)) goto fail; if (!grow_to_fit(&buf, &buf_size, off, len, len)) goto fail; memcpy(buf + off, scratch, len); off += len; } /* kludge line: PID spam, \r-terminated */ len = snprintf(scratch, sizeof(scratch), "%cPID: Subtext %s\r", 0x1, get_version(false)); if (len > sizeof(scratch)) goto fail; if (!grow_to_fit(&buf, &buf_size, off, len, len)) goto fail; memcpy(buf + off, scratch, len); off += len; if (!grow_to_fit(&buf, &buf_size, off, msg->body_len + 7, msg->body_len + 7)) goto fail; /* * FidoNet messages can only have \r line-breaks and we probably used * \r\n during the message editor, so strip out \n. */ for (n = 0; n < msg->body_len; n++) { if (n == msg->body_len - 1 && msg->body[n] == '\r') /* supress trailing \r, we'll add two next */ continue; if (msg->body[n] == '\0') break; if (msg->body[n] == '\n') { if (msg->body[n - 1] == '\r') continue; buf[off++] = '\r'; } else buf[off++] = msg->body[n]; } off += sprintf(buf + off, "\r"); if (msg->origin[0]) { /* add tear line */ off += sprintf(buf + off, "\r--- \r"); /* " * Origin: " */ len = 11 + strlen(msg->origin) + 2; if (!grow_to_fit(&buf, &buf_size, off, len, len)) goto fail; off += sprintf(buf + off, " * Origin: %s\r", msg->origin); } /* * Messages are terminated by a null, and the whole packet is * terminated by two nulls. */ if (!grow_to_fit(&buf, &buf_size, off, 3, 3)) goto fail; buf[off++] = '\0'; buf[off++] = '\0'; buf[off++] = '\0'; *ret_buf = buf; return off; fail: logger_printf("[fidopkt] Failed growing buf of size %ld", buf_size); if (buf != NULL) xfree(&buf); return 0; } void fidopkt_message_free(struct fidopkt_message **msgp) { struct fidopkt_message *msg = (struct fidopkt_message *)*msgp; if (msg->body) xfree(&msg->body); xfree(&msg); *msgp = NULL; }