~ubuntu-branches/debian/stretch/alpine/stretch

« back to all changes in this revision

Viewing changes to pith/reply.c

  • Committer: Bazaar Package Importer
  • Author(s): Asheesh Laroia
  • Date: 2007-02-17 13:17:42 UTC
  • Revision ID: james.westby@ubuntu.com-20070217131742-99x5c6cpg1pbkdhw
Tags: upstream-0.82+dfsg
ImportĀ upstreamĀ versionĀ 0.82+dfsg

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#if !defined(lint) && !defined(DOS)
 
2
static char rcsid[] = "$Id: reply.c 387 2007-01-24 18:42:22Z hubert@u.washington.edu $";
 
3
#endif
 
4
 
 
5
/*
 
6
 * ========================================================================
 
7
 * Copyright 2006-2007 University of Washington
 
8
 *
 
9
 * Licensed under the Apache License, Version 2.0 (the "License");
 
10
 * you may not use this file except in compliance with the License.
 
11
 * You may obtain a copy of the License at
 
12
 *
 
13
 *     http://www.apache.org/licenses/LICENSE-2.0
 
14
 *
 
15
 * ========================================================================
 
16
 */
 
17
 
 
18
#include "../pith/headers.h"
 
19
#include "../pith/reply.h"
 
20
#include "../pith/init.h"
 
21
#include "../pith/state.h"
 
22
#include "../pith/conf.h"
 
23
#include "../pith/remote.h"
 
24
#include "../pith/status.h"
 
25
#include "../pith/mailview.h"
 
26
#include "../pith/filter.h"
 
27
#include "../pith/newmail.h"
 
28
#include "../pith/bldaddr.h"
 
29
#include "../pith/mailindx.h"
 
30
#include "../pith/rfc2231.h"
 
31
#include "../pith/detach.h"
 
32
#include "../pith/help.h"
 
33
#include "../pith/pipe.h"
 
34
#include "../pith/addrstring.h"
 
35
#include "../pith/news.h"
 
36
#include "../pith/util.h"
 
37
#include "../pith/pattern.h"
 
38
#include "../pith/detoken.h"
 
39
#include "../pith/stream.h"
 
40
#include "../pith/busy.h"
 
41
#include "../pith/readfile.h"
 
42
 
 
43
 
 
44
/*
 
45
 * Internal prototypes
 
46
 */
 
47
void     reply_append_addr(ADDRESS **, ADDRESS *);
 
48
void     bounce_mask_header(char **, char *);
 
49
 
 
50
 
 
51
int     (*pith_opt_replyto_prompt)(void);
 
52
int     (*pith_opt_reply_to_all_prompt)(int *);
 
53
 
 
54
 
 
55
/*
 
56
 * standard type of storage object used for body parts...
 
57
 */
 
58
#if     defined(DOS) && !defined(WIN32)
 
59
#define           PART_SO_TYPE  TmpFileStar
 
60
#else
 
61
#define           PART_SO_TYPE  CharStar
 
62
#endif
 
63
 
 
64
 
 
65
/*
 
66
 * reply_harvest - 
 
67
 *
 
68
 *  Returns: 1 if addresses successfully copied
 
69
 *           0 on user cancel or error
 
70
 *
 
71
 *  Input flags: 
 
72
 *               RSF_FORCE_REPLY_TO
 
73
 *               RSF_QUERY_REPLY_ALL
 
74
 *               RSF_FORCE_REPLY_ALL
 
75
 *
 
76
 *  Output flags:
 
77
 *               RSF_FORCE_REPLY_ALL
 
78
 * 
 
79
 */
 
80
int
 
81
reply_harvest(struct pine *ps, long int msgno, char *section, ENVELOPE *env,
 
82
              struct mail_address **saved_from, struct mail_address **saved_to,
 
83
              struct mail_address **saved_cc, struct mail_address **saved_resent,
 
84
              int *flags)
 
85
{
 
86
    ADDRESS *ap, *ap2, *rep_address;
 
87
    int      ret = 0, sniff_resent = 0;
 
88
    char    *rep_field;
 
89
 
 
90
      /*
 
91
       * If Reply-To is same as From just treat it like it was From.
 
92
       * Otherwise, always use the reply-to if we're replying to more
 
93
       * than one msg or say ok to using it, even if it's us.
 
94
       * If there's no reply-to or it's the same as the from, assume
 
95
       * that the user doesn't want to reply to himself, unless there's
 
96
       * nobody else.
 
97
       */
 
98
    if(env->reply_to && !addr_lists_same(env->reply_to, env->from)
 
99
       && (F_ON(F_AUTO_REPLY_TO, ps_global)
 
100
           || ((*flags) & RSF_FORCE_REPLY_TO)
 
101
           || (pith_opt_replyto_prompt && (*pith_opt_replyto_prompt)() == 'y'))){
 
102
        rep_field   = "reply-to";
 
103
        rep_address = env->reply_to;
 
104
    }
 
105
    else{
 
106
        rep_field   = "From";
 
107
        rep_address = env->from;
 
108
    }
 
109
 
 
110
    ap = reply_cp_addr(ps, msgno, section, rep_field, *saved_from,
 
111
                       (ADDRESS *) NULL, rep_address, 0);
 
112
 
 
113
    if(ret == 'x') {
 
114
        cmd_cancelled("Reply");
 
115
        return(0);
 
116
    }
 
117
 
 
118
    reply_append_addr(saved_from, ap);
 
119
 
 
120
    /*--------- check for other recipients ---------*/
 
121
    if(((*flags) & (RSF_FORCE_REPLY_ALL | RSF_QUERY_REPLY_ALL))){
 
122
 
 
123
        if(ap = reply_cp_addr(ps, msgno, section, "To", *saved_to,
 
124
                              *saved_from, env->to, 0))
 
125
          reply_append_addr(saved_to, ap);
 
126
 
 
127
        if(ap = reply_cp_addr(ps, msgno, section, "Cc", *saved_cc,
 
128
                              *saved_from, env->cc, 0))
 
129
          reply_append_addr(saved_cc, ap);
 
130
 
 
131
        /*
 
132
         * In these cases, we either need to look at the resent headers
 
133
         * to include in the reply-to-all, or to decide whether or not
 
134
         * we need to ask the reply-to-all question.
 
135
         */
 
136
        if(((*flags) & RSF_FORCE_REPLY_ALL)
 
137
           || (((*flags) & RSF_QUERY_REPLY_ALL)
 
138
               && ((!(*saved_from) && !(*saved_cc))
 
139
                   || (*saved_from && !(*saved_to) && !(*saved_cc))))){
 
140
 
 
141
            sniff_resent++;
 
142
            if(ap2 = reply_resent(ps, msgno, section)){
 
143
                /*
 
144
                 * look for bogus addr entries and replace
 
145
                 */
 
146
                if(ap = reply_cp_addr(ps, 0, NULL, NULL, *saved_resent,
 
147
                                      *saved_from, ap2, 0))
 
148
 
 
149
                  reply_append_addr(saved_resent, ap);
 
150
 
 
151
                mail_free_address(&ap2);
 
152
            }
 
153
        }
 
154
 
 
155
        /*
 
156
         * It makes sense to ask reply-to-all now.
 
157
         */
 
158
        if(((*flags) & RSF_QUERY_REPLY_ALL)
 
159
           && ((*saved_from && (*saved_to || *saved_cc || *saved_resent))
 
160
               || (*saved_cc || *saved_resent))){
 
161
            *flags &= ~RSF_QUERY_REPLY_ALL;
 
162
            if(pith_opt_reply_to_all_prompt
 
163
               && (*pith_opt_reply_to_all_prompt)(flags) < 0){
 
164
                cmd_cancelled("Reply");
 
165
                return(0);
 
166
            }
 
167
        }
 
168
 
 
169
        /*
 
170
         * If we just answered yes to the reply-to-all question and
 
171
         * we still haven't collected the resent headers, do so now.
 
172
         */
 
173
        if(((*flags) & RSF_FORCE_REPLY_ALL) && !sniff_resent
 
174
           && (ap2 = reply_resent(ps, msgno, section))){
 
175
            /*
 
176
             * look for bogus addr entries and replace
 
177
             */
 
178
            if(ap = reply_cp_addr(ps, 0, NULL, NULL, *saved_resent,
 
179
                                  *saved_from, ap2, 0))
 
180
              reply_append_addr(saved_resent, ap);
 
181
 
 
182
            mail_free_address(&ap2);
 
183
        }
 
184
    }
 
185
 
 
186
    return(1);
 
187
}
 
188
 
 
189
 
 
190
/*----------------------------------------------------------------------
 
191
    Return a pointer to a copy of the given address list
 
192
  filtering out those already in the "mask" lists and ourself.
 
193
 
 
194
Args:  mask1  -- Don't copy if in this list
 
195
       mask2  --  or if in this list
 
196
       source -- List to be copied
 
197
       us_too -- Don't filter out ourself.
 
198
 
 
199
  ---*/
 
200
ADDRESS *
 
201
reply_cp_addr(struct pine *ps, long int msgno, char *section, char *field,
 
202
              struct mail_address *mask1, struct mail_address *mask2,
 
203
              struct mail_address *source, int us_too)
 
204
{
 
205
    ADDRESS *tmp1, *tmp2, *ret = NULL, **ret_tail;
 
206
 
 
207
    for(tmp1 = source; msgno && tmp1; tmp1 = tmp1->next)
 
208
      if(tmp1->host && tmp1->host[0] == '.'){
 
209
          char *h, *fields[2];
 
210
 
 
211
          fields[0] = field;
 
212
          fields[1] = NULL;
 
213
          if(h = pine_fetchheader_lines(ps ? ps->mail_stream : NULL,
 
214
                                        msgno, section, fields)){
 
215
              char *p, fname[32];
 
216
              int q;
 
217
 
 
218
              q = strlen(h);
 
219
 
 
220
              strncpy(fname, field, sizeof(fname)-2);
 
221
              fname[sizeof(fname)-2] = '\0';
 
222
              strncat(fname, ":", sizeof(fname)-strlen(fname));
 
223
              fname[sizeof(fname)-1] = '\0';
 
224
 
 
225
              for(p = h; p = strstr(p, fname); )
 
226
                rplstr(p, q-(p-h), strlen(fname), "");  /* strip field strings */
 
227
 
 
228
              sqznewlines(h);                   /* blat out CR's & LF's */
 
229
              for(p = h; p = strchr(p, TAB); )
 
230
                *p++ = ' ';                     /* turn TABs to whitespace */
 
231
 
 
232
              if(*h){
 
233
                  long l;
 
234
                  size_t ll;
 
235
 
 
236
                  ret = (ADDRESS *) fs_get(sizeof(ADDRESS));
 
237
                  memset(ret, 0, sizeof(ADDRESS));
 
238
 
 
239
                  /* get rid of leading white space */
 
240
                  for(p = h; *p == SPACE; p++)
 
241
                    ;
 
242
                  
 
243
                  if(p != h){
 
244
                      memmove(h, p, l = strlen(p));
 
245
                      h[l] = '\0';
 
246
                  }
 
247
 
 
248
                  /* base64 armor plate the gunk to protect against
 
249
                   * c-client quoting in output.
 
250
                   */
 
251
                  p = (char *) rfc822_binary(h, strlen(h),
 
252
                                             (unsigned long *) &l);
 
253
                  sqznewlines(p);
 
254
                  fs_give((void **) &h);
 
255
                  /*
 
256
                   * Seems like the 4 ought to be a 2, but I'll leave it
 
257
                   * to be safe, in case something else adds 2 chars later.
 
258
                   */
 
259
                  ll = strlen(p) + 4;
 
260
                  ret->mailbox = (char *) fs_get(ll * sizeof(char));
 
261
                  snprintf(ret->mailbox, ll, "&%s", p);
 
262
                  ret->mailbox[ll-1] = '\0';
 
263
                  fs_give((void **) &p);
 
264
                  ret->host = cpystr(".RAW-FIELD.");
 
265
              }
 
266
          }
 
267
 
 
268
          return(ret);
 
269
      }
 
270
 
 
271
    ret_tail = &ret;
 
272
    for(source = first_addr(source); source; source = source->next){
 
273
        for(tmp1 = first_addr(mask1); tmp1; tmp1 = tmp1->next)
 
274
          if(address_is_same(source, tmp1))
 
275
            break;
 
276
 
 
277
        for(tmp2 = first_addr(mask2); !tmp1 && tmp2; tmp2 = tmp2->next)
 
278
          if(address_is_same(source, tmp2))
 
279
            break;
 
280
 
 
281
        /*
 
282
         * If there's no match in masks *and* this address isn't us, copy...
 
283
         */
 
284
        if(!tmp1 && !tmp2 && (us_too || !ps || !address_is_us(source, ps))){
 
285
            tmp1         = source->next;
 
286
            source->next = NULL;        /* only copy one addr! */
 
287
            *ret_tail    = rfc822_cpy_adr(source);
 
288
            ret_tail     = &(*ret_tail)->next;
 
289
            source->next = tmp1;        /* restore rest of list */
 
290
        }
 
291
    }
 
292
 
 
293
    return(ret);
 
294
}
 
295
 
 
296
 
 
297
ACTION_S *
 
298
set_role_from_msg(struct pine *ps, long int rflags, long int msgno, char *section)
 
299
{
 
300
    ACTION_S      *role = NULL;
 
301
    PAT_S         *pat = NULL;
 
302
    SEARCHSET     *ss = NULL;
 
303
    PAT_STATE      pstate;
 
304
 
 
305
    if(!nonempty_patterns(rflags, &pstate))
 
306
      return(role);
 
307
 
 
308
    if(msgno > 0L){
 
309
        ss = mail_newsearchset();
 
310
        ss->first = ss->last = (unsigned long)msgno;
 
311
    }
 
312
 
 
313
    /* Go through the possible roles one at a time until we get a match. */
 
314
    pat = first_pattern(&pstate);
 
315
 
 
316
    /* calculate this message's score if needed */
 
317
    if(ss && pat && scores_are_used(SCOREUSE_GET) & SCOREUSE_ROLES &&
 
318
       get_msg_score(ps->mail_stream, msgno) == SCORE_UNDEF)
 
319
      (void)calculate_some_scores(ps->mail_stream, ss, 0);
 
320
 
 
321
    while(!role && pat){
 
322
        if(match_pattern(pat->patgrp, ps->mail_stream, ss, section,
 
323
                         get_msg_score, SO_NOSERVER|SE_NOPREFETCH)){
 
324
            if(!pat->action || pat->action->bogus)
 
325
              break;
 
326
 
 
327
            role = pat->action;
 
328
        }
 
329
        else
 
330
          pat = next_pattern(&pstate);
 
331
    }
 
332
 
 
333
    if(ss)
 
334
      mail_free_searchset(&ss);
 
335
 
 
336
    return(role);
 
337
}
 
338
 
 
339
 
 
340
/*
 
341
 * reply_seed - fill in reply header
 
342
 * 
 
343
 */
 
344
void
 
345
reply_seed(struct pine *ps, ENVELOPE *outgoing, ENVELOPE *env,
 
346
           struct mail_address *saved_from, struct mail_address *saved_to,
 
347
           struct mail_address *saved_cc, struct mail_address *saved_resent,
 
348
           char **fcc, int replytoall, char **errmsg)
 
349
{
 
350
    ADDRESS **to_tail, **cc_tail;
 
351
    
 
352
    to_tail = &outgoing->to;
 
353
    cc_tail = &outgoing->cc;
 
354
 
 
355
    if(saved_from){
 
356
        /* Put Reply-To or From in To. */
 
357
        *to_tail = reply_cp_addr(ps, 0, NULL, NULL, outgoing->to,
 
358
                                 (ADDRESS *) NULL, saved_from, 1);
 
359
        /* and the rest in cc */
 
360
        if(replytoall){
 
361
            *cc_tail = reply_cp_addr(ps, 0, NULL, NULL, outgoing->cc,
 
362
                                     outgoing->to, saved_to, 1);
 
363
            while(*cc_tail)             /* stay on last address */
 
364
              cc_tail = &(*cc_tail)->next;
 
365
 
 
366
            *cc_tail = reply_cp_addr(ps, 0, NULL, NULL, outgoing->cc,
 
367
                                     outgoing->to, saved_cc, 1);
 
368
            while(*cc_tail)
 
369
              cc_tail = &(*cc_tail)->next;
 
370
 
 
371
            *cc_tail = reply_cp_addr(ps, 0, NULL, NULL, outgoing->cc,
 
372
                                     outgoing->to, saved_resent, 1);
 
373
        }
 
374
    }
 
375
    else if(saved_to){
 
376
        /* No From (maybe from us), put To in To. */
 
377
        *to_tail = reply_cp_addr(ps, 0, NULL, NULL, outgoing->to,
 
378
                                 (ADDRESS *)NULL, saved_to, 1);
 
379
        /* and the rest in cc */
 
380
        if(replytoall){
 
381
            *cc_tail = reply_cp_addr(ps, 0, NULL, NULL, outgoing->cc,
 
382
                                     outgoing->to, saved_cc, 1);
 
383
            while(*cc_tail)
 
384
              cc_tail = &(*cc_tail)->next;
 
385
 
 
386
            *cc_tail = reply_cp_addr(ps, 0, NULL, NULL, outgoing->cc,
 
387
                                     outgoing->to, saved_resent, 1);
 
388
        }
 
389
    }
 
390
    else{
 
391
        /* No From or To, put everything else in To if replytoall, */
 
392
        if(replytoall){
 
393
            *to_tail = reply_cp_addr(ps, 0, NULL, NULL, outgoing->to,
 
394
                                     (ADDRESS *) NULL, saved_cc, 1);
 
395
            while(*to_tail)
 
396
              to_tail = &(*to_tail)->next;
 
397
 
 
398
            *to_tail = reply_cp_addr(ps, 0, NULL, NULL, outgoing->to,
 
399
                                     (ADDRESS *) NULL, saved_resent, 1);
 
400
        }
 
401
        /* else, reply to original From which must be us */
 
402
        else{
 
403
            /*
 
404
             * Put self in To if in original From.
 
405
             */
 
406
            if(!outgoing->newsgroups)
 
407
              *to_tail = reply_cp_addr(ps, 0, NULL, NULL, outgoing->to,
 
408
                                       (ADDRESS *) NULL, env->from, 1);
 
409
        }
 
410
    }
 
411
 
 
412
    /* add any missing personal data */
 
413
    reply_fish_personal(outgoing, env);
 
414
 
 
415
    /* get fcc */
 
416
    if(fcc && outgoing->to && outgoing->to->host[0] != '.'){
 
417
        *fcc = get_fcc_based_on_to(outgoing->to);
 
418
    }
 
419
    else if(fcc && outgoing->newsgroups){
 
420
        char *newsgroups_returned = NULL;
 
421
        int   rv;
 
422
 
 
423
        rv = news_grouper(outgoing->newsgroups, &newsgroups_returned, errmsg, fcc, NULL);
 
424
        if(rv != -1 &&
 
425
            strcmp(outgoing->newsgroups, newsgroups_returned)){
 
426
            fs_give((void **)&outgoing->newsgroups);
 
427
            outgoing->newsgroups = newsgroups_returned;
 
428
        }
 
429
        else
 
430
          fs_give((void **) &newsgroups_returned);
 
431
    }
 
432
}
 
