chiark / gitweb /
Commit 2.4.5-5 as unpacked
[inn-innduct.git] / lib / wire.c
1 /*  $Id: wire.c 7258 2005-06-06 03:14:45Z eagle $
2 **
3 **  Wire format article utilities.
4 **
5 **  Originally written by Alex Kiernan (alex.kiernan@thus.net)
6 **
7 **  These routines manipulate wire format articles; in particular, they should
8 **  be safe in the presence of embedded NULs.  They assume wire format
9 **  conventions (\r\n as a line ending, in particular) and will not work with
10 **  articles in native format.
11 **
12 **  The functions in this file take const char * pointers and return char *
13 **  pointers so that they can work on both const char * and char * article
14 **  bodies without changing the const sense.  This unfortunately means that
15 **  the routines in this file will produce warnings about const being cast
16 **  away.  To avoid those, one would need to duplicate all the code in this
17 **  file or use C++.
18 */
19
20 #include "config.h"
21 #include "clibrary.h"
22 #include <assert.h>
23
24 #include "inn/wire.h"
25 #include "libinn.h"
26
27 /*
28 **  Given a pointer to the start of an article, locate the first octet of the
29 **  body (which may be the octet beyond the end of the buffer if your article
30 **  is bodiless).
31 */
32 char *
33 wire_findbody(const char *article, size_t length)
34 {
35     char *p;
36     const char *end;
37
38     /* Handle the degenerate case of an article with no headers. */
39     if (length > 5 && article[0] == '\r' && article[1] == '\n')
40         return (char *) article + 2;
41
42     /* Jump from \r to \r and give up if we're too close to the end. */
43     end = article + length;
44     for (p = (char *) article; (p + 4) <= end; ++p) {
45         p = memchr(p, '\r', end - p - 3);
46         if (p == NULL)
47             break;
48         if (memcmp(p, "\r\n\r\n", 4) == 0) {
49             p += 4;
50             return p;
51         }
52     }
53     return NULL;
54 }
55
56
57 /*
58 **  Given a pointer into an article and a pointer to the last octet of the
59 **  article, find the next line ending and return a pointer to the first
60 **  character after that line ending.  If no line ending is found in the
61 **  article or if it is at the end of the article, return NULL.
62 */
63 char *
64 wire_nextline(const char *article, const char *end)
65 {
66     char *p;
67
68     for (p = (char *) article; (p + 2) <= end; ++p) {
69         p = memchr(p, '\r', end - p - 2);
70         if (p == NULL)
71             break;
72         if (p[1] == '\n') {
73             p += 2;
74             return p;
75         }
76     }
77     return NULL;
78 }
79
80
81 /*
82 **  Returns true if line is the beginning of a valid header for header, also
83 **  taking the length of the header name as a third argument.  Assumes that
84 **  there is at least length + 2 bytes of data at line, and that the header
85 **  name doesn't contain nul.
86 */
87 static bool
88 isheader(const char *line, const char *header, size_t length)
89 {
90     if (line[length] != ':' || !ISWHITE(line[length + 1]))
91         return false;
92     return strncasecmp(line, header, length) == 0;
93 }
94
95
96 /*
97 **  Skip over folding whitespace, as defined by RFC 2822.  Takes a pointer to
98 **  where to start skipping and a pointer to the end of the data, and will not
99 **  return a pointer past the end pointer.  If skipping folding whitespace
100 **  takes us past the end of data, return NULL.
101 */
102 static char *
103 skip_fws(char *text, const char *end)
104 {
105     char *p;
106
107     for (p = text; p <= end; p++) {
108         if (p < end + 1 && p[0] == '\r' && p[1] == '\n' && ISWHITE(p[2]))
109             p += 2;
110         if (!ISWHITE(*p))
111             return p;
112     }
113     return NULL;
114 }
115
116
117 /*
118 **  Given a pointer to the start of the article, the article length, and the
119 **  header to look for, find the first occurance of that header in the
120 **  article.  Skip over headers with no content, but allow for headers that
121 **  are folded before the first text in the header.  If no matching headers
122 **  with content other than spaces and tabs are found, return NULL.
123 */
124 char *
125 wire_findheader(const char *article, size_t length, const char *header)
126 {
127     char *p;
128     const char *end;
129     ptrdiff_t headerlen;
130
131     headerlen = strlen(header);
132     end = article + length - 1;
133
134     /* There has to be enough space left in the article for at least the
135        header, the colon, whitespace, and one non-whitespace character, hence
136        3, minus 1 since the character pointed to by end is part of the
137        article. */
138     p = (char *) article;
139     while (p != NULL && end - p > headerlen + 2) {
140         if (p[0] == '\r' && p[1] == '\n')
141             return NULL;
142         else if (isheader(p, header, headerlen)) {
143             p = skip_fws(p + headerlen + 2, end);
144             if (p == NULL)
145                 return NULL;
146             if (p >= end || p[0] != '\r' || p[1] != '\n')
147                 return p;
148         }
149         p = wire_nextline(p, end);
150     }
151     return NULL;
152 }
153
154
155 /*
156 **  Given a pointer to a header and a pointer to the last octet of the
157 **  article, find the end of the header (a pointer to the final \n of the
158 **  header value).  If the header contents don't end in \r\n, return NULL.
159 */
160 char *
161 wire_endheader(const char *header, const char *end)
162 {
163     char *p;
164
165     p = wire_nextline(header, end);
166     while (p != NULL) {
167         if (!ISWHITE(*p))
168             return p - 1;
169         p = wire_nextline(p, end);
170     }
171     if (end - header >= 1 && *end == '\n' && *(end - 1) == '\r')
172         return (char *) end;
173     return NULL;
174 }