File: wire.c

package info (click to toggle)
inn2 2.5.2-2~squeeze1
  • links: PTS
  • area: main
  • in suites: squeeze
  • size: 11,072 kB
  • ctags: 8,521
  • sloc: ansic: 91,418; sh: 13,249; perl: 12,311; makefile: 2,928; yacc: 868; python: 342; lex: 266
file content (297 lines) | stat: -rw-r--r-- 8,943 bytes parent folder | download | duplicates (3)
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;
}