433
 
 
434
 
 
435
/*----------------------------------------------------------------------
 
436
    Test the given address lists for equivalence
 
437
 
 
438
Args:  x -- First address list for comparison
 
439
       y -- Second address for comparison
 
440
 
 
441
  ---*/
 
442
int
 
443
addr_lists_same(struct mail_address *x, struct mail_address *y)
 
444
{
 
445
    for(x = first_addr(x), y = first_addr(y);
 
446
        x && y;
 
447
        x = first_addr(x->next), y = first_addr(y->next)){
 
448
        if(!address_is_same(x, y))
 
449
          return(0);
 
450
    }
 
451
 
 
452
    return(!x && !y);                   /* true if ran off both lists */
 
453
}
 
454
 
 
455
 
 
456
/*----------------------------------------------------------------------
 
457
    Test the given address against those in the given envelope's to, cc
 
458
 
 
459
Args:  addr -- address for comparison
 
460
       env  -- envelope to compare against
 
461
 
 
462
  ---*/
 
463
int
 
464
addr_in_env(struct mail_address *addr, ENVELOPE *env)
 
465
{
 
466
    ADDRESS *ap;
 
467
 
 
468
    for(ap = env ? env->to : NULL; ap; ap = ap->next)
 
469
      if(address_is_same(addr, ap))
 
470
        return(1);
 
471
 
 
472
    for(ap = env ? env->cc : NULL; ap; ap = ap->next)
 
473
      if(address_is_same(addr, ap))
 
474
        return(1);
 
475
 
 
476
    return(0);                          /* not found! */
 
477
}
 
478
 
 
479
 
 
480
/*----------------------------------------------------------------------
 
481
    Add missing personal info dest from src envelope
 
482
 
 
483
Args:  dest -- envelope to add personal info to
 
484
       src  -- envelope to get personal info from
 
485
 
 
486
NOTE: This is just kind of a courtesy function.  It's really not adding
 
487
      anything needed to get the mail thru, but it is nice for the user
 
488
      under some odd circumstances.
 
489
  ---*/
 
490
void
 
491
reply_fish_personal(ENVELOPE *dest, ENVELOPE *src)
 
492
{
 
493
    ADDRESS *da, *sa;
 
494
 
 
495
    for(da = dest ? dest->to : NULL; da; da = da->next){
 
496
        if(da->personal && !da->personal[0])
 
497
          fs_give((void **)&da->personal);
 
498
 
 
499
        for(sa = src ? src->to : NULL; sa && !da->personal ; sa = sa->next)
 
500
          if(address_is_same(da, sa) && sa->personal && sa->personal[0])
 
501
            da->personal = cpystr(sa->personal);
 
502
 
 
503
        for(sa = src ? src->cc : NULL; sa && !da->personal; sa = sa->next)
 
504
          if(address_is_same(da, sa) && sa->personal && sa->personal[0])
 
505
            da->personal = cpystr(sa->personal);
 
506
    }
 
507
 
 
508
    for(da = dest ? dest->cc : NULL; da; da = da->next){
 
509
        if(da->personal && !da->personal[0])
 
510
          fs_give((void **)&da->personal);
 
511
 
 
512
        for(sa = src ? src->to : NULL; sa && !da->personal; sa = sa->next)
 
513
          if(address_is_same(da, sa) && sa->personal && sa->personal[0])
 
514
            da->personal = cpystr(sa->personal);
 
515
 
 
516
        for(sa = src ? src->cc : NULL; sa && !da->personal; sa = sa->next)
 
517
          if(address_is_same(da, sa) && sa->personal && sa->personal[0])
 
518
            da->personal = cpystr(sa->personal);
 
519
    }
 
520
}
 
521
 
 
522
 
 
523
/*----------------------------------------------------------------------
 
524
   Given a header field and envelope, build "References: " header data
 
525
 
 
526
Args:  
 
527
 
 
528
Returns: 
 
529
  ---*/
 
530
char *
 
531
reply_build_refs(ENVELOPE *env)
 
532
{
 
533
    int   len, id_len, first_ref_len = 0, foldslop;
 
534
    char *p, *refs = NULL, *h = env->references;
 
535
    char *first_ref = NULL, *tail_refs = NULL;
 
536
 
 
537
 
 
538
    if(!(env->message_id && (id_len = strlen(env->message_id))))
 
539
      return(NULL);
 
540
 
 
541
    if(h){
 
542
        /*
 
543
         * The length we have to work with doesn't seem to appear in any
 
544
         * standards. Steve Jones says that in comp.news discussions he
 
545
         * has seen 1024 as the longest length of a header value.
 
546
         * In the inn news source we find MAXHEADERSIZE = 1024. It appears
 
547
         * that is the maximum length of the header value, including 
 
548
         * newlines for folded lines (that is, the newlines are counted).
 
549
         * We'll be conservative and figure every reference will take up a
 
550
         * line of its own when we fold. We'll also count 2 for CRLF instead
 
551
         * of just one for LF just to be safe.       hubert 2001-jan
 
552
         * J.B. Moreno <planb@newsreaders.com> says "The server limit is
 
553
         * more commonly encountered at 999/1000 bytes [...]". So we'll
 
554
         * back off to 999 instead of 1024.
 
555
         */
 
556
#define MAXHEADERSIZE (999)
 
557
 
 
558
        /* count the total number of potential folds, max of 2 bytes each */
 
559
        for(foldslop = 2, p = h; (p = strstr(p+1, "> <")); )
 
560
          foldslop += 2;
 
561
        
 
562
        if((len=strlen(h)) + 1+id_len + foldslop >= MAXHEADERSIZE
 
563
           && (p = strstr(h, "> <"))){
 
564
            /*
 
565
             * If the references line is so long that we are going to have
 
566
             * to delete some of the references, delete the 2nd, 3rd, ...
 
567
             * We don't want to delete the first message in the thread.
 
568
             */
 
569
            p[1] = '\0';
 
570
            first_ref = cpystr(h);
 
571
            first_ref_len = strlen(first_ref)+1;        /* len includes space */
 
572
            p[1] = ' ';
 
573
            tail_refs = p+2;
 
574
            /* get rid of 2nd, 3rd, ... until it fits */
 
575
            while((len=strlen(tail_refs)) + first_ref_len + 1+id_len +
 
576
                                                    foldslop >= MAXHEADERSIZE
 
577
                  && (p = strstr(tail_refs, "> <"))){
 
578
                tail_refs = p + 2;
 
579
                foldslop -= 2;
 
580
            }
 
581
 
 
582
            /*
 
583
             * If the individual references are seriously long, somebody
 
584
             * is messing with us and we don't care if it works right.
 
585
             */
 
586
            if((len=strlen(tail_refs)) + first_ref_len + 1+id_len +
 
587
                                                    foldslop >= MAXHEADERSIZE){
 
588
                first_ref_len = len = 0;
 
589
                tail_refs = NULL;
 
590
                if(first_ref)
 
591
                  fs_give((void **)&first_ref);
 
592
            }
 
593
        }
 
594
        else
 
595
          tail_refs = h;
 
596
 
 
597
        refs = (char *)fs_get((first_ref_len + 1+id_len + len + 1) *
 
598
                                                        sizeof(char));
 
599
        snprintf(refs, first_ref_len + 1+id_len + len + 1, "%s%s%s%s%s",
 
600
                first_ref ? first_ref : "",
 
601
                first_ref ? " " : "",
 
602
                tail_refs ? tail_refs : "",
 
603
                tail_refs ? " " : "",
 
604
                env->message_id);
 
605
        refs[first_ref_len + 1+id_len + len] = '\0';
 
606
    }
 
607
 
 
608
    if(!refs && id_len)
 
609
      refs = cpystr(env->message_id);
 
610
 
 
611
    if(first_ref)
 
612
      fs_give((void **)&first_ref);
 
613
 
 
614
    return(refs);
 
615
}
 
616
 
 
617
 
 
618
 
 
619
/*----------------------------------------------------------------------
 
620
   Snoop for any Resent-* headers, and return an ADDRESS list
 
621
 
 
622
Args:  stream -- 
 
623
       msgno -- 
 
624
 
 
625
Returns: either NULL if no Resent-* or parsed ADDRESS struct list
 
626
  ---*/
 
627
ADDRESS *
 
628
reply_resent(struct pine *pine_state, long int msgno, char *section)
 
629
{
 
630
#define RESENTFROM 0
 
631
#define RESENTTO   1
 
632
#define RESENTCC   2
 
633
    ADDRESS     *rlist = NULL, **a, **b;
 
634
    char        *hdrs, *values[RESENTCC+1];
 
635
    int          i;
 
636
    static char *fields[] = {"Resent-From", "Resent-To", "Resent-Cc", NULL};
 
637
    static char *fakedomain = "@";
 
638
 
 
639
    if(hdrs = pine_fetchheader_lines(pine_state->mail_stream,
 
640
                                     msgno, section, fields)){
 
641
        memset(values, 0, (RESENTCC+1) * sizeof(char *));
 
642
        simple_header_parse(hdrs, fields, values);
 
643
        for(i = RESENTFROM; i <= RESENTCC; i++)
 
644
          rfc822_parse_adrlist(&rlist, values[i],
 
645
                               (F_ON(F_COMPOSE_REJECTS_UNQUAL, pine_state))
 
646
                                 ? fakedomain : pine_state->maildomain);
 
647
 
 
648
        /* pare dup's ... */
 
649
        for(a = &rlist; *a; a = &(*a)->next)    /* compare every address */
 
650
          for(b = &(*a)->next; *b; )            /* to the others         */
 
651
            if(address_is_same(*a, *b)){
 
652
                ADDRESS *t = *b;
 
653
 
 
654
                if(!(*a)->personal){            /* preserve personal name */
 
655
                    (*a)->personal = (*b)->personal;
 
656
                    (*b)->personal = NULL;
 
657
                }
 
658
 
 
659
                *b      = t->next;
 
660
                t->next = NULL;
 
661
                mail_free_address(&t);
 
662
            }
 
663
            else
 
664
              b = &(*b)->next;
 
665
    }
 
666
 
 
667
    if(hdrs)
 
668
      fs_give((void **) &hdrs);
 
669
    
 
670
    return(rlist);
 
671
}
 
672
 
 
673
 
 
674
/*----------------------------------------------------------------------
 
675
    Format and return subject suitable for the reply command
 
676
 
 
677
Args:  subject -- subject to build reply subject for
 
678
       buf -- buffer to use for writing.  If non supplied, alloc one.
 
679
       buflen -- length of buf if supplied, else ignored
 
680
 
 
681
Returns: with either "Re:" prepended or not, if already there.
 
682
         returned string is allocated.
 
683
  ---*/
 
684
char *
 
685
reply_subject(char *subject, char *buf, size_t buflen)
 
686
{
 
687
    size_t  l   = (subject && *subject) ? 4*strlen(subject) : 10;
 
688
    char   *tmp = fs_get(l + 1), *decoded, *p;
 
689
 
 
690
    if(!buf){
 
691
        buflen = l + 5;
 
692
        buf = fs_get(buflen);
 
693
    }
 
694
 
 
695
    /* decode any 8bit into tmp buffer */
 
696
    decoded = (char *) rfc1522_decode((unsigned char *)tmp, l+1, subject, NULL);
 
697
 
 
698
    buf[0] = '\0';
 
699
    if(decoded                                  /* already "re:" ? */
 
700
       && (decoded[0] == 'R' || decoded[0] == 'r')
 
701
       && (decoded[1] == 'E' || decoded[1] == 'e')){
 
702
 
 
703
        if(decoded[2] == ':')
 
704
          snprintf(buf, buflen, "%.*s", buflen-1, subject);
 
705
        else if((decoded[2] == '[') && (p = strchr(decoded, ']'))){
 
706
            p++;
 
707
            while(*p && isspace((unsigned char)*p)) p++;
 
708
            if(p[0] == ':')
 
709
              snprintf(buf, buflen, "%.*s", buflen-1, subject);
 
710
        }
 
711
    }
 
712
 
 
713
    if(!buf[0])
 
714
      snprintf(buf, buflen, "Re: %.*s", buflen-1,
 
715
              (subject && *subject) ? subject : "your mail");
 
716
 
 
717
    buf[buflen-1] = '\0';
 
718
 
 
719
    fs_give((void **) &tmp);
 
720
    return(buf);
 
721
}
 
722
 
 
723
 
 
724
/*----------------------------------------------------------------------
 
725
    return initials for the given personal name
 
726
 
 
727
Args:  name -- Personal name to extract initials from
 
728
 
 
729
Returns: pointer to name overwritten with initials
 
730
  ---*/
 
731
char *
 
732
reply_quote_initials(char *name)
 
733
{
 
734
    char *s = name,
 
735
         *w = name;
 
736
    
 
737
    /* while there are still characters to look at */
 
738
    while(s && *s){
 
739
        /* skip to next initial */
 
740
        while(*s && isspace((unsigned char) *s))
 
741
          s++;
 
742
        
 
743
        /* skip over cruft like single quotes */
 
744
        while(*s && !isalnum((unsigned char) *s))
 
745
          s++;
 
746
 
 
747
        /* copy initial */
 
748
        if(*s)
 
749
          *w++ = *s++;
 
750
        
 
751
        /* skip to end of this piece of name */
 
752
        while(*s && !isspace((unsigned char) *s))
 
753
          s++;
 
754
    }
 
755
 
 
756
    if(w)
 
757
      *w = '\0';
 
758
 
 
759
    return(name);
 
760
}
 
761
 
 
762
/*
 
763
 * There is an assumption that MAX_SUBSTITUTION is <= the size of the
 
764
 * tokens being substituted for (only in the size of buf below).
 
765
 */
 
766
#define MAX_SUBSTITUTION 6
 
767
#define MAX_PREFIX 63
 
768
static char *from_token = "_FROM_";
 
769
static char *nick_token = "_NICK_";
 
770
static char *init_token = "_INIT_";
 
771
 
 
772
/*----------------------------------------------------------------------
 
773
    return a quoting string, "> " by default, for replied text
 
774
 
 
775
Args:  env -- envelope of message being replied to
 
776
 
 
777
Returns: malloc'd array containing quoting string, freed by caller
 
778
  ---*/
 
779
char *
 
780
reply_quote_str(ENVELOPE *env)
 
781
{
 
782
    char *prefix, *repl, *p, buf[MAX_PREFIX+1], pbf[MAX_SUBSTITUTION+1];
 
783
 
 
784
    strncpy(buf, ps_global->VAR_REPLY_STRING, sizeof(buf)-1);
 
785
    buf[sizeof(buf)-1] = '\0';
 
786
 
 
787
    /* set up the prefix to quote included text */
 
788
    if(p = strstr(buf, from_token)){
 
789
        repl = (env && env->from && env->from->mailbox) ? env->from->mailbox
 
790
                                                        : "";
 
791
        strncpy(pbf, repl, sizeof(pbf)-1);
 
792
        pbf[sizeof(pbf)-1] = '\0';;
 
793
        rplstr(p, sizeof(buf)-(p-buf), strlen(from_token), pbf);
 
794
    }
 
795
    
 
796
    if(p = strstr(buf, nick_token)){
 
797
        repl = (env &&
 
798
                env->from &&
 
799
                env->from &&
 
800
                get_nickname_from_addr(env->from, tmp_20k_buf, 1000))
 
801
                 ? tmp_20k_buf : "";
 
802
        strncpy(pbf, repl, sizeof(pbf)-1);
 
803
        pbf[sizeof(pbf)-1] = '\0';;
 
804
        rplstr(p, sizeof(buf)-(p-buf), strlen(nick_token), pbf);
 
805
    }
 
806
 
 
807
    if(p = strstr(buf, init_token)){
 
808
        char *q = NULL;
 
809
        char *dummy = NULL;
 
810
        char  buftmp[MAILTMPLEN];
 
811
 
 
812
        snprintf(buftmp, sizeof(buftmp), "%.200s",
 
813
         (env && env->from && env->from->personal) ? env->from->personal : "");
 
814
        buftmp[sizeof(buftmp)-1] = '\0';
 
815
 
 
816
        repl = (env && env->from && env->from->personal)
 
817
                 ? reply_quote_initials(q = cpystr((char *)rfc1522_decode(
 
818
                                                (unsigned char *)tmp_20k_buf, 
 
819
                                                SIZEOF_20KBUF,
 
820
                                                buftmp, &dummy)))
 
821
                 : "";
 
822
 
 
823
        if(dummy)
 
824
          fs_give((void **)&dummy);
 
825
 
 
826
        istrncpy(pbf, repl, sizeof(pbf)-1);
 
827
        pbf[sizeof(pbf)-1] = '\0';;
 
828
        rplstr(p, sizeof(buf)-(p-buf), strlen(init_token), pbf);
 
829
        if(q)
 
830
          fs_give((void **)&q);
 
831
    }
 
832
    
 
833
    prefix = removing_quotes(cpystr(buf));
 
834
 
 
835
    return(prefix);
 
836
}
 
837
 
 
838
int
 
839
reply_quote_str_contains_tokens(void)
 
840
{
 
841
    return(ps_global->VAR_REPLY_STRING && ps_global->VAR_REPLY_STRING[0] &&
 
842
           (strstr(ps_global->VAR_REPLY_STRING, from_token) ||
 
843
            strstr(ps_global->VAR_REPLY_STRING, nick_token) ||
 
844
            strstr(ps_global->VAR_REPLY_STRING, init_token)));
 
845
}
 
846
 
 
847
 
 
848
/*----------------------------------------------------------------------
 
849
  Build the body for the message number/part being replied to
 
850
 
 
851
    Args: 
 
852
 
 
853
  Result: BODY structure suitable for sending
 
854
 
 
855
  ----------------------------------------------------------------------*/
 
856
BODY *
 
857
reply_body(MAILSTREAM *stream, ENVELOPE *env, struct mail_bodystruct *orig_body,
 
858
           long int msgno, char *sect_prefix, void *msgtext, char *prefix,
 
859
           int plustext, ACTION_S *role, int toplevel, REDRAFT_POS_S **redraft_pos)
 
