* New alpha version 2.24.1
[alpine.git] / pith / detach.c
blobd664a1127d4d2a66a8e298c19d83f006c7d0a1fc
1 #if !defined(lint) && !defined(DOS)
2 static char rcsid[] = "$Id: detach.c 1074 2008-06-04 00:08:43Z hubert@u.washington.edu $";
3 #endif
5 /*
6 * ========================================================================
7 * Copyright 2013-2021 Eduardo Chappa
8 * Copyright 2006-2008 University of Washington
10 * Licensed under the Apache License, Version 2.0 (the "License");
11 * you may not use this file except in compliance with the License.
12 * You may obtain a copy of the License at
14 * http://www.apache.org/licenses/LICENSE-2.0
16 * ========================================================================
19 #include "../pith/headers.h"
20 #include "../pith/detach.h"
21 #include "../pith/state.h"
22 #include "../pith/conf.h"
23 #include "../pith/store.h"
24 #include "../pith/filter.h"
25 #include "../pith/mailview.h"
26 #include "../pith/status.h"
27 #include "../pith/addrstring.h"
28 #include "../pith/bldaddr.h"
29 #include "../pith/mimedesc.h"
30 #include "../pith/adjtime.h"
31 #include "../pith/pipe.h"
32 #include "../pith/busy.h"
33 #include "../pith/signal.h"
34 #include "../pico/osdep/filesys.h"
38 * We need to define simple functions here for the piping and
39 * temporary storage object below. We can use the filter.c functions
40 * because they're already in use for the "putchar" function passed to
41 * detach.
43 static STORE_S *detach_so = NULL;
47 * The display filter is locally global because it's set in df_trigger_cmp
48 * which sniffs at lines of the unencoded segment...
50 typedef struct _trigger {
51 int (*cmp)(char *, char *);
52 char *text;
53 char *cmd;
54 struct _trigger *next;
55 } TRGR_S;
57 static char *display_filter;
58 static TRGR_S *df_trigger_list;
60 FETCH_READC_S *g_fr_desc;
62 #define INIT_FETCH_CHUNK ((unsigned long)(8 * 1024L))
63 #define MIN_FETCH_CHUNK ((unsigned long)(4 * 1024L))
64 #define MAX_FETCH_CHUNK ((unsigned long)(256 * 1024L))
65 #define TARGET_INTR_TIME ((unsigned long)2000000L) /* two seconds */
66 #define FETCH_READC g_fr_desc->readc
70 * Internal Prototypes
73 * This function is intentionally declared without an argument type so
74 * that warnings will go away in Windows. We're using gf_io_t for both
75 * input and output functions and the arguments aren't actually the
76 * same in the two cases. We should really have a read version and
77 * a write version of gf_io_t. That's why this is like this for now.
79 int detach_writec();
80 TRGR_S *build_trigger_list(void);
81 void blast_trigger_list(TRGR_S **);
82 int df_trigger_cmp(long, char *, LT_INS_S **, void *);
83 int df_trigger_cmp_text(char *, char *);
84 int df_trigger_cmp_lwsp(char *, char *);
85 int df_trigger_cmp_start(char *, char *);
86 int fetch_readc_cleanup(int);
87 char *fetch_gets(readfn_t, void *, unsigned long, GETS_DATA *);
88 int fetch_readc(unsigned char *);
92 /*----------------------------------------------------------------------
93 detach the given raw body part; don't do any decoding
95 Args: a bunch
97 Returns: NULL on success, error message otherwise
98 ----*/
99 char *
100 detach_raw(MAILSTREAM *stream, /* c-client stream to use */
101 long int msg_no, /* message number to deal with */
102 char *part_no, /* part number of message */
103 gf_io_t pc, /* where to put it */
104 int flags)
106 FETCH_READC_S *frd = (FETCH_READC_S *)fs_get(sizeof(FETCH_READC_S));
107 char *err = NULL;
108 int column = (flags & FM_DISPLAY) ? ps_global->ttyo->screen_cols : 80;
110 memset(g_fr_desc = frd, 0, sizeof(FETCH_READC_S));
111 frd->stream = stream;
112 frd->msgno = msg_no;
113 frd->section = part_no;
114 frd->size = 0; /* wouldn't be here otherwise */
115 frd->readc = fetch_readc;
116 frd->chunk = pine_mail_fetch_text(stream, msg_no, NULL, &frd->read, 0);
117 frd->endp = &frd->chunk[frd->read];
118 frd->chunkp = frd->chunk;
120 gf_filter_init();
121 if (!(flags & FM_NOWRAP))
122 gf_link_filter(gf_wrap, gf_wrap_filter_opt(column, column, NULL, 0,
123 (flags & FM_DISPLAY)
124 ? GFW_HANDLES : 0));
125 err = gf_pipe(FETCH_READC, pc);
127 return(err);
131 /*----------------------------------------------------------------------
132 detach the given body part using the given encoding
134 Args: a bunch
136 Returns: NULL on success, error message otherwise
137 ----*/
138 char *
139 detach(MAILSTREAM *stream, /* c-client stream to use */
140 long int msg_no, /* message number to deal with */
141 char *part_no, /* part number of message */
142 long int partial, /* if >0, limit read to this many bytes */
143 long int *len, /* returns bytes read in this arg */
144 gf_io_t pc, /* where to put it */
145 FILTLIST_S *aux_filters, /* null terminated array of filts */
146 long flags)
148 unsigned long rv;
149 unsigned long size;
150 long fetch_flags;
151 int we_cancel = 0, is_text;
152 char *status, trigger[MAILTMPLEN];
153 char *charset = NULL;
154 BODY *body;
155 static char err_string[100];
156 FETCH_READC_S fetch_part;
158 err_string[0] = '\0';
160 if(!ps_global->print && !pc_is_picotext(pc))
161 we_cancel = busy_cue(NULL, NULL, 1);
163 gf_filter_init(); /* prepare for filtering! */
165 if(!(body = mail_body(stream, msg_no, (unsigned char *) part_no)))
166 return(_("Can't find body for requested message"));
168 is_text = body->type == TYPETEXT;
170 size = body->size.bytes;
171 if(partial > 0L && partial < size)
172 size = partial;
174 fetch_flags = (flags & ~DT_NODFILTER);
175 fetch_readc_init(&fetch_part, stream, msg_no, part_no, body->size.bytes, partial,
176 fetch_flags);
177 rv = size ? size : 1;
179 switch(body->encoding) { /* handle decoding */
180 case ENC7BIT:
181 case ENC8BIT:
182 case ENCBINARY:
183 break;
185 case ENCBASE64:
186 gf_link_filter(gf_b64_binary, NULL);
187 break;
189 case ENCQUOTEDPRINTABLE:
190 gf_link_filter(gf_qp_8bit, NULL);
191 break;
193 case ENCOTHER:
194 default:
195 dprint((1, "detach: unknown CTE: \"%s\" (%d)\n",
196 (body->encoding <= ENCMAX
197 && body_encodings[body->encoding])
198 ? body_encodings[body->encoding]
199 : "BEYOND-KNOWN-TYPES",
200 body->encoding));
201 break;
204 /* convert all text to UTF-8 */
205 if(is_text & !(flags & (DT_BINARY | DT_EXTERNAL))){
206 charset = parameter_val(body->parameter, "charset");
209 * If the charset is unlabeled or unknown replace it
210 * with the user's configured unknown charset.
212 if(!charset || !strucmp(charset, UNKNOWN_CHARSET)
213 || !strucmp(charset, "MISSING_PARAMETER_VALUE")
214 || !strucmp(charset, "us-ascii")){
215 if(charset)
216 fs_give((void **) &charset);
218 if(ps_global->VAR_UNK_CHAR_SET)
219 charset = cpystr(ps_global->VAR_UNK_CHAR_SET);
222 /* some messages are mislabeled as iso-8859-1 when in reality
223 * they are windows-1252, so we treat them as windows-1252
224 * from the beginning. If we did not make this change, we might
225 * see '?' characters in the message, without an apparent explanation
226 * of this fact.
228 if(charset && !strucmp(charset, "iso-8859-1")){
229 fs_give((void **) &charset);
230 charset = cpystr("windows-1252");
233 /* convert to UTF-8 */
234 if(!(charset && !strucmp(charset, "utf-8")))
235 gf_link_filter(gf_utf8, gf_utf8_opt(charset));
237 if(charset)
238 fs_give((void **) &charset);
242 * If we're detaching a text segment and there are user-defined
243 * filters and there are text triggers to look for, install filter
244 * to let us look at each line...
246 display_filter = NULL;
247 if(is_text
248 && ps_global->tools.display_filter
249 && ps_global->tools.display_filter_trigger
250 && ps_global->VAR_DISPLAY_FILTERS
251 && !(flags & DT_NODFILTER)){
252 /* check for "static" triggers (i.e., none or CHARSET) */
253 if(!(display_filter = (*ps_global->tools.display_filter_trigger)(body, trigger, sizeof(trigger)))
254 && (df_trigger_list = build_trigger_list())){
255 /* else look for matching text trigger */
256 gf_link_filter(gf_line_test,
257 gf_line_test_opt(df_trigger_cmp, NULL));
260 else
261 /* add aux filters if we're not going to MIME decode into a temporary
262 * storage object, otherwise we pass the aux_filters on to gf_filter
263 * below so it can pass what comes out of the external filter command
264 * thru the rest of the filters...
266 for( ; aux_filters && aux_filters->filter; aux_filters++)
267 gf_link_filter(aux_filters->filter, aux_filters->data);
270 * Following canonical model, after decoding convert newlines from
271 * crlf to local convention. ALSO, convert newlines if we're fetching
272 * a multipart segment since an external handler's going to have to
273 * make sense of it...
275 if((is_text & !(flags & DT_BINARY))
276 || body->type == TYPEMESSAGE
277 || body->type == TYPEMULTIPART)
278 gf_link_filter(gf_nvtnl_local, NULL);
280 if(flags & DT_EXTERNAL){
281 char i = flags & DT_ALLIMAGES ? 'i' : '\0';
282 gf_link_filter(gf_html_cid2file, (void *) &i);
286 * If we're detaching a text segment and a user-defined filter may
287 * need to be invoked later (see below), decode the segment into
288 * a temporary storage object...
290 if(is_text
291 && ps_global->tools.display_filter
292 && ps_global->tools.display_filter_trigger
293 && ps_global->VAR_DISPLAY_FILTERS
294 && !(flags & DT_NODFILTER)
295 && !(detach_so = so_get(CharStar, NULL, EDIT_ACCESS))){
296 strncpy(err_string,
297 _("Formatting error: no space to make copy, no display filters used"), sizeof(err_string));
298 err_string[sizeof(err_string)-1] = '\0';
301 if((status = gf_pipe(FETCH_READC, detach_so ? detach_writec : pc)) != NULL) {
302 snprintf(err_string, sizeof(err_string), "Formatting error: %s", status);
303 rv = 0L;
307 * If we wrote to a temporary area, there MAY be a user-defined
308 * filter to invoke. Filter it it (or not if no trigger match)
309 * *AND* send the output thru any auxiliary filters, destroy the
310 * temporary object and be done with it...
312 if(detach_so){
313 if(!err_string[0] && display_filter && *display_filter){
314 FILTLIST_S *p, *aux = NULL;
315 size_t count;
317 if(aux_filters && !(flags & DT_BINARY)){
318 /* insert NL conversion filters around remaining aux_filters
319 * so they're not tripped up by local NL convention
321 for(p = aux_filters; p->filter; p++) /* count aux_filters */
324 count = (p - aux_filters) + 3;
325 p = aux = (FILTLIST_S *) fs_get(count * sizeof(FILTLIST_S));
326 memset(p, 0, count * sizeof(FILTLIST_S));
327 p->filter = gf_local_nvtnl;
328 p++;
329 for(; aux_filters->filter; p++, aux_filters++)
330 *p = *aux_filters;
332 p->filter = gf_nvtnl_local;
335 if((status = (*ps_global->tools.display_filter)(display_filter, detach_so, pc, aux)) != NULL){
336 snprintf(err_string, sizeof(err_string), "Formatting error: %s", status);
337 rv = 0L;
340 if(aux)
341 fs_give((void **)&aux);
343 else{ /* just copy it, then */
344 gf_io_t gc;
346 gf_set_so_readc(&gc, detach_so);
347 so_seek(detach_so, 0L, 0);
348 gf_filter_init();
349 if(aux_filters){
350 /* if other filters are involved, correct for
351 * newlines on either side of the pipe...
353 gf_link_filter(gf_local_nvtnl, NULL);
354 for( ; aux_filters->filter ; aux_filters++)
355 gf_link_filter(aux_filters->filter, aux_filters->data);
357 if(!(flags & DT_BINARY))
358 gf_link_filter(gf_nvtnl_local, NULL);
361 if((status = gf_pipe(gc, pc)) != NULL){ /* Second pass, sheesh */
362 snprintf(err_string, sizeof(err_string), "Formatting error: %s", status);
363 rv = 0L;
366 gf_clear_so_readc(detach_so);
369 so_give(&detach_so); /* blast temp copy */
372 if(!ps_global->print && we_cancel)
373 cancel_busy_cue(0);
375 if (len)
376 *len = rv;
378 if(df_trigger_list)
379 blast_trigger_list(&df_trigger_list);
381 return((err_string[0] == '\0') ? NULL : err_string);
386 detach_writec(int c)
388 return(so_writec(c, detach_so));
393 * build_trigger_list - return possible triggers in a list of triggers
394 * structs
396 TRGR_S *
397 build_trigger_list(void)
399 TRGR_S *tp = NULL, **trailp;
400 char **l, *test, *str, *ep, *cmd = NULL;
401 int i;
403 trailp = &tp;
404 for(l = ps_global->VAR_DISPLAY_FILTERS ; l && *l; l++){
405 get_pair(*l, &test, &cmd, 1, 1);
406 if(test && valid_filter_command(&cmd)){
407 *trailp = (TRGR_S *) fs_get(sizeof(TRGR_S));
408 (*trailp)->cmp = df_trigger_cmp_text;
409 str = test;
410 if(*test == '_' && (i = strlen(test)) > 10
411 && *(ep = &test[i-1]) == '_' && *--ep == ')'){
412 if(struncmp(test, "_CHARSET(", 9) == 0){
413 fs_give((void **)&test);
414 fs_give((void **)&cmd);
415 fs_give((void **)trailp);
416 continue;
419 if(strncmp(test+1, "LEADING(", 8) == 0){
420 (*trailp)->cmp = df_trigger_cmp_lwsp;
421 *ep = '\0';
422 str = cpystr(test+9);
423 fs_give((void **)&test);
425 else if(strncmp(test+1, "BEGINNING(", 10) == 0){
426 (*trailp)->cmp = df_trigger_cmp_start;
427 *ep = '\0';
428 str = cpystr(test+11);
429 fs_give((void **)&test);
433 (*trailp)->text = str;
434 (*trailp)->cmd = cmd;
435 *(trailp = &(*trailp)->next) = NULL;
437 else{
438 fs_give((void **)&test);
439 fs_give((void **)&cmd);
443 return(tp);
448 * blast_trigger_list - zot any list of triggers we've been using
450 void
451 blast_trigger_list(TRGR_S **tlist)
453 if((*tlist)->next)
454 blast_trigger_list(&(*tlist)->next);
456 fs_give((void **)&(*tlist)->text);
457 fs_give((void **)&(*tlist)->cmd);
458 fs_give((void **)tlist);
463 * df_trigger_cmp - compare the line passed us with the list of defined
464 * display filter triggers
467 df_trigger_cmp(long int n, char *s, LT_INS_S **e, void *l)
469 register TRGR_S *tp;
470 int result;
472 if(!display_filter) /* already found? */
473 for(tp = df_trigger_list; tp; tp = tp->next)
474 if(tp->cmp){
475 if((result = (*tp->cmp)(s, tp->text)) < 0)
476 tp->cmp = NULL;
477 else if(result > 0)
478 return(((display_filter = tp->cmd) != NULL) ? 1 : 0);
481 return(0);
486 * df_trigger_cmp_text - return 1 if s1 is in s2
489 df_trigger_cmp_text(char *s1, char *s2)
491 return(strstr(s1, s2) != NULL);
496 * df_trigger_cmp_lwsp - compare the line passed us with the list of defined
497 * display filter triggers. returns:
499 * 0 if we don't know yet
500 * 1 if we match
501 * -1 if we clearly don't match
504 df_trigger_cmp_lwsp(char *s1, char *s2)
506 while(*s1 && isspace((unsigned char)*s1))
507 s1++;
509 return((*s1) ? (!strncmp(s1, s2, strlen(s2)) ? 1 : -1) : 0);
514 * df_trigger_cmp_start - return 1 if first strlen(s2) chars start s1
517 df_trigger_cmp_start(char *s1, char *s2)
519 return(!strncmp(s1, s2, strlen(s2)));
524 * valid_filter_command - make sure argv[0] of command really exists.
525 * "cmd" is required to be an alloc'd string since
526 * it will get realloc'd if the command's path is
527 * expanded.
530 valid_filter_command(char **cmd)
532 int i;
533 char cpath[MAXPATH+1], *p;
535 if(!(cmd && *cmd))
536 return(FALSE);
539 * copy cmd to build expanded path if necessary.
541 for(i = 0; i < sizeof(cpath) && (cpath[i] = (*cmd)[i]); i++)
542 if(isspace((unsigned char)(*cmd)[i])){
543 cpath[i] = '\0'; /* tie off command's path*/
544 break;
547 #if defined(DOS) || defined(OS2)
548 if(is_absolute_path(cpath)){
549 size_t l;
551 fixpath(cpath, sizeof(cpath));
552 l = strlen(cpath) + strlen(&(*cmd)[i]);
553 p = (char *) fs_get((l+1) * sizeof(char));
554 strncpy(p, cpath, l); /* copy new path */
555 p[l] = '\0';
556 strncat(p, &(*cmd)[i], l+1-1-strlen(p)); /* and old args */
557 p[l] = '\0';
558 fs_give((void **) cmd); /* free it */
559 *cmd = p; /* and assign new buf */
561 #else
562 if(cpath[0] == '~'){
563 if(fnexpand(cpath, sizeof(cpath))){
564 size_t l;
566 l = strlen(cpath) + strlen(&(*cmd)[i]);
567 p = (char *) fs_get((l+1) * sizeof(char));
568 strncpy(p, cpath, l); /* copy new path */
569 p[l] = '\0';
570 strncat(p, &(*cmd)[i], l+1-1-strlen(p)); /* and old args */
571 p[l] = '\0';
572 fs_give((void **) cmd); /* free it */
573 *cmd = p; /* and assign new buf */
575 else
576 return(FALSE);
578 #endif
580 return(is_absolute_path(cpath) && can_access(cpath, EXECUTE_ACCESS) == 0);
584 void
585 fetch_readc_init(FETCH_READC_S *frd, MAILSTREAM *stream, long int msgno,
586 char *section, unsigned long size, long partial, long int flags)
588 int nointr = 0;
590 nointr = flags & DT_NOINTR;
591 flags &= ~DT_NOINTR;
593 memset(g_fr_desc = frd, 0, sizeof(FETCH_READC_S));
594 frd->stream = stream;
595 frd->msgno = msgno;
596 frd->section = section;
597 frd->flags = flags;
598 frd->size = size;
599 frd->readc = fetch_readc;
601 #ifdef SMIME
603 * The call to imap_cache below will return true in the case where
604 * we've already stashed fake data in the content of the part.
605 * This happens when an S/MIME message is decrypted.
607 #endif
609 if(modern_imap_stream(stream)
610 && !imap_cache(stream, msgno, section, NULL, NULL)
611 && (size > INIT_FETCH_CHUNK || (partial > 0L && partial < size))
612 && (F_OFF(F_QUELL_PARTIAL_FETCH, ps_global)
614 #ifdef _WINDOWS
615 F_ON(F_QUELL_SSL_LARGEBLOCKS, ps_global)
616 #else
618 #endif
621 if(partial > 0L && partial < size){
622 /* partial fetch is being asked for */
623 frd->size = partial;
626 frd->allocsize = MIN(INIT_FETCH_CHUNK,frd->size);
627 frd->chunk = (char *) fs_get ((frd->allocsize + 1) * sizeof(char));
628 frd->chunksize = frd->allocsize/2; /* this gets doubled 1st time */
629 frd->endp = frd->chunk;
630 frd->free_me = 1;
632 if(!nointr)
633 if(intr_handling_on())
634 frd->we_turned_on = 1;
636 if(!(partial > 0L && partial < size)){
637 frd->cache = so_get(CharStar, NULL, EDIT_ACCESS);
638 so_truncate(frd->cache, size); /* pre-allocate */
641 else{ /* fetch the whole bloody thing here */
642 frd->chunk = mail_fetch_body(stream, msgno, section, &frd->read, flags);
644 /* This only happens if the server gave us a bogus size */
645 if(partial > 0L && partial < size){
646 /* partial fetch is being asked for */
647 frd->size = partial;
648 frd->endp = &frd->chunk[frd->size];
650 else if(size != frd->read){
651 dprint((1,
652 "fetch_readc_init: size mismatch: size=%lu read=%lu, continue...\n",
653 frd->size, frd->read));
654 q_status_message(SM_ORDER | SM_DING, 0, 3,
655 _("Message size does not match expected size, continuing..."));
656 frd->size = MIN(size, frd->read);
657 frd->endp = &frd->chunk[frd->read];
659 else
660 frd->endp = &frd->chunk[frd->read];
663 frd->chunkp = frd->chunk;
668 fetch_readc_cleanup(int store)
670 if(g_fr_desc){
671 if(g_fr_desc->we_turned_on)
672 intr_handling_off();
674 if(g_fr_desc->chunk && g_fr_desc->free_me)
675 fs_give((void **) &g_fr_desc->chunk);
677 if(g_fr_desc->cache && store){
678 SIZEDTEXT text;
680 text.size = g_fr_desc->size;
681 text.data = (unsigned char *) so_text(g_fr_desc->cache);
682 imap_cache(g_fr_desc->stream, g_fr_desc->msgno,
683 g_fr_desc->section, NULL, &text);
684 g_fr_desc->cache->txt = (void *) NULL;
685 so_give(&g_fr_desc->cache);
689 return(0);
693 char *
694 fetch_gets(readfn_t f, void *stream, long unsigned int size, GETS_DATA *md)
696 unsigned long n;
698 n = MIN(g_fr_desc->chunksize, size);
699 g_fr_desc->read += n;
700 g_fr_desc->endp = &g_fr_desc->chunk[n];
702 (*f) (stream, n, g_fr_desc->chunkp = g_fr_desc->chunk);
704 if(g_fr_desc->cache)
705 so_nputs(g_fr_desc->cache, g_fr_desc->chunk, (long) n);
707 /* BUG: need to read requested "size" in case it's larger than chunk? */
709 return(NULL);
714 fetch_readc(unsigned char *c)
716 extern void gf_error(char *);
718 if(ps_global->intr_pending){
719 (void) fetch_readc_cleanup(0);
720 /* TRANSLATORS: data transfer was interrupted by something */
721 gf_error(g_fr_desc->error ? g_fr_desc->error :_("Transfer interrupted!"));
722 /* no return */
724 else if(g_fr_desc->chunkp == g_fr_desc->endp){
726 /* Anything to read, do it */
727 if(g_fr_desc->read < g_fr_desc->size){
728 void *old_gets;
729 int rv;
730 TIMEVAL_S before, after;
731 long diff, wdiff;
732 unsigned long save_read;
734 old_gets = mail_parameters(g_fr_desc->stream, GET_GETS,
735 (void *)NULL);
736 mail_parameters(g_fr_desc->stream, SET_GETS, (void *) fetch_gets);
739 * Adjust chunksize with the goal that it will be about
740 * TARGET_INTR_TIME useconds +- 20%
741 * to finish the partial fetch. We want that time
742 * to be small so that interrupts will happen fast, but we want
743 * the chunksize large so that the whole fetch will happen
744 * fast. So it's a tradeoff between those two things.
746 * If the estimated fetchtime is getting too large, we
747 * half the chunksize. If it is small, we double
748 * the chunksize. If it is in between, we leave it. There is
749 * some risk of oscillating between two values, but who cares?
751 if(g_fr_desc->fetchtime <
752 TARGET_INTR_TIME - TARGET_INTR_TIME/5)
753 g_fr_desc->chunksize *= 2;
754 else if(g_fr_desc->fetchtime >
755 TARGET_INTR_TIME + TARGET_INTR_TIME/5)
756 g_fr_desc->chunksize /= 2;
758 g_fr_desc->chunksize = MIN(MAX_FETCH_CHUNK,
759 MAX(MIN_FETCH_CHUNK,
760 g_fr_desc->chunksize));
762 #ifdef _WINDOWS
764 * If this feature is set, limit the max size to less than
765 * 16K - 5, the magic number that avoids Microsoft's bug.
766 * Let's just go with 12K instead of 16K - 5.
768 if(F_ON(F_QUELL_SSL_LARGEBLOCKS, ps_global))
769 g_fr_desc->chunksize =
770 MIN(AVOID_MICROSOFT_SSL_CHUNKING_BUG, g_fr_desc->chunksize);
771 #endif
773 /* don't ask for more than there should be left to ask for */
774 g_fr_desc->chunksize =
775 MIN(g_fr_desc->size - g_fr_desc->read, g_fr_desc->chunksize);
778 * If chunksize grew, reallocate chunk.
780 if(g_fr_desc->chunksize > g_fr_desc->allocsize){
781 g_fr_desc->allocsize = g_fr_desc->chunksize;
782 fs_give((void **) &g_fr_desc->chunk);
783 g_fr_desc->chunk = (char *) fs_get ((g_fr_desc->allocsize + 1)
784 * sizeof(char));
785 g_fr_desc->endp = g_fr_desc->chunk;
786 g_fr_desc->chunkp = g_fr_desc->chunk;
789 save_read = g_fr_desc->read;
790 (void)get_time(&before);
792 rv = mail_partial_body(g_fr_desc->stream, g_fr_desc->msgno,
793 g_fr_desc->section, g_fr_desc->read,
794 g_fr_desc->chunksize, g_fr_desc->flags);
797 * If the amount we actually read is less than the amount we
798 * asked for we assume that is because the server gave us a
799 * bogus size when we originally asked for it.
801 if(g_fr_desc->chunksize > (g_fr_desc->read - save_read)){
802 dprint((1,
803 "partial_body returned less than asked for: asked=%lu got=%lu, continue...\n",
804 g_fr_desc->chunksize, g_fr_desc->read - save_read));
805 if(g_fr_desc->read - save_read > 0)
806 q_status_message(SM_ORDER | SM_DING, 0, 3,
807 _("Message size does not match expected size, continuing..."));
808 else{
809 rv = 0;
810 q_status_message(SM_ORDER | SM_DING, 3, 3,
811 _("Server returns zero bytes, Quell-Partial-Fetch feature may help"));
814 g_fr_desc->size = g_fr_desc->read;
817 if(get_time(&after) == 0){
818 diff = time_diff(&after, &before);
819 wdiff = MIN(TARGET_INTR_TIME + TARGET_INTR_TIME/2,
820 MAX(TARGET_INTR_TIME - TARGET_INTR_TIME/2, diff));
822 * Fetchtime is an exponentially weighted average of the number
823 * of usecs it takes to do a single fetch of whatever the
824 * current chunksize is. Since the fetch time probably isn't
825 * simply proportional to the chunksize, we don't try to
826 * calculate a chunksize by keeping track of the bytes per
827 * second. Instead, we just double or half the chunksize if
828 * we are too fast or too slow. That happens the next time
829 * through the loop a few lines up.
830 * Too settle it down a bit, Windsorize the mean.
832 g_fr_desc->fetchtime = (g_fr_desc->fetchtime == 0)
833 ? wdiff
834 : g_fr_desc->fetchtime/2 + wdiff/2;
835 dprint((8,
836 "fetch: diff=%ld wdiff=%ld fetchave=%ld prev chunksize=%ld\n",
837 diff, wdiff, g_fr_desc->fetchtime, g_fr_desc->chunksize));
839 else /* just set it so it won't affect anything */
840 g_fr_desc->fetchtime = TARGET_INTR_TIME;
842 /* UNinstall mailgets */
843 mail_parameters(g_fr_desc->stream, SET_GETS, old_gets);
845 if(!rv){
846 (void) fetch_readc_cleanup(0);
847 gf_error("Partial fetch failed!");
848 /* no return */
851 else /* clean up and return done. */
852 return(fetch_readc_cleanup(1));
855 *c = *g_fr_desc->chunkp++;
857 return(1);