1 #if !defined(lint) && !defined(DOS)
2 static char rcsid
[] = "$Id: save.c 1204 2009-02-02 19:54:23Z hubert@u.washington.edu $";
6 * ========================================================================
7 * Copyright 2006-2009 University of Washington
8 * Copyright 2013-2020 Eduardo Chappa
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/save.h"
21 #include "../pith/state.h"
22 #include "../pith/conf.h"
23 #include "../pith/mimedesc.h"
24 #include "../pith/filter.h"
25 #include "../pith/context.h"
26 #include "../pith/folder.h"
27 #include "../pith/copyaddr.h"
28 #include "../pith/mailview.h"
29 #include "../pith/mailcmd.h"
30 #include "../pith/bldaddr.h"
31 #include "../pith/flag.h"
32 #include "../pith/status.h"
33 #include "../pith/ablookup.h"
34 #include "../pith/news.h"
35 #include "../pith/util.h"
36 #include "../pith/reply.h"
37 #include "../pith/sequence.h"
38 #include "../pith/stream.h"
39 #include "../pith/options.h"
45 int save_ex_replace_body(char *, unsigned long *,BODY
*,gf_io_t
);
46 int save_ex_output_body(MAILSTREAM
*, long, char *, BODY
*, unsigned long *, gf_io_t
);
47 int save_ex_mask_types(char *, unsigned long *, gf_io_t
);
48 int save_ex_explain_body(BODY
*, unsigned long *, gf_io_t
);
49 int save_ex_explain_parts(BODY
*, int, unsigned long *, gf_io_t
);
50 int save_ex_output_line(char *, unsigned long *, gf_io_t
);
51 int save_ex_output_text(char *, int, unsigned long *, gf_io_t
);
57 int (*pith_opt_save_create_prompt
)(CONTEXT_S
*, char *, int);
58 int (*pith_opt_save_size_changed_prompt
)(long, int);
62 /*----------------------------------------------------------------------
63 save_get_default - return default folder name for saving
66 save_get_default(struct pine
*state
, ENVELOPE
*e
, long int rawno
,
67 char *section
, CONTEXT_S
**cntxt
)
74 context_was_set
= ((*cntxt
) != NULL
);
76 /* start with the default save context */
78 && ((*cntxt
) = default_save_context(state
->context_list
)) == NULL
)
79 (*cntxt
) = state
->context_list
;
81 if(!e
|| ps_global
->save_msg_rule
== SAV_RULE_LAST
82 || ps_global
->save_msg_rule
== SAV_RULE_DEFLT
){
83 if(ps_global
->save_msg_rule
== SAV_RULE_LAST
&& ps_global
->last_save_context
){
85 (*cntxt
) = ps_global
->last_save_context
;
88 strncpy(ps_global
->last_save_folder
,
89 ps_global
->VAR_DEFAULT_SAVE_FOLDER
,
90 sizeof(ps_global
->last_save_folder
)-1);
91 ps_global
->last_save_folder
[sizeof(ps_global
->last_save_folder
)-1] = '\0';
94 * If the user entered "inbox" as their default save folder it is very
95 * likely they meant the real inbox, not the inbox in the primary collection.
98 && !strucmp(ps_global
->inbox_name
, ps_global
->last_save_folder
))
99 (*cntxt
) = state
->context_list
;
103 save_get_fldr_from_env(ps_global
->last_save_folder
,
104 sizeof(ps_global
->last_save_folder
),
105 e
, state
, rawno
, section
);
106 /* somebody expunged current message */
107 if(sp_expunge_count(ps_global
->mail_stream
))
111 return(ps_global
->last_save_folder
);
116 /*----------------------------------------------------------------------
117 Grope through envelope to find default folder name to save to
119 Args: fbuf -- Buffer to return result in
120 nfbuf -- Size of fbuf
121 e -- The envelope to look in
122 state -- Usual pine state
123 rawmsgno -- Raw c-client sequence number of message
124 section -- Mime section of header data (for message/rfc822)
126 Result: The appropriate default folder name is copied into fbuf.
129 save_get_fldr_from_env(char *fbuf
, int nfbuf
, ENVELOPE
*e
, struct pine
*state
,
130 long int rawmsgno
, char *section
)
133 ADDRESS
*tmp_adr
= NULL
;
134 char buf
[MAX(MAXFOLDER
,MAX_NICKNAME
) + 1];
136 char *folder_name
= NULL
;
137 static char botch
[] = "programmer botch, unknown message save rule";
138 unsigned save_msg_rule
;
143 /* copy this because we might change it below */
144 save_msg_rule
= state
->save_msg_rule
;
146 /* first get the relevant address to base the folder name on */
147 switch(save_msg_rule
){
149 case SAV_RULE_NICK_FROM
:
150 case SAV_RULE_NICK_FROM_DEF
:
151 case SAV_RULE_FCC_FROM
:
152 case SAV_RULE_FCC_FROM_DEF
:
153 case SAV_RULE_RN_FROM
:
154 case SAV_RULE_RN_FROM_DEF
:
155 tmp_adr
= e
->from
? copyaddr(e
->from
)
156 : e
->sender
? copyaddr(e
->sender
) : NULL
;
159 case SAV_RULE_SENDER
:
160 case SAV_RULE_NICK_SENDER
:
161 case SAV_RULE_NICK_SENDER_DEF
:
162 case SAV_RULE_FCC_SENDER
:
163 case SAV_RULE_FCC_SENDER_DEF
:
164 case SAV_RULE_RN_SENDER
:
165 case SAV_RULE_RN_SENDER_DEF
:
166 tmp_adr
= e
->sender
? copyaddr(e
->sender
)
167 : e
->from
? copyaddr(e
->from
) : NULL
;
170 case SAV_RULE_REPLYTO
:
171 case SAV_RULE_NICK_REPLYTO
:
172 case SAV_RULE_NICK_REPLYTO_DEF
:
173 case SAV_RULE_FCC_REPLYTO
:
174 case SAV_RULE_FCC_REPLYTO_DEF
:
175 case SAV_RULE_RN_REPLYTO
:
176 case SAV_RULE_RN_REPLYTO_DEF
:
177 tmp_adr
= e
->reply_to
? copyaddr(e
->reply_to
)
178 : e
->from
? copyaddr(e
->from
)
179 : e
->sender
? copyaddr(e
->sender
) : NULL
;
183 case SAV_RULE_NICK_RECIP
:
184 case SAV_RULE_NICK_RECIP_DEF
:
185 case SAV_RULE_FCC_RECIP
:
186 case SAV_RULE_FCC_RECIP_DEF
:
187 case SAV_RULE_RN_RECIP
:
188 case SAV_RULE_RN_RECIP_DEF
:
190 if(state
->mail_stream
&& IS_NEWS(state
->mail_stream
)){
191 char *tmp_a_string
, *ng_name
;
194 fakedomain
[1] = '\0';
196 /* find the news group name */
197 if((ng_name
= strstr(state
->mail_stream
->mailbox
,"#news")) != NULL
)
200 ng_name
= state
->mail_stream
->mailbox
; /* shouldn't happen */
202 /* copy this string so rfc822_parse_adrlist can't blast it */
203 tmp_a_string
= cpystr(ng_name
);
205 rfc822_parse_adrlist(&tmp_adr
, tmp_a_string
, fakedomain
);
206 fs_give((void **)&tmp_a_string
);
207 if(tmp_adr
&& tmp_adr
->host
&& tmp_adr
->host
[0] == '@')
208 tmp_adr
->host
[0] = '\0';
212 static char *fields
[] = {"Resent-To", NULL
};
213 char *extras
, *values
[sizeof(fields
)/sizeof(fields
[0])];
215 extras
= pine_fetchheader_lines(state
->mail_stream
, rawmsgno
,
220 memset(values
, 0, sizeof(fields
));
221 simple_header_parse(extras
, fields
, values
);
222 fs_give((void **)&extras
);
224 for(i
= 0; i
< sizeof(fields
)/sizeof(fields
[0]); i
++)
226 if(tmp_adr
) /* take last matching value */
227 mail_free_address(&tmp_adr
);
229 /* build a temporary address list */
231 fakedomain
[1] = '\0';
232 rfc822_parse_adrlist(&tmp_adr
, values
[i
], fakedomain
);
233 fs_give((void **)&values
[i
]);
238 tmp_adr
= e
->to
? copyaddr(e
->to
) : NULL
;
248 /* For that address, lookup the fcc or nickname from address book */
249 switch(save_msg_rule
){
250 case SAV_RULE_NICK_FROM
:
251 case SAV_RULE_NICK_SENDER
:
252 case SAV_RULE_NICK_REPLYTO
:
253 case SAV_RULE_NICK_RECIP
:
254 case SAV_RULE_FCC_FROM
:
255 case SAV_RULE_FCC_SENDER
:
256 case SAV_RULE_FCC_REPLYTO
:
257 case SAV_RULE_FCC_RECIP
:
258 case SAV_RULE_NICK_FROM_DEF
:
259 case SAV_RULE_NICK_SENDER_DEF
:
260 case SAV_RULE_NICK_REPLYTO_DEF
:
261 case SAV_RULE_NICK_RECIP_DEF
:
262 case SAV_RULE_FCC_FROM_DEF
:
263 case SAV_RULE_FCC_SENDER_DEF
:
264 case SAV_RULE_FCC_REPLYTO_DEF
:
265 case SAV_RULE_FCC_RECIP_DEF
:
266 switch(save_msg_rule
){
267 case SAV_RULE_NICK_FROM
:
268 case SAV_RULE_NICK_SENDER
:
269 case SAV_RULE_NICK_REPLYTO
:
270 case SAV_RULE_NICK_RECIP
:
271 case SAV_RULE_NICK_FROM_DEF
:
272 case SAV_RULE_NICK_SENDER_DEF
:
273 case SAV_RULE_NICK_REPLYTO_DEF
:
274 case SAV_RULE_NICK_RECIP_DEF
:
275 bufp
= get_nickname_from_addr(tmp_adr
, buf
, sizeof(buf
));
278 case SAV_RULE_FCC_FROM
:
279 case SAV_RULE_FCC_SENDER
:
280 case SAV_RULE_FCC_REPLYTO
:
281 case SAV_RULE_FCC_RECIP
:
282 case SAV_RULE_FCC_FROM_DEF
:
283 case SAV_RULE_FCC_SENDER_DEF
:
284 case SAV_RULE_FCC_REPLYTO_DEF
:
285 case SAV_RULE_FCC_RECIP_DEF
:
286 bufp
= get_fcc_from_addr(tmp_adr
, buf
, sizeof(buf
));
291 istrncpy(fbuf
, bufp
, nfbuf
- 1);
292 fbuf
[nfbuf
- 1] = '\0';
295 /* fall back to non-nick/non-fcc version of rule */
296 switch(save_msg_rule
){
297 case SAV_RULE_NICK_FROM
:
298 case SAV_RULE_FCC_FROM
:
299 save_msg_rule
= SAV_RULE_FROM
;
302 case SAV_RULE_NICK_SENDER
:
303 case SAV_RULE_FCC_SENDER
:
304 save_msg_rule
= SAV_RULE_SENDER
;
307 case SAV_RULE_NICK_REPLYTO
:
308 case SAV_RULE_FCC_REPLYTO
:
309 save_msg_rule
= SAV_RULE_REPLYTO
;
312 case SAV_RULE_NICK_RECIP
:
313 case SAV_RULE_FCC_RECIP
:
314 save_msg_rule
= SAV_RULE_RECIP
;
318 istrncpy(fbuf
, ps_global
->VAR_DEFAULT_SAVE_FOLDER
, nfbuf
- 1);
319 fbuf
[nfbuf
- 1] = '\0';
327 switch(save_msg_rule
){
328 case SAV_RULE_RN_FROM_DEF
:
329 case SAV_RULE_RN_FROM
:
330 case SAV_RULE_RN_SENDER_DEF
:
331 case SAV_RULE_RN_SENDER
:
332 case SAV_RULE_RN_RECIP_DEF
:
333 case SAV_RULE_RN_RECIP
:
334 case SAV_RULE_RN_REPLYTO_DEF
:
335 case SAV_RULE_RN_REPLYTO
:
336 /* Fish out the realname */
337 if(tmp_adr
&& tmp_adr
->personal
&& tmp_adr
->personal
[0])
338 folder_name
= (char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf
,
339 SIZEOF_20KBUF
, tmp_adr
->personal
);
341 if(folder_name
&& folder_name
[0]){
342 istrncpy(fbuf
, folder_name
, nfbuf
- 1);
343 fbuf
[nfbuf
- 1] = '\0';
345 else{ /* fall back to other behaviors */
346 switch(save_msg_rule
){
347 case SAV_RULE_RN_FROM
:
348 save_msg_rule
= SAV_RULE_FROM
;
351 case SAV_RULE_RN_SENDER
:
352 save_msg_rule
= SAV_RULE_SENDER
;
355 case SAV_RULE_RN_RECIP
:
356 save_msg_rule
= SAV_RULE_RECIP
;
359 case SAV_RULE_RN_REPLYTO
:
360 save_msg_rule
= SAV_RULE_REPLYTO
;
364 istrncpy(fbuf
, ps_global
->VAR_DEFAULT_SAVE_FOLDER
, nfbuf
- 1);
365 fbuf
[nfbuf
- 1] = '\0';
373 /* get the username out of the mailbox for this address */
374 switch(save_msg_rule
){
376 case SAV_RULE_SENDER
:
377 case SAV_RULE_REPLYTO
:
380 * Fish out the user's name from the mailbox portion of
381 * the address and put it in folder.
383 folder_name
= (tmp_adr
&& tmp_adr
->mailbox
&& tmp_adr
->mailbox
[0])
384 ? tmp_adr
->mailbox
: NULL
;
385 if(!get_uname(folder_name
, fbuf
, nfbuf
)){
386 istrncpy(fbuf
, ps_global
->VAR_DEFAULT_SAVE_FOLDER
, nfbuf
- 1);
387 fbuf
[nfbuf
- 1] = '\0';
394 mail_free_address(&tmp_adr
);
398 /*----------------------------------------------------------------------
399 Do the work of actually saving messages to a folder
401 Args: state -- pine state struct (for stream pointers)
402 stream -- source stream, which msgmap refers to
403 context -- context to interpret name in if not fully qualified
404 folder -- The folder to save the message in
405 msgmap -- message map of currently selected messages
406 flgs -- Possible bits are
407 SV_DELETE - delete after saving
408 SV_FOR_FILT - called from filtering function, not save
409 SV_FIX_DELS - remove Del mark before saving
410 SV_INBOXWOCNTXT - "inbox" is interpreted without context making
411 it the one-true inbox instead
413 Result: Returns number of messages saved
415 Note: There's a bit going on here; temporary clearing of deleted flags
416 since they are *not* preserved, picking or creating the stream for
417 copy or append, and dealing with errors...
418 We try to preserve user keywords by setting them in the destination.
421 save(struct pine
*state
, MAILSTREAM
*stream
, CONTEXT_S
*context
, char *folder
,
422 MSGNO_S
*msgmap
, int flgs
)
424 int rv
, rc
, j
, our_stream
= 0, cancelled
= 0;
425 int delete, filter
, k
, worry_about_keywords
= 0;
426 char *save_folder
, *seq
, *flags
= NULL
, date
[64], tmp
[MAILTMPLEN
];
427 long i
, nmsgs
, rawno
;
430 MAILSTREAM
*save_stream
= NULL
;
433 delete = flgs
& SV_DELETE
;
434 filter
= flgs
& SV_FOR_FILT
;
436 if(strucmp(folder
, state
->inbox_name
) == 0 && flgs
& SV_INBOXWOCNTXT
){
437 save_folder
= state
->VAR_INBOX_PATH
;
441 save_folder
= folder
;
444 * Because the COPY/APPEND command doesn't always create keywords when they
445 * aren't already defined in a mailbox, we need to ensure that the keywords
446 * exist in the destination (are defined and settable) before we do the copies.
447 * Here's what the code is doing
449 * If we have keywords set in the source messages
450 * Add a dummy message to destination mailbox
452 * for each keyword that is set in the set of messages we're saving
453 * set the keyword in that message (thus creating it)
455 * remember deleted messages
457 * delete dummy message
459 * delete remembered messages
461 * After that the assumption is that the keywords will be saved by a
462 * COPY command. We need to set the flags string ourself for appends.
465 /* are any keywords set in the source messages? */
466 for(i
= mn_first_cur(msgmap
); !worry_about_keywords
&& i
> 0L; i
= mn_next_cur(msgmap
)){
467 rawno
= mn_m2raw(msgmap
, i
);
468 mc
= (rawno
> 0L && stream
&& rawno
<= stream
->nmsgs
)
469 ? mail_elt(stream
, rawno
) : NULL
;
470 if(mc
&& mc
->user_flags
)
471 worry_about_keywords
++;
474 if(worry_about_keywords
){
475 MAILSTREAM
*dstn_stream
= NULL
;
476 int already_open
= 0;
477 int we_blocked_reuse
= 0;
480 * Possible problem created by our stream re-use
481 * strategy. If we are going to open a new stream
482 * here, we want to be sure not to re-use the
483 * stream we are saving _from_, so take it out of the
484 * re-use pool before we call open.
486 if(sp_flagged(stream
, SP_USEPOOL
)){
488 sp_unflag(stream
, SP_USEPOOL
);
491 /* see if there is a stream open already */
493 dstn_stream
= context_already_open_stream(context
,
496 already_open
= dstn_stream
? 1 : 0;
500 dstn_stream
= context_open(context
, NULL
,
502 SP_USEPOOL
| SP_TEMPUSE
,
505 if(dstn_stream
&& dstn_stream
->kwd_create
){
506 imapuid_t dummy_uid
= 0L;
507 long dummy_msgno
, delete_count
;
510 char *user_flag_name
, *duser_flag_name
;
512 /* find keywords that need to be defined */
513 for(k
= 0; stream_to_user_flag_name(stream
, k
); k
++){
514 user_flag_name
= stream_to_user_flag_name(stream
, k
);
515 if(user_flag_name
&& user_flag_name
[0]){
516 /* is this flag set in any of the save set? */
517 for(set
= 0, i
= mn_first_cur(msgmap
);
519 i
= mn_next_cur(msgmap
)){
520 rawno
= mn_m2raw(msgmap
, i
);
521 if(user_flag_is_set(stream
, rawno
, user_flag_name
))
527 * The flag may already be defined in this
528 * mailbox. Check for that first.
530 for(j
= 0; stream_to_user_flag_name(dstn_stream
, j
); j
++){
531 duser_flag_name
= stream_to_user_flag_name(dstn_stream
, j
);
532 if(duser_flag_name
&& duser_flag_name
[0]
533 && !strucmp(duser_flag_name
, user_flag_name
)){
542 len
= strlen(user_flag_name
) + 1;
543 flags
= (char *) fs_get((len
+1) * sizeof(char));
544 snprintf(flags
, len
+1, "%s ", user_flag_name
);
550 newlen
= strlen(user_flag_name
) + 1;
552 fs_resize((void **) &flags
, (len
+1) * sizeof(char));
553 p
= flags
+ strlen(flags
);
554 snprintf(p
, newlen
+1, "%s ", user_flag_name
);
569 newlen
= strlen("\\DELETED");
571 fs_resize((void **) &flags
, (len
+1) * sizeof(char));
572 p
= flags
+ strlen(flags
);
573 snprintf(p
, newlen
+1, "%s", "\\DELETED");
575 id
= oauth2_generate_state();
576 idused
= id
? id
: "<xyz>";
577 snprintf(dummymsg
, sizeof(dummymsg
), "Date: Thu, 18 May 2006 00:00 -0700\r\nFrom: dummy@example.com\r\nSubject: dummy\r\nMessage-ID: %s@example.com\r\n\r\ndummy\r\n", idused
);
580 * We need to get the uid of the message we are about to
581 * append so that we can delete it when we're done and
582 * so we don't affect other messages.
585 if(is_imap_stream(dstn_stream
) && LEVELUIDPLUS (dstn_stream
)){
586 au
= mail_parameters(NIL
, GET_APPENDUID
, NIL
);
587 mail_parameters(NIL
, SET_APPENDUID
, (void *) appenduid_cb
);
590 INIT(&msg
, mail_string
, (void *) dummymsg
, strlen(dummymsg
));
591 if(pine_mail_append(dstn_stream
, dstn_stream
->mailbox
, &msg
)){
593 (void) pine_mail_ping(dstn_stream
);
595 if(is_imap_stream(dstn_stream
) && LEVELUIDPLUS (dstn_stream
))
596 dummy_uid
= get_last_append_uid();
599 dummy_msgno
= get_msgno_by_msg_id(dstn_stream
, idused
,
600 sp_msgmap(dstn_stream
));
601 if(dummy_msgno
<= 0L || dummy_msgno
> dstn_stream
->nmsgs
)
602 dummy_msgno
= dstn_stream
->nmsgs
;
604 rawno
= mn_m2raw(sp_msgmap(dstn_stream
), dummy_msgno
);
605 if(rawno
> 0L && rawno
<= dstn_stream
->nmsgs
)
606 dummy_uid
= mail_uid(dstn_stream
, rawno
);
609 dummy_msgno
= dstn_stream
->nmsgs
;
613 * We need to remember which messages are deleted,
614 * undelete them, do the expunge, then delete them again.
616 delete_count
= count_flagged(dstn_stream
, F_DEL
);
618 for(i
= 1L; i
<= dstn_stream
->nmsgs
; i
++)
619 if(((mc
= mail_elt(dstn_stream
, i
)) && mc
->valid
&& mc
->deleted
)
620 || (mc
&& !mc
->valid
&& mc
->searched
)){
622 expbits
= MSG_EX_DELETE
;
623 msgno_exceptions(dstn_stream
, i
, "0", &expbits
, TRUE
);
625 else if((mc
= mail_elt(dstn_stream
, i
)) != NULL
)
628 if((seq
= build_sequence(dstn_stream
, NULL
, NULL
)) != NULL
){
629 mail_flag(dstn_stream
, seq
, "\\DELETED", ST_SILENT
);
630 fs_give((void **) &seq
);
635 mail_flag(dstn_stream
, ulong2string(dummy_uid
),
636 flags
, ST_SET
| ST_UID
| ST_SILENT
);
638 mail_flag(dstn_stream
, ulong2string(dummy_msgno
),
639 flags
, ST_SET
| ST_SILENT
);
641 ps_global
->mm_log_error
= 0;
642 ps_global
->expunge_in_progress
= 1;
643 mail_expunge(dstn_stream
);
644 ps_global
->expunge_in_progress
= 0;
647 for(i
= 1L; i
<= dstn_stream
->nmsgs
; i
++)
648 if((mc
= mail_elt(dstn_stream
, i
)) != NULL
){
650 = (msgno_exceptions(dstn_stream
, i
, "0", &expbits
, FALSE
)
651 && (expbits
& MSG_EX_DELETE
));
654 * Remove the EX_DELETE bit in case we're still using
658 expbits
&= ~MSG_EX_DELETE
;
659 msgno_exceptions(dstn_stream
, i
, "0", &expbits
, TRUE
);
663 if((seq
= build_sequence(dstn_stream
, NULL
, NULL
)) != NULL
){
664 mail_flag(dstn_stream
, seq
, "\\DELETED", ST_SET
| ST_SILENT
);
665 fs_give((void **) &seq
);
670 if(is_imap_stream(dstn_stream
) && LEVELUIDPLUS (dstn_stream
))
671 mail_parameters(NIL
, SET_APPENDUID
, (void *) au
);
674 fs_give((void **) &id
);
676 fs_give((void **) &flags
);
680 if(dstn_stream
&& !already_open
)
681 pine_mail_close(dstn_stream
);
684 sp_set_flags(stream
, sp_flags(stream
) | SP_USEPOOL
);
688 * If any of the messages have exceptional attachment handling
689 * we have to fall thru below to do the APPEND by hand...
691 if(!msgno_any_deletedparts(stream
, msgmap
)){
695 * Compare the current stream (the save's source) and the stream
696 * the destination folder will need...
698 context_apply(tmp
, context
, save_folder
, sizeof(tmp
));
701 * If we're going to be doing a cross-format copy we're better off
702 * using the else code below that knows how to do multi-append.
703 * The part in the if is leaving save_stream set to NULL in the
704 * case that the stream is local and the folder is local and they
705 * are different formats (like unix and tenex). That will cause us
706 * to fall thru to the APPEND case which is faster than using
707 * copy which will use our imap_proxycopy which doesn't know
710 loc_to_loc
= stream
&& stream
->dtb
711 && stream
->dtb
->flags
& DR_LOCAL
&& !IS_REMOTE(tmp
);
712 if(!loc_to_loc
|| (stream
->dtb
->valid
&& (*stream
->dtb
->valid
)(tmp
)))
713 save_stream
= loc_to_loc
? stream
714 : context_same_stream(context
, save_folder
, stream
);
717 /* if needed, this'll get set in mm_notify */
718 ps_global
->try_to_create
= 0;
723 * At this point, if we have a save_stream, then none of the messages
724 * being saved involve special handling that would require our use
725 * of mail_append, so go with mail_copy since in the IMAP case it
726 * means no data on the wire...
729 char *dseq
= NULL
, *oseq
;
731 if((flgs
& SV_FIX_DELS
) &&
732 (dseq
= currentf_sequence(stream
, msgmap
, F_DEL
, NULL
,
734 mail_flag(stream
, dseq
, "\\DELETED", 0L);
736 seq
= currentf_sequence(stream
, msgmap
, 0L, &nmsgs
, 0, NULL
, NULL
);
737 if(!(flgs
& SV_PRESERVE
)
738 && (F_ON(F_AGG_SEQ_COPY
, ps_global
)
739 || (mn_get_sort(msgmap
) == SortArrival
&& !mn_get_revsort(msgmap
)))){
742 * currentf_sequence() above lit all the elt "sequence"
743 * bits of the interesting messages. Now, build a sequence
744 * that preserves sort order...
746 oseq
= build_sequence(stream
, msgmap
, &nmsgs
);
749 oseq
= NULL
; /* no single sequence! */
751 i
= mn_first_cur(msgmap
); /* set first to copy */
755 while(!(rv
= (int) context_copy(context
, save_stream
,
756 oseq
? oseq
: long2string(mn_m2raw(msgmap
, i
)),
758 if(rc
++ || !ps_global
->try_to_create
) /* abysmal failure! */
759 break; /* c-client returned error? */
761 if((context
&& context
->use
& CNTXT_INCMNG
)
762 && context_isambig(save_folder
)){
763 q_status_message(SM_ORDER
, 3, 5,
764 _("Can only save to existing folders in Incoming Collection"));
768 ps_global
->try_to_create
= 0; /* reset for next time */
769 if((j
= create_for_save(context
, save_folder
)) < 1){
771 cancelled
= 1; /* user cancels */
777 if(rv
){ /* failure or finished? */
778 if(oseq
) /* all done? */
785 nmsgs
= 0L; /* nothing copy'd */
790 while((i
= mn_next_cur(msgmap
)) > 0L);
792 if(rv
&& delete) /* delete those saved */
793 mail_flag(stream
, seq
, "\\DELETED", ST_SET
);
794 else if(dseq
) /* or restore previous state */
795 mail_flag(stream
, dseq
, "\\DELETED", ST_SET
);
797 if(dseq
) /* clean up */
798 fs_give((void **)&dseq
);
801 fs_give((void **)&oseq
);
803 fs_give((void **)&seq
);
807 * Special handling requires mail_append. See if we can
808 * re-use stream source messages are on...
810 save_stream
= context_same_stream(context
, save_folder
, stream
);
813 * IF the destination's REMOTE, open a stream here so c-client
814 * doesn't have to open it for each aggregate save...
817 if(context_apply(tmp
, context
, save_folder
, sizeof(tmp
))[0] == '{'
818 && (save_stream
= context_open(context
, NULL
,
820 OP_HALFOPEN
| SP_USEPOOL
| SP_TEMPUSE
,
826 * Allocate a storage object to temporarily store the message
827 * object in. Below it'll get mapped into a c-client STRING struct
828 * in preparation for handing off to context_append...
830 if(!(so
= so_get(CharStar
, NULL
, WRITE_ACCESS
))){
831 dprint((1, "Can't allocate store for save: %s\n",
832 error_description(errno
)));
833 q_status_message(SM_ORDER
| SM_DING
, 3, 4,
834 "Problem creating space for message text.");
838 * get a sequence of invalid elt's so we can get their flags...
840 if((seq
= invalid_elt_sequence(stream
, msgmap
)) != NULL
){
841 mail_fetch_fast(stream
, seq
, 0L);
842 fs_give((void **) &seq
);
846 * If we're supposed set the deleted flag, clear the elt bit
847 * we'll use to build the sequence later...
850 for(i
= 1L; i
<= stream
->nmsgs
; i
++)
851 if((mc
= mail_elt(stream
, i
)) != NULL
)
856 if(pith_opt_save_size_changed_prompt
)
857 (*pith_opt_save_size_changed_prompt
)(0L, SSCP_INIT
);
860 * if there is more than one message, do multiappend.
861 * otherwise, we can use our already open stream.
863 if(!save_stream
|| !is_imap_stream(save_stream
) ||
864 (LEVELMULTIAPPEND(save_stream
) && mn_total_cur(msgmap
) > 1)){
869 /* tell save_fetch_append_cb whether or not to leave deleted flag */
870 pkg
.flags
= flgs
& SV_FIX_DELS
? NULL
: cpystr("\\DELETED");
875 if ((pkg
.so
= so
) && ((pkg
.msgno
= mn_first_cur(msgmap
)) > 0L)) {
879 * we've gotta make sure this is a stream that we've
883 while(!(rv
= context_append_multiple(context
,
884 our_stream
? save_stream
886 save_fetch_append_cb
,
890 if(rc
++ || !ps_global
->try_to_create
)
892 if((context
&& context
->use
& CNTXT_INCMNG
)
893 && context_isambig(save_folder
)){
894 q_status_message(SM_ORDER
, 3, 5,
895 _("Can only save to existing folders in Incoming Collection"));
899 ps_global
->try_to_create
= 0;
900 if((j
= create_for_save(context
, save_folder
)) < 1){
908 fs_give((void **) &pkg
.flags
);
910 ps_global
->noshow_error
= 0;
914 * Success! Count it, and if it's not already deleted and
915 * it's supposed to be, mark it to get deleted later...
917 for(i
= mn_first_cur(msgmap
); so
&& i
> 0L;
918 i
= mn_next_cur(msgmap
)){
921 rawno
= mn_m2raw(msgmap
, i
);
922 mc
= (rawno
> 0L && stream
&& rawno
<= stream
->nmsgs
)
923 ? mail_elt(stream
, rawno
) : NULL
;
924 if(mc
&& !mc
->deleted
)
925 mc
->sequence
= 1; /* mark for later deletion */
931 cancelled
= 1; /* No messages to append! */
933 if(sp_expunge_count(stream
))
934 cancelled
= 1; /* All bets are off! */
937 for(i
= mn_first_cur(msgmap
); so
&& i
> 0L; i
= mn_next_cur(msgmap
)){
938 int preserve_these_flags
;
942 rawno
= mn_m2raw(msgmap
, i
);
943 mc
= (rawno
> 0L && stream
&& rawno
<= stream
->nmsgs
)
944 ? mail_elt(stream
, rawno
) : NULL
;
946 /* always preserve these flags */
947 preserve_these_flags
= F_ANS
|F_FWD
|F_FLAG
|F_SEEN
|F_KEYWORD
;
948 /* maybe preserve deleted flag */
949 preserve_these_flags
|= flgs
& SV_FIX_DELS
? 0 : F_DEL
;
950 flags
= flag_string(stream
, rawno
, preserve_these_flags
);
957 rv
= save_fetch_append(stream
, mn_m2raw(msgmap
, i
),
958 NULL
, save_stream
, save_folder
, context
,
959 mc
? mc
->rfc822_size
: 0L, flags
, date
, so
);
962 fs_give((void **) &flags
);
964 if(sp_expunge_count(stream
))
965 rv
= -1; /* All bets are off! */
969 * Success! Count it, and if it's not already deleted and
970 * it's supposed to be, mark it to get deleted later...
974 rawno
= mn_m2raw(msgmap
, i
);
975 mc
= (rawno
> 0L && stream
&& rawno
<= stream
->nmsgs
)
976 ? mail_elt(stream
, rawno
) : NULL
;
977 if(mc
&& !mc
->deleted
)
978 mc
->sequence
= 1; /* mark for later deletion */
983 cancelled
= 1; /* else horrendous failure */
989 if(pith_opt_save_size_changed_prompt
)
990 (*pith_opt_save_size_changed_prompt
)(0L, SSCP_END
);
993 pine_mail_close(save_stream
);
998 if(delete && (seq
= build_sequence(stream
, NULL
, NULL
))){
999 mail_flag(stream
, seq
, "\\DELETED", ST_SET
);
1000 fs_give((void **)&seq
);
1004 ps_global
->try_to_create
= 0; /* reset for next time */
1005 if(!cancelled
&& nmsgs
< mn_total_cur(msgmap
)){
1006 dprint((1, "FAILED save of msg %s (c-client sequence #)\n",
1007 long2string(mn_m2raw(msgmap
, mn_get_cur(msgmap
)))));
1008 if((mn_total_cur(msgmap
) > 1L) && nmsgs
!= 0){
1009 /* this shouldn't happen cause it should be all or nothing */
1010 snprintf(tmp_20k_buf
, SIZEOF_20KBUF
,
1011 "%ld of %ld messages saved before error occurred",
1012 nmsgs
, mn_total_cur(msgmap
));
1013 dprint((1, "\t%s\n", tmp_20k_buf
));
1014 q_status_message(SM_ORDER
, 3, 5, tmp_20k_buf
);
1016 else if(mn_total_cur(msgmap
) == 1){
1017 snprintf(tmp_20k_buf
, SIZEOF_20KBUF
,
1018 "%s to folder \"%s\" FAILED",
1019 filter
? "Filter" : "Save",
1020 strsquish(tmp_20k_buf
+500, 500, folder
, 35));
1021 dprint((1, "\t%s\n", tmp_20k_buf
));
1022 q_status_message(SM_ORDER
| SM_DING
, 3, 5, tmp_20k_buf
);
1025 snprintf(tmp_20k_buf
, SIZEOF_20KBUF
,
1026 "%s of %s messages to folder \"%s\" FAILED",
1027 filter
? "Filter" : "Save", comatose(mn_total_cur(msgmap
)),
1028 strsquish(tmp_20k_buf
+500, 500, folder
, 35));
1029 dprint((1, "\t%s\n", tmp_20k_buf
));
1030 q_status_message(SM_ORDER
| SM_DING
, 3, 5, tmp_20k_buf
);
1038 /* Append message callback
1039 * Accepts: MAIL stream
1040 * append data package
1041 * pointer to return initial flags
1042 * pointer to return message internal date
1043 * pointer to return stringstruct of message or NIL to stop
1044 * Returns: T if success (have message or stop), NIL if error
1047 long save_fetch_append_cb(MAILSTREAM
*stream
, void *data
, char **flags
,
1048 char **date
, STRING
**message
)
1050 unsigned long size
= 0;
1051 APPENDPACKAGE
*pkg
= (APPENDPACKAGE
*) data
;
1055 unsigned long raw
, hlen
, tlen
, mlen
;
1057 if(pkg
->so
&& (pkg
->msgno
> 0L)) {
1058 raw
= mn_m2raw(pkg
->msgmap
, pkg
->msgno
);
1059 mc
= (raw
> 0L && pkg
->stream
&& raw
<= pkg
->stream
->nmsgs
)
1060 ? mail_elt(pkg
->stream
, raw
) : NULL
;
1062 int preserve_these_flags
= F_ANS
|F_FWD
|F_FLAG
|F_SEEN
|F_KEYWORD
;
1064 size
= mc
->rfc822_size
;
1067 * If the caller wants us to preserve the state of the
1068 * \DELETED flag then pkg->flags will be \DELETED, otherwise
1069 * let it be undeleted. Fix from Eduardo Chappa.
1072 if(strstr(pkg
->flags
,"\\DELETED"))
1073 preserve_these_flags
|= F_DEL
;
1075 /* not used anymore */
1076 fs_give((void **) &pkg
->flags
);
1079 pkg
->flags
= flag_string(pkg
->stream
, raw
, preserve_these_flags
);
1083 mail_date(pkg
->date
, mc
);
1086 if((fetch
= mail_fetch_header(pkg
->stream
, raw
, NULL
, NULL
, &hlen
,
1089 saved_date(pkg
->date
, fetch
);
1092 return(0); /* fetchtext writes error */
1094 rc
= MSG_EX_DELETE
; /* "rc" overloaded */
1095 if(msgno_exceptions(pkg
->stream
, raw
, NULL
, &rc
, 0)){
1101 size
= 0; /* all bets off, abort sanity test */
1102 gf_set_so_writec(&pc
, pkg
->so
);
1105 if(!pine_mail_fetch_structure(pkg
->stream
, raw
, &body
, 0))
1108 if(msgno_part_deleted(pkg
->stream
, raw
, "")){
1110 failure
= !save_ex_replace_body(fetch
, &hlen
, body
, pc
);
1113 failure
= !(so_nputs(pkg
->so
, fetch
, (long) hlen
)
1114 && save_ex_output_body(pkg
->stream
, raw
, section
,
1117 gf_clear_so_writec(pkg
->so
);
1122 q_status_message(SM_ORDER
, 3, 3,
1123 /* TRANSLATORS: A warning to user that the message parts
1124 they deleted will not be included in the copy they
1125 are now saving to. */
1126 _("NOTE: Deleted message parts NOT included in saved copy!"));
1130 if(!so_nputs(pkg
->so
, fetch
, (long) hlen
))
1133 fetch
= pine_mail_fetch_text(pkg
->stream
, raw
, NULL
, &tlen
, FT_PEEK
);
1135 if(!(fetch
&& so_nputs(pkg
->so
, fetch
, tlen
)))
1139 so_seek(pkg
->so
, 0L, 0); /* rewind just in case */
1142 if(size
&& mlen
< size
){
1145 if(sp_dead_stream(pkg
->stream
)){
1146 snprintf(buf
, sizeof(buf
), _("Cannot save because current folder is Closed"));
1147 q_status_message(SM_ORDER
| SM_DING
, 3, 4, buf
);
1148 dprint((1, "save_fetch_append_cb: %s\n", buf
));
1152 if(pith_opt_save_size_changed_prompt
1153 && (*pith_opt_save_size_changed_prompt
)(mn_raw2m(pkg
->msgmap
, raw
), 0) == 'y'){
1154 snprintf(buf
, sizeof(buf
),
1155 "Message to save shrank! (raw msg# %ld: %ld -> %ld): User says continue",
1157 dprint((1, "save_fetch_append_cb: %s", buf
));
1158 snprintf(buf
, sizeof(buf
),
1159 "Message to save shrank: source msg # %ld may be saved incorrectly",
1160 mn_raw2m(pkg
->msgmap
, raw
));
1161 if(F_OFF(F_IGNORE_SIZE
, ps_global
))
1162 q_status_message(SM_ORDER
, 0, 3, buf
);
1165 snprintf(buf
, sizeof(buf
),
1166 "Message to save shrank: raw msg # %ld went from announced size %ld to actual size %ld",
1168 dprint((1, "save_fetch_append_cb: %s", buf
));
1174 INIT(pkg
->msg
, mail_string
, (void *)so_text(pkg
->so
), mlen
);
1175 *message
= pkg
->msg
;
1177 pkg
->msgno
= mn_next_cur(pkg
->msgmap
);
1179 else /* No more messages */
1182 *flags
= pkg
->flags
;
1183 *date
= (pkg
->date
&& *pkg
->date
) ? pkg
->date
: NIL
;
1184 return LONGT
; /* Return success */
1188 /*----------------------------------------------------------------------
1189 FETCH an rfc822 message header and body and APPEND to destination folder
1198 save_fetch_append(MAILSTREAM
*stream
, long int raw
, char *sect
,
1199 MAILSTREAM
*save_stream
, char *save_folder
, CONTEXT_S
*context
,
1200 long unsigned int size
, char *flags
, char *date
, STORE_S
*so
)
1202 int rc
, rv
, old_imap_server
= 0;
1205 unsigned long hlen
, tlen
, mlen
;
1208 if((fetch
= mail_fetch_header(stream
, raw
, sect
, NULL
, &hlen
, FT_PEEK
)) != NULL
){
1210 * If there's no date string, then caller found the
1211 * MESSAGECACHE for this message element didn't already have it.
1212 * So, parse the "internal date" by hand since fetchstructure
1213 * hasn't been called yet for this particular message, and
1214 * we don't want to call it now just to get the date since
1215 * the full header has what we want. Likewise, don't even
1216 * think about calling mail_fetchfast either since it also
1217 * wants to load mc->rfc822_size (which we could actually
1218 * use but...), which under some drivers is *very*
1219 * expensive to acquire (can you say NNTP?)...
1222 saved_date(date
, fetch
);
1225 return(0); /* fetchtext writes error */
1227 rc
= MSG_EX_DELETE
; /* "rc" overloaded */
1228 if(msgno_exceptions(stream
, raw
, NULL
, &rc
, 0)){
1234 size
= 0; /* all bets off, abort sanity test */
1235 gf_set_so_writec(&pc
, so
);
1238 snprintf(section
, sizeof(section
), "%s.1", sect
);
1239 if(!(body
= mail_body(stream
, raw
, (unsigned char *) section
)))
1244 if(!pine_mail_fetch_structure(stream
, raw
, &body
, 0))
1249 * Walk the MIME structure looking for exceptional segments,
1250 * writing them in the requested fashion.
1252 * First, though, check for the easy case...
1254 if(msgno_part_deleted(stream
, raw
, sect
? sect
: "")){
1256 failure
= !save_ex_replace_body(fetch
, &hlen
, body
, pc
);
1260 * Otherwise, roll up your sleeves and get to work...
1261 * start by writing msg header and then the processed body
1263 failure
= !(so_nputs(so
, fetch
, (long) hlen
)
1264 && save_ex_output_body(stream
, raw
, section
,
1268 gf_clear_so_writec(so
);
1273 q_status_message(SM_ORDER
, 3, 3,
1274 _("NOTE: Deleted message parts NOT included in saved copy!"));
1278 /* First, write the header we just fetched... */
1279 if(!so_nputs(so
, fetch
, (long) hlen
))
1282 old_imap_server
= is_imap_stream(stream
) && !modern_imap_stream(stream
);
1284 /* Second, go fetch the corresponding text... */
1285 fetch
= pine_mail_fetch_text(stream
, raw
, sect
, &tlen
,
1286 !old_imap_server
? FT_PEEK
: 0);
1289 * Special handling in case we're fetching a Message/rfc822
1290 * segment and we're talking to an old server...
1292 if(fetch
&& *fetch
== '\0' && sect
&& (hlen
+ tlen
) != size
){
1294 fetch
= pine_mail_fetch_body(stream
, raw
, sect
, &tlen
, 0L);
1298 * Pre IMAP4 servers can't do a non-peeking mail_fetch_text,
1299 * so if the message we are saving from was originally unseen,
1300 * we have to change it back to unseen. Flags contains the
1301 * string "SEEN" if the original message was seen.
1303 if(old_imap_server
&& (!flags
|| !srchstr(flags
,"SEEN"))){
1306 strncpy(seq
, long2string(raw
), sizeof(seq
));
1307 seq
[sizeof(seq
)-1] = '\0';
1308 mail_flag(stream
, seq
, "\\SEEN", 0);
1311 /* If fetch succeeded, write the result */
1312 if(!(fetch
&& so_nputs(so
, fetch
, tlen
)))
1316 so_seek(so
, 0L, 0); /* rewind just in case */
1319 * Set up a c-client string driver so we can hand the
1320 * collected text down to mail_append.
1322 * NOTE: We only test the size if and only if we already
1323 * have it. See, in some drivers, especially under
1324 * dos, its too expensive to get the size (full
1325 * header and body text fetch plus MIME parse), so
1326 * we only verify the size if we already know it.
1330 if(size
&& mlen
< size
){
1333 if(sp_dead_stream(stream
)){
1334 snprintf(buf
, sizeof(buf
), _("Cannot save because current folder is Closed"));
1335 q_status_message(SM_ORDER
| SM_DING
, 3, 4, buf
);
1336 dprint((1, "save_fetch_append: %s", buf
));
1340 if(pith_opt_save_size_changed_prompt
1341 && (*pith_opt_save_size_changed_prompt
)(mn_raw2m(sp_msgmap(stream
), raw
), 0) == 'y'){
1342 snprintf(buf
, sizeof(buf
),
1343 "Message to save shrank! (raw msg# %ld: %ld -> %ld): User says continue",
1345 dprint((1, "save_fetch_append: %s", buf
));
1346 snprintf(buf
, sizeof(buf
),
1347 "Message to save shrank: source msg # %ld may be saved incorrectly",
1348 mn_raw2m(sp_msgmap(stream
), raw
));
1349 q_status_message(SM_ORDER
, 0, 3, buf
);
1352 snprintf(buf
, sizeof(buf
),
1353 "Message to save shrank: source raw msg # %ld went from announced size %ld to actual size %ld",
1355 dprint((1, "save_fetch_append: %s", buf
));
1361 INIT(&msg
, mail_string
, (void *)so_text(so
), mlen
);
1364 while(!(rv
= (int) context_append_full(context
, save_stream
,
1366 *date
? date
: NULL
,
1368 if(rc
++ || !ps_global
->try_to_create
) /* abysmal failure! */
1369 break; /* c-client returned error? */
1371 if(context
&& (context
->use
& CNTXT_INCMNG
)
1372 && context_isambig(save_folder
)){
1373 q_status_message(SM_ORDER
, 3, 5,
1374 _("Can only save to existing folders in Incoming Collection"));
1378 ps_global
->try_to_create
= 0; /* reset for next time */
1379 if((j
= create_for_save(context
, save_folder
)) < 1){
1381 rv
= -1; /* user cancelled */
1386 SETPOS((&msg
), 0L); /* reset string driver */
1394 * save_ex_replace_body -
1396 * NOTE : hlen points to a cell that has the byte count of "hdr" on entry
1397 * *BUT* which is to contain the count of written bytes on exit
1400 save_ex_replace_body(char *hdr
, long unsigned int *hlen
, struct mail_bodystruct
*body
, gf_io_t pc
)
1405 * "X-" out the given MIME headers unless they're
1406 * the same as we're going to substitute...
1408 if(body
->type
== TYPETEXT
1409 && (!body
->subtype
|| !strucmp(body
->subtype
, "plain"))
1410 && body
->encoding
== ENC7BIT
){
1411 if(!gf_nputs(hdr
, *hlen
, pc
)) /* write out header */
1418 * write header, "X-"ing out transport headers bothersome to
1419 * software but potentially useful to the human recipient...
1421 for(len
= *hlen
; len
; len
--){
1426 if(save_ex_mask_types(hdr
, &n
, pc
))
1427 *hlen
+= n
; /* add what we inserted */
1432 if(*hdr
== '\015' && *(hdr
+1) == '\012'){
1433 bol
++; /* remember beginning of line */
1434 len
--; /* account for LF */
1435 if(gf_nputs(hdr
, 2, pc
))
1440 else if(!(*pc
)(*hdr
++))
1444 if(len
) /* bytes remain! */
1448 /* Now, blat out explanatory text as the body... */
1449 if(save_ex_explain_body(body
, &len
, pc
)){
1459 save_ex_output_body(MAILSTREAM
*stream
, long int raw
, char *section
,
1460 struct mail_bodystruct
*body
, long unsigned int *len
, gf_io_t pc
)
1462 char *txtp
, newsect
[128];
1465 txtp
= mail_fetch_mime(stream
, raw
, section
, len
, FT_PEEK
);
1467 if(msgno_part_deleted(stream
, raw
, section
))
1468 return(save_ex_replace_body(txtp
, len
, body
, pc
));
1470 if(body
->type
== TYPEMULTIPART
){
1471 #define BOUNDARYLEN 128
1472 char *subsect
, boundary
[BOUNDARYLEN
];
1474 PART
*part
= body
->nested
.part
;
1477 /* Locate supplied multipart boundary */
1478 for (param
= body
->parameter
; param
; param
= param
->next
)
1479 if (!strucmp(param
->attribute
, "boundary")){ /* BOUNDARYLEN == sizeof(boundary) */
1480 snprintf(boundary
, sizeof(boundary
), "--%.*s\015\012", BOUNDARYLEN
-10,
1482 blen
= strlen(boundary
);
1487 q_status_message(SM_ORDER
|SM_DING
, 3, 3, "Missing MIME boundary");
1492 * BUG: if multi/digest and a message deleted (which we'll
1493 * change to text/plain), we need to coerce this composite
1494 * type to multi/mixed !!
1496 if(!gf_nputs(txtp
, *len
, pc
)) /* write MIME header */
1499 /* Prepare to specify sub-sections */
1500 strncpy(newsect
, section
, sizeof(newsect
));
1501 newsect
[sizeof(newsect
)-1] = '\0';
1502 subsect
= &newsect
[n
= strlen(newsect
)];
1503 if(n
> 2 && !strcmp(&newsect
[n
-2], ".0"))
1506 if((subsect
-newsect
) < sizeof(newsect
))
1511 do { /* for each part */
1512 strncpy(subsect
, int2string(n
++), sizeof(newsect
)-(subsect
-newsect
));
1513 newsect
[sizeof(newsect
)-1] = '\0';
1514 if(gf_puts(boundary
, pc
)
1515 && save_ex_output_body(stream
, raw
, newsect
,
1516 &part
->body
, &ilen
, pc
))
1517 *len
+= blen
+ ilen
;
1521 while ((part
= part
->next
) != NULL
); /* until done */
1522 /* BOUNDARYLEN = sizeof(boundary) */
1523 snprintf(boundary
, sizeof(boundary
), "--%.*s--\015\012", BOUNDARYLEN
-10,param
->value
);
1525 return(gf_puts(boundary
, pc
));
1528 /* Start by writing the part's MIME header */
1529 if(!gf_nputs(txtp
, *len
, pc
))
1532 if(body
->type
== TYPEMESSAGE
1533 && (!body
->subtype
|| !strucmp(body
->subtype
, "rfc822"))){
1534 /* write RFC 822 message's header */
1535 if((txtp
= mail_fetch_header(stream
,raw
,section
,NULL
,&ilen
,FT_PEEK
))
1536 && gf_nputs(txtp
, ilen
, pc
))
1541 /* then go deal with its body parts */
1542 snprintf(newsect
, sizeof(newsect
), "%.10s%s%s", section
, section
? "." : "",
1543 (body
->nested
.msg
->body
->type
== TYPEMULTIPART
) ? "0" : "1");
1544 if(save_ex_output_body(stream
, raw
, newsect
,
1545 body
->nested
.msg
->body
, &ilen
, pc
)){
1553 /* Write corresponding body part */
1554 if((txtp
= pine_mail_fetch_body(stream
, raw
, section
, &ilen
, FT_PEEK
))
1555 && gf_nputs(txtp
, (long) ilen
, pc
) && gf_puts("\015\012", pc
)){
1564 /*----------------------------------------------------------------------
1565 Mask off any header entries we don't want to show up in exceptional saves
1567 Args: hdr -- pointer to start of a header line
1568 pc -- function to write the prefix
1572 save_ex_mask_types(char *hdr
, long unsigned int *len
, gf_io_t pc
)
1576 if(!struncmp(hdr
, "content-type:", 13))
1577 s
= "Content-Type: Text/Plain; charset=UTF-8\015\012X-";
1578 else if(!struncmp(hdr
, "content-description:", 20))
1579 s
= "Content-Description: Deleted Attachment\015\012X-";
1580 else if(!struncmp(hdr
, "content-transfer-encoding:", 26)
1581 || !struncmp(hdr
, "content-disposition:", 20))
1584 return((*len
= s
? strlen(s
) : 0) ? gf_puts(s
, pc
) : 1);
1589 save_ex_explain_body(struct mail_bodystruct
*body
, long unsigned int *len
, gf_io_t pc
)
1593 static char *blurb
[] = {
1594 N_("The following attachment was DELETED when this message was saved:"),
1599 for(blurbp
= blurb
; *blurbp
; blurbp
++)
1600 if(save_ex_output_line(_(*blurbp
), &ilen
, pc
))
1605 if(!save_ex_explain_parts(body
, 0, &ilen
, pc
))
1614 save_ex_explain_parts(struct mail_bodystruct
*body
, int depth
, long unsigned int *len
, gf_io_t pc
)
1616 char *tmp
, namebuf
[MAILTMPLEN
], descbuf
[MAILTMPLEN
];
1617 unsigned long ilen
, tmplen
;
1618 char *name
= parameter_val(body
->parameter
, "name");
1621 if(body
->disposition
.type
&&
1622 !strucmp(body
->disposition
.type
, "ATTACHMENT"))
1623 name
= parameter_val(body
->disposition
.parameter
, "filename");
1626 rfc1522_decode_to_utf8((unsigned char *)namebuf
, sizeof(namebuf
), name
);
1628 if(body
->description
&& *body
->description
)
1629 rfc1522_decode_to_utf8((unsigned char *)descbuf
, sizeof(descbuf
), body
->description
);
1632 if(body
->type
== TYPEMULTIPART
) { /* multipart gets special handling */
1633 PART
*part
= body
->nested
.part
; /* first body part */
1636 if(body
->description
&& *body
->description
){
1637 tmplen
= strlen(_("A ")) + strlen(body_type_names(body
->type
)) + 1
1638 + strlen(body
->subtype
? body
->subtype
: "Unknown")
1639 + strlen(name
? " (Name=\"" : "")
1640 + strlen(name
? namebuf
: "")
1641 + strlen(name
? "\"" : "") + strlen(_(" segment described as "))
1642 + strlen(descbuf
) + strlen(_(" containing:")) + 1;
1643 tmp
= fs_get((tmplen
+ 1)*sizeof(char));
1644 sprintf(tmp
, "%s%s/%s%s%s%s%s%s%s", _("A "),
1645 body_type_names(body
->type
), body
->subtype
? body
->subtype
: "Unknown",
1646 name
? " (Name=\"" : "", name
? namebuf
: "", name
? "\")" : "",
1647 _(" segment described as "), descbuf
, _(" containing:"));
1648 if(!save_ex_output_text(tmp
, depth
, len
, pc
))
1652 tmplen
= strlen(_("A ")) + strlen(body_type_names(body
->type
)) + 1
1653 + strlen(body
->subtype
? body
->subtype
: "Unknown")
1654 + strlen(name
? " (Name=\"" : "")
1655 + strlen(name
? namebuf
: "")
1656 + strlen(name
? "\"" : "") + strlen(_(" segment containing:")) + 1;
1657 tmp
= fs_get((tmplen
+ 1)*sizeof(char));
1658 sprintf(tmp
, "%s%s/%s%s%s%s%s", _("A "),
1659 body_type_names(body
->type
), body
->subtype
? body
->subtype
: "Unknown",
1660 name
? " (Name=\"" : "", name
? namebuf
: "", name
? "\")" : "",
1661 _(" segment containing:"));
1664 if(save_ex_output_text(tmp
, depth
, &ilen
, pc
))
1670 do /* for each part */
1671 if(save_ex_explain_parts(&part
->body
, depth
, &ilen
, pc
))
1675 while ((part
= part
->next
) != NULL
); /* until done */
1678 char *comatosep
= comatose((body
->encoding
== ENCBASE64
)
1679 ? ((body
->size
.bytes
* 3)/4)
1680 : body
->size
.bytes
);
1681 tmplen
= strlen(_("A ")) + strlen(body_type_names(body
->type
)) + 1
1682 + strlen(body
->subtype
&& *body
->subtype
? body
->subtype
: "Unknown")
1683 + strlen(name
? " (Name=\"" : "")
1684 + strlen(name
? namebuf
: "")
1685 + strlen(name
? "\"" : "") + strlen(_(" segment of about "))
1686 + strlen(comatosep
) + strlen(_(" bytes")) + 1
1687 + strlen(body
->description
&& *body
->description
? _(" described as \"") : "")
1688 + strlen(body
->description
&& *body
->description
? descbuf
: "")
1689 + strlen(body
->description
&& *body
->description
? "\"": "")
1691 tmp
= fs_get((tmplen
+ 1)*sizeof(char));
1692 sprintf(tmp
, "%s%s/%s%s%s%s%s%s%s%s%s%s%s",
1694 body_type_names(body
->type
),
1695 body
->subtype
&& *body
->subtype
? body
->subtype
: "Unknown",
1696 name
? " (Name=\"" : "", name
? namebuf
: "", name
? "\")" : "",
1697 _(" segment of about "),
1700 body
->description
&& *body
->description
? "," : ".",
1701 body
->description
&& *body
->description
? " described as \"" : "",
1702 body
->description
&& *body
->description
? descbuf
: "",
1703 body
->description
&& *body
->description
? "\"" : "");
1705 if(save_ex_output_text(tmp
, depth
, &ilen
, pc
))
1714 /* save output one line at the time. This uses save_ex_output_line, which
1715 * only allows 68 characters of text per line, so what we do is to get justify
1716 * the input text to 68 characters long. If it is not possible to do this,
1717 * we chop the line at 68 characters and move the remaining text to the next
1721 save_ex_output_text(char *text
, int depth
, unsigned long *len
, gf_io_t pc
)
1723 int starti
, i
, j
, startpos
, pos
, rv
;
1724 int line_len
= 68 - depth
;
1725 char tmp
[100]; /* a number bigger than 68, we justify text here. */
1733 sprintf(tmp
, "%*.*s", depth
, depth
, " ");
1735 for(starti
= i
= depth
; i
< 68 && text
[pos
] != '\0'; i
++, pos
++){
1737 if(tmp
[i
] == ' '){ /* save when we reach a space */
1744 if(text
[pos
] != '\0' && text
[pos
] != ' '){ /* if we are not at the end of a word */
1745 if(startpos
< pos
&& starti
!= depth
){ /* rewind */
1751 rv
+= save_ex_output_line(tmp
, &ilen
, pc
);
1754 while (text
[pos
] != '\0');
1759 save_ex_output_line(char *line
, long unsigned int *len
, gf_io_t pc
)
1761 snprintf(tmp_20k_buf
, SIZEOF_20KBUF
, " [ %-*.*s ]\015\012", 68, 68, line
);
1762 *len
= strlen(tmp_20k_buf
);
1763 return(gf_puts(tmp_20k_buf
, pc
));
1767 /*----------------------------------------------------------------------
1768 Save() helper function to create canonical date string from given header
1770 Args: date -- buf to receive canonical date string
1771 header -- rfc822 header to fish date string from
1773 Result: date filled with canonicalized date in header, or null string
1776 saved_date(char *date
, char *header
)
1782 if((toupper((unsigned char)(*(d
= header
)))
1783 == 'D' && !strncmp(d
, "date:", 5))
1784 || (d
= srchstr(header
, "\015\012date:"))){
1785 for(d
+= 7; *d
== ' '; d
++)
1786 ; /* skip white space */
1788 if((p
= strstr(d
, "\015\012")) != NULL
){
1789 for(; p
> d
&& *p
== ' '; p
--)
1790 ; /* skip white space */
1793 *p
= '\0'; /* tie off internal date */
1796 if(mail_parse_date(&elt
, (unsigned char *) d
)) /* normalize it */
1797 mail_date(date
, &elt
);
1799 if(p
) /* restore header */
1806 save_msg_stream(CONTEXT_S
*context
, char *folder
, int *we_opened
)
1808 char tmp
[MAILTMPLEN
];
1809 MAILSTREAM
*save_stream
= NULL
;
1811 if(IS_REMOTE(context_apply(tmp
, context
, folder
, sizeof(tmp
)))
1812 && !(save_stream
= sp_stream_get(tmp
, SP_MATCH
))
1813 && !(save_stream
= sp_stream_get(tmp
, SP_SAME
))){
1814 if((save_stream
= context_open(context
, NULL
, folder
,
1815 OP_HALFOPEN
| SP_USEPOOL
| SP_TEMPUSE
,
1820 return(save_stream
);
1824 /*----------------------------------------------------------------------
1825 Offer to create a non-existent folder to copy message[s] into
1827 Args: context -- context to create folder in
1828 folder -- name of folder to create
1830 Result: 0 if create failed (c-client writes error)
1831 1 if create successful
1832 -1 if user declines to create folder
1835 create_for_save(CONTEXT_S
*context
, char *folder
)
1839 if(pith_opt_save_create_prompt
1840 && (ret
= (*pith_opt_save_create_prompt
)(context
, folder
, 1)) != 1)
1843 return(context_create(context
, NULL
, folder
));