860
{
 
861
    char     *p, *sig = NULL, *section, sect_buf[256];
 
862
    BODY     *body = NULL, *tmp_body = NULL;
 
863
    PART     *part;
 
864
    gf_io_t   pc;
 
865
    int       impl, template_len = 0, leave_cursor_at_top = 0, reply_raw_body = 0;
 
866
 
 
867
    if(sect_prefix)
 
868
      snprintf(section = sect_buf, sizeof(sect_buf), "%.*s.1", sizeof(sect_buf)-1, sect_prefix);
 
869
    else
 
870
      section = "1";
 
871
 
 
872
    sect_buf[sizeof(sect_buf)-1] = '\0';
 
873
 
 
874
    if(ps_global->full_header == 2
 
875
       && F_ON(F_ENABLE_FULL_HDR_AND_TEXT, ps_global))
 
876
      reply_raw_body = 1;
 
877
 
 
878
    gf_set_so_writec(&pc, (STORE_S *) msgtext);
 
879
 
 
880
    if(toplevel){
 
881
        char *filtered;
 
882
 
 
883
        impl = 0;
 
884
        filtered = detoken(role, env, 0,
 
885
                           F_ON(F_SIG_AT_BOTTOM, ps_global) ? 1 : 0,
 
886
                           0, redraft_pos, &impl);
 
887
        if(filtered){
 
888
            if(*filtered){
 
889
                so_puts((STORE_S *)msgtext, filtered);
 
890
                if(impl == 1)
 
891
                  template_len = strlen(filtered);
 
892
                else if(impl == 2)
 
893
                  leave_cursor_at_top++;
 
894
            }
 
895
            
 
896
            fs_give((void **)&filtered);
 
897
        }
 
898
        else
 
899
          impl = 1;
 
900
    }
 
901
    else
 
902
      impl = 1;
 
903
 
 
904
    if(toplevel &&
 
905
       (sig = reply_signature(role, env, redraft_pos, &impl)) &&
 
906
       F_OFF(F_SIG_AT_BOTTOM, ps_global)){
 
907
 
 
908
        /*
 
909
         * If CURSORPOS was set explicitly in sig_file, and there was a
 
910
         * template file before that, we need to adjust the offset by the
 
911
         * length of the template file. However, if the template had
 
912
         * a set CURSORPOS in it then impl was 2 before getting to the
 
913
         * signature, so offset wouldn't have been reset by the signature
 
914
         * CURSORPOS and offset would already be correct. That case will
 
915
         * be ok here because template_len will be 0 and adding it does
 
916
         * nothing. If template
 
917
         * didn't have CURSORPOS in it, then impl was 1 and got set to 2
 
918
         * by the CURSORPOS in the sig. In that case we have to adjust the
 
919
         * offset. That's what the next line does. It adjusts it if
 
920
         * template_len is nonzero and if CURSORPOS was set in sig_file.
 
921
         */
 
922
        if(impl == 2)
 
923
          (*redraft_pos)->offset += template_len;
 
924
        
 
925
        if(*sig)
 
926
          so_puts((STORE_S *)msgtext, sig);
 
927
        
 
928
        /*
 
929
         * Set sig to NULL because we've already used it. If SIG_AT_BOTTOM
 
930
         * is set, we won't have used it yet and want it to be non-NULL.
 
931
         */
 
932
        fs_give((void **)&sig);
 
933
    }
 
934
 
 
935
    /*
 
936
     * Only put cursor in sig if there is a cursorpos there but not
 
937
     * one in the template, and sig-at-bottom.
 
938
     */
 
939
    if(!(sig && impl == 2 && !leave_cursor_at_top))
 
940
      leave_cursor_at_top++;
 
941
 
 
942
    if(plustext){
 
943
        if(!orig_body
 
944
           || orig_body->type == TYPETEXT
 
945
           || reply_raw_body
 
946
           || F_OFF(F_ATTACHMENTS_IN_REPLY, ps_global)){
 
947
            /*------ Simple text-only message ----*/
 
948
            body                     = mail_newbody();
 
949
            body->type               = TYPETEXT;
 
950
            body->contents.text.data = msgtext;
 
951
            reply_delimiter(env, role, pc);
 
952
            if(F_ON(F_INCLUDE_HEADER, ps_global))
 
953
              reply_forward_header(stream, msgno, sect_prefix,
 
954
                                   env, pc, prefix);
 
955
 
 
956
            if(!orig_body || reply_raw_body || reply_body_text(orig_body, &tmp_body)){
 
957
                get_body_part_text(stream, reply_raw_body ? NULL : tmp_body, msgno,
 
958
                                   reply_raw_body ? sect_prefix
 
959
                                   : (p = body_partno(stream, msgno, tmp_body)),
 
960
                                   0L, pc, prefix, NULL);
 
961
                if(!reply_raw_body && p)
 
962
                  fs_give((void **) &p);
 
963
            }
 
964
            else{
 
965
                gf_puts(NEWLINE, pc);
 
966
                gf_puts("  [NON-Text Body part not included]", pc);
 
967
                gf_puts(NEWLINE, pc);
 
968
            }
 
969
        }
 
970
        else if(orig_body->type == TYPEMULTIPART){
 
971
            /*------ Message is Multipart ------*/
 
972
            if(orig_body->subtype
 
973
               && !strucmp(orig_body->subtype, "signed")
 
974
               && orig_body->nested.part){
 
975
                /* operate only on the signed part */
 
976
                body = reply_body(stream, env,
 
977
                                  &orig_body->nested.part->body,
 
978
                                  msgno, section, msgtext, prefix,
 
979
                                  plustext, role, 0, redraft_pos);
 
980
            }
 
981
            else if(orig_body->subtype
 
982
                    && !strucmp(orig_body->subtype, "alternative")){
 
983
                /* Set up the simple text reply */
 
984
                body                     = mail_newbody();
 
985
                body->type               = TYPETEXT;
 
986
                body->contents.text.data = msgtext;
 
987
 
 
988
                if(reply_body_text(orig_body, &tmp_body)){
 
989
                    reply_delimiter(env, role, pc);
 
990
                    if(F_ON(F_INCLUDE_HEADER, ps_global))
 
991
                      reply_forward_header(stream, msgno, sect_prefix,
 
992
                                           env, pc, prefix);
 
993
 
 
994
                    get_body_part_text(stream, tmp_body, msgno,
 
995
                                       p = body_partno(stream,msgno,tmp_body),
 
996
                                       0L, pc, prefix, NULL);
 
997
                    fs_give((void **) &p);
 
998
                }
 
999
                else
 
1000
                  q_status_message(SM_ORDER | SM_DING, 3, 3,
 
1001
                            "No suitable multipart text found for inclusion!");
 
1002
            }
 
1003
            else{
 
1004
                body = copy_body(NULL, orig_body);
 
1005
 
 
1006
                /*
 
1007
                 * whatever subtype it is, demote it
 
1008
                 * to plain old MIXED.
 
1009
                 */
 
1010
                if(body->subtype)
 
1011
                  fs_give((void **) &body->subtype);
 
1012
 
 
1013
                body->subtype = cpystr("Mixed");
 
1014
 
 
1015
                if(body->nested.part &&
 
1016
                   body->nested.part->body.type == TYPETEXT) {
 
1017
                    char *new_charset = NULL;
 
1018
 
 
1019
                    /*---- First part of the message is text -----*/
 
1020
                    body->nested.part->body.contents.text.data = msgtext;
 
1021
                    if(body->nested.part->body.subtype &&
 
1022
                       strucmp(body->nested.part->body.subtype, "Plain")){
 
1023
                        fs_give((void **)&body->nested.part->body.subtype);
 
1024
                        body->nested.part->body.subtype = cpystr("Plain");
 
1025
                    }
 
1026
                    reply_delimiter(env, role, pc);
 
1027
                    if(F_ON(F_INCLUDE_HEADER, ps_global))
 
1028
                      reply_forward_header(stream, msgno, sect_prefix,
 
1029
                                           env, pc, prefix);
 
1030
 
 
1031
                    if(!(get_body_part_text(stream,
 
1032
                                            &body->nested.part->body,
 
1033
                                            msgno, section, 0L, pc, prefix, &new_charset)
 
1034
                         && fetch_contents(stream, msgno, sect_prefix, body)))
 
1035
                      q_status_message(SM_ORDER | SM_DING, 3, 4,
 
1036
                                       _("Error including all message parts"));
 
1037
                    else if(new_charset)
 
1038
                      fix_charset_parameter(&body->nested.part->body, new_charset);
 
1039
                }
 
1040
                else if(orig_body->nested.part->body.type == TYPEMULTIPART
 
1041
                        && orig_body->nested.part->body.subtype
 
1042
                        && !strucmp(orig_body->nested.part->body.subtype,
 
1043
                                    "alternative")
 
1044
                        && reply_body_text(&orig_body->nested.part->body,
 
1045
                                           &tmp_body)){
 
1046
                    int partnum;
 
1047
 
 
1048
                    reply_delimiter(env, role, pc);
 
1049
                    if(F_ON(F_INCLUDE_HEADER, ps_global))
 
1050
                      reply_forward_header(stream, msgno, sect_prefix,
 
1051
                                           env, pc, prefix);
 
1052
 
 
1053
                    snprintf(sect_buf, sizeof(sect_buf), "%.*s%s%.*s",
 
1054
                            sizeof(sect_buf)/2-2,
 
1055
                            sect_prefix ? sect_prefix : "",
 
1056
                            sect_prefix ? "." : "",
 
1057
                            sizeof(sect_buf)/2-2,
 
1058
                            p = partno(orig_body, tmp_body));
 
1059
                    sect_buf[sizeof(sect_buf)-1] = '\0';
 
1060
                    fs_give((void **) &p);
 
1061
                    get_body_part_text(stream, tmp_body, msgno,
 
1062
                                       sect_buf, 0L, pc, prefix, NULL);
 
1063
 
 
1064
                    part = body->nested.part->next;
 
1065
                    body->nested.part->next = NULL;
 
1066
                    mail_free_body_part(&body->nested.part);
 
1067
                    body->nested.part = mail_newbody_part();
 
1068
                    body->nested.part->body.type = TYPETEXT;
 
1069
                    body->nested.part->body.subtype = cpystr("Plain");
 
1070
                    body->nested.part->body.contents.text.data = msgtext;
 
1071
                    body->nested.part->next = part;
 
1072
 
 
1073
                    for(partnum = 2; part != NULL; part = part->next){
 
1074
                        snprintf(sect_buf, sizeof(sect_buf), "%.*s%s%d",
 
1075
                                sizeof(sect_buf)/2,
 
1076
                                sect_prefix ? sect_prefix : "",
 
1077
                                sect_prefix ? "." : "", partnum++);
 
1078
                        sect_buf[sizeof(sect_buf)-1] = '\0';
 
1079
 
 
1080
                        if(!fetch_contents(stream, msgno,
 
1081
                                           sect_buf, &part->body)){
 
1082
                            break;
 
1083
                        }
 
1084
                    }
 
1085
                }
 
1086
                else {
 
1087
                    /*--- Fetch the original pieces ---*/
 
1088
                    if(!fetch_contents(stream, msgno, sect_prefix, body))
 
1089
                      q_status_message(SM_ORDER | SM_DING, 3, 4,
 
1090
                                       _("Error including all message parts"));
 
1091
 
 
1092
                    /*--- No text part, create a blank one ---*/
 
1093
                    part                          = mail_newbody_part();
 
1094
                    part->next                    = body->nested.part;
 
1095
                    body->nested.part             = part;
 
1096
                    part->body.contents.text.data = msgtext;
 
1097
                }
 
1098
            }
 
1099
        }
 
1100
        else{
 
1101
            /*---- Single non-text message of some sort ----*/
 
1102
            body              = mail_newbody();
 
1103
            body->type        = TYPEMULTIPART;
 
1104
            part              = mail_newbody_part();
 
1105
            body->nested.part = part;
 
1106
    
 
1107
            /*--- The first part, a blank text part to be edited ---*/
 
1108
            part->body.type               = TYPETEXT;
 
1109
            part->body.contents.text.data = msgtext;
 
1110
 
 
1111
            /*--- The second part, what ever it is ---*/
 
1112
            part->next    = mail_newbody_part();
 
1113
            part          = part->next;
 
1114
            part->body.id = generate_message_id();
 
1115
            copy_body(&(part->body), orig_body);
 
1116
 
 
1117
            /*
 
1118
             * the idea here is to fetch part into storage object
 
1119
             */
 
1120
            if(part->body.contents.text.data = (void *) so_get(PART_SO_TYPE,
 
1121
                                                            NULL,EDIT_ACCESS)){
 
1122
#if     defined(DOS) && !defined(WIN32)
 
1123
                mail_parameters(ps_global->mail_stream, SET_GETS,
 
1124
                                (void *)dos_gets); /* fetched to disk */
 
1125
                append_file = (FILE *)so_text(
 
1126
                                    (STORE_S *) part->body.contents.text.data);
 
1127
 
 
1128
                if(!mail_fetchbody(stream, msgno, section,
 
1129
                                   &part->body.size.bytes))
 
1130
                  mail_free_body(&body);
 
1131
 
 
1132
                mail_parameters(stream, SET_GETS,(void *)NULL);
 
1133
                append_file = NULL;
 
1134
                mail_gc(stream, GC_TEXTS);
 
1135
#else
 
1136
                if(p = pine_mail_fetch_body(stream, msgno, section,
 
1137
                                            &part->body.size.bytes, NIL)){
 
1138
                    so_nputs((STORE_S *)part->body.contents.text.data,
 
1139
                             p, part->body.size.bytes);
 
1140
                }
 
1141
                else
 
1142
                  mail_free_body(&body);
 
1143
#endif
 
1144
            }
 
1145
            else
 
1146
              mail_free_body(&body);
 
1147
        }
 
1148
    }
 
1149
    else{
 
1150
        /*--------- No text included --------*/
 
1151
        body                     = mail_newbody();
 
1152
        body->type               = TYPETEXT;
 
1153
        body->contents.text.data = msgtext;
 
1154
    }
 
1155
 
 
1156
    if(!leave_cursor_at_top){
 
1157
        long  cnt = 0L;
 
1158
        unsigned char c;
 
1159
 
 
1160
        /* rewind and count chars to start of sig file */
 
1161
        so_seek((STORE_S *)msgtext, 0L, 0);
 
1162
        while(so_readc(&c, (STORE_S *)msgtext))
 
1163
          cnt++;
 
1164
 
 
1165
        if(!*redraft_pos){
 
1166
            *redraft_pos = (REDRAFT_POS_S *)fs_get(sizeof(**redraft_pos));
 
1167
            memset((void *)*redraft_pos, 0,sizeof(**redraft_pos));
 
1168
            (*redraft_pos)->hdrname = cpystr(":");
 
1169
        }
 
1170
 
 
1171
        /*
 
1172
         * If explicit cursor positioning in sig file,
 
1173
         * add offset to start of sig file plus offset into sig file.
 
1174
         * Else, just offset to start of sig file.
 
1175
         */
 
1176
        (*redraft_pos)->offset += cnt;
 
1177
    }
 
1178
 
 
1179
    if(sig){
 
1180
        if(*sig)
 
1181
          so_puts((STORE_S *)msgtext, sig);
 
1182
        
 
1183
        fs_give((void **)&sig);
 
1184
    }
 
1185
 
 
1186
    gf_clear_so_writec((STORE_S *) msgtext);
 
1187
 
 
1188
    return(body);
 
1189
}
 
1190
 
 
1191
 
 
1192
/*
 
1193
 * reply_part - first replyable multipart of a multipart.
 
1194
 */
 
1195
int
 
1196
reply_body_text(struct mail_bodystruct *body, struct mail_bodystruct **new_body)
 
1197
{
 
1198
    if(body){
 
1199
        switch(body->type){
 
1200
          case TYPETEXT :
 
1201
            *new_body = body;
 
1202
            return(1);
 
1203
 
 
1204
          case TYPEMULTIPART :
 
1205
            if(body->subtype && !strucmp(body->subtype, "alternative")){
 
1206
                PART *part;
 
1207
 
 
1208
                /* Pick out the interesting text piece */
 
1209
                for(part = body->nested.part; part; part = part->next)
 
1210
                  if((!part->body.type || part->body.type == TYPETEXT)
 
1211
                     && (!part->body.subtype
 
1212
                         || !strucmp(part->body.subtype, "plain"))){
 
1213
                      *new_body = &part->body;
 
1214
                      return(1);
 
1215
                  }
 
1216
            }
 
1217
            else if(body->nested.part)
 
1218
              /* NOTE: we're only interested in "first" part of mixed */
 
1219
              return(reply_body_text(&body->nested.part->body, new_body));
 
1220
 
 
1221
            break;
 
1222
 
 
1223
          default:
 
1224
            break;
 
1225
        }
 
1226
    }
 
1227
 
 
1228
    return(0);
 
1229
}
 
1230
 
 
1231
 
 
1232
char *
 
1233
reply_signature(ACTION_S *role, ENVELOPE *env, REDRAFT_POS_S **redraft_pos, int *impl)
 
1234
{
 
1235
    char *sig;
 
1236
    size_t l;
 
1237
 
 
1238
    sig = detoken(role, env,
 
1239
                  2, F_ON(F_SIG_AT_BOTTOM, ps_global) ? 0 : 1, 1,
 
1240
                  redraft_pos, impl);
 
1241
 
 
1242
    if(F_OFF(F_SIG_AT_BOTTOM, ps_global) && (!sig || !*sig)){
 
1243
        if(sig)
 
1244
          fs_give((void **)&sig);
 
1245
 
 
1246
        l = 2 * strlen(NEWLINE);
 
1247
        sig = (char *)fs_get((l+1) * sizeof(char));
 
1248
        strncpy(sig, NEWLINE, l);
 
1249
        sig[l] = '\0';
 
1250
        strncat(sig, NEWLINE, l-strlen(sig));
 
1251
        sig[l] = '\0';
 
1252
        return(sig);
 
1253
    }
 
1254
 
 
1255
    return(sig);
 
1256
}
 
1257
 
 
1258
 
 
1259
/*
 
1260
 * Buf is at least size maxlen+1
 
1261
 */
 
1262
void
 
1263
get_addr_data(ENVELOPE *env, IndexColType type, char *buf, size_t maxlen)
 
