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 |
} |