1 /* $OpenBSD: protocol.c,v 1.4 2005/04/13 02:33:09 deraadt Exp $ */
2
3 /*
4 * POP protocol handling.
5 */
6
7 #include <stdio.h>
8 #include <unistd.h>
9 #include <stdlib.h>
10 #include <signal.h>
11 #include <setjmp.h>
12 #include <string.h>
13 #include <stdarg.h>
14 #include <errno.h>
15
16 #include "misc.h"
17 #include "params.h"
18 #include "protocol.h"
19
20 struct pop_buffer pop_buffer;
21 static sigjmp_buf pop_timed_out;
22
pop_init(void)23 void pop_init(void)
24 {
25 pop_buffer.ptr = pop_buffer.size = 0;
26 }
27
pop_clean(void)28 void pop_clean(void)
29 {
30 memset(pop_buffer.data, 0, pop_buffer.ptr);
31 memmove(pop_buffer.data, &pop_buffer.data[pop_buffer.ptr],
32 pop_buffer.size -= pop_buffer.ptr);
33 pop_buffer.ptr = 0;
34 }
35
pop_sane(void)36 int pop_sane(void)
37 {
38 return pop_buffer.size <= sizeof(pop_buffer.data) &&
39 pop_buffer.ptr <= pop_buffer.size;
40 }
41
pop_timeout(int signum)42 static void pop_timeout(int signum)
43 {
44 signal(SIGALRM, SIG_DFL);
45 siglongjmp(pop_timed_out, 1);
46 }
47
pop_fetch(void)48 static void pop_fetch(void)
49 {
50 int size;
51
52 signal(SIGALRM, pop_timeout);
53 alarm(POP_TIMEOUT);
54
55 size = read(STDIN_FILENO, pop_buffer.data, sizeof(pop_buffer.data));
56
57 alarm(0);
58 signal(SIGALRM, SIG_DFL);
59
60 pop_buffer.ptr = 0;
61 pop_buffer.size = (size >= 0) ? size : 0;
62 }
63
pop_get_char(void)64 static int pop_get_char(void)
65 {
66 if (pop_buffer.ptr >= pop_buffer.size) {
67 pop_fetch();
68 if (!pop_buffer.size) return -1;
69 }
70
71 return (unsigned char)pop_buffer.data[pop_buffer.ptr++];
72 }
73
pop_get_line(char * line,int size)74 static char *pop_get_line(char *line, int size)
75 {
76 int pos;
77 int seen_cr, seen_nul;
78 int c;
79
80 pos = 0;
81 seen_cr = seen_nul = 0;
82 while ((c = pop_get_char()) >= 0) {
83 if (c == '\n') {
84 if (seen_cr) line[pos - 1] = 0;
85 break;
86 }
87 if (pos < size - 1)
88 seen_cr = ((line[pos++] = c) == '\r');
89 else
90 seen_cr = 0;
91 seen_nul |= !c;
92 }
93 line[pos] = 0;
94
95 if (seen_nul)
96 line[0] = 0;
97
98 if (pos || c >= 0)
99 return line;
100 else
101 return NULL;
102 }
103
pop_handle_state(struct pop_command * commands)104 int pop_handle_state(struct pop_command *commands)
105 {
106 char line[POP_BUFFER_SIZE];
107 char *params;
108 struct pop_command *command;
109 int response;
110
111 if (sigsetjmp(pop_timed_out, 1)) return POP_CRASH_NETTIME;
112
113 while (pop_get_line(line, sizeof(line))) {
114 if ((params = strchr(line, ' '))) {
115 *params++ = 0;
116 if (!*params) params = NULL;
117 }
118
119 response = POP_ERROR;
120 for (command = commands; command->name; command++)
121 if (!strcasecmp(command->name, line)) {
122 response = command->handler(params);
123 break;
124 }
125
126 switch (response) {
127 case POP_OK:
128 if (pop_reply_ok()) return POP_CRASH_NETFAIL;
129 break;
130
131 case POP_ERROR:
132 if (pop_reply_error()) return POP_CRASH_NETFAIL;
133
134 case POP_QUIET:
135 break;
136
137 case POP_LEAVE:
138 if (pop_reply_ok()) return POP_CRASH_NETFAIL;
139
140 default:
141 return response;
142 }
143 }
144
145 return POP_CRASH_NETFAIL;
146 }
147
pop_get_param(char ** params)148 char *pop_get_param(char **params)
149 {
150 char *current, *next;
151
152 if ((current = *params)) {
153 if ((next = strchr(current, ' '))) {
154 *next++ = 0;
155 *params = *next ? next : NULL;
156 } else
157 *params = NULL;
158
159 if (strlen(current) > 40) current = NULL;
160 }
161
162 return current;
163 }
164
pop_get_int(char ** params)165 int pop_get_int(char **params)
166 {
167 char *param, *error;
168 long value;
169
170 if ((param = pop_get_param(params))) {
171 /* SUSv2 says:
172 * "Because 0, LONG_MIN and LONG_MAX are returned on error and are also
173 * valid returns on success, an application wishing to check for error
174 * situations should set errno to 0, then call strtol(), then check errno." */
175 errno = 0;
176 value = strtol(param, &error, 10);
177 if (errno || !*param || *error ||
178 value < 0 || (long)(int)value != value)
179 return -1;
180
181 return (int)value;
182 }
183
184 return -1;
185 }
186
pop_reply(char * format,...)187 int pop_reply(char *format, ...)
188 {
189 va_list args;
190
191 va_start(args, format);
192 vfprintf(stdout, format, args);
193 va_end(args);
194
195 putc('\r', stdout);
196 putc('\n', stdout);
197
198 switch (format[0]) {
199 case '+':
200 case '-':
201 return fflush(stdout);
202
203 case '.':
204 if (!format[1]) return fflush(stdout);
205 }
206
207 return ferror(stdout);
208 }
209
pop_reply_ok(void)210 int pop_reply_ok(void)
211 {
212 return pop_reply("+OK");
213 }
214
pop_reply_error(void)215 int pop_reply_error(void)
216 {
217 return pop_reply("-ERR");
218 }
219
pop_reply_multiline(int fd,unsigned long size,int lines)220 int pop_reply_multiline(int fd, unsigned long size, int lines)
221 {
222 char *in_buffer, *out_buffer;
223 char *in, *out;
224 int in_block, out_block;
225 int start, body;
226
227 if (lines >= 0) lines++;
228
229 if (pop_reply_ok()) return POP_CRASH_NETFAIL;
230
231 in_buffer = malloc(RETR_BUFFER_SIZE * 3);
232 if (!in_buffer) return POP_CRASH_SERVER;
233 out_buffer = &in_buffer[RETR_BUFFER_SIZE];
234
235 start = 1;
236 body = 0;
237 while (size && lines) {
238 if (size > RETR_BUFFER_SIZE)
239 in_block = read(fd, in_buffer, RETR_BUFFER_SIZE);
240 else
241 in_block = read(fd, in_buffer, size);
242 if (in_block <= 0) {
243 free(in_buffer);
244 return POP_CRASH_SERVER;
245 }
246
247 in = in_buffer;
248 out = out_buffer;
249 while (in < &in_buffer[in_block] && lines)
250 switch (*in) {
251 case '\n':
252 *out++ = '\r';
253 *out++ = *in++;
254 if (start) body = 1;
255 if (body) lines--;
256 start = 1;
257 break;
258
259 case '.':
260 if (start) *out++ = '.';
261
262 default:
263 *out++ = *in++;
264 start = 0;
265 }
266
267 out_block = out - out_buffer;
268 if (write_loop(1, out_buffer, out_block) != out_block) {
269 free(in_buffer);
270 return POP_CRASH_NETFAIL;
271 }
272
273 size -= in_block;
274 }
275
276 free(in_buffer);
277
278 if (!start)
279 if (pop_reply("%s", "")) return POP_CRASH_NETFAIL;
280
281 return POP_OK;
282 }
283
pop_reply_terminate(void)284 int pop_reply_terminate(void)
285 {
286 return pop_reply(".");
287 }
288