1264
{
 
1265
    ADDRESS *addr = NULL;
 
1266
    ADDRESS *last_to = NULL;
 
1267
    ADDRESS *first_addr = NULL, *second_addr = NULL;
 
1268
    ADDRESS *third_addr = NULL, *fourth_addr = NULL;
 
1269
    int      cntaddr, l;
 
1270
    size_t   orig_maxlen;
 
1271
    char    *p;
 
1272
 
 
1273
    buf[0] = '\0';
 
1274
 
 
1275
    switch(type){
 
1276
      case iFrom:
 
1277
        addr = env ? env->from : NULL;
 
1278
        break;
 
1279
 
 
1280
      case iTo:
 
1281
        addr = env ? env->to : NULL;
 
1282
        break;
 
1283
 
 
1284
      case iCc:
 
1285
        addr = env ? env->cc : NULL;
 
1286
        break;
 
1287
 
 
1288
      case iSender:
 
1289
        addr = env ? env->sender : NULL;
 
1290
        break;
 
1291
 
 
1292
      /*
 
1293
       * Recips is To and Cc togeter. We hook the two adrlists together
 
1294
       * temporarily.
 
1295
       */
 
1296
      case iRecips:
 
1297
        addr = env ? env->to : NULL;
 
1298
        /* Find end of To list */
 
1299
        for(last_to = addr; last_to && last_to->next; last_to = last_to->next)
 
1300
          ;
 
1301
        
 
1302
        /* Make the end of To list point to cc list */
 
1303
        if(last_to)
 
1304
          last_to->next = (env ? env->cc : NULL);
 
1305
          
 
1306
        break;
 
1307
 
 
1308
      /*
 
1309
       * Initials.
 
1310
       */
 
1311
      case iInit:
 
1312
        if(env && env->from && env->from->personal){
 
1313
            char *name, *initials = NULL, *dummy = NULL;
 
1314
 
 
1315
            name = (char *)rfc1522_decode((unsigned char *)tmp_20k_buf,
 
1316
                                          SIZEOF_20KBUF, env->from->personal,
 
1317
                                          &dummy);
 
1318
            if(dummy)
 
1319
              fs_give((void **)&dummy);
 
1320
 
 
1321
            if(name == env->from->personal){
 
1322
                strncpy(tmp_20k_buf, name, SIZEOF_20KBUF-1);
 
1323
                tmp_20k_buf[SIZEOF_20KBUF - 1] = '\0';
 
1324
                name = tmp_20k_buf;
 
1325
            }
 
1326
 
 
1327
            if(name && *name){
 
1328
                initials = reply_quote_initials(name);
 
1329
                iutf8ncpy(buf, initials, maxlen);
 
1330
                buf[maxlen] = '\0';
 
1331
            }
 
1332
        }
 
1333
 
 
1334
        return;
 
1335
    }
 
1336
 
 
1337
    orig_maxlen = maxlen;
 
1338
 
 
1339
    first_addr = addr;
 
1340
    /* skip over rest of c-client group addr */
 
1341
    if(first_addr && first_addr->mailbox && !first_addr->host){
 
1342
        for(second_addr = first_addr->next;
 
1343
            second_addr && second_addr->host;
 
1344
            second_addr = second_addr->next)
 
1345
          ;
 
1346
        
 
1347
        if(second_addr && !second_addr->host)
 
1348
          second_addr = second_addr->next;
 
1349
    }
 
1350
    else if(!(first_addr && first_addr->host && first_addr->host[0] == '.'))
 
1351
      second_addr = first_addr ? first_addr->next : NULL;
 
1352
 
 
1353
    if(second_addr && second_addr->mailbox && !second_addr->host){
 
1354
        for(third_addr = second_addr->next;
 
1355
            third_addr && third_addr->host;
 
1356
            third_addr = third_addr->next)
 
1357
          ;
 
1358
        
 
1359
        if(third_addr && !third_addr->host)
 
1360
          third_addr = third_addr->next;
 
1361
    }
 
1362
    else if(!(second_addr && second_addr->host && second_addr->host[0] == '.'))
 
1363
      third_addr = second_addr ? second_addr->next : NULL;
 
1364
 
 
1365
    if(third_addr && third_addr->mailbox && !third_addr->host){
 
1366
        for(fourth_addr = third_addr->next;
 
1367
            fourth_addr && fourth_addr->host;
 
1368
            fourth_addr = fourth_addr->next)
 
1369
          ;
 
1370
        
 
1371
        if(fourth_addr && !fourth_addr->host)
 
1372
          fourth_addr = fourth_addr->next;
 
1373
    }
 
1374
    else if(!(third_addr && third_addr->host && third_addr->host[0] == '.'))
 
1375
      fourth_addr = third_addr ? third_addr->next : NULL;
 
1376
 
 
1377
    /* Just attempting to make a nice display */
 
1378
    if(first_addr && ((first_addr->personal && first_addr->personal[0]) ||
 
1379
                      (first_addr->mailbox && first_addr->mailbox[0]))){
 
1380
      if(second_addr){
 
1381
        if((second_addr->personal && second_addr->personal[0]) ||
 
1382
           (second_addr->mailbox && second_addr->mailbox[0])){
 
1383
          if(third_addr){
 
1384
            if((third_addr->personal && third_addr->personal[0]) ||
 
1385
               (third_addr->mailbox && third_addr->mailbox[0])){
 
1386
              if(fourth_addr)
 
1387
                cntaddr = 4;
 
1388
              else
 
1389
                cntaddr = 3;
 
1390
            }
 
1391
            else
 
1392
              cntaddr = -1;
 
1393
          }
 
1394
          else
 
1395
            cntaddr = 2;
 
1396
        }
 
1397
        else
 
1398
          cntaddr = -1;
 
1399
      }
 
1400
      else
 
1401
        cntaddr = 1;
 
1402
    }
 
1403
    else
 
1404
      cntaddr = -1;
 
1405
 
 
1406
    p = buf;
 
1407
    if(cntaddr == 1)
 
1408
      a_little_addr_string(first_addr, p, maxlen);
 
1409
    else if(cntaddr == 2){
 
1410
        a_little_addr_string(first_addr, p, maxlen);
 
1411
        maxlen -= (l=strlen(p));
 
1412
        p += l;
 
1413
        if(maxlen > 7){
 
1414
            strncpy(p, " and ", maxlen);
 
1415
            maxlen -= 5;
 
1416
            p += 5;
 
1417
            a_little_addr_string(second_addr, p, maxlen);
 
1418
        }
 
1419
    }
 
1420
    else if(cntaddr == 3){
 
1421
        a_little_addr_string(first_addr, p, maxlen);
 
1422
        maxlen -= (l=strlen(p));
 
1423
        p += l;
 
1424
        if(maxlen > 7){
 
1425
            strncpy(p, ", ", maxlen);
 
1426
            maxlen -= 2;
 
1427
            p += 2;
 
1428
            a_little_addr_string(second_addr, p, maxlen);
 
1429
            maxlen -= (l=strlen(p));
 
1430
            p += l;
 
1431
            if(maxlen > 7){
 
1432
                strncpy(p, ", and ", maxlen);
 
1433
                maxlen -= 6;
 
1434
                p += 6;
 
1435
                a_little_addr_string(third_addr, p, maxlen);
 
1436
            }
 
1437
        }
 
1438
    }
 
1439
    else if(cntaddr > 3){
 
1440
        a_little_addr_string(first_addr, p, maxlen);
 
1441
        maxlen -= (l=strlen(p));
 
1442
        p += l;
 
1443
        if(maxlen > 7){
 
1444
            strncpy(p, ", ", maxlen);
 
1445
            maxlen -= 2;
 
1446
            p += 2;
 
1447
            a_little_addr_string(second_addr, p, maxlen);
 
1448
            maxlen -= (l=strlen(p));
 
1449
            p += l;
 
1450
            if(maxlen > 7){
 
1451
                strncpy(p, ", ", maxlen);
 
1452
                maxlen -= 2;
 
1453
                p += 2;
 
1454
                a_little_addr_string(third_addr, p, maxlen);
 
1455
                maxlen -= (l=strlen(p));
 
1456
                p += l;
 
1457
                if(maxlen >= 12)
 
1458
                  strncpy(p, ", and others", maxlen);
 
1459
                else if(maxlen >= 3)
 
1460
                  strncpy(p, "...", maxlen);
 
1461
            }
 
1462
        }
 
1463
    }
 
1464
    else if(addr){
 
1465
        char *a_string;
 
1466
 
 
1467
        a_string = addr_list_string(addr, NULL, 0);
 
1468
        iutf8ncpy(buf, a_string, maxlen);
 
1469
 
 
1470
        fs_give((void **)&a_string);
 
1471
    }
 
1472
 
 
1473
    if(last_to)
 
1474
      last_to->next = NULL;
 
1475
 
 
1476
    buf[orig_maxlen] = '\0';
 
1477
}
 
1478
 
 
1479
 
 
1480
/*
 
1481
 * Buf is at least size maxlen+1
 
1482
 */
 
1483
void
 
1484
get_news_data(ENVELOPE *env, IndexColType type, char *buf, size_t maxlen)
 
1485
{
 
1486
    int      cntnews = 0, orig_maxlen;
 
1487
    char    *news = NULL, *p, *q;
 
1488
 
 
1489
    switch(type){
 
1490
      case iNews:
 
1491
      case iNewsAndTo:
 
1492
      case iToAndNews:
 
1493
      case iNewsAndRecips:
 
1494
      case iRecipsAndNews:
 
1495
        news = env ? env->newsgroups : NULL;
 
1496
        break;
 
1497
 
 
1498
      case iCurNews:
 
1499
        if(ps_global->mail_stream && IS_NEWS(ps_global->mail_stream))
 
1500
          news = ps_global->cur_folder;
 
1501
 
 
1502
        break;
 
1503
    }
 
1504
 
 
1505
    orig_maxlen = maxlen;
 
1506
 
 
1507
    if(news){
 
1508
        q = news;
 
1509
        while(isspace((unsigned char)*q))
 
1510
          q++;
 
1511
 
 
1512
        if(*q)
 
1513
          cntnews++;
 
1514
        
 
1515
        while((q = strindex(q, ',')) != NULL){
 
1516
            q++;
 
1517
            while(isspace((unsigned char)*q))
 
1518
              q++;
 
1519
            
 
1520
            if(*q)
 
1521
              cntnews++;
 
1522
            else
 
1523
              break;
 
1524
        }
 
1525
    }
 
1526
 
 
1527
    if(cntnews == 1){
 
1528
        istrncpy(buf, news, maxlen);
 
1529
        buf[maxlen] = '\0';
 
1530
        removing_leading_and_trailing_white_space(buf);
 
1531
    }
 
1532
    else if(cntnews == 2){
 
1533
        p = buf;
 
1534
        q = news;
 
1535
        while(isspace((unsigned char)*q))
 
1536
          q++;
 
1537
        
 
1538
        while(maxlen > 0 && *q && !isspace((unsigned char)*q) && *q != ','){
 
1539
            *p++ = *q++;
 
1540
            maxlen--;
 
1541
        }
 
1542
        
 
1543
        if(maxlen > 7){
 
1544
            strncpy(p, " and ", maxlen);
 
1545
            p += 5;
 
1546
            maxlen -= 5;
 
1547
        }
 
1548
 
 
1549
        while(isspace((unsigned char)*q) || *q == ',')
 
1550
          q++;
 
1551
 
 
1552
        while(maxlen > 0 && *q && !isspace((unsigned char)*q) && *q != ','){
 
1553
            *p++ = *q++;
 
1554
            maxlen--;
 
1555
        }
 
1556
 
 
1557
        *p = '\0';
 
1558
 
 
1559
        istrncpy(tmp_20k_buf, buf, 10000);
 
1560
        strncpy(buf, tmp_20k_buf, orig_maxlen);
 
1561
    }
 
1562
    else if(cntnews > 2){
 
1563
        char b[100];
 
1564
 
 
1565
        p = buf;
 
1566
        q = news;
 
1567
        while(isspace((unsigned char)*q))
 
1568
          q++;
 
1569
        
 
1570
        while(maxlen > 0 && *q && !isspace((unsigned char)*q) && *q != ','){
 
1571
            *p++ = *q++;
 
1572
            maxlen--;
 
1573
        }
 
1574
        
 
1575
        *p = '\0';
 
1576
        snprintf(b, sizeof(b), " and %d other newsgroups", cntnews-1);
 
1577
        b[sizeof(b)-1] = '\0';
 
1578
        if(maxlen >= strlen(b))
 
1579
          strncpy(p, b, maxlen);
 
1580
        else if(maxlen >= 3)
 
1581
          strncpy(p, "...", maxlen);
 
1582
 
 
1583
        buf[orig_maxlen] = '\0';
 
1584
 
 
1585
        istrncpy(tmp_20k_buf, buf, 10000);
 
1586
        tmp_20k_buf[10000-1] = '\0';
 
1587
        strncpy(buf, tmp_20k_buf, orig_maxlen);
 
1588
    }
 
1589
 
 
1590
    buf[orig_maxlen] = '\0';
 
1591
}
 
1592
 
 
1593
 
 
1594
/*
 
1595
 * Buf is at least size maxlen+1
 
1596
 */
 
1597
char *
 
1598
get_reply_data(ENVELOPE *env, ACTION_S *role, IndexColType type, char *buf, size_t maxlen)
 
1599
{
 
1600
    char        *space = NULL;
 
1601
    IndexColType addrtype;
 
1602
 
 
1603
    buf[0] = '\0';
 
1604
 
 
1605
    switch(type){
 
1606
      case iRDate: case iSDate: case iSTime:
 
1607
      case iS1Date: case iS2Date: case iS3Date: case iS4Date:
 
1608
      case iSDateIso: case iSDateIsoS:
 
1609
      case iSDateS1: case iSDateS2: case iSDateS3: case iSDateS4: 
 
1610
      case iSDateTime:
 
1611
      case iSDateTimeIso: case iSDateTimeIsoS:
 
1612
      case iSDateTimeS1: case iSDateTimeS2: case iSDateTimeS3: case iSDateTimeS4: 
 
1613
      case iSDateTime24:
 
1614
      case iSDateTimeIso24: case iSDateTimeIsoS24:
 
1615
      case iSDateTimeS124: case iSDateTimeS224: case iSDateTimeS324: case iSDateTimeS424: 
 
1616
      case iDateIso: case iDateIsoS: case iTime24: case iTime12:
 
1617
      case iDay: case iDayOrdinal: case iDay2Digit:
 
1618
      case iMonAbb: case iMonLong: case iMon: case iMon2Digit:
 
1619
      case iYear: case iYear2Digit:
 
1620
      case iDate: case iLDate:
 
1621
      case iTimezone: case iDayOfWeekAbb: case iDayOfWeek:
 
1622
        if(env && env->date && env->date[0] && maxlen >= 20)
 
1623
          date_str((char *) env->date, type, 1, buf, maxlen+1);
 
1624
 
 
1625
        break;
 
1626
 
 
1627
      case iCurDate:
 
1628
      case iCurDateIso:
 
1629
      case iCurDateIsoS:
 
1630
      case iCurTime24:
 
1631
      case iCurTime12:
 
1632
      case iCurDay:
 
1633
      case iCurDay2Digit:
 
1634
      case iCurDayOfWeek:
 
1635
      case iCurDayOfWeekAbb:
 
1636
      case iCurMon:
 
1637
      case iCurMon2Digit:
 
1638
      case iCurMonLong:
 
1639
      case iCurMonAbb:
 
1640
      case iCurYear:
 
1641
      case iCurYear2Digit:
 
1642
      case iLstMon:
 
1643
      case iLstMon2Digit:
 
1644
      case iLstMonLong:
 
1645
      case iLstMonAbb:
 
1646
      case iLstMonYear:
 
1647
      case iLstMonYear2Digit:
 
1648
      case iLstYear:
 
1649
      case iLstYear2Digit:
 
1650
        if(maxlen >= 20)
 
1651
          date_str(NULL, type, 1, buf, maxlen+1);
 
1652
 
 
1653
        break;
 
1654
 
 
1655
      case iFrom:
 
1656
      case iTo:
 
1657
      case iCc:
 
1658
      case iSender:
 
1659
      case iRecips:
 
1660
      case iInit:
 
1661
        get_addr_data(env, type, buf, maxlen);
 
1662
        break;
 
1663
 
 
1664
      case iRoleNick:
 
1665
        if(role && role->nick){
 
1666
            strncpy(buf, role->nick, maxlen);
 
1667
            buf[maxlen] = '\0';
 
1668
        }
 
1669
        break;
 
1670
 
 
1671
      case iAddress:
 
1672
      case iMailbox:
 
1673
        if(env && env->from && env->from->mailbox && env->from->mailbox[0] &&
 
1674
           strlen(env->from->mailbox) <= maxlen){
 
1675
            strncpy(buf, env->from->mailbox, maxlen);
 
1676
            buf[maxlen] = '\0';
 
1677
            if(type == iAddress &&
 
1678
               env->from->host &&
 
1679
               env->from->host[0] &&
 
1680
               env->from->host[0] != '.' &&
 
1681
               strlen(buf) + strlen(env->from->host) + 1 <= maxlen){
 
1682
                strncat(buf, "@", maxlen-strlen(buf));
 
1683
                buf[maxlen] = '\0';
 
1684
                strncat(buf, env->from->host, maxlen-strlen(buf));
 
1685
                buf[maxlen] = '\0';
 
1686
            }
 
1687
        }
 
1688
 
 
1689
        break;
 
1690
 
 
1691
      case iNews:
 
1692
      case iCurNews:
 
1693
        get_news_data(env, type, buf, maxlen);
 
1694
        break;
 
1695
 
 
1696
      case iToAndNews:
 
1697
      case iNewsAndTo:
 
1698
      case iRecipsAndNews:
 
1699
      case iNewsAndRecips:
 
1700
        if(type == iToAndNews || type == iNewsAndTo)
 
1701
          addrtype = iTo;
 
1702
        else
 
1703
          addrtype = iRecips;
 
1704
 
 
1705
        if(env && env->newsgroups){
 
1706
            space = (char *)fs_get((maxlen+1) * sizeof(char));
 
1707
            get_news_data(env, type, space, maxlen);
 
1708
        }
 
1709
 
 
1710
        get_addr_data(env, addrtype, buf, maxlen);
 
1711
 
 
1712
        if(space && *space && *buf){
 
1713
            if(strlen(space) + strlen(buf) + 5 > maxlen){
 
1714
                if(strlen(space) > maxlen/2)
 
1715
                  get_news_data(env, type, space, maxlen - strlen(buf) - 5);
 
1716
                else
 
1717
                  get_addr_data(env, addrtype, buf, maxlen - strlen(space) - 5);
 
1718
            }
 
1719
 
 
1720
            if(type == iToAndNews || type == iRecipsAndNews)
 
1721
              snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s and %s", buf, space);
 
1722
            else
 
1723
              snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s and %s", space, buf);
 
1724
 
 
1725
            tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
 
1726
 
 
1727
            strncpy(buf, tmp_20k_buf, maxlen);
 
1728
            buf[maxlen] = '\0';
 
1729
        }
 
1730
        else if(space && *space){
 
1731
            strncpy(buf, space, maxlen);
 
1732
            buf[maxlen] = '\0';
 
1733
        }
 
1734
        
 
1735
        if(space)
 
1736
          fs_give((void **)&space);
 
1737
 
 
1738
        break;
 
1739
 
 
1740
      case iSubject:
 
1741
        if(env && env->subject){
 
1742
            size_t n, len;
 
1743
            unsigned char *p, *tmp = NULL;
 
1744
            char  *dummy = NULL;
 
1745
 
 
1746
            if((n = 4*strlen(env->subject)) > SIZEOF_20KBUF-1){
 
1747
                len = n+1;
 
1748
                p = tmp = (unsigned char *)fs_get(len * sizeof(char));
 
1749
            }
 
1750
            else{
 
1751
                len = SIZEOF_20KBUF;
 
1752
                p = (unsigned char *)tmp_20k_buf;
 
1753
            }
 
1754
          
 
1755
            istrncpy(buf, (char *)rfc1522_decode(p, len, env->subject, &dummy),
 
1756
                     maxlen);
 
1757
 
 
1758
            buf[maxlen] = '\0';
 
1759
            if(dummy)
 
1760
              fs_give((void **)&dummy);
 
1761
            if(tmp)
 
1762
              fs_give((void **)&tmp);
 
1763
        }
 
1764
 
 
1765
        break;
 
1766
    
 
1767
      case iMsgID:
 
1768
        if(env && env->message_id){
 
1769
            strncpy(buf, env->message_id, maxlen);
 
1770
            buf[maxlen] = '\0';
 
1771
        }
 
1772
 
 
1773
        break;
 
1774
    
 
1775
      default:
 
1776
        break;
 
1777
    }
 
1778
 
 
1779
    buf[maxlen] = '\0';
 
1780
    return(buf);
 
1781
}
 
