1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297
|
/* $Id: wire.c 8962 2010-02-08 20:54:57Z iulius $
**
** Wire format article utilities.
**
** Originally written by Alex Kiernan (alex.kiernan@thus.net)
**
** These routines manipulate wire format articles; in particular, they should
** be safe in the presence of embedded NULs. They assume wire format
** conventions (\r\n as a line ending, in particular) and will not work with
** articles in native format (with the exception of wire_from_native, of
** course).
**
** The functions in this file take const char * pointers and return char *
** pointers so that they can work on both const char * and char * article
** bodies without changing the const sense. This unfortunately means that
** the routines in this file will produce warnings about const being cast
** away. To avoid those, one would need to duplicate all the code in this
** file or use C++.
*/
#include "config.h"
#include "clibrary.h"
#include <assert.h>
#include "inn/wire.h"
#include "inn/libinn.h"
/*
** Given a pointer to the start of an article, locate the first octet of the
** body (which may be the octet beyond the end of the buffer if your article
** is bodiless).
*/
char *
wire_findbody(const char *article, size_t length)
{
char *p;
const char *end;
/* Handle the degenerate case of an article with no headers. */
if (length > 5 && article[0] == '\r' && article[1] == '\n')
return (char *) article + 2;
/* Jump from \r to \r and give up if we're too close to the end. */
end = article + length;
for (p = (char *) article; (p + 4) <= end; ++p) {
p = memchr(p, '\r', end - p - 3);
if (p == NULL)
break;
if (memcmp(p, "\r\n\r\n", 4) == 0) {
p += 4;
return p;
}
}
return NULL;
}
/*
** Given a pointer into an article and a pointer to the last octet of the
** article, find the next line ending and return a pointer to the first
** character after that line ending. If no line ending is found in the
** article or if it is at the end of the article, return NULL.
*/
char *
wire_nextline(const char *article, const char *end)
{
char *p;
for (p = (char *) article; (p + 2) <= end; ++p) {
p = memchr(p, '\r', end - p - 2);
if (p == NULL)
break;
if (p[1] == '\n') {
p += 2;
return p;
}
}
return NULL;
}
/*
** Returns true if line is the beginning of a valid header for header, also
** taking the length of the header name as a third argument. Assumes that
** there is at least length + 2 bytes of data at line, and that the header
** name doesn't contain nul.
*/
static bool
isheader(const char *line, const char *header, size_t length)
{
if (line[length] != ':' || !ISWHITE(line[length + 1]))
return false;
return strncasecmp(line, header, length) == 0;
}
/*
** Skip over folding whitespace, as defined by RFC 5322. Takes a pointer to
** where to start skipping and a pointer to the end of the data, and will not
** return a pointer past the end pointer. If skipping folding whitespace
** takes us past the end of data, return NULL.
*/
static char *
skip_fws_bounded(char *text, const char *end)
{
char *p;
for (p = text; p <= end; p++) {
if (p < end + 1 && p[0] == '\r' && p[1] == '\n' && ISWHITE(p[2]))
p += 2;
if (!ISWHITE(*p))
return p;
}
return NULL;
}
/*
** Given a pointer to the start of the article, the article length, and the
** header to look for, find the first occurrence of that header in the
** article. Skip over headers with no content, but allow for headers that
** are folded before the first text in the header. If no matching headers
** with content other than spaces and tabs are found, return NULL.
*/
char *
wire_findheader(const char *article, size_t length, const char *header,
bool stripspaces)
{
char *p;
const char *end;
ptrdiff_t headerlen;
headerlen = strlen(header);
end = article + length - 1;
/* There has to be enough space left in the article for at least the
header, the colon, whitespace, and one non-whitespace character, hence
3, minus 1 since the character pointed to by end is part of the
article. */
p = (char *) article;
while (p != NULL && end - p > headerlen + 2) {
if (p[0] == '\r' && p[1] == '\n')
return NULL;
else if (isheader(p, header, headerlen)) {
p += headerlen + 2;
if (stripspaces)
p = skip_fws_bounded(p, end);
if (p == NULL)
return NULL;
if (p >= end || p[0] != '\r' || p[1] != '\n')
return p;
}
p = wire_nextline(p, end);
}
return NULL;
}
/*
** Given a pointer to a header and a pointer to the last octet of the
** article, find the end of the header (a pointer to the final \n of the
** header value). If the header contents don't end in \r\n, return NULL.
*/
char *
wire_endheader(const char *header, const char *end)
{
char *p;
p = wire_nextline(header, end);
while (p != NULL) {
if (!ISWHITE(*p))
return p - 1;
p = wire_nextline(p, end);
}
if (end - header >= 1 && *end == '\n' && *(end - 1) == '\r')
return (char *) end;
return NULL;
}
/*
** Given an article and length in non-wire format, return a malloced region
** containing the article in wire format. Set *newlen to the length of the
** new article. The caller is responsible for freeing the allocated memory.
*/
char *
wire_from_native(const char *article, size_t len, size_t *newlen)
{
size_t bytes;
char *newart;
const char *p;
char *dest;
bool at_start = true;
/* First go thru article and count number of bytes we need. Add a CR for
every LF and an extra character for any period at the beginning of a
line for dot-stuffing. Add 3 characters at the end for .\r\n. */
for (bytes = 0, p = article; p < article + len; p++) {
if (at_start && *p == '.')
bytes++;
bytes++;
at_start = (*p == '\n');
if (at_start)
bytes++;
}
bytes += 3;
/* Now copy the article, making the required changes. */
newart = xmalloc(bytes + 1);
*newlen = bytes;
at_start = true;
for (p = article, dest = newart; p < article + len; p++) {
if (*p == '\n') {
*dest++ = '\r';
*dest++ = '\n';
at_start = true;
} else {
if (at_start && *p == '.')
*dest++ = '.';
*dest++ = *p;
at_start = false;
}
}
*dest++ = '.';
*dest++ = '\r';
*dest++ = '\n';
*dest = '\0';
return newart;
}
/*
** Given an article and length in wire format, return a malloced region
** containing the article in native format. Set *newlen to the length of the
** new article. The caller is responsible for freeing the allocated memory.
*/
char *
wire_to_native(const char *article, size_t len, size_t *newlen)
{
size_t bytes;
char *newart;
const char *p, *end;
char *dest;
bool at_start = true;
/* If the article is shorter than three bytes, it's definitely not in wire
format. Just return a copy of it. */
if (len < 3) {
*newlen = len;
return xstrndup(article, len);
}
end = article + len - 3;
/* First go thru article and count number of bytes we need. Once we reach
.\r\n, we're done. We'll remove one . from .. at the start of a line
and change CRLF to just LF. */
for (bytes = 0, p = article; p < article + len; ) {
if (p == end && p[0] == '.' && p[1] == '\r' && p[2] == '\n')
break;
if (at_start && p < article + len - 1 && p[0] == '.' && p[1] == '.') {
bytes++;
p += 2;
at_start = false;
} else if (p < article + len - 1 && p[0] == '\r' && p[1] == '\n') {
bytes++;
p += 2;
at_start = true;
} else {
bytes++;
p++;
at_start = false;
}
}
/* Now, create the new space and copy the article over. */
newart = xmalloc(bytes + 1);
*newlen = bytes;
at_start = true;
for (p = article, dest = newart; p < article + len; ) {
if (p == end && p[0] == '.' && p[1] == '\r' && p[2] == '\n')
break;
if (at_start && p < article + len - 1 && p[0] == '.' && p[1] == '.') {
*dest++ = '.';
p += 2;
at_start = false;
} else if (p < article + len - 1 && p[0] == '\r' && p[1] == '\n') {
*dest++ = '\n';
p += 2;
at_start = true;
} else {
*dest++ = *p++;
at_start = false;
}
}
*dest = '\0';
return newart;
}
|