/[Apache-SVN]/httpd/sandbox/mod_pop3/pop_protocol.c
ViewVC logotype

Contents of /httpd/sandbox/mod_pop3/pop_protocol.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 653648 - (show annotations) (download)
Tue May 6 00:02:04 2008 UTC (16 years, 3 months ago) by fielding
File MIME type: text/plain
File size: 18894 byte(s)
moving mod_pop3 to sandbox
1 /* ====================================================================
2 * The Apache Software License, Version 1.1
3 *
4 * Copyright (c) 2000-2002 The Apache Software Foundation. All rights
5 * reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 *
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 *
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in
16 * the documentation and/or other materials provided with the
17 * distribution.
18 *
19 * 3. The end-user documentation included with the redistribution,
20 * if any, must include the following acknowledgment:
21 * "This product includes software developed by the
22 * Apache Software Foundation (http://www.apache.org/)."
23 * Alternately, this acknowledgment may appear in the software itself,
24 * if and wherever such third-party acknowledgments normally appear.
25 *
26 * 4. The names "Apache" and "Apache Software Foundation" must
27 * not be used to endorse or promote products derived from this
28 * software without prior written permission. For written
29 * permission, please contact apache@apache.org.
30 *
31 * 5. Products derived from this software may not be called "Apache",
32 * nor may "Apache" appear in their name, without prior written
33 * permission of the Apache Software Foundation.
34 *
35 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
36 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
37 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
38 * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
39 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
40 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
41 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
42 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
43 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
44 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
45 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
46 * SUCH DAMAGE.
47 * ====================================================================
48 *
49 * This software consists of voluntary contributions made by many
50 * individuals on behalf of the Apache Software Foundation. For more
51 * information on the Apache Software Foundation, please see
52 * <http://www.apache.org/>.
53 *
54 */
55
56 #define CORE_PRIVATE
57 #include "httpd.h"
58 #include "http_protocol.h"
59 #include "http_config.h"
60 #include "http_connection.h"
61 #include "http_core.h"
62 #include "http_request.h"
63 #include "http_log.h"
64 #include "ap_config.h"
65 #include "apr_md5.h"
66 #include "apr_pools.h"
67 #include "apr_strings.h"
68 #include "apr_buckets.h"
69 #include "util_filter.h"
70 #include "scoreboard.h"
71 #include "pop.h"
72
73 #include <sys/types.h>
74 #include <assert.h>
75
76 static void md5_convert(unsigned char digest[(2 * APR_MD5_DIGESTSIZE) + 1])
77 {
78 char *ptr;
79 int i;
80 unsigned char hash[APR_MD5_DIGESTSIZE];
81 const char *hex = "0123456789abcdef";
82
83 memcpy(hash, digest, APR_MD5_DIGESTSIZE);
84
85 for (i = 0, ptr = digest; i < APR_MD5_DIGESTSIZE; i++) {
86 *ptr++ = hex[hash[i] >> 4];
87 *ptr++ = hex[hash[i] & 0xF];
88 }
89 *ptr = '\0';
90 }
91
92 static char *compute_md5(request_rec *r, pop_msg *msg)
93 {
94 apr_mmap_t *mm = NULL;
95 apr_finfo_t finfo;
96 pop_user_rec *ur = (pop_user_rec *)ap_get_module_config(r->request_config,
97 &pop_module);
98 unsigned char *digest = apr_pcalloc(ur->p, 2 * APR_MD5_DIGESTSIZE + 1);
99
100 apr_stat(&finfo, r->filename, APR_FINFO_SIZE, ur->p);
101 apr_mmap_create(&mm, ur->fp, 0,
102 finfo.size, APR_MMAP_READ, ur->p);
103
104 apr_md5_init(ur->ctx);
105 apr_md5_update(ur->ctx, (char*)mm->mm + msg->header_start, msg->msg_end - msg->header_start);
106 apr_md5_final(digest, ur->ctx);
107 md5_convert(digest);
108 return digest;
109 }
110
111 int process_pop_connection_internal(request_rec *r, apr_bucket_brigade *bb)
112 {
113 char cmdbuff[POP_STRING_LENGTH];
114 char *buffer; /* a pointer to cmdbuff */
115 char *command;
116 int invalid_cmd = 0;
117 apr_size_t len;
118 pop_handler_st *handle_func;
119 apr_pool_t *p;
120 pop_user_rec *ur = (pop_user_rec *)ap_get_module_config(r->request_config,
121 &pop_module);
122
123 apr_pool_create(&p, r->pool);
124 r->uri = apr_pstrdup(r->pool, "pop:");
125
126 ap_run_map_to_storage(r);
127
128 while (1) {
129 int res;
130 buffer = cmdbuff; /* reset buffer pointer */
131 apr_pool_clear(p);
132
133 if ((invalid_cmd > MAX_INVALID_CMD) ||
134 ap_rgetline(&buffer, POP_STRING_LENGTH, &len, r, 0, bb) != APR_SUCCESS)
135 {
136 break;
137 }
138
139 /* The command moves the pointer of buffer to the end of the extracted string */
140 command = ap_getword_white_nc(p, &buffer);
141 ap_str_tolower(command);
142 handle_func = apr_hash_get(ap_pop_hash, command, APR_HASH_KEY_STRING);
143
144 if (!handle_func) {
145 ap_rprintf(r, "-ERR command not understood\r\n");
146 ap_rflush(r);
147 invalid_cmd++;
148 continue;
149 }
150 if (!(handle_func->states & ur->state)) {
151 ap_rprintf(r, "-ERR command %s not allowed in this state\r\n", command);
152 ap_rflush(r);
153 invalid_cmd++;
154 continue;
155 }
156 res = handle_func->func(r, buffer);
157 if (res == POP_QUIT) {
158 break;
159 }
160 }
161
162 return OK;
163 }
164
165 static pop_msg *find_message(request_rec *r, pop_mbox *mbox, int num, int report)
166 {
167 pop_msg *msg = APR_RING_FIRST(&(mbox)->list);
168 int j;
169
170 for (j = 1; j < num; j++) {
171 msg = APR_RING_NEXT(msg, link);
172 if (msg == APR_RING_SENTINEL(&(mbox)->list, pop_msg, link)) {
173 msg = NULL;
174 break;
175 }
176 }
177 if (msg == NULL) {
178 if (report) {
179 ap_rprintf(r, "-ERR no such message, only %d messages in "
180 "maildrop\r\n",
181 APR_RING_LAST(&(mbox)->list)->id);
182 }
183 msg = NULL;
184 }
185 else if (num == 0) {
186 if (report) {
187 ap_rprintf(r, "-ERR no such message, messages start at 1\r\n");
188 }
189 msg = NULL;
190 }
191 else if (msg->deleted) {
192 if (report) {
193 ap_rprintf(r, "-ERR message %d already deleted\r\n", num);
194 }
195 msg = NULL;
196 }
197 return msg;
198 }
199
200 static pop_msg *generate_scan_listing(request_rec *r, pop_mbox *mbox, int num,
201 int with_ok)
202 {
203 /* We do not want to report errors from find_message unless we are sending
204 * the ok string.
205 */
206 pop_msg *msg = find_message(r, mbox, num, with_ok);
207
208 if (msg != NULL) {
209 if (with_ok) {
210 ap_rwrite("+OK ", strlen("+OK "), r);
211 }
212 ap_rprintf(r, "%d %"APR_OFF_T_FMT"\r\n",
213 num, msg->msg_end - msg->header_start);
214 }
215 return msg;
216 }
217
218 static pop_msg *generate_unique_listing(request_rec *r, pop_mbox *mbox, int num,
219 int with_ok)
220 {
221 /* We do not want to report errors from find_message unless we are sending
222 * the ok string.
223 */
224 pop_msg *msg = find_message(r, mbox, num, with_ok);
225
226 if (msg != NULL) {
227 if (with_ok) {
228 ap_rwrite("+OK ", strlen("+OK "), r);
229 }
230 ap_rprintf(r, "%d %s\r\n",
231 num, compute_md5(r, msg));
232 }
233 return msg;
234 }
235
236 static void msg_num_and_size(pop_mbox *mbox, apr_size_t *num, apr_size_t *size)
237 {
238 pop_msg *msg;
239 apr_size_t i = 0, j = 0;
240
241 /* APR_RING_FOREACH(msg, &(mbox)->list, pop_msg, link) { */
242 while ( !APR_RING_EMPTY( &(mbox)->list, pop_msg, link )) {
243 msg = APR_RING_FIRST( &(mbox)->list );
244
245 if (!msg->deleted) {
246 i++;
247 j += (msg->msg_end - msg->header_start + 1);
248 }
249 APR_RING_REMOVE( msg, link);
250 }
251 *num = i;
252 *size = j;
253 }
254
255 int ap_handle_user(request_rec *r, char *buffer)
256 {
257 char *user;
258 pop_user_rec *ur = (pop_user_rec *)ap_get_module_config(r->request_config,
259 &pop_module);
260
261 user = ap_getword_white_nc(r->pool, &buffer);
262 if (!strcmp(user, "")) {
263 ap_rprintf(r, "-ERR Must provide user name\r\n");
264 ap_rflush(r);
265 return POP_USER_NOT_ALLOWED;
266 }
267 r->user = ur->user = apr_pstrdup(ur->p, user);
268
269 ap_rprintf(r, "+OK %s is welcome here\r\n", r->user);
270 ap_rflush(r);
271 ur->state = USER_ACK;
272 return OK;
273 }
274
275 int ap_handle_passwd(request_rec *r, char *buffer)
276 {
277 char *passwd;
278 int res;
279 pop_user_rec *ur = (pop_user_rec *)ap_get_module_config(r->request_config,
280 &pop_module);
281
282 if ((res = pop_parse_maildrop(r, &ur->mbox)) != OK) {
283 ur->state = POP_AUTH;
284 return res;
285 }
286
287 passwd = apr_psprintf(r->pool, "%s:%s", ur->user,
288 ap_getword_white_nc(r->pool, &buffer));
289 ur->auth_string = apr_psprintf(r->connection->pool, "Basic %s",
290 ap_pbase64encode(r->pool, passwd));
291
292 apr_table_set(r->headers_in, "Authorization", ur->auth_string);
293
294 ap_run_map_to_storage(r);
295
296 if ((res = ap_run_check_user_id(r)) != OK) {
297 ap_rprintf(r,
298 "-ERR user %s not known here or invalid password\r\n", ur->user);
299 ap_rflush(r);
300 ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, r,
301 "Unauthorized user tried to log in");
302 ur->state = POP_AUTH;
303 return POP_USER_NOT_ALLOWED;
304 }
305
306 ap_rprintf(r, "+OK, user %s is welcome to read his mail\r\n", ur->user);
307 ap_rflush(r);
308
309 ur->state = POP_TRANSACTION;
310
311 return OK;
312 }
313
314 static int rewrite_mbox(request_rec *r)
315 {
316 apr_file_t *f2;
317 const char *filename;
318 char str[8192];
319 apr_size_t len, totsize;
320 apr_off_t zero = 0;
321 apr_status_t rv, rv2;
322 apr_mmap_t *mm;
323 pop_msg *msg;
324 pop_user_rec *ur = (pop_user_rec *)ap_get_module_config(r->request_config,
325 &pop_module);
326
327 filename = apr_pstrcat(ur->p, r->filename, ".tmp", NULL);
328 rv = apr_file_open(&f2, filename, APR_READ | APR_WRITE | APR_CREATE |
329 APR_DELONCLOSE, APR_OS_DEFAULT, ur->p);
330 if (rv != APR_SUCCESS) {
331 fprintf(stderr, "%d\n", rv);
332 }
333 len = totsize = 0;
334 apr_file_seek(ur->fp, APR_SET, &zero);
335 do {
336 len = 8192;
337 rv = apr_file_read(ur->fp, str, &len);
338 totsize += len;
339 rv2 = apr_file_write(f2, str, &len);
340 } while (rv != APR_EOF);
341
342 apr_file_trunc(ur->fp, 0);
343 apr_mmap_create(&mm, f2, 0, totsize, APR_MMAP_READ, ur->p);
344
345 msg = APR_RING_FIRST(&(ur->mbox)->list);
346 while (msg != APR_RING_SENTINEL(&(ur->mbox)->list, pop_msg, link)) {
347 if (!msg->deleted) {
348 apr_size_t len = msg->msg_end - msg->header_start + 1;
349 apr_file_write(ur->fp, (char *)mm->mm + msg->header_start, &len);
350 }
351 msg = APR_RING_NEXT(msg, link);
352 }
353
354 return 0;
355 }
356
357 int ap_handle_quit(request_rec *r, char *buffer)
358 {
359 apr_size_t num, size;
360 pop_user_rec *ur = (pop_user_rec *)ap_get_module_config(r->request_config,
361 &pop_module);
362
363 if (ur->state == POP_TRANSACTION) {
364 ur->state = UPDATE;
365
366 rewrite_mbox(r);
367 msg_num_and_size(ur->mbox, &num, &size);
368 apr_file_unlock(ur->fp);
369 ap_rprintf(r, "+OK %s POP3 server signing off ", ap_get_server_name(r));
370 if (num == 0) {
371 ap_rputs("(maildrop empty)\r\n", r);
372 }
373 else {
374 ap_rprintf(r, "(%d messages left)\r\n", num);
375 }
376 }
377 else {
378 ap_rprintf(r, "+OK %s POP3 server signing off\r\n",
379 ap_get_server_name(r));
380 }
381 ap_rflush(r);
382 ur->state = POP_AUTH;
383 return POP_QUIT;
384 }
385
386 int ap_handle_dele(request_rec *r, char *buffer)
387 {
388 pop_msg *msg;
389 int num;
390 pop_user_rec *ur = (pop_user_rec *)ap_get_module_config(r->request_config,
391 &pop_module);
392
393 num = atoi(buffer);
394 msg = find_message(r, ur->mbox, num, 1);
395
396 if (msg != NULL) {
397 msg->deleted = 1;
398 ap_rprintf(r, "+OK message %d deleted\r\n", num);
399 }
400 ap_rflush(r);
401
402 return OK;
403 }
404
405 /* This method has been removed from POP3 in RFC 1725, but fetchmail
406 * is using it, so we need to leave it in the code. There may be other
407 * POP3 clients that don't use it, but if even one does, we must continue
408 * to support this field.
409 */
410 int ap_handle_last(request_rec *r, char *buffer)
411 {
412 pop_user_rec *ur = (pop_user_rec *)ap_get_module_config(r->request_config,
413 &pop_module);
414
415 ap_rprintf(r, "+OK %d\r\n", ur->high_access);
416 ap_rflush(r);
417
418 return OK;
419 }
420
421 int ap_handle_list(request_rec *r, char *buffer)
422 {
423 char *num;
424 pop_user_rec *ur = (pop_user_rec *)ap_get_module_config(r->request_config,
425 &pop_module);
426
427 num = ap_getword_white_nc(r->pool, &buffer);
428 if (!strcmp(num, "")) {
429 pop_msg *msg;
430 int last_msg, i;
431
432 if (!APR_RING_EMPTY(&(ur->mbox)->list, pop_msg, link)) {
433 msg = APR_RING_LAST(&(ur->mbox)->list);
434 last_msg = msg->id;
435 }
436 else {
437 ap_rputs("-ERR No messages in maildrop\r\n", r);
438 ap_rflush(r);
439 return APR_SUCCESS;
440 }
441
442 apr_stat(&r->finfo, r->filename, APR_FINFO_SIZE, r->pool);
443
444 ap_rprintf(r, "+OK %d messages (%"APR_OFF_T_FMT" octets)\r\n",
445 last_msg, r->finfo.size);
446
447 for (i = 1; i <= last_msg; i++) {
448 generate_scan_listing(r, ur->mbox, i, 0);
449 }
450 ap_rwrite(".\r\n", strlen(".\r\n"), r);
451 ap_rflush(r);
452 }
453 else {
454 generate_scan_listing(r, ur->mbox, atoi(num), 1);
455 ap_rflush(r);
456 }
457 return OK;
458 }
459
460 int ap_handle_noop(request_rec *r, char *buffer)
461 {
462 ap_rwrite( "+OK\r\n", strlen("+OK\r\n"), r);
463 ap_rflush(r);
464 return OK;
465 }
466
467 int ap_handle_retr(request_rec *r, char *buffer)
468 {
469 char *num;
470 int i;
471 apr_size_t bytes_sent;
472 pop_msg *msg = NULL;
473 pop_user_rec *ur = (pop_user_rec *)ap_get_module_config(r->request_config,
474 &pop_module);
475
476 num = ap_getword_white_nc(r->pool, &buffer);
477 if (!strcmp(num, "")) {
478 ap_rputs( "-ERR must supply a message id\r\n", r);
479 ap_rflush(r);
480 return POP_BAD_MSG_NUM;
481 }
482 i = atoi(num);
483 msg = generate_scan_listing(r, ur->mbox, i, 1);
484 ap_rflush(r);
485
486 if (msg == NULL) {
487 ap_rflush(r);
488 return POP_BAD_MSG_NUM;
489 }
490
491 ap_send_fd(ur->fp, r, msg->header_start, msg->msg_end - msg->header_start + 1,
492 &bytes_sent);
493 ap_rwrite(".\r\n", strlen(".\r\n"), r);
494 ap_rflush(r);
495 ur->high_access = msg->id;
496
497 return OK;
498 }
499
500 int ap_handle_rset(request_rec *r, char *buffer)
501 {
502 pop_msg *msg;
503 apr_size_t num, size;
504 pop_user_rec *ur = (pop_user_rec *)ap_get_module_config(r->request_config,
505 &pop_module);
506 /* APR_RING_FOREACH(msg, &(ur->mbox)->list, pop_msg, link) {*/
507 while ( !APR_RING_EMPTY( &(ur->mbox)->list, pop_msg, link )) {
508 msg = APR_RING_FIRST( &(ur->mbox)->list );
509
510 if (msg->deleted) {
511 msg->deleted = 0;
512 }
513 APR_RING_REMOVE( msg, link);
514 }
515 ur->high_access = 0;
516
517 msg_num_and_size(ur->mbox, &num, &size);
518
519 ap_rprintf(r, "+OK maildrop has %d messages (%d octets)\r\n", num, size);
520 ap_rflush(r);
521
522 return OK;
523 }
524
525 int ap_handle_stat(request_rec *r, char *buffer)
526 {
527 apr_size_t num, size;
528 pop_user_rec *ur = (pop_user_rec *)ap_get_module_config(r->request_config,
529 &pop_module);
530 msg_num_and_size(ur->mbox, &num, &size);
531
532 ap_rprintf(r, "+OK %d %d\r\n", num, size);
533 ap_rflush(r);
534 return OK;
535 }
536
537 int ap_handle_uidl(request_rec *r, char *buffer)
538 {
539 char *num;
540 pop_user_rec *ur = (pop_user_rec *)ap_get_module_config(r->request_config,
541 &pop_module);
542
543 num = ap_getword_white_nc(r->pool, &buffer);
544 if (!strcmp(num, "")) {
545 pop_msg *msg;
546 int last_msg, i;
547
548 if (!APR_RING_EMPTY(&(ur->mbox)->list, pop_msg, link)) {
549 msg = APR_RING_LAST(&(ur->mbox)->list);
550 last_msg = msg->id;
551 }
552 else {
553 ap_rputs("-ERR No messages in maildrop\r\n", r);
554 ap_rflush(r);
555 return APR_SUCCESS;
556 }
557
558 for (i = 1; i <= last_msg; i++) {
559 generate_unique_listing(r, ur->mbox, i, 0);
560 }
561 ap_rwrite(".\r\n", strlen(".\r\n"), r);
562 ap_rflush(r);
563 }
564 else {
565 generate_unique_listing(r, ur->mbox, atoi(num), 1);
566 ap_rflush(r);
567 }
568 return OK;
569 }
570
571 int ap_handle_top(request_rec *r, char *buffer)
572 {
573 const char *msgnum, *lines;
574 apr_size_t bytes_sent;
575 int i;
576 pop_msg *msg;
577 apr_off_t off;
578 pop_user_rec *ur = (pop_user_rec *)ap_get_module_config(r->request_config,
579 &pop_module);
580
581 msgnum = ap_getword_white_nc(r->pool, &buffer);
582 lines = ap_getword_white_nc(r->pool, &buffer);
583 if (!strcmp(msgnum, "") || !strcmp(lines, "")) {
584 ap_rputs("-ERR TOP requires two arguments\r\n", r);
585 ap_rflush(r);
586 return POP_BAD_STATE;
587 }
588
589 msg = find_message(r, ur->mbox, atoi(msgnum), 1);
590 if (msg == NULL) {
591 ap_rflush(r);
592 return POP_BAD_STATE;
593 }
594
595 ap_rputs("+OK\r\n", r);
596 ap_send_fd(ur->fp, r, msg->header_start,
597 msg->header_end - msg->header_start, &bytes_sent);
598 ap_rputs("\r\n", r);
599 off = msg->msg_start;
600 apr_file_seek(ur->fp, APR_SET, &off);
601 bytes_sent = 0;
602
603 for (i = 0; i < atoi(lines); i++) {
604 char str[8192];
605 int bytes = 8192;
606
607 apr_file_gets(str, bytes, ur->fp);
608 bytes_sent += strlen(str);
609 ap_rwrite(str, strlen(str), r);
610 if (bytes_sent >= (msg->msg_end - msg->msg_start)) {
611 break;
612 }
613 }
614 ap_rputs(".\r\n", r);
615 ap_rflush(r);
616
617 return OK;
618 }

Properties

Name Value
svn:eol-style native

infrastructure at apache.org
ViewVC Help
Powered by ViewVC 1.1.26