1782
 
 
1783
 
 
1784
/*
 
1785
 * reply_delimiter - output formatted reply delimiter for given envelope
 
1786
 *                   with supplied character writing function.
 
1787
 */
 
1788
void
 
1789
reply_delimiter(ENVELOPE *env, ACTION_S *role, gf_io_t pc)
 
1790
{
 
1791
#define MAX_DELIM 2000
 
1792
    char           buf[MAX_DELIM+1];
 
1793
    char          *p;
 
1794
    char          *filtered = NULL;
 
1795
    int            len;
 
1796
 
 
1797
 
 
1798
    if(!env)
 
1799
      return;
 
1800
 
 
1801
    strncpy(buf, ps_global->VAR_REPLY_INTRO, MAX_DELIM);
 
1802
    buf[MAX_DELIM] = '\0';
 
1803
    /* preserve exact default behavior from before */
 
1804
    if(!strcmp(buf, DEFAULT_REPLY_INTRO)){
 
1805
        struct date d;
 
1806
        int include_date;
 
1807
 
 
1808
        parse_date((char *) env->date, &d);
 
1809
        include_date = !(d.day == -1 || d.month == -1 || d.year == -1);
 
1810
        if(include_date){
 
1811
            gf_puts("On ", pc);                 /* All delims have... */
 
1812
            if(d.wkday != -1){                  /* "On day, date month year" */
 
1813
                gf_puts(week_abbrev(d.wkday), pc);      /* in common */
 
1814
                gf_puts(", ", pc);
 
1815
            }
 
1816
 
 
1817
            gf_puts(int2string(d.day), pc);
 
1818
            (*pc)(' ');
 
1819
            gf_puts(month_abbrev(d.month), pc);
 
1820
            (*pc)(' ');
 
1821
            gf_puts(int2string(d.year), pc);
 
1822
        }
 
1823
 
 
1824
        if(env->from
 
1825
           && ((env->from->personal && env->from->personal[0])
 
1826
               || (env->from->mailbox && env->from->mailbox[0]))){
 
1827
            char buftmp[MAILTMPLEN];
 
1828
 
 
1829
            a_little_addr_string(env->from, buftmp, sizeof(buftmp)-1);
 
1830
            if(include_date)
 
1831
              gf_puts(", ", pc);
 
1832
 
 
1833
            gf_puts(buftmp, pc);
 
1834
            gf_puts(" wrote:", pc);
 
1835
        }
 
1836
        else{
 
1837
            if(include_date)
 
1838
              gf_puts(", it was written", pc);
 
1839
            else
 
1840
              gf_puts("It was written", pc);
 
1841
        }
 
1842
 
 
1843
    }
 
1844
    else{
 
1845
        filtered = detoken_src(buf, FOR_REPLY_INTRO, env, role,
 
1846
                               NULL, NULL);
 
1847
 
 
1848
        /* try to truncate if too long */
 
1849
        if(filtered && (len = strlen(filtered)) > 80){
 
1850
            int ended_with_colon = 0;
 
1851
            int ended_with_quote = 0;
 
1852
            int ended_with_quote_colon = 0;
 
1853
 
 
1854
            if(filtered[len-1] == ':'){
 
1855
                ended_with_colon = ':';
 
1856
                if(filtered[len-2] == QUOTE || filtered[len-2] == '\'')
 
1857
                  ended_with_quote_colon = filtered[len-2];
 
1858
            }
 
1859
            else if(filtered[len-1] == QUOTE || filtered[len-1] == '\'')
 
1860
              ended_with_quote = filtered[len-1];
 
1861
 
 
1862
            /* try to find space to break at */
 
1863
            for(p = &filtered[75]; p > &filtered[60] && 
 
1864
                  !isspace((unsigned char)*p); p--)
 
1865
              ;
 
1866
            
 
1867
            if(!isspace((unsigned char)*p))
 
1868
              p = &filtered[75];
 
1869
 
 
1870
            *p++ = '.';
 
1871
            *p++ = '.';
 
1872
            *p++ = '.';
 
1873
            if(ended_with_quote_colon){
 
1874
                *p++ = ended_with_quote_colon;
 
1875
                *p++ = ':';
 
1876
            }
 
1877
            else if(ended_with_colon)
 
1878
              *p++ = ended_with_colon;
 
1879
            else if(ended_with_quote)
 
1880
              *p++ = ended_with_quote;
 
1881
 
 
1882
            *p = '\0';
 
1883
        }
 
1884
 
 
1885
        if(filtered && *filtered)
 
1886
          gf_puts(filtered, pc);
 
1887
    }
 
1888
 
 
1889
    /* and end with two newlines unless no leadin at all */
 
1890
    if(!strcmp(buf, DEFAULT_REPLY_INTRO) || (filtered && *filtered)){
 
1891
        gf_puts(NEWLINE, pc);
 
1892
        gf_puts(NEWLINE, pc);
 
1893
    }
 
1894
 
 
1895
    if(filtered)
 
1896
      fs_give((void **)&filtered);
 
1897
}
 
1898
 
 
1899
 
 
1900
void
 
1901
free_redraft_pos(REDRAFT_POS_S **redraft_pos)
 
1902
{
 
1903
    if(redraft_pos && *redraft_pos){
 
1904
        if((*redraft_pos)->hdrname)
 
1905
          fs_give((void **)&(*redraft_pos)->hdrname);
 
1906
        
 
1907
        fs_give((void **)redraft_pos);
 
1908
    }
 
1909
}
 
1910
 
 
1911
 
 
1912
/*----------------------------------------------------------------------
 
1913
  Build the body for the message number/part being forwarded as ATTACHMENT
 
1914
 
 
1915
    Args: 
 
1916
 
 
1917
  Result: PARTS suitably attached to body
 
1918
 
 
1919
  ----------------------------------------------------------------------*/
 
1920
int
 
1921
forward_mime_msg(MAILSTREAM *stream, long int msgno, char *section, ENVELOPE *env, struct mail_body_part **partp, void *msgtext)
 
1922
{
 
1923
    char           *tmp_text;
 
1924
    unsigned long   len;
 
1925
    BODY           *b;
 
1926
 
 
1927
    *partp         = mail_newbody_part();
 
1928
    b              = &(*partp)->body;
 
1929
    b->type        = TYPEMESSAGE;
 
1930
    b->id          = generate_message_id();
 
1931
    b->description = forward_subject(env, FS_CONVERT_QUOTES);
 
1932
    b->nested.msg  = mail_newmsg();
 
1933
 
 
1934
    /*---- Package each message in a storage object ----*/
 
1935
    if((b->contents.text.data = (void *) so_get(PART_SO_TYPE,NULL,EDIT_ACCESS))
 
1936
       && (tmp_text = mail_fetch_header(stream,msgno,section,NIL,NIL,FT_PEEK))
 
1937
       && *tmp_text){
 
1938
        so_puts((STORE_S *) b->contents.text.data, tmp_text);
 
1939
 
 
1940
        b->size.bytes = strlen(tmp_text);
 
1941
        so_puts((STORE_S *) b->contents.text.data, "\015\012");
 
1942
        if(tmp_text = pine_mail_fetch_text (stream,msgno,section,&len,NIL)){
 
1943
            so_nputs((STORE_S *)b->contents.text.data,tmp_text,(long) len);
 
1944
            b->size.bytes += len;
 
1945
            return(1);
 
1946
        }
 
1947
    }
 
1948
 
 
1949
    return(0);
 
1950
}
 
1951
 
 
1952
 
 
1953
/*
 
1954
 * forward_delimiter - return delimiter for forwarded text
 
1955
 */
 
1956
void
 
1957
forward_delimiter(gf_io_t pc)
 
1958
{
 
1959
    gf_puts(NEWLINE, pc);
 
1960
    /* TRANSLATORS: When a message is forwarded by the user this is the
 
1961
       text that shows where the forwarded part of the message begins. */
 
1962
    gf_puts(_("---------- Forwarded message ----------"), pc);
 
1963
    gf_puts(NEWLINE, pc);
 
1964
}
 
1965
 
 
1966
 
 
1967
/*----------------------------------------------------------------------
 
1968
    Wrapper for header formatting tool
 
1969
 
 
1970
    Args: stream --
 
1971
          msgno --
 
1972
          env --
 
1973
          pc --
 
1974
          prefix --
 
1975
 
 
1976
  Result: header suitable for reply/forward text written using "pc"
 
1977
 
 
1978
  ----------------------------------------------------------------------*/
 
1979
void
 
1980
reply_forward_header(MAILSTREAM *stream, long int msgno, char *part, ENVELOPE *env,
 
1981
                     gf_io_t pc, char *prefix)
 
1982
{
 
1983
    int      rv;
 
1984
    HEADER_S h;
 
1985
    char   **list, **new_list = NULL;
 
1986
 
 
1987
    list = ps_global->VAR_VIEW_HEADERS;
 
1988
 
 
1989
    /*
 
1990
     * If VIEW_HEADERS is set, we should remove BCC from the list so that
 
1991
     * the user doesn't inadvertently forward the BCC header.
 
1992
     */
 
1993
    if(list && list[0]){
 
1994
        int    i, cnt = 0;
 
1995
        char **p;
 
1996
 
 
1997
        while(list[cnt++])
 
1998
          ;
 
1999
 
 
2000
        p = new_list = (char **) fs_get((cnt+1) * sizeof(char *));
 
2001
 
 
2002
        for(i=0; list[i]; i++)
 
2003
          if(strucmp(list[i], "bcc"))
 
2004
            *p++ = cpystr(list[i]);
 
2005
        
 
2006
        *p = NULL;
 
2007
 
 
2008
        if(new_list && new_list[0])
 
2009
          list = new_list;
 
2010
 
 
2011
    }
 
2012
 
 
2013
    HD_INIT(&h, list, ps_global->view_all_except, FE_DEFAULT & ~FE_BCC);
 
2014
    if(rv = format_header(stream, msgno, part, env, &h, prefix, NULL,
 
2015
                          FM_NOINDENT, pc)){
 
2016
        if(rv == 1)
 
2017
          gf_puts("  [Error fetching message header data]", pc);
 
2018
    }
 
2019
    else
 
2020
      gf_puts(NEWLINE, pc);             /* write header delimiter */
 
2021
    
 
2022
    if(new_list)
 
2023
      free_list_array(&new_list);
 
2024
}
 
2025
 
 
2026
 
 
2027
/*----------------------------------------------------------------------
 
2028
  Build the subject for the message number being forwarded
 
2029
 
 
2030
    Args: pine_state -- The usual pine structure
 
2031
          msgno      -- The message number to build subject for
 
2032
 
 
2033
  Result: malloc'd string containing new subject or NULL on error
 
2034
 
 
2035
  ----------------------------------------------------------------------*/
 
2036
char *
 
2037
forward_subject(ENVELOPE *env, int flags)
 
2038
{
 
2039
    size_t l;
 
2040
    char  *p, *cs = NULL, buftmp[MAILTMPLEN];
 
2041
    
 
2042
    if(!env)
 
2043
      return(NULL);
 
2044
 
 
2045
    dprint((9, "checking subject: \"%s\"\n",
 
2046
               env->subject ? env->subject : "NULL"));
 
2047
 
 
2048
    if(env->subject && env->subject[0]){                /* add (fwd)? */
 
2049
        snprintf(buftmp, sizeof(buftmp), "%s", env->subject);
 
2050
        buftmp[sizeof(buftmp)-1] = '\0';
 
2051
        /* decode any 8bit (copy to the temp buffer if decoding doesn't) */
 
2052
        if(rfc1522_decode((unsigned char *) tmp_20k_buf,
 
2053
                          SIZEOF_20KBUF, buftmp, &cs) == (unsigned char *) buftmp)
 
2054
          strncpy(tmp_20k_buf, buftmp, SIZEOF_20KBUF);
 
2055
 
 
2056
        tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
 
2057
 
 
2058
        removing_trailing_white_space(tmp_20k_buf);
 
2059
        if((l=strlen(tmp_20k_buf)) < 1000 &&
 
2060
           (l < 5 || strcmp(tmp_20k_buf+l-5,"(fwd)"))){
 
2061
            snprintf(tmp_20k_buf+2000, SIZEOF_20KBUF-2000, "%s (fwd)",
 
2062
                     (!cs || !strucmp(cs, "us-ascii") || !strucmp(cs, "utf-8")) ? tmp_20k_buf : env->subject);
 
2063
            tmp_20k_buf[SIZEOF_20KBUF-2000-1] = '\0';
 
2064
            strncpy(tmp_20k_buf, tmp_20k_buf+2000, SIZEOF_20KBUF);
 
2065
            tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
 
2066
        }
 
2067
 
 
2068
        /*
 
2069
         * HACK:  composer can't handle embedded double quotes in attachment
 
2070
         * comments so we substitute two single quotes.
 
2071
         */
 
2072
        if(flags & FS_CONVERT_QUOTES)
 
2073
          while(p = strchr(tmp_20k_buf, QUOTE))
 
2074
            (void)rplstr(p, SIZEOF_20KBUF-(p-tmp_20k_buf), 1, "''");
 
2075
 
 
2076
        if(cs)
 
2077
          fs_give((void **) &cs);
 
2078
          
 
2079
        return(cpystr(tmp_20k_buf));
 
2080
 
 
2081
    }
 
2082
 
 
2083
    return(cpystr("Forwarded mail...."));
 
2084
}
 
2085
 
 
2086
 
 
2087
/*----------------------------------------------------------------------
 
2088
  Build the body for the message number/part being forwarded
 
2089
 
 
2090
    Args: 
 
2091
 
 
2092
  Result: BODY structure suitable for sending
 
2093
 
 
2094
  ----------------------------------------------------------------------*/
 
2095
BODY *
 
2096
forward_body(MAILSTREAM *stream, ENVELOPE *env, struct mail_bodystruct *orig_body,
 
2097
             long int msgno, char *sect_prefix, void *msgtext, int flags)
 
2098
{
 
2099
    BODY    *body = NULL, *text_body, *tmp_body;
 
2100
    PART    *part;
 
2101
    gf_io_t  pc;
 
2102
    char    *tmp_text, *section, sect_buf[256];
 
2103
    int      forward_raw_body = 0;
 
2104
 
 
2105
    /*
 
2106
     * Check to see if messages got expunged out from underneath us. This
 
2107
     * could have happened during the prompt to the user asking whether to
 
2108
     * include the message as an attachment. Either the message is gone or
 
2109
     * it might be at a different sequence number. We'd better bail.
 
2110
     */
 
2111
    if(ps_global->full_header == 2
 
2112
       && F_ON(F_ENABLE_FULL_HDR_AND_TEXT, ps_global))
 
2113
      forward_raw_body = 1;
 
2114
    if(sp_expunge_count(stream))
 
2115
      return(NULL);
 
2116
 
 
2117
    if(sect_prefix && forward_raw_body == 0)
 
2118
      snprintf(section = sect_buf, sizeof(sect_buf), "%s.1", sect_prefix);
 
2119
    else if(sect_prefix && forward_raw_body)
 
2120
      section = sect_prefix;
 
2121
    else if(!sect_prefix && forward_raw_body)
 
2122
      section = NULL;
 
2123
    else
 
2124
      section = "1";
 
2125
 
 
2126
    sect_buf[sizeof(sect_buf)-1] = '\0';
 
2127
 
 
2128
    gf_set_so_writec(&pc, (STORE_S *) msgtext);
 
2129
    if(!orig_body || orig_body->type == TYPETEXT || forward_raw_body) {
 
2130
        /*---- Message has a single text part -----*/
 
2131
        body                     = mail_newbody();
 
2132
        body->type               = TYPETEXT;
 
2133
        body->contents.text.data = msgtext;
 
2134
        if(!(flags & FWD_ANON)){
 
2135
            forward_delimiter(pc);
 
2136
            reply_forward_header(stream, msgno, sect_prefix, env, pc, "");
 
2137
        }
 
2138
 
 
2139
        if(!get_body_part_text(stream, forward_raw_body ? NULL : orig_body, msgno, section, 0L, pc, NULL, NULL)){
 
2140
            mail_free_body(&body);
 
2141
            return(NULL);
 
2142
        }
 
2143
    }
 
2144
    else if(orig_body->type == TYPEMULTIPART) {
 
2145
        if(orig_body->subtype && !strucmp(orig_body->subtype, "signed")
 
2146
           && orig_body->nested.part){
 
2147
            /* only operate on the signed data (not the signature) */
 
2148
            body = forward_body(stream, env, &orig_body->nested.part->body,
 
2149
                                msgno, sect_prefix, msgtext, flags);
 
2150
        }
 
2151
        /*---- Message is multipart ----*/
 
2152
        else if(!(orig_body->subtype && !strucmp(orig_body->subtype,
 
2153
                                                 "alternative")
 
2154
                  && (body = forward_multi_alt(stream, env, orig_body, msgno,
 
2155
                                               sect_prefix, msgtext,
 
2156
                                               pc, flags)))){
 
2157
            /*--- Copy the body and entire structure  ---*/
 
2158
            body = copy_body(NULL, orig_body);
 
2159
 
 
2160
            /*
 
2161
             * whatever subtype it is, demote it
 
2162
             * to plain old MIXED.
 
2163
             */
 
2164
            if(body->subtype)
 
2165
              fs_give((void **) &body->subtype);
 
2166
 
 
2167
            body->subtype = cpystr("Mixed");
 
2168
 
 
2169
            /*--- The text part of the message ---*/
 
2170
            if(!body->nested.part){
 
2171
                q_status_message(SM_ORDER | SM_DING, 3, 6,
 
2172
                                 "Error referencing body part 1");
 
2173
                mail_free_body(&body);
 
2174
            }
 
2175
            else if(body->nested.part->body.type == TYPETEXT) {
 
2176
                char *new_charset = NULL;
 
2177
 
 
2178
                /*--- The first part is text ----*/
 
2179
                text_body                     = &body->nested.part->body;
 
2180
                text_body->contents.text.data = msgtext;
 
2181
                if(text_body->subtype && strucmp(text_body->subtype, "Plain")){
 
2182
                    /* this text is going to the composer, it should be Plain */
 
2183
                    fs_give((void **)&text_body->subtype);
 
2184
                    text_body->subtype = cpystr("PLAIN");
 
2185
                }
 
2186
                if(!(flags & FWD_ANON)){
 
2187
                    forward_delimiter(pc);
 
2188
                    reply_forward_header(stream, msgno,
 
2189
                                         sect_prefix, env, pc, "");
 
2190
                }
 
2191
 
 
2192
                if(!(get_body_part_text(stream, text_body,
 
2193
                                        msgno, section, 0L, pc, NULL, &new_charset)
 
2194
                     && fetch_contents(stream, msgno, sect_prefix, body)))
 
2195
                  mail_free_body(&body);
 
2196
                else if(new_charset)
 
2197
                  fix_charset_parameter(text_body, new_charset);
 
2198
 
 
2199
/* BUG: ? matter that we're not setting body.size.bytes */
 
2200
            }
 
2201
            else if(body->nested.part->body.type == TYPEMULTIPART
 
2202
                    && body->nested.part->body.subtype
 
2203
                    && !strucmp(body->nested.part->body.subtype, "alternative")
 
2204
                    && (tmp_body = forward_multi_alt(stream, env,
 
2205
                                                     &body->nested.part->body,
 
2206
                                                     msgno, sect_prefix,
 
2207
                                                     msgtext, pc,
 
2208
                                                     flags | FWD_NESTED))){
 
2209
              /* for the forward_multi_alt call above, we want to pass
 
2210
               * sect_prefix instead of section so we can obtain the header.
 
2211
               * Set the FWD_NESTED flag so we fetch the right body_part.
 
2212
               */ 
 
2213
                int partnum;
 
2214
 
 
2215
                part = body->nested.part->next;
 
2216
                body->nested.part->next = NULL;
 
2217
                mail_free_body_part(&body->nested.part);
 
2218
                body->nested.part = mail_newbody_part();
 
2219
                body->nested.part->body = *tmp_body;
 
2220
                body->nested.part->next = part;
 
2221
 
 
2222
                for(partnum = 2; part != NULL; part = part->next){
 
2223
                    snprintf(sect_buf, sizeof(sect_buf), "%s%s%d",
 
2224
                            sect_prefix ? sect_prefix : "",
 
2225
                            sect_prefix ? "." : "", partnum++);
 
2226
                    sect_buf[sizeof(sect_buf)-1] = '\0';
 
2227
 
 
2228
                    if(!fetch_contents(stream, msgno, sect_buf, &part->body)){
 
2229
                        mail_free_body(&body);
 
2230
                        break;
 
2231
                    }
 
2232
                }
 
2233
            }
 
2234
            else {
 
2235
                if(fetch_contents(stream, msgno, sect_prefix, body)){
 
2236
                    /*--- Create a new blank text part ---*/
 
2237
                    part                          = mail_newbody_part();
 
2238
                    part->next                    = body->nested.part;
 
2239
                    body->nested.part             = part;
 
2240
                    part->body.contents.text.data = msgtext;
 
2241
                }
 
2242
                else
 
2243
                  mail_free_body(&body);
 
2244
            }
 
2245
        }
 
2246
    }
 
2247
    else {
 
2248
        /*---- A single part message, not of type text ----*/
 
2249
        body                     = mail_newbody();
 
2250
        body->type               = TYPEMULTIPART;
 
2251
        part                     = mail_newbody_part();
 
2252
        body->nested.part      = part;
 
2253
 
 
2254
        /*--- The first part, a blank text part to be edited ---*/
 
2255
        part->body.type            = TYPETEXT;
 
2256
        part->body.contents.text.data = msgtext;
 
2257
 
 
2258
        /*--- The second part, what ever it is ---*/
 
2259
        part->next               = mail_newbody_part();
 
2260
        part                     = part->next;
 
2261
        part->body.id            = generate_message_id();
 
2262
        copy_body(&(part->body), orig_body);
 
2263
 
 
2264
        /*
 
2265
         * the idea here is to fetch part into storage object
 
2266
         */
 
2267
        if(part->body.contents.text.data = (void *) so_get(PART_SO_TYPE, NULL,
 
2268
                                                           EDIT_ACCESS)){
 
2269
#if     defined(DOS) && !defined(WIN32)
 
2270
            /* fetched text to disk */
 
2271
            mail_parameters(stream, SET_GETS, (void *)dos_gets);
 
2272
            append_file = (FILE *)so_text(
 
2273
                                   (STORE_S *) part->body.contents.text.data);
 
2274
 
 
2275
            if(mail_fetchbody(stream, msgno, part, &part->body.size.bytes)){
 
2276
                /* next time body may stay in core */
 
2277
                mail_parameters(stream, SET_GETS, (void *)NULL);
 
2278
                append_file = NULL;
 
2279
                mail_gc(stream, GC_TEXTS);
 
2280
            }
 
2281
            else
 
2282
              mail_free_body(&body);
 
2283
#else
 
2284
            if(tmp_text = pine_mail_fetch_body(stream, msgno, section,
 
2285
                                         &part->body.size.bytes, NIL))
 
2286
              so_nputs((STORE_S *)part->body.contents.text.data, tmp_text,
 
2287
                       part->body.size.bytes);
 
2288
            else
 
2289
              mail_free_body(&body);
 
2290
#endif
 
2291
        }
 
2292
        else
 
2293
          mail_free_body(&body);
 
2294
    }
 
2295
 
 
2296
    gf_clear_so_writec((STORE_S *) msgtext);
 
2297
 
 
2298
    return(body);
 
2299
}
 
