1 #if !defined(lint) && !defined(DOS)
2 static char rcsid
[] = "$Id: detach.c 1074 2008-06-04 00:08:43Z hubert@u.washington.edu $";
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
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 *);
54 struct _trigger
*next
;
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
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.
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
97 Returns: NULL on success, error message otherwise
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 */
106 FETCH_READC_S
*frd
= (FETCH_READC_S
*)fs_get(sizeof(FETCH_READC_S
));
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
;
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
;
121 if (!(flags
& FM_NOWRAP
))
122 gf_link_filter(gf_wrap
, gf_wrap_filter_opt(column
, column
, NULL
, 0,
125 err
= gf_pipe(FETCH_READC
, pc
);
131 /*----------------------------------------------------------------------
132 detach the given body part using the given encoding
136 Returns: NULL on success, error message otherwise
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 */
151 int we_cancel
= 0, is_text
;
152 char *status
, trigger
[MAILTMPLEN
];
153 char *charset
= NULL
;
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
)
174 fetch_flags
= (flags
& ~DT_NODFILTER
);
175 fetch_readc_init(&fetch_part
, stream
, msg_no
, part_no
, body
->size
.bytes
, partial
,
177 rv
= size
? size
: 1;
179 switch(body
->encoding
) { /* handle decoding */
186 gf_link_filter(gf_b64_binary
, NULL
);
189 case ENCQUOTEDPRINTABLE
:
190 gf_link_filter(gf_qp_8bit
, NULL
);
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",
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")){
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
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
));
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
;
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
));
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...
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
))){
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
);
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...
313 if(!err_string
[0] && display_filter
&& *display_filter
){
314 FILTLIST_S
*p
, *aux
= NULL
;
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
;
329 for(; aux_filters
->filter
; 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
);
341 fs_give((void **)&aux
);
343 else{ /* just copy it, then */
346 gf_set_so_readc(&gc
, detach_so
);
347 so_seek(detach_so
, 0L, 0);
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
);
366 gf_clear_so_readc(detach_so
);
369 so_give(&detach_so
); /* blast temp copy */
372 if(!ps_global
->print
&& we_cancel
)
379 blast_trigger_list(&df_trigger_list
);
381 return((err_string
[0] == '\0') ? NULL
: err_string
);
388 return(so_writec(c
, detach_so
));
393 * build_trigger_list - return possible triggers in a list of triggers
397 build_trigger_list(void)
399 TRGR_S
*tp
= NULL
, **trailp
;
400 char **l
, *test
, *str
, *ep
, *cmd
= NULL
;
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
;
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
);
419 if(strncmp(test
+1, "LEADING(", 8) == 0){
420 (*trailp
)->cmp
= df_trigger_cmp_lwsp
;
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
;
428 str
= cpystr(test
+11);
429 fs_give((void **)&test
);
433 (*trailp
)->text
= str
;
434 (*trailp
)->cmd
= cmd
;
435 *(trailp
= &(*trailp
)->next
) = NULL
;
438 fs_give((void **)&test
);
439 fs_give((void **)&cmd
);
448 * blast_trigger_list - zot any list of triggers we've been using
451 blast_trigger_list(TRGR_S
**tlist
)
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
)
472 if(!display_filter
) /* already found? */
473 for(tp
= df_trigger_list
; tp
; tp
= tp
->next
)
475 if((result
= (*tp
->cmp
)(s
, tp
->text
)) < 0)
478 return(((display_filter
= tp
->cmd
) != NULL
) ? 1 : 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
501 * -1 if we clearly don't match
504 df_trigger_cmp_lwsp(char *s1
, char *s2
)
506 while(*s1
&& isspace((unsigned char)*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
530 valid_filter_command(char **cmd
)
533 char cpath
[MAXPATH
+1], *p
;
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*/
547 #if defined(DOS) || defined(OS2)
548 if(is_absolute_path(cpath
)){
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 */
556 strncat(p
, &(*cmd
)[i
], l
+1-1-strlen(p
)); /* and old args */
558 fs_give((void **) cmd
); /* free it */
559 *cmd
= p
; /* and assign new buf */
563 if(fnexpand(cpath
, sizeof(cpath
))){
566 l
= strlen(cpath
) + strlen(&(*cmd
)[i
]);
567 p
= (char *) fs_get((l
+1) * sizeof(char));
568 strncpy(p
, cpath
, l
); /* copy new path */
570 strncat(p
, &(*cmd
)[i
], l
+1-1-strlen(p
)); /* and old args */
572 fs_give((void **) cmd
); /* free it */
573 *cmd
= p
; /* and assign new buf */
580 return(is_absolute_path(cpath
) && can_access(cpath
, EXECUTE_ACCESS
) == 0);
585 fetch_readc_init(FETCH_READC_S
*frd
, MAILSTREAM
*stream
, long int msgno
,
586 char *section
, unsigned long size
, long partial
, long int flags
)
590 nointr
= flags
& DT_NOINTR
;
593 memset(g_fr_desc
= frd
, 0, sizeof(FETCH_READC_S
));
594 frd
->stream
= stream
;
596 frd
->section
= section
;
599 frd
->readc
= fetch_readc
;
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.
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
)
615 F_ON(F_QUELL_SSL_LARGEBLOCKS
, ps_global
)
621 if(partial
> 0L && partial
< size
){
622 /* partial fetch is being asked for */
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
;
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 */
648 frd
->endp
= &frd
->chunk
[frd
->size
];
650 else if(size
!= frd
->read
){
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
];
660 frd
->endp
= &frd
->chunk
[frd
->read
];
663 frd
->chunkp
= frd
->chunk
;
668 fetch_readc_cleanup(int store
)
671 if(g_fr_desc
->we_turned_on
)
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
){
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
);
694 fetch_gets(readfn_t f
, void *stream
, long unsigned int size
, GETS_DATA
*md
)
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
);
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? */
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!"));
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
){
730 TIMEVAL_S before
, after
;
732 unsigned long save_read
;
734 old_gets
= mail_parameters(g_fr_desc
->stream
, GET_GETS
,
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
,
760 g_fr_desc
->chunksize
));
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
);
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)
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
)){
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..."));
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)
834 : g_fr_desc
->fetchtime
/2 + wdiff
/2;
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
);
846 (void) fetch_readc_cleanup(0);
847 gf_error("Partial fetch failed!");
851 else /* clean up and return done. */
852 return(fetch_readc_cleanup(1));
855 *c
= *g_fr_desc
->chunkp
++;