2300
 
 
2301
 
 
2302
 
 
2303
/*
 
2304
 * bounce_msg_body - build body from specified message suitable
 
2305
 *                   for sending as bounced message
 
2306
 */
 
2307
char *
 
2308
bounce_msg_body(MAILSTREAM *stream,
 
2309
                long int rawno,
 
2310
                char *part,
 
2311
                char **to,
 
2312
                char *subject,
 
2313
                ENVELOPE **outgoingp,
 
2314
                BODY **bodyp,
 
2315
                int *seenp)
 
2316
{
 
2317
    char     *h, *p, *errstr = NULL;
 
2318
    int       i;
 
2319
    STORE_S  *msgtext;
 
2320
    gf_io_t   pc;
 
2321
 
 
2322
    *outgoingp           = mail_newenvelope();
 
2323
    (*outgoingp)->message_id = generate_message_id();
 
2324
    (*outgoingp)->subject    = cpystr(subject ? subject : "Resent mail....");
 
2325
 
 
2326
    /*
 
2327
     * Fill in destination if we were given one.  If so, note that we
 
2328
     * call p_s_s() below such that it won't prompt...
 
2329
     */
 
2330
    if(to && *to){
 
2331
        static char *fakedomain = "@";
 
2332
        char        *tmp_a_string;
 
2333
 
 
2334
        /* rfc822_parse_adrlist feels free to destroy input so copy */
 
2335
        tmp_a_string = cpystr(*to);
 
2336
        rfc822_parse_adrlist(&(*outgoingp)->to, tmp_a_string,
 
2337
                             (F_ON(F_COMPOSE_REJECTS_UNQUAL, ps_global))
 
2338
                             ? fakedomain : ps_global->maildomain);
 
2339
        fs_give((void **) &tmp_a_string);
 
2340
    }
 
2341
 
 
2342
    /* build remail'd header */
 
2343
    if(h = mail_fetch_header(stream, rawno, part, NULL, 0, FT_PEEK)){
 
2344
        for(p = h, i = 0; p = strchr(p, ':'); p++)
 
2345
          i++;
 
2346
 
 
2347
        /* allocate it */
 
2348
        (*outgoingp)->remail = (char *) fs_get(strlen(h) + (2 * i) + 1);
 
2349
 
 
2350
        /*
 
2351
         * copy it, "X-"ing out transport headers bothersome to
 
2352
         * software but potentially useful to the human recipient...
 
2353
         */
 
2354
        p = (*outgoingp)->remail;
 
2355
        bounce_mask_header(&p, h);
 
2356
        do
 
2357
          if(*h == '\015' && *(h+1) == '\012'){
 
2358
              *p++ = *h++;              /* copy CR LF */
 
2359
              *p++ = *h++;
 
2360
              bounce_mask_header(&p, h);
 
2361
          }
 
2362
        while(*p++ = *h++);
 
2363
    }
 
2364
    /* BUG: else complain? */
 
2365
 
 
2366
    /* NOT bound for the composer, so no need for PicoText */
 
2367
    if(!(msgtext = so_get(CharStar, NULL, EDIT_ACCESS))){
 
2368
        mail_free_envelope(outgoingp);
 
2369
        return(_("Error allocating message text"));
 
2370
    }
 
2371
 
 
2372
    /* mark object for special handling */
 
2373
    so_attr(msgtext, "rawbody", "1");
 
2374
 
 
2375
    /*
 
2376
     * Build a fake body description.  It's ignored by pine_rfc822_header,
 
2377
     * but we need to set it to something that makes set_mime_types
 
2378
     * not sniff it and pine_rfc822_output_body not re-encode it.
 
2379
     * Setting the encoding to (ENCMAX + 1) will work and shouldn't cause
 
2380
     * problems unless something tries to access body_encodings[] using
 
2381
     * it without proper precautions.  We don't want to use ENCOTHER
 
2382
     * cause that tells set_mime_types to sniff it, and we don't want to
 
2383
     * use ENC8BIT since that tells pine_rfc822_output_body to qp-encode
 
2384
     * it.  When there's time, it'd be nice to clean this interaction
 
2385
     * up...
 
2386
     */
 
2387
    *bodyp                       = mail_newbody();
 
2388
    (*bodyp)->type               = TYPETEXT;
 
2389
    (*bodyp)->encoding           = ENCMAX + 1;
 
2390
    (*bodyp)->subtype            = cpystr("Plain");
 
2391
    (*bodyp)->contents.text.data = (void *) msgtext;
 
2392
    gf_set_so_writec(&pc, msgtext);
 
2393
 
 
2394
    if(seenp && rawno > 0L && stream && rawno <= stream->nmsgs){
 
2395
        MESSAGECACHE *mc;
 
2396
 
 
2397
        if(mc = mail_elt(stream,  rawno))
 
2398
          *seenp = mc->seen;
 
2399
    }
 
2400
 
 
2401
    /* pass NULL body to force mail_fetchtext */
 
2402
    if(!get_body_part_text(stream, NULL, rawno, part, 0L, pc, NULL, NULL))
 
2403
      errstr = _("Error fetching message contents. Can't Bounce message");
 
2404
 
 
2405
    gf_clear_so_writec(msgtext);
 
2406
 
 
2407
    return(errstr);
 
2408
}
 
2409
 
 
2410
 
 
2411
 
 
2412
/*----------------------------------------------------------------------
 
2413
    Mask off any header entries we don't want xport software to see
 
2414
 
 
2415
Args:  d -- destination string pointer pointer
 
2416
       s -- source string pointer pointer
 
2417
 
 
2418
       Postfix uses Delivered-To to detect loops.
 
2419
       Received line counting is also used to detect loops in places.
 
2420
 
 
2421
  ----*/
 
2422
void
 
2423
bounce_mask_header(char **d, char *s)
 
2424
{
 
2425
    if(((*s == 'R' || *s == 'r')
 
2426
        && (!struncmp(s+1, "esent-", 6) || !struncmp(s+1, "eceived:", 8)
 
2427
            || !struncmp(s+1, "eturn-Path", 10)))
 
2428
       || !struncmp(s, "Delivered-To:", 13)){
 
2429
        *(*d)++ = 'X';                          /* write mask */
 
2430
        *(*d)++ = '-';
 
2431
    }
 
2432
}
 
2433
 
 
2434
        
 
2435
/*----------------------------------------------------------------------
 
2436
    Fetch and format text for forwarding
 
2437
 
 
2438
Args:  stream  -- Mail stream to fetch text from
 
2439
       body    -- Body structure of message being forwarded
 
2440
       msg_no  -- Message number of text for forward
 
2441
       part_no -- Part number of text to forward
 
2442
       partial -- If this is > 0 a partial fetch will be done and it will
 
2443
                    be done using FT_PEEK so the message will remain unseen.
 
2444
       pc      -- Function to write to
 
2445
       prefix  -- Prefix for each line
 
2446
       ret_charset -- If we translate to another charset return that
 
2447
                      new charset here
 
2448
 
 
2449
Returns:  true if OK, false if problem occured while filtering
 
2450
 
 
2451
If the text is richtext, it will be converted to plain text, since there's
 
2452
no rich text editing capabilities in Pine (yet).
 
2453
 
 
2454
It's up to calling routines to plug in signature appropriately
 
2455
 
 
2456
As with all internal text, NVT end-of-line conventions are observed.
 
2457
DOESN'T sanity check the prefix given!!!
 
2458
  ----*/
 
2459
int
 
2460
get_body_part_text(MAILSTREAM *stream, struct mail_bodystruct *body, long int msg_no,
 
2461
                   char *part_no, long partial, gf_io_t pc, char *prefix, char **ret_charset)
 
2462
{
 
2463
    int         i, we_cancel = 0, dashdata, wrapflags = 0, flow_res = 0;
 
2464
    FILTLIST_S  filters[11];
 
2465
    long        len;
 
2466
    char       *err, *charset, *prefix_p = NULL;
 
2467
    int         filtcnt = 0, flags = 0;
 
2468
 
 
2469
    memset(filters, 0, sizeof(filters));
 
2470
    if(ret_charset)
 
2471
      *ret_charset = NULL;
 
2472
 
 
2473
    if(!pc_is_picotext(pc))
 
2474
      we_cancel = busy_cue(NULL, NULL, 1);
 
2475
 
 
2476
    /* if null body, we must be talking to a non-IMAP2bis server.
 
2477
     * No MIME parsing provided, so we just grab the message text...
 
2478
     */
 
2479
    if(body == NULL){
 
2480
        char         *text, *decode_error;
 
2481
        gf_io_t       gc;
 
2482
        SourceType    src = CharStar;
 
2483
        int           rv = 0;
 
2484
 
 
2485
        (void) pine_mail_fetchstructure(stream, msg_no, NULL);
 
2486
 
 
2487
        if(text = pine_mail_fetch_text(stream, msg_no, part_no, NULL, 0)){
 
2488
            gf_set_readc(&gc, text, (unsigned long)strlen(text), src);
 
2489
 
 
2490
            gf_filter_init();           /* no filters needed */
 
2491
            if(prefix)
 
2492
              gf_link_filter(gf_prefix, gf_prefix_opt(prefix));
 
2493
            if(decode_error = gf_pipe(gc, pc)){
 
2494
                snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s%s    [Formatting error: %s]%s",
 
2495
                        NEWLINE, NEWLINE,
 
2496
                        decode_error, NEWLINE);
 
2497
                tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
 
2498
                gf_puts(tmp_20k_buf, pc);
 
2499
                rv++;
 
2500
            }
 
2501
        }
 
2502
        else{
 
2503
            gf_puts(NEWLINE, pc);
 
2504
            gf_puts(_("    [ERROR fetching text of message]"), pc);
 
2505
            gf_puts(NEWLINE, pc);
 
2506
            gf_puts(NEWLINE, pc);
 
2507
            rv++;
 
2508
        }
 
2509
 
 
2510
        if(we_cancel)
 
2511
          cancel_busy_cue(-1);
 
2512
 
 
2513
        return(rv == 0);
 
2514
    }
 
2515
 
 
2516
    charset = rfc2231_get_param(body->parameter, "charset", NULL, NULL);
 
2517
 
 
2518
    flags |= FM_UTF8;
 
2519
    if(charset && strucmp(charset, "utf-8") && strucmp(charset, "us-ascii")){
 
2520
        /* translate filtered message text to UTF-8? */
 
2521
        if(utf8able(charset)){
 
2522
            filters[filtcnt].filter = gf_utf8;
 
2523
            filters[filtcnt++].data = gf_utf8_opt(charset);
 
2524
 
 
2525
            if(ret_charset)
 
2526
              *ret_charset = "UTF-8";
 
2527
        }
 
2528
        else{
 
2529
            /* BUG: what to do if not transliterable? */
 
2530
            flags &= ~FM_UTF8;
 
2531
        }
 
2532
    }
 
2533
 
 
2534
    /*
 
2535
     * just use detach, but add an auxiliary filter to insert prefix,
 
2536
     * and, perhaps, digest richtext
 
2537
     */
 
2538
    if(ps_global->full_header != 2
 
2539
       && !ps_global->postpone_no_flow
 
2540
       && (flags & FM_UTF8)
 
2541
       && (!body->subtype || !strucmp(body->subtype, "plain"))){
 
2542
        char *parmval;
 
2543
 
 
2544
        flow_res = (F_OFF(F_QUELL_FLOWED_TEXT, ps_global)
 
2545
                     && F_OFF(F_STRIP_WS_BEFORE_SEND, ps_global)
 
2546
                     && (!prefix || (strucmp(prefix,"> ") == 0)
 
2547
                         || strucmp(prefix, ">") == 0));
 
2548
        if(parmval = rfc2231_get_param(body->parameter,
 
2549
                                       "format", NULL, NULL)){
 
2550
            if(!strucmp(parmval, "flowed")){
 
2551
                wrapflags |= GFW_FLOWED;
 
2552
 
 
2553
                fs_give((void **) &parmval);
 
2554
                if(parmval = rfc2231_get_param(body->parameter,
 
2555
                                               "delsp", NULL, NULL)){
 
2556
                    if(!strucmp(parmval, "yes")){
 
2557
                        filters[filtcnt++].filter = gf_preflow;
 
2558
                        wrapflags |= GFW_DELSP;
 
2559
                    }
 
2560
 
 
2561
                    fs_give((void **) &parmval);
 
2562
                }
 
2563
 
 
2564
                /*
 
2565
                 * if there's no prefix we're forwarding text
 
2566
                 * otherwise it's reply text.  unless the user's
 
2567
                 * tied our hands, alter the prefix to continue flowed
 
2568
                 * formatting...
 
2569
                 */
 
2570
                if(flow_res)
 
2571
                  wrapflags |= GFW_FLOW_RESULT;
 
2572
 
 
2573
                if(flags & FM_UTF8)
 
2574
                  wrapflags |= GFW_UTF8;
 
2575
 
 
2576
                filters[filtcnt].filter = gf_wrap;
 
2577
                /* 
 
2578
                 * The 80 will cause longer lines than what is likely
 
2579
                 * set by composer_fillcol, so we'll have longer
 
2580
                 * quoted and forwarded lines than the lines we type.
 
2581
                 * We're fine with that since the alternative is the
 
2582
                 * possible wrapping of lines we shouldn't have, which
 
2583
                 * is mitigated by this higher 80 limit.
 
2584
                 *
 
2585
                 * If we were to go back to using composer_fillcol,
 
2586
                 * the correct value is composer_fillcol + 1, pine
 
2587
                 * is off-by-one from pico.
 
2588
                 */
 
2589
                filters[filtcnt++].data = gf_wrap_filter_opt(
 
2590
                    MAX(MIN(ps_global->ttyo->screen_cols
 
2591
                            - (prefix ? strlen(prefix) : 0),
 
2592
                            80 - (prefix ? strlen(prefix) : 0)),
 
2593
                        30), /* doesn't have to be 30 */
 
2594
                    990, /* 998 is the SMTP limit */
 
2595
                    NULL, 0, wrapflags);
 
2596
            }
 
2597
        }
 
2598
 
 
2599
        /*
 
2600
         * if not flowed, remove trailing whitespace to reduce
 
2601
         * confusion, since we're sending out as flowed if we
 
2602
         * can.  At some future point, we might try 
 
2603
         * plugging in a user-option-controlled heuristic
 
2604
         * flowing filter 
 
2605
         *
 
2606
         * We also want to fold "> " quotes so we get the
 
2607
         * attributions correct.
 
2608
         */
 
2609
        if(flow_res && prefix && !strucmp(prefix, "> "))
 
2610
          *(prefix_p = prefix + 1) = '\0';
 
2611
 
 
2612
        if(!(wrapflags & GFW_FLOWED)
 
2613
           && flow_res){
 
2614
            filters[filtcnt].filter = gf_line_test;
 
2615
            filters[filtcnt++].data = gf_line_test_opt(twsp_strip, NULL);
 
2616
 
 
2617
            filters[filtcnt].filter = gf_line_test;
 
2618
            filters[filtcnt++].data = gf_line_test_opt(quote_fold, NULL);
 
2619
        }
 
2620
    }
 
2621
    else if(body->subtype){
 
2622
        if(strucmp(body->subtype,"richtext") == 0){
 
2623
            filters[filtcnt].filter = gf_rich2plain;
 
2624
            filters[filtcnt++].data = gf_rich2plain_opt(1);
 
2625
        }
 
2626
        else if(strucmp(body->subtype,"enriched") == 0){
 
2627
            filters[filtcnt].filter = gf_enriched2plain;
 
2628
            filters[filtcnt++].data = gf_enriched2plain_opt(1);
 
2629
        }
 
2630
        else if(strucmp(body->subtype,"html") == 0){
 
2631
            filters[filtcnt].filter = gf_html2plain;
 
2632
            filters[filtcnt++].data = gf_html2plain_opt(NULL,
 
2633
                                                  ps_global->ttyo->screen_cols,
 
2634
                                                  NULL, NULL, GFHP_STRIPPED);
 
2635
        }
 
2636
    }
 
2637
 
 
2638
    if(prefix){
 
2639
        if(ps_global->full_header != 2
 
2640
           && (F_ON(F_ENABLE_SIGDASHES, ps_global)
 
2641
               || F_ON(F_ENABLE_STRIP_SIGDASHES, ps_global))){
 
2642
            dashdata = 0;
 
2643
            filters[filtcnt].filter = gf_line_test;
 
2644
            filters[filtcnt++].data = gf_line_test_opt(sigdash_strip, &dashdata);
 
2645
        }
 
2646
 
 
2647
        filters[filtcnt].filter = gf_prefix;
 
2648
        filters[filtcnt++].data = gf_prefix_opt(prefix);
 
2649
 
 
2650
        if(wrapflags & GFW_FLOWED || flow_res){
 
2651
            filters[filtcnt].filter = gf_line_test;
 
2652
            filters[filtcnt++].data = gf_line_test_opt(post_quote_space, NULL);
 
2653
        }
 
2654
    }
 
2655
 
 
2656
    err = detach(stream, msg_no, part_no, partial, &len, pc,
 
2657
                 filters[0].filter ? filters : NULL, (partial > 0L) ? FT_PEEK : 0);
 
2658
 
 
2659
    if(prefix_p)
 
2660
      *prefix_p = ' ';
 
2661
 
 
2662
    if (err != (char *) NULL)
 
2663
       /* TRANSLATORS: The first arg is error text, the %ld is the message number */
 
2664
       q_status_message2(SM_ORDER, 3, 4, "%s: message number %ld",
 
2665
                         err, (void *) msg_no);
 
2666
 
 
2667
    if(we_cancel)
 
2668
      cancel_busy_cue(-1);
 
2669
 
 
2670
    return((int) len);
 
2671
}
 
2672
 
 
2673
 
 
2674
int
 
2675
quote_fold(long int linenum, char *line, LT_INS_S **ins, void *local)
 
2676
{
 
2677
    char *p;
 
2678
 
 
2679
    if(*line == '>'){
 
2680
        for(p = line; *p; p++){
 
2681
            if(isspace((unsigned char) *p)){
 
2682
                if(*(p+1) == '>')
 
2683
                  ins = gf_line_test_new_ins(ins, p, "", -1);
 
2684
            }
 
2685
            else if(*p != '>')
 
2686
              break;
 
2687
        }
 
2688
    }
 
2689
 
 
2690
    return(0);
 
2691
}
 
2692
 
 
2693
 
 
2694
int
 
2695
twsp_strip(long int linenum, char *line, LT_INS_S **ins, void *local)
 
2696
{
 
2697
    char *p, *ws = NULL;
 
2698
 
 
2699
    for(p = line; *p; p++)
 
2700
      if(isspace((unsigned char) *p)){
 
2701
          if(!ws)
 
2702
            ws = p;
 
2703
      }
 
2704
      else
 
2705
        ws = NULL;
 
2706
 
 
2707
    if(ws)
 
2708
      ins = gf_line_test_new_ins(ins, ws, "", -(p - ws));
 
2709
 
 
2710
    return(0);
 
2711
}
 
2712
 
 
2713
int
 
2714
post_quote_space(long int linenum, char *line, LT_INS_S **ins, void *local)
 
2715
{
 
2716
    char *p;
 
2717
 
 
2718
    for(p = line; *p; p++)
 
2719
      if(*p != '>'){
 
2720
          if(p != line && *p != ' ')
 
2721
            ins = gf_line_test_new_ins(ins, p, " ", 1);
 
2722
 
 
2723
          break;
 
2724
      }
 
2725
 
 
2726
    return(0);
 
2727
}
 
2728
 
 
2729
 
 
2730
int
 
2731
sigdash_strip(long int linenum, char *line, LT_INS_S **ins, void *local)
 
2732
{
 
2733
    if(*((int *)local)
 
2734
       || (*line == '-' && *(line+1) == '-'
 
2735
           && *(line+2) == ' ' && !*(line+3))){
 
2736
        *((int *) local) = 1;
 
2737
        return(2);              /* skip this line! */
 
2738
    }
 
2739
 
 
2740
    return(0);
 
2741
}
 
2742
 
 
2743
 
 
2744
/*----------------------------------------------------------------------
 
2745
  return the c-client reference name for the given end_body part
 
2746
  ----*/
 
2747
char *
 
2748
body_partno(MAILSTREAM *stream, long int msgno, struct mail_bodystruct *end_body)
 
2749
{
 
2750
    BODY *body;
 
2751
 
 
2752
    (void) pine_mail_fetchstructure(stream, msgno, &body);
 
2753
    return(partno(body, end_body));
 
2754
}
 
2755
 
 
2756
 
 
2757
/*----------------------------------------------------------------------
 
2758
  return the c-client reference name for the given end_body part
 
2759
  ----*/
 
2760
char *
 
2761
partno(struct mail_bodystruct *body, struct mail_bodystruct *end_body)
 
2762
{
 
2763
    PART *part;
 
2764
    int   num = 0;
 
2765
    char  tmp[64], *p = NULL;
 
2766
 
 
2767
    if(body && body->type == TYPEMULTIPART) {
 
2768
        part = body->nested.part;       /* first body part */
 
2769
 
 
2770
        do {                            /* for each part */
 
2771
            num++;
 
2772
            if(&part->body == end_body || (p = partno(&part->body, end_body))){
 
2773
                snprintf(tmp, sizeof(tmp), "%d%s%.*s", num, (p) ? "." : "",
 
2774
                        sizeof(tmp)-10, (p) ? p : "");
 
2775
                tmp[sizeof(tmp)-1] = '\0';
 
2776
                if(p)
 
2777
                  fs_give((void **)&p);
 
2778
 
 
2779
                return(cpystr(tmp));
 
2780
            }
 
2781
        } while (part = part->next);    /* until done */
 
2782
 
 
2783
        return(NULL);
 
2784
    }
 
2785
    else if(body && body->type == TYPEMESSAGE && body->subtype 
 
2786
            && !strucmp(body->subtype, "rfc822")){
 
2787
        return(partno(body->nested.msg->body, end_body));
 
2788
    }
 
2789
 
 
2790
    return((body == end_body) ? cpystr("1") : NULL);
 
2791
}
 
2792
 
 
2793
 
 
2794
/*----------------------------------------------------------------------
 
2795
   Fill in the contents of each body part
 
2796
 
 
2797
Args: stream      -- Stream the message is on
 
2798
      msgno       -- Message number the body structure is for
 
2799
      section     -- body section associated with body pointer
 
2800
      body        -- Body pointer to fill in
 
2801
 
 
2802
Result: 1 if all went OK, 0 if there was a problem
 
2803
 
 
2804
This function copies the contents from an original message/body to
 
2805
a new message/body.  It recurses down all multipart levels.
 
2806
 
 
2807
If one or more part (but not all) can't be fetched, a status message
 
2808
will be queued.
 
2809
 ----*/
 
2810
int
 
2811
fetch_contents(MAILSTREAM *stream, long int msgno, char *section, struct mail_bodystruct *body)
 
2812
{
 
2813
    char *tp;
 
2814
    int   got_one = 0;
 
2815
 
 
2816
    if(!body->id)
 
2817
      body->id = generate_message_id();
 
2818
          
 
2819
    if(body->type == TYPEMULTIPART){
 
2820
        char  subsection[256], *subp;
 
2821
        int   n, last_one = 10;         /* remember worst case */
 
2822
        PART *part     = body->nested.part;
 
2823
 
 
2824
        if(!(part = body->nested.part))
 
2825
          return(0);
 
2826
 
 
2827
        subp = subsection;
 
2828
        if(section && *section){
 
2829
            for(n = 0;
 
2830
                n < sizeof(subsection)-20 && (*subp = section[n]); n++, subp++)
 
2831
              ;
 
2832
 
 
2833
            *subp++ = '.';
 
2834
        }
 
2835
 
 
2836
        n = 1;
 
2837
        do {
 
2838
            snprintf(subp, sizeof(subsection)-(subp-subsection), "%d", n++);
 
2839
            subsection[sizeof(subsection)-1] = '\0';
 
2840
            got_one  = fetch_contents(stream, msgno, subsection, &part->body);
 
2841
            last_one = MIN(last_one, got_one);
 
2842
        }
 
2843
        while(part = part->next);
 
2844
 
 
2845
        return(last_one);
 
2846
    }
 
2847
 
 
2848
    if(body->contents.text.data)
 
2849
      return(1);                        /* already taken care of... */
 
2850
 
 
2851
    if(body->type == TYPEMESSAGE){
 
2852
        if(body->subtype && strucmp(body->subtype,"external-body")){
 
2853
            /*
 
2854
             * the idea here is to fetch everything into storage objects
 
2855
             */
 
2856
            body->contents.text.data = (void *) so_get(PART_SO_TYPE, NULL,
 
2857
                                                    EDIT_ACCESS);
 
2858
#if     defined(DOS) && !defined(WIN32)
 
2859
            if(body->contents.text.data){
 
2860
                /* fetch text to disk */
 
2861
                mail_parameters(stream, SET_GETS, (void *)dos_gets);
 
2862
                append_file =(FILE *)so_text((STORE_S *)body->contents.text.data);
 
2863
 
 
2864
                if(mail_fetchbody(stream, msgno, section, &body->size.bytes)){
 
2865
                    so_release((STORE_S *)body->contents.text.data);
 
2866
                    got_one = 1;
 
2867
                }
 
2868
                else
 
2869
                  q_status_message1(SM_ORDER | SM_DING, 3, 3,
 
2870
                                    _("Error fetching part %s"), section);
 
2871
 
 
2872
                /* next time body may stay in core */
 
2873
                mail_parameters(stream, SET_GETS, (void *)NULL);
 
2874
                append_file = NULL;
 
2875
                mail_gc(stream, GC_TEXTS);
 
2876
            }
 
2877
#else
 
2878
            if(body->contents.text.data
 
2879
               && (tp = pine_mail_fetch_body(stream, msgno, section,
 
2880
                                       &body->size.bytes, NIL))){
 
2881
                so_truncate((STORE_S *)body->contents.text.data, 
 
2882
                            body->size.bytes + 2048);
 
2883
                so_nputs((STORE_S *)body->contents.text.data, tp,
 
2884
                         body->size.bytes);
 
2885
                got_one = 1;
 
2886
            }
 
2887
#endif
 
2888
            else
 
2889
              q_status_message1(SM_ORDER | SM_DING, 3, 3,
 
2890
                                _("Error fetching part %s"), section);
 
2891
        } else {
 
2892
            got_one = 1;
 
2893
        }
 
2894
    } else {
 
2895
        /*
 
2896
         * the idea here is to fetch everything into storage objects
 
2897
         * so, grab one, then fetch the body part
 
2898
         */
 
2899
        body->contents.text.data = (void *)so_get(PART_SO_TYPE,NULL,EDIT_ACCESS);
 
2900
#if     defined(DOS) && !defined(WIN32)
 
2901
        if(body->contents.text.data){
 
2902
            /* write fetched text to disk */
 
2903
            mail_parameters(stream, SET_GETS, (void *)dos_gets);
 
2904
            append_file = (FILE *)so_text((STORE_S *)body->contents.text.data);
 
2905
            if(mail_fetchbody(stream, msgno, section, &body->size.bytes)){
 
2906
                so_release((STORE_S *)body->contents.text.data);
 
2907
                got_one = 1;
 
2908
            }
 
2909
            else
 
2910
              q_status_message1(SM_ORDER | SM_DING, 3, 3,
 
2911
                                _("Error fetching part %s"), section);
 
2912
 
 
2913
            /* next time body may stay in core */
 
2914
            mail_parameters(stream, SET_GETS, (void *)NULL);
 
2915
            append_file = NULL;
 
2916
            mail_gc(stream, GC_TEXTS);
 
2917
        }
 
2918
#else
 
2919
        if(body->contents.text.data
 
2920
           && (tp=pine_mail_fetch_body(stream, msgno, section,
 
2921
                                       &body->size.bytes, NIL))){
 
2922
            so_truncate((STORE_S *)body->contents.text.data, 
 
2923
                            body->size.bytes + 2048);
 
2924
            so_nputs((STORE_S *)body->contents.text.data, tp,
 
2925
                     body->size.bytes);
 
2926
            got_one = 1;
 
2927
        }
 
2928
#endif
 
2929
        else
 
2930
          q_status_message1(SM_ORDER | SM_DING, 3, 3,
 
2931
                            _("Error fetching part %s"), section);
 
2932
    }
 
2933
 
 
2934
    return(got_one);
 
2935
}
 
2936
 
 
2937
 
 
2938
/*----------------------------------------------------------------------
 
2939
    Copy the body structure
 
2940
 
 
2941
Args: new_body -- Pointer to already allocated body, or NULL, if none
 
2942
      old_body -- The Body to copy
 
2943
 
 
2944
 
 
2945
 This is traverses the body structure recursively copying all elements.
 
2946
The new_body parameter can be NULL in which case a new body is
 
2947
allocated. Alternatively it can point to an already allocated body
 
2948
structure. This is used when copying body parts since a PART includes a 
 
2949
BODY. The contents fields are *not* filled in.
 
2950
  ----*/
 
2951
 
 
2952
BODY *
 
2953
copy_body(struct mail_bodystruct *new_body, struct mail_bodystruct *old_body)
 
2954
{
 
2955
    if(old_body == NULL)
 
2956
      return(NULL);
 
2957
 
 
2958
    if(new_body == NULL)
 
2959
      new_body = mail_newbody();
 
2960
 
 
2961
    new_body->type = old_body->type;
 
2962
    new_body->encoding = old_body->encoding;
 
2963
 
 
2964
    if(old_body->subtype)
 
2965
      new_body->subtype = cpystr(old_body->subtype);
 
2966
 
 
2967
    new_body->parameter = copy_parameters(old_body->parameter);
 
2968
 
 
2969
    if(old_body->id)
 
2970
      new_body->id = cpystr(old_body->id);
 
2971
 
 
2972
    if(old_body->description)
 
2973
      new_body->description = cpystr(old_body->description);
 
2974
 
 
2975
    if(old_body->disposition.type)
 
2976
      new_body->disposition.type = cpystr(old_body->disposition.type);
 
2977
 
 
2978
    new_body->disposition.parameter
 
2979
                            = copy_parameters(old_body->disposition.parameter);
 
2980
    
 
2981
    new_body->size = old_body->size;
 
2982
 
 
2983
    if(new_body->type == TYPEMESSAGE
 
2984
       && new_body->subtype && !strucmp(new_body->subtype, "rfc822")){
 
2985
        new_body->nested.msg = mail_newmsg();
 
2986
        new_body->nested.msg->body
 
2987
                                 = copy_body(NULL, old_body->nested.msg->body);
 
2988
    }
 
2989
    else if(new_body->type == TYPEMULTIPART) {
 
2990
        PART **new_partp, *old_part;
 
2991
 
 
2992
        new_partp = &new_body->nested.part;
 
2993
        for(old_part = old_body->nested.part;
 
2994
            old_part != NULL;
 
2995
            old_part = old_part->next){
 
2996
            *new_partp = mail_newbody_part();
 
2997
            copy_body(&(*new_partp)->body, &old_part->body);
 
2998
            new_partp = &(*new_partp)->next;
 
2999
        }
 
3000
    }
 
3001
 
 
3002
    return(new_body);
 
3003
}
 
3004
 
 
3005
 
 
3006
/*----------------------------------------------------------------------
 
3007
    Copy the MIME parameter list
 
3008
 
 
3009
 Allocates storage for new part, and returns pointer to new paramter
 
3010
list. If old_p is NULL, NULL is returned.
 
3011
 ----*/
 
3012
 
 
3013
PARAMETER *
 
3014
copy_parameters(struct mail_body_parameter *old_p)
 
3015
{
 
3016
    PARAMETER *new_p, *p1, *p2;
 
3017
 
 
3018
    if(old_p == NULL)
 
3019
      return((PARAMETER *)NULL);
 
3020
 
 
3021
    new_p = p2 = NULL;
 
3022
    for(p1 = old_p; p1 != NULL; p1 = p1->next) {
 
3023
        if(new_p == NULL) {
 
3024
            p2 = mail_newbody_parameter();
 
3025
            new_p = p2;
 
3026
        } else {
 
3027
            p2->next = mail_newbody_parameter();
 
3028
            p2 = p2->next;
 
3029
        }
 
3030
        p2->attribute = cpystr(p1->attribute);
 
3031
        p2->value     = cpystr(p1->value);
 
3032
    }
 
3033
    return(new_p);
 
3034
}
 
3035
    
 
3036
 
 
3037
/*----------------------------------------------------------------------
 
3038
    Make a complete copy of an envelope and all it's fields
 
3039
 
 
3040
Args:    e -- the envelope to copy
 
3041
 
 
3042
Result:  returns the new envelope, or NULL, if the given envelope was NULL
 
3043
 
 
3044
  ----*/
 
3045
 
 
3046
ENVELOPE *    
 
3047
copy_envelope(register ENVELOPE *e)
 
3048
{
 
3049
    register ENVELOPE *e2;
 
3050
 
 
3051
    if(!e)
 
3052
      return(NULL);
 
3053
 
 
3054
    e2              = mail_newenvelope();
 
3055
    e2->remail      = e->remail      ? cpystr(e->remail)              : NULL;
 
3056
    e2->return_path = e->return_path ? rfc822_cpy_adr(e->return_path) : NULL;
 
3057
    e2->date        = e->date        ? (unsigned char *)cpystr((char *) e->date)
 
3058
                                                                      : NULL;
 
3059
    e2->from        = e->from        ? rfc822_cpy_adr(e->from)        : NULL;
 
3060
    e2->sender      = e->sender      ? rfc822_cpy_adr(e->sender)      : NULL;
 
3061
    e2->reply_to    = e->reply_to    ? rfc822_cpy_adr(e->reply_to)    : NULL;
 
3062
    e2->subject     = e->subject     ? cpystr(e->subject)             : NULL;
 
3063
    e2->to          = e->to          ? rfc822_cpy_adr(e->to)          : NULL;
 
3064
    e2->cc          = e->cc          ? rfc822_cpy_adr(e->cc)          : NULL;
 
3065
    e2->bcc         = e->bcc         ? rfc822_cpy_adr(e->bcc)         : NULL;
 
3066
    e2->in_reply_to = e->in_reply_to ? cpystr(e->in_reply_to)         : NULL;
 
3067
    e2->newsgroups  = e->newsgroups  ? cpystr(e->newsgroups)          : NULL;
 
3068
    e2->message_id  = e->message_id  ? cpystr(e->message_id)          : NULL;
 
3069
    e2->references  = e->references  ? cpystr(e->references)          : NULL;
 
3070
    e2->followup_to = e->followup_to ? cpystr(e->references)          : NULL;
 
3071
    return(e2);
 
3072
}
 
3073
 
 
3074
 
 
3075
/*----------------------------------------------------------------------
 
3076
     Generate the "In-reply-to" text from message header
 
3077
 
 
3078
  Args: message -- Envelope of original message
 
3079
 
 
3080
  Result: returns an alloc'd string or NULL if there is a problem
 
3081
 ----*/
 
3082
char *
 
3083
reply_in_reply_to(ENVELOPE *env)
 
3084
{
 
3085
    return((env && env->message_id) ? cpystr(env->message_id) : NULL);
 
3086
}
 
3087
 
 
3088
 
 
3089
/*----------------------------------------------------------------------
 
3090
        Generate a unique message id string.
 
3091
 
 
3092
   Args: ps -- The usual pine structure
 
3093
 
 
3094
  Result: Alloc'd unique string is returned
 
3095
 
 
3096
Uniqueness is gaurenteed by using the host name, process id, date to the
 
3097
second and a single unique character
 
3098
*----------------------------------------------------------------------*/
 
3099
char *
 
3100
generate_message_id(void)
 
3101
{
 
3102
    static short osec = 0, cnt = 0;
 
3103
    char         idbuf[128];
 
3104
    char        *id;
 
3105
    time_t       now;
 
3106
    struct tm   *now_x;
 
3107
    char        *hostpart = NULL;
 
3108
 
 
3109
    now   = time((time_t *)0);
 
3110
    now_x = localtime(&now);
 
3111
 
 
3112
    if(now_x->tm_sec == osec)
 
3113
      cnt++;
 
3114
    else{
 
3115
        cnt = 0;
 
3116
        osec = now_x->tm_sec;
 
3117
    }
 
3118
 
 
3119
    hostpart = F_ON(F_ROT13_MESSAGE_ID, ps_global)
 
3120
                 ? rot13(ps_global->hostname)
 
3121
                 : cpystr(ps_global->hostname);
 
3122
    
 
3123
    if(!hostpart)
 
3124
      hostpart = cpystr("huh");
 
3125
 
 
3126
    snprintf(idbuf, sizeof(idbuf), "<alpine.%.4s.%.20s.%02d%02d%02d%02d%02d%02d%X.%d@%.50s>",
 
3127
            SYSTYPE, ALPINE_VERSION, (now_x->tm_year) % 100, now_x->tm_mon + 1,
 
3128
            now_x->tm_mday, now_x->tm_hour, now_x->tm_min, now_x->tm_sec, 
 
3129
            cnt, getpid(), hostpart);
 
3130
    idbuf[sizeof(idbuf)-1] = '\0';
 
3131
 
 
3132
    id = cpystr(idbuf);
 
3133
 
 
3134
    if(hostpart)
 
3135
      fs_give((void **) &hostpart);
 
3136
 
 
3137
    return(id);
 
3138
}
 
3139
 
 
3140
 
 
3141
char *
 
3142
rot13(char *src)
 
3143
{
 
3144
    char byte, cap, *p, *ret = NULL;
 
3145
 
 
3146
    if(src && *src){
 
3147
        ret = (char *) fs_get((strlen(src)+1) * sizeof(char));
 
3148
        p = ret;
 
3149
        while(byte = *src++){
 
3150
            cap   = byte & 32;
 
3151
            byte &= ~cap;
 
3152
            *p++ = ((byte >= 'A') && (byte <= 'Z')
 
3153
                    ? ((byte - 'A' + 13) % 26 + 'A') : byte) | cap;
 
3154
        }
 
3155
 
 
3156
        *p = '\0';
 
3157
    }
 
3158
 
 
3159
    return(ret);
 
3160
}
 
3161
 
 
3162
 
 
3163
/*----------------------------------------------------------------------
 
3164
  Return the first true address pointer (modulo group syntax allowance)
 
3165
 
 
3166
  Args: addr  -- Address list
 
3167
 
 
3168
 Result: First real address pointer, or NULL
 
3169
  ----------------------------------------------------------------------*/
 
3170
ADDRESS *
 
3171
first_addr(struct mail_address *addr)
 
3172
{
 
3173
    while(addr && !addr->host)
 
3174
      addr = addr->next;
 
3175
 
 
3176
    return(addr);
 
3177
}
 
3178
 
 
3179
 
 
3180
/*----------------------------------------------------------------------
 
3181
           lit -- this is the source
 
3182
   prenewlines -- prefix the file contents with this many newlines
 
3183
  postnewlines -- postfix the file contents with this many newlines
 
3184
        is_sig -- this is a signature (not a template)
 
3185
  decode_constants -- change C-style constants into their values
 
3186
  ----*/
 
3187
char *
 
3188
get_signature_lit(char *lit, int prenewlines, int postnewlines, int is_sig, int decode_constants)
 
3189
{
 
3190
    char *sig = NULL;
 
3191
 
 
3192
    /*
 
3193
     * Should make this smart enough not to do the copying and double
 
3194
     * allocation of space.
 
3195
     */
 
3196
    if(lit){
 
3197
        char  *tmplit = NULL, *p, *q, *d, save;
 
3198
        size_t len;
 
3199
 
 
3200
        if(decode_constants){
 
3201
            tmplit = (char *) fs_get((strlen(lit)+1) * sizeof(char));
 
3202
            tmplit[0] = '\0';
 
3203
            cstring_to_string(lit, tmplit);
 
3204
        }
 
3205
        else
 
3206
          tmplit = cpystr(lit);
 
3207
 
 
3208
        len = strlen(tmplit) + 5 + (prenewlines+postnewlines) * strlen(NEWLINE);
 
3209
        sig = (char *) fs_get((len+1) * sizeof(char));
 
3210
        memset(sig, 0, len+1);
 
3211
        d = sig;
 
3212
        while(prenewlines--)
 
3213
          sstrncpy(&d, NEWLINE, len-(d-sig));
 
3214
 
 
3215
        if(is_sig && F_ON(F_ENABLE_SIGDASHES, ps_global) &&
 
3216
           !sigdashes_are_present(tmplit)){
 
3217
            sstrncpy(&d, SIGDASHES, len-(d-sig));
 
3218
            sstrncpy(&d, NEWLINE, len-(d-sig));
 
3219
        }
 
3220
 
 
3221
        sig[len] = '\0';
 
3222
 
 
3223
        p = tmplit;
 
3224
        while(*p){
 
3225
            /* get a line */
 
3226
            q = strpbrk(p, "\n\r");
 
3227
            if(q){
 
3228
                save = *q;
 
3229
                *q = '\0';
 
3230
            }
 
3231
 
 
3232
            /*
 
3233
             * Strip trailing space if we are doing a signature and
 
3234
             * this line is not sigdashes.
 
3235
             */
 
3236
            if(is_sig && strcmp(p, SIGDASHES))
 
3237
              removing_trailing_white_space(p);
 
3238
 
 
3239
            while((d-sig) <= len && (*d = *p++) != '\0')
 
3240
              d++;
 
3241
 
 
3242
            if(q){
 
3243
                if((d-sig) <= len)
 
3244
                  *d++ = save;
 
3245
                  
 
3246
                p = q+1;
 
3247
            }
 
3248
            else
 
3249
              break;
 
3250
        }
 
3251
 
 
3252
        while(postnewlines--)
 
3253
          sstrncpy(&d, NEWLINE, len-(d-sig));
 
3254
 
 
3255
        sig[len] = '\0';
 
3256
 
 
3257
        if((d-sig) <= len)
 
3258
          *d = '\0';
 
3259
 
 
3260
        if(tmplit)
 
3261
          fs_give((void **) &tmplit);
 
3262
    }
 
3263
 
 
3264
    return(sig);
 
3265
}
 
3266
 
 
3267
 
 
3268
int
 
3269
sigdashes_are_present(char *sig)
 
3270
{
 
3271
    char *p;
 
3272
 
 
3273
    p = srchstr(sig, SIGDASHES);
 
3274
    while(p && !((p == sig || (p[-1] == '\n' || p[-1] == '\r')) &&
 
3275
                 (p[3] == '\0' || p[3] == '\n' || p[3] == '\r')))
 
3276
      p = srchstr(p+1, SIGDASHES);
 
3277
    
 
3278
    return(p ? 1 : 0);
 
3279
}
 
3280
 
 
3281
 
 
3282
/*----------------------------------------------------------------------
 
3283
  Acquire the pinerc defined signature file pathname
 
3284
 
 
3285
  ----*/
 
3286
char *
 
3287
signature_path(char *sname, char *sbuf, size_t len)
 
3288
{
 
3289
    *sbuf = '\0';
 
3290
    if(sname && *sname){
 
3291
        size_t spl = strlen(sname);
 
3292
        if(IS_REMOTE(sname)){
 
3293
            if(spl < len - 1)
 
3294
              strncpy(sbuf, sname, len-1);
 
3295
        }
 
3296
        else if(is_absolute_path(sname)){
 
3297
            strncpy(sbuf, sname, len-1);
 
3298
            sbuf[len-1] = '\0';
 
3299
            fnexpand(sbuf, len);
 
3300
        }
 
3301
        else if(ps_global->VAR_OPER_DIR){
 
3302
            if(strlen(ps_global->VAR_OPER_DIR) + spl < len - 1)
 
3303
              build_path(sbuf, ps_global->VAR_OPER_DIR, sname, len);
 
3304
        }
 
3305
        else{
 
3306
            char *lc = last_cmpnt(ps_global->pinerc);
 
3307
 
 
3308
            sbuf[0] = '\0';
 
3309
            if(lc != NULL){
 
3310
                strncpy(sbuf,ps_global->pinerc,MIN(len-1,lc-ps_global->pinerc));
 
3311
                sbuf[MIN(len-1,lc-ps_global->pinerc)] = '\0';
 
3312
            }
 
3313
 
 
3314
            strncat(sbuf, sname, MAX(len-1-strlen(sbuf), 0));
 
3315
        }
 
3316
    }
 
3317
 
 
3318
    return(*sbuf ? sbuf : NULL);
 
3319
}
 
3320
 
 
3321
 
 
3322
char *
 
3323
read_remote_sigfile(char *name)
 
3324
{
 
3325
    int        try_cache;
 
3326
    REMDATA_S *rd;
 
3327
    char      *file = NULL;
 
3328
 
 
3329
 
 
3330
    dprint((7, "read_remote_sigfile \"%s\"\n", name ? name : "?"));
 
3331
 
 
3332
    /*
 
3333
     * We could parse the name here to find what type it is. So far we
 
3334
     * only have type RemImap.
 
3335
     */
 
3336
    rd = rd_create_remote(RemImap, name, (void *)REMOTE_SIG_SUBTYPE,
 
3337
                          NULL, _("Error: "), _("Can't fetch remote configuration."));
 
3338
    if(!rd)
 
3339
      goto bail_out;
 
3340
    
 
3341
    try_cache = rd_read_metadata(rd);
 
3342
 
 
3343
    if(rd->access == MaybeRorW){
 
3344
        if(rd->read_status == 'R')
 
3345
          rd->access = ReadOnly;
 
3346
        else
 
3347
          rd->access = ReadWrite;
 
3348
    }
 
3349
 
 
3350
    if(rd->access != NoExists){
 
3351
 
 
3352
        rd_check_remvalid(rd, 1L);
 
3353
 
 
3354
        /*
 
3355
         * If the cached info says it is readonly but
 
3356
         * it looks like it's been fixed now, change it to readwrite.
 
3357
         */
 
3358
        if(rd->read_status == 'R'){
 
3359
            /*
 
3360
             * We go to this trouble since readonly sigfiles
 
3361
             * are likely a mistake. They are usually supposed to be
 
3362
             * readwrite so we open it and check if it's been fixed.
 
3363
             */
 
3364
            rd_check_readonly_access(rd);
 
3365
            if(rd->read_status == 'W'){
 
3366
                rd->access = ReadWrite;
 
3367
                rd->flags |= REM_OUTOFDATE;
 
3368
            }
 
3369
            else
 
3370
              rd->access = ReadOnly;
 
3371
        }
 
3372
 
 
3373
        if(rd->flags & REM_OUTOFDATE){
 
3374
            if(rd_update_local(rd) != 0){
 
3375
 
 
3376
                dprint((1,
 
3377
                       "read_remote_sigfile: rd_update_local failed\n"));
 
3378
                /*
 
3379
                 * Don't give up altogether. We still may be
 
3380
                 * able to use a cached copy.
 
3381
                 */
 
3382
            }
 
3383
            else{
 
3384
                dprint((7,
 
3385
                       "%s: copied remote to local (%ld)\n",
 
3386
                       rd->rn ? rd->rn : "?", (long)rd->last_use));
 
3387
            }
 
3388
        }
 
3389
 
 
3390
        if(rd->access == ReadWrite)
 
3391
          rd->flags |= DO_REMTRIM;
 
3392
    }
 
3393
 
 
3394
    /* If we couldn't get to remote folder, try using the cached copy */
 
3395
    if(rd->access == NoExists || rd->flags & REM_OUTOFDATE){
 
3396
        if(try_cache){
 
3397
            rd->access = ReadOnly;
 
3398
            rd->flags |= USE_OLD_CACHE;
 
3399
            q_status_message(SM_ORDER, 3, 4,
 
3400
             "Can't contact remote sig server, using cached copy");
 
3401
            dprint((2,
 
3402
    "Can't open remote sigfile %s, using local cached copy %s readonly\n",
 
3403
                   rd->rn ? rd->rn : "?",
 
3404
                   rd->lf ? rd->lf : "?"));
 
3405
        }
 
3406
        else{
 
3407
            rd->flags &= ~DO_REMTRIM;
 
3408
            goto bail_out;
 
3409
        }
 
3410
    }
 
3411
 
 
3412
    file = read_file(rd->lf, 0);
 
3413
 
 
3414
bail_out:
 
3415
    if(rd)
 
3416
      rd_close_remdata(&rd);
 
3417
 
 
3418
    return(file);
 
3419
}
 
3420
 
 
3421
 
 
3422
/*----------------------------------------------------------------------
 
3423
  Build the body for the multipart/alternative part
 
3424
 
 
3425
    Args: 
 
3426
 
 
3427
  Result: 
 
3428
 
 
3429
  ----------------------------------------------------------------------*/
 
3430
BODY *
 
3431
forward_multi_alt(MAILSTREAM *stream, ENVELOPE *env, struct mail_bodystruct *orig_body,
 
3432
                  long int msgno, char *sect_prefix, void *msgtext, gf_io_t pc, int flags)
 
3433
{
 
3434
    BODY *body = NULL;
 
3435
    PART *part;
 
3436
    char  tmp_buf[256];
 
3437
    char *new_charset = NULL;
 
3438
    int   partnum;
 
3439
 
 
3440
    /* Pick out the interesting text piece */
 
3441
    for(part = orig_body->nested.part, partnum = 1;
 
3442
        part;
 
3443
        part = part->next, partnum++)
 
3444
      if((!part->body.type || part->body.type == TYPETEXT)
 
3445
         && (!part->body.subtype
 
3446
             || !strucmp(part->body.subtype, "plain")))
 
3447
        break;
 
3448
 
 
3449
    /*
 
3450
     * IF something's interesting insert it
 
3451
     * AND forget the rest of the multipart
 
3452
     */
 
3453
    if(part){
 
3454
        body                     =  mail_newbody();
 
3455
        body->type               = TYPETEXT;
 
3456
        body->contents.text.data = msgtext;
 
3457
 
 
3458
        /* record character set, flowing, etc */
 
3459
        body->parameter = copy_parameters(part->body.parameter);
 
3460
        body->size.bytes = part->body.size.bytes;
 
3461
 
 
3462
        if(!(flags & FWD_ANON)){
 
3463
            forward_delimiter(pc);
 
3464
            reply_forward_header(stream, msgno, sect_prefix, env, pc, "");
 
3465
        }
 
3466
 
 
3467
        snprintf(tmp_buf, sizeof(tmp_buf), "%.*s%s%s%d",
 
3468
                sizeof(tmp_buf)/2, sect_prefix ? sect_prefix : "",
 
3469
                sect_prefix ? "." : "", flags & FWD_NESTED ? "1." : "",
 
3470
                partnum);
 
3471
        tmp_buf[sizeof(tmp_buf)-1] = '\0';
 
3472
        get_body_part_text(stream, body, msgno, tmp_buf, 0L, pc, NULL, &new_charset);
 
3473
 
 
3474
        /*
 
3475
         * get_body_part_text translated the data to a new charset.
 
3476
         * We need to record that fact in body.
 
3477
         */
 
3478
        if(new_charset)
 
3479
          fix_charset_parameter(body, new_charset);
 
3480
    }
 
3481
    else
 
3482
      q_status_message(SM_ORDER | SM_DING, 3, 3,
 
3483
                       "No suitable part found.  Forwarding as attachment");
 
3484
 
 
3485
    return(body);
 
3486
}
 
3487
 
 
3488
 
 
3489
/*
 
3490
 * Change existing charset parameter to new value.
 
3491
 */
 
3492
void
 
3493
fix_charset_parameter(struct mail_bodystruct *body, char *new_charset)
 
3494
{
 
3495
    PARAMETER *pm;
 
3496
 
 
3497
    if(!body)
 
3498
      return;
 
3499
 
 
3500
    for(pm = body->parameter; pm; pm = pm->next)
 
3501
      if(pm->attribute && !strucmp(pm->attribute, "charset"))
 
3502
        break;
 
3503
 
 
3504
    if(pm){
 
3505
        if(pm->value)
 
3506
          fs_give((void **) &pm->value);
 
3507
 
 
3508
        if(new_charset)
 
3509
          pm->value = cpystr(new_charset);
 
3510
    }
 
3511
}
 
3512
 
 
3513
 
 
3514
void
 
3515
reply_append_addr(struct mail_address **dest, struct mail_address *src)
 
3516
{
 
3517
    for( ; *dest; dest = &(*dest)->next)
 
3518
      ;
 
3519
 
 
3520
    *dest = src;
 
3521
}