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

1 by Asheesh Laroia
Import upstream version 0.82+dfsg
1
#if !defined(lint) && !defined(DOS)
1.1.8 by Asheesh Laroia
Import upstream version 2.02
2
static char rcsid[] = "$Id: send.c 1204 2009-02-02 19:54:23Z hubert@u.washington.edu $";
1 by Asheesh Laroia
Import upstream version 0.82+dfsg
3
#endif
4
5
/*
6
 * ========================================================================
1.2.1 by Asheesh Laroia
Import upstream version 1.10+dfsg
7
 * Copyright 2006-2008 University of Washington
1.1.10 by Asheesh Laroia
Import upstream version 2.10+dfsg
8
 * Copyright 2013 Eduardo Chappa
1 by Asheesh Laroia
Import upstream version 0.82+dfsg
9
 *
10
 * Licensed under the Apache License, Version 2.0 (the "License");
11
 * you may not use this file except in compliance with the License.
12
 * You may obtain a copy of the License at
13
 *
14
 *     http://www.apache.org/licenses/LICENSE-2.0
15
 *
16
 * ========================================================================
17
 */
18
19
#include "../pith/headers.h"
20
#include "../pith/send.h"
21
#include "../pith/state.h"
22
#include "../pith/conf.h"
23
#include "../pith/store.h"
24
#include "../pith/mimedesc.h"
25
#include "../pith/context.h"
26
#include "../pith/status.h"
27
#include "../pith/folder.h"
28
#include "../pith/bldaddr.h"
29
#include "../pith/pipe.h"
30
#include "../pith/mailview.h"
31
#include "../pith/mailindx.h"
32
#include "../pith/list.h"
33
#include "../pith/filter.h"
34
#include "../pith/reply.h"
35
#include "../pith/addrstring.h"
36
#include "../pith/rfc2231.h"
37
#include "../pith/stream.h"
38
#include "../pith/util.h"
39
#include "../pith/adrbklib.h"
40
#include "../pith/options.h"
41
#include "../pith/busy.h"
42
#include "../pith/text.h"
43
#include "../pith/imap.h"
1.1.3 by Asheesh Laroia
Import upstream version 0.9999+dfsg
44
#include "../pith/ablookup.h"
1.1.5 by Asheesh Laroia
Import upstream version 1.0+dfsg
45
#include "../pith/sort.h"
1.1.7 by Asheesh Laroia
Import upstream version 2.00+dfsg
46
#include "../pith/smime.h"
1 by Asheesh Laroia
Import upstream version 0.82+dfsg
47
48
#include "../c-client/smtp.h"
49
#include "../c-client/nntp.h"
50
51
1.1.3 by Asheesh Laroia
Import upstream version 0.9999+dfsg
52
/* this is used in pine_send and pine_simple_send */
53
/* name::type::canedit::writehdr::localcopy::rcptto */
54
PINEFIELD pf_template[] = {
55
  {"X-Auth-Received",    FreeText,	0, 1, 1, 0},	/* N_AUTHRCVD */
56
  {"From",        Address,	0, 1, 1, 0},
57
  {"Reply-To",    Address,	0, 1, 1, 0},
58
  {TONAME,        Address,	1, 1, 1, 1},
59
  {CCNAME,        Address,	1, 1, 1, 1},
60
  {"bcc",         Address,	1, 0, 1, 1},
61
  {"Newsgroups",  FreeText,	1, 1, 1, 0},
62
  {"Fcc",         Fcc,		1, 0, 0, 0},
63
  {"Lcc",         Address,	1, 0, 1, 1},
64
  {"Attchmnt",    Attachment,	1, 1, 1, 0},
65
  {SUBJNAME,      Subject,	1, 1, 1, 0},
66
  {"References",  FreeText,	0, 1, 1, 0},
67
  {"Date",        FreeText,	0, 1, 1, 0},
68
  {"In-Reply-To", FreeText,	0, 1, 1, 0},
69
  {"Message-ID",  FreeText,	0, 1, 1, 0},
70
  {PRIORITYNAME,  FreeText,	0, 1, 1, 0},
1.1.4 by Asheesh Laroia
Import upstream version 0.99999+dfsg
71
  {"User-Agent",  FreeText,	0, 1, 1, 0},
1.1.3 by Asheesh Laroia
Import upstream version 0.9999+dfsg
72
  {"To",          Address,	0, 0, 0, 0},	/* N_NOBODY */
73
  {"X-Post-Error",FreeText,	0, 0, 0, 0},	/* N_POSTERR */
74
  {"X-Reply-UID", FreeText,	0, 0, 0, 0},	/* N_RPLUID */
75
  {"X-Reply-Mbox",FreeText,	0, 0, 0, 0},	/* N_RPLMBOX */
76
  {"X-SMTP-Server",FreeText,	0, 0, 0, 0},	/* N_SMTP */
77
  {"X-NNTP-Server",FreeText,	0, 0, 0, 0},	/* N_NNTP */
78
  {"X-Cursor-Pos",FreeText,	0, 0, 0, 0},	/* N_CURPOS */
79
  {"X-Our-ReplyTo",FreeText,	0, 0, 0, 0},	/* N_OURREPLYTO */
80
  {OUR_HDRS_LIST, FreeText,	0, 0, 0, 0},	/* N_OURHDRS */
81
#if	!(defined(DOS) || defined(OS2)) || defined(NOAUTH)
82
  {"X-X-Sender",    Address,	0, 1, 1, 0},
83
#endif
84
  {NULL,         FreeText}
85
};
86
87
88
PRIORITY_S priorities[] = {
89
    {1, "Highest"},
90
    {2, "High"},
91
    {3, "Normal"},
92
    {4, "Low"},
93
    {5, "Lowest"},
94
    {0, NULL}
95
};
96
1 by Asheesh Laroia
Import upstream version 0.82+dfsg
97
98
#define ctrl(c) ((c) & 0x1f)
99
100
/* which message part to test for xliteration */
101
typedef enum {MsgBody, HdrText} MsgPart;
102
103
104
/*
105
 * Internal prototypes
106
 */
107
long       post_rfc822_output(char *, ENVELOPE *, BODY *, soutr_t, TCPSTREAM *, long);
108
int        l_flush_net(int);
109
int        l_putc(int);
110
int	   pine_write_header_line(char *, char *, STORE_S *);
111
int	   pine_write_params(PARAMETER *, STORE_S *);
112
char      *tidy_smtp_mess(char *, char *, char *, size_t);
113
int	   lmc_body_header_line(char *, int);
114
int	   lmc_body_header_finish(void);
115
int	   pwbh_finish(int, STORE_S *);
116
int	   sent_percent(void);
1.1.4 by Asheesh Laroia
Import upstream version 0.99999+dfsg
117
unsigned short *setup_avoid_table(void);
1 by Asheesh Laroia
Import upstream version 0.82+dfsg
118
#ifndef	_WINDOWS
1.1.3 by Asheesh Laroia
Import upstream version 0.9999+dfsg
119
int	   mta_handoff(METAENV *, BODY *, char *, size_t, void (*)(char *, int),
120
		       void (*)(PIPE_S *, int, void *));
121
char	  *post_handoff(METAENV *, BODY *, char *, size_t, void (*)(char *, int),
122
			void (*)(PIPE_S *, int, void *));
123
char	  *mta_parse_post(METAENV *, BODY *, char *, char *, size_t, void (*)(char *, int),
124
			  void (*)(PIPE_S *, int, void *));
1 by Asheesh Laroia
Import upstream version 0.82+dfsg
125
long	   pine_pipe_soutr_nl(void *, char *);
126
#endif
127
char	  *smtp_command(char *, size_t);
128
void	  *piped_smtp_open(char *, char *, unsigned long);
129
void	  *piped_aopen(NETMBX *, char *, char *);
130
long	   piped_soutr(void *, char *);
131
long	   piped_sout(void *, char *, unsigned long);
132
char	  *piped_getline(void *);
133
void	   piped_close(void *);
134
void	   piped_abort(void *);
135
char	  *piped_host(void *);
136
unsigned long piped_port(void *);
1.1.3 by Asheesh Laroia
Import upstream version 0.9999+dfsg
137
char	  *posting_characterset(void *, char *, MsgPart);
1 by Asheesh Laroia
Import upstream version 0.82+dfsg
138
int        body_is_translatable(void *, char *);
139
int        text_is_translatable(void *, char *);
140
int        dummy_putc(int);
1.1.5 by Asheesh Laroia
Import upstream version 1.0+dfsg
141
unsigned long *init_charsetchecker(char *);
142
int        representable_in_charset(unsigned long, char *);
143
char      *most_preferred_charset(unsigned long);
1 by Asheesh Laroia
Import upstream version 0.82+dfsg
144
145
/* 
146
 * Storage object where the FCC (or postponed msg) is to be written.
147
 * This is amazingly bogus.  Much work was done to put messages 
148
 * together and encode them as they went to the tmp file for sendmail
149
 * or into the SMTP slot (especially for for DOS, to prevent a temporary
150
 * file (and needlessly copying the message).
151
 * 
152
 * HOWEVER, since there's no piping into c-client routines
153
 * (particularly mail_append() which copies the fcc), the fcc will have
154
 * to be copied to disk.  This global tells pine's copy of the rfc822
155
 * output functions where to also write the message bytes for the fcc.
156
 * With piping in the c-client we could just have two pipes to shove
157
 * down rather than messing with damn copies.  FIX THIS!
158
 *
159
 * The function open_fcc, locates the actual folder and creates it if
160
 * requested before any mailing or posting is done.
161
 */
162
struct local_message_copy lmc;
163
164
165
/*
166
 * Locally global pointer to stream used for sending/posting.
167
 * It's also used to indicate when/if we write the Bcc: field in
168
 * the header.
169
 */
170
static SENDSTREAM *sending_stream = NULL;
171
172
173
static struct hooks {
174
    void	*rfc822_out;			/* Message outputter */
175
} sending_hooks;
176
177
178
static FILE	  *verbose_send_output = NULL;
179
static long	   send_bytes_sent, send_bytes_to_send;
180
static METAENV	  *send_header = NULL;
181
182
/*
183
 * Hooks for prompts and confirmations
184
 */
185
int (*pith_opt_daemon_confirm)(void);
186
187
188
static NETDRIVER piped_io = {
189
    piped_smtp_open,		/* open a connection */
190
    piped_aopen,		/* open an authenticated connection */
191
    piped_getline,		/* get a line */
192
    NULL,			/* get a buffer */
193
    piped_soutr,		/* output pushed data */
194
    piped_sout,			/* output string */
195
    piped_close,		/* close connection */
196
    piped_host,			/* return host name */
197
    piped_host,			/* remotehost */
198
    piped_port,			/* return port number */
199
    piped_host			/* return local host (NOTE: same as host!) */
200
};
201
202
203
/*
204
 * Since c-client preallocates, it's necessary here to define a limit
205
 * such that we don't blow up in c-client (see rfc822_address_line()).
206
 */
207
#define	MAX_SINGLE_ADDR	MAILTMPLEN
208
1.1.4 by Asheesh Laroia
Import upstream version 0.99999+dfsg
209
#define AVOID_2022_JP_FOR_PUNC "AVOID_2022_JP_FOR_PUNC"
210
1 by Asheesh Laroia
Import upstream version 0.82+dfsg
211
212
/*
213
 * Phone home hash controls
214
 */
215
#define PH_HASHBITS	24
216
#define PH_MAXHASH	(1<<(PH_HASHBITS))
217
218
219
/*
220
 * postponed_stream - return stream associated with postponed messages
221
 *                    in argument.
222
 */
223
int
1.1.5 by Asheesh Laroia
Import upstream version 1.0+dfsg
224
postponed_stream(MAILSTREAM **streamp, char *mbox, char *type, int checknmsgs)
1 by Asheesh Laroia
Import upstream version 0.82+dfsg
225
{
226
    MAILSTREAM *stream = NULL;
227
    CONTEXT_S  *p_cntxt = NULL;
1.1.5 by Asheesh Laroia
Import upstream version 1.0+dfsg
228
    char       *p, *q, tmp[MAILTMPLEN], *fullname = NULL;
1 by Asheesh Laroia
Import upstream version 0.82+dfsg
229
    int	        exists;
230
1.1.5 by Asheesh Laroia
Import upstream version 1.0+dfsg
231
    if(!(streamp && mbox))
1 by Asheesh Laroia
Import upstream version 0.82+dfsg
232
      return(0);
233
234
    *streamp = NULL;
235
236
    /*
237
     * find default context to look for folder...
238
     *
239
     * The "mbox" is assumed to be local if we're given what looks
240
     * like an absolute path.  This is different from Goto/Save
241
     * where we do alot of work to interpret paths relative to the
242
     * server.  This reason is to support all the pre-4.00 pinerc'
243
     * that specified a path and because there's yet to be a way
244
     * in c-client to specify otherwise in the face of a remote
245
     * context.
246
     */
1.1.5 by Asheesh Laroia
Import upstream version 1.0+dfsg
247
    if(!is_absolute_path(mbox)
1 by Asheesh Laroia
Import upstream version 0.82+dfsg
248
       && !(p_cntxt = default_save_context(ps_global->context_list)))
249
      p_cntxt = ps_global->context_list;
250
251
    /* check to see if the folder exists, the user wants to continue
252
     * and that we can actually read something in...
253
     */
254
    exists = folder_name_exists(p_cntxt, mbox, &fullname);
255
    if(fullname)
256
      mbox = fullname;
257
258
    if(exists & FEX_ISFILE){
259
	context_apply(tmp, p_cntxt, mbox, sizeof(tmp));
260
	if(!(IS_REMOTE(tmp) || is_absolute_path(tmp))){
261
	    /*
262
	     * The mbox is relative to the home directory.
263
	     * Make it absolute so we can compare it to
264
	     * stream->mailbox.
265
	     */
266
	    build_path(tmp_20k_buf, ps_global->ui.homedir, tmp,
267
		       SIZEOF_20KBUF);
268
	    strncpy(tmp, tmp_20k_buf, sizeof(tmp));
269
	    tmp[sizeof(tmp)-1] = '\0';
270
	}
271
272
	if((stream = ps_global->mail_stream)
273
	   && !(stream->mailbox
274
		&& ((*tmp != '{' && !strcmp(tmp, stream->mailbox))
275
		    || (*tmp == '{'
276
			&& same_stream(tmp, stream)
277
			&& (p = strchr(tmp, '}'))
278
			&& (q = strchr(stream->mailbox,'}'))
279
			&& !strcmp(p + 1, q + 1)))))
280
	  stream = NULL;
281
1.1.5 by Asheesh Laroia
Import upstream version 1.0+dfsg
282
	if(!stream){
283
	    stream = context_open(p_cntxt, NULL, mbox,
284
				  SP_USEPOOL|SP_TEMPUSE, NULL);
285
	    if(stream && !stream->halfopen){
286
		if(stream->nmsgs > 0)
287
		  refresh_sort(stream, sp_msgmap(stream), SRT_NON);
288
289
		if(checknmsgs && stream->nmsgs < 1){
290
		    pine_mail_close(stream);
291
		    exists = 0;
292
		    stream = NULL;
293
		}
294
	    }
295
	    else{
296
		q_status_message2(SM_ORDER | SM_DING, 3, 3,
297
				  _("Can't open %s mailbox: %s"), type, mbox);
298
		if(stream)
299
		  pine_mail_close(stream);
300
301
		exists = 0;
302
		stream = NULL;
303
	    }
304
	}
305
    }
306
    else{
307
	if(F_ON(F_ALT_COMPOSE_MENU, ps_global)){
1 by Asheesh Laroia
Import upstream version 0.82+dfsg
308
	    q_status_message1(SM_ORDER | SM_DING, 3, 3,
1.1.5 by Asheesh Laroia
Import upstream version 1.0+dfsg
309
			     _("%s message folder doesn't exist!"), type);
1 by Asheesh Laroia
Import upstream version 0.82+dfsg
310
	}
311
    }
312
313
    if(fullname)
314
      fs_give((void **) &fullname);
315
316
    *streamp = stream;
317
318
    return(exists);
319
}
320
321
322
int
323
redraft_work(MAILSTREAM **streamp, long int cont_msg, ENVELOPE **outgoing,
324
	     struct mail_bodystruct **body, char **fcc, char **lcc,
325
	     REPLY_S **reply, REDRAFT_POS_S **redraft_pos, PINEFIELD **custom,
326
	     ACTION_S **role, int flags, STORE_S *so)
327
{
328
    MAILSTREAM	*stream;
329
    ENVELOPE	*e = NULL;
330
    BODY	*b;
331
    PART        *part;
332
    PINEFIELD   *pf;
333
    gf_io_t	 pc;
334
    char	*extras, **fields, **values, *p;
335
    char        *hdrs[2], *h, *charset;
336
    char       **smtp_servers = NULL, **nntp_servers = NULL;
337
    int		 i, pine_generated = 0, our_replyto = 0;
338
    int          added_to_role = 0;
1.1.7 by Asheesh Laroia
Import upstream version 2.00+dfsg
339
    unsigned	 gbpt_flags = GBPT_NONE;
1 by Asheesh Laroia
Import upstream version 0.82+dfsg
340
    MESSAGECACHE *mc;
341
342
    if(!(streamp && *streamp))
343
      return(redraft_cleanup(streamp, TRUE, flags));
344
345
    stream = *streamp;
346
1.1.7 by Asheesh Laroia
Import upstream version 2.00+dfsg
347
    if(flags & REDRAFT_HTML)
348
      gbpt_flags |= GBPT_HTML_OK;
349
1 by Asheesh Laroia
Import upstream version 0.82+dfsg
350
    /* grok any user-defined or non-c-client headers */
1.1.3 by Asheesh Laroia
Import upstream version 0.9999+dfsg
351
    if((e = pine_mail_fetchstructure(stream, cont_msg, &b)) != NULL){
1 by Asheesh Laroia
Import upstream version 0.82+dfsg
352
353
	/*
354
	 * The custom headers to look for in the suspended message should
355
	 * have been stored in the X-Our-Headers header. So first we get
356
	 * that list. If we can't find it (version that stored the
357
	 * message < 4.30) then we use the global custom list.
358
	 */
359
	hdrs[0] = OUR_HDRS_LIST;
360
	hdrs[1] = NULL;
361
	if((h = pine_fetchheader_lines(stream, cont_msg, NULL, hdrs)) != NULL){
362
	    int    commas = 0;
363
	    char **list;
364
	    char  *hdrval = NULL;
365
366
	    if((hdrval = strindex(h, ':')) != NULL){
367
		for(hdrval++; *hdrval && isspace((unsigned char)*hdrval); 
368
		    hdrval++)
369
		  ;
370
	    }
371
372
	    /* count elements in list */
373
	    for(p = hdrval; p && *p; p++)
374
	      if(*p == ',')
375
		commas++;
376
377
	    if(hdrval && (list = parse_list(hdrval,commas+1,0,NULL)) != NULL){
378
		
379
		*custom = parse_custom_hdrs(list, Replace);
380
		add_defaults_from_list(*custom,
381
				       ps_global->VAR_CUSTOM_HDRS);
382
		free_list_array(&list);
383
	    }
384
385
	    if(*custom && !(*custom)->name){
386
		free_customs(*custom);
387
		*custom = NULL;
388
	    }
389
390
	    fs_give((void **)&h);
391
	}
392
393
	if(!*custom)
394
	  *custom = parse_custom_hdrs(ps_global->VAR_CUSTOM_HDRS, UseAsDef);
395
396
#define INDEX_FCC         0
397
#define INDEX_POSTERR     1
398
#define INDEX_REPLYUID    2
399
#define INDEX_REPLYMBOX   3
400
#define INDEX_SMTP        4
401
#define INDEX_NNTP        5
402
#define INDEX_CURSORPOS   6
403
#define INDEX_OUR_REPLYTO 7
404
#define INDEX_LCC         8	/* MUST REMAIN LAST FIELD DECLARED */
405
#define FIELD_COUNT       9
406
407
	i = count_custom_hdrs_pf(*custom,1) + FIELD_COUNT + 1;
408
409
	/*
410
	 * Having these two fields separated isn't the slickest, but
411
	 * converting the pointer array for fetchheader_lines() to
412
	 * a list of structures or some such for simple_header_parse()
413
	 * is too goonie.  We could do something like re-use c-client's
414
	 * PARAMETER struct which is a simple char * pairing, but that
415
	 * doesn't make sense to pass to fetchheader_lines()...
416
	 */
417
	fields = (char **) fs_get((size_t) i * sizeof(char *));
418
	values = (char **) fs_get((size_t) i * sizeof(char *));
419
	memset(fields, 0, (size_t) i * sizeof(char *));
420
	memset(values, 0, (size_t) i * sizeof(char *));
421
422
	fields[i = INDEX_FCC] = "Fcc";	/* Fcc: special case */
423
	fields[++i]   = "X-Post-Error";	/* posting errors too */
424
	fields[++i]   = "X-Reply-UID";	/* Reply'd to msg's UID */
425
	fields[++i]   = "X-Reply-Mbox";	/* Reply'd to msg's Mailbox */
426
	fields[++i]   = "X-SMTP-Server";/* SMTP server to use */
427
	fields[++i]   = "X-NNTP-Server";/* NNTP server to use */
428
	fields[++i]   = "X-Cursor-Pos";	/* Cursor position */
429
	fields[++i]   = "X-Our-ReplyTo";	/* ReplyTo is real */
430
	fields[++i]   = "Lcc";		/* Lcc: too... */
431
	if(++i != FIELD_COUNT)
432
	  panic("Fix FIELD_COUNT");
433
434
	for(pf = *custom; pf && pf->name; pf = pf->next)
435
	  if(!pf->standard)
436
	    fields[i++] = pf->name;		/* assign custom fields */
437
1.1.3 by Asheesh Laroia
Import upstream version 0.9999+dfsg
438
	if((extras = pine_fetchheader_lines(stream, cont_msg, NULL,fields)) != NULL){
1 by Asheesh Laroia
Import upstream version 0.82+dfsg
439
	    simple_header_parse(extras, fields, values);
440
	    fs_give((void **) &extras);
441
442
	    /*
443
	     * translate RFC 1522 strings,
444
	     * starting with "Lcc" field
445
	     */
446
	    for(i = INDEX_LCC; fields[i]; i++)
447
	      if(values[i]){
448
		  size_t len;
449
		  char *bufp, *biggerbuf = NULL;
450
  
451
		  if((len=4*strlen(values[i])) > SIZEOF_20KBUF-1){
452
		      len++;
453
		      biggerbuf = (char *)fs_get(len * sizeof(char));
454
		      bufp = biggerbuf;
455
		  }
456
		  else{
457
		      bufp = tmp_20k_buf;
458
		      len = SIZEOF_20KBUF;
459
		  }
460
  
1.1.1 by Asheesh Laroia
Import upstream version 0.98+dfsg
461
		  p = (char *)rfc1522_decode_to_utf8((unsigned char*)bufp, len, values[i]);
1 by Asheesh Laroia
Import upstream version 0.82+dfsg
462
463
		  if(p == tmp_20k_buf){
464
		      fs_give((void **)&values[i]);
465
		      values[i] = cpystr(p);
466
		  }
467
468
		  if(biggerbuf)
469
		    fs_give((void **)&biggerbuf);
470
	      }
471
472
	    for(pf = *custom, i = FIELD_COUNT;
473
		pf && pf->name;
474
		pf = pf->next){
475
		if(pf->standard){
476
		    /*
477
		     * Because the value is already in the envelope.
478
		     */
479
		    pf->cstmtype = NoMatch;
480
		    continue;
481
		}
482
483
		if(values[i]){	/* use this instead of default */
484
		    if(pf->textbuf)
485
		      fs_give((void **)&pf->textbuf);
486
487
		    pf->textbuf = values[i]; /* freed in pine_send! */
488
		}
489
		else if(pf->textbuf)  /* was erased before postpone */
490
		  fs_give((void **)&pf->textbuf);
491
		
492
		i++;
493
	    }
494
495
	    if(values[INDEX_FCC])		/* If "Fcc:" was there...  */
496
	      pine_generated = 1;		/* we put it there? */
497
498
	    /*
499
	     * Since c-client fills in the reply_to field in the envelope
500
	     * even if there isn't a Reply-To header in the message we
501
	     * have to work around that. When we postpone we add
502
	     * a second header that has value "Empty" if there really
503
	     * was a Reply-To and it was empty. It has the
504
	     * value "Full" if we put the Reply-To contents there
505
	     * intentionally (and it isn't empty).
506
	     */
507
	    if(values[INDEX_OUR_REPLYTO]){
508
		if(values[INDEX_OUR_REPLYTO][0] == 'E')
509
		  our_replyto = 'E';  /* we put an empty one there */
510
		else if(values[INDEX_OUR_REPLYTO][0] == 'F')
511
		  our_replyto = 'F';  /* we put it there */
512
513
		fs_give((void **) &values[INDEX_OUR_REPLYTO]);
514
	    }
515
516
	    if(fcc)				/* fcc: special case... */
517
	      *fcc = values[INDEX_FCC] ? values[INDEX_FCC] : cpystr("");
518
	    else if(values[INDEX_FCC])
519
	      fs_give((void **) &values[INDEX_FCC]);
520
521
	    if(values[INDEX_POSTERR]){		/* x-post-error?!?1 */
522
		q_status_message(SM_ORDER|SM_DING, 4, 4,
523
				 values[INDEX_POSTERR]);
524
		fs_give((void **) &values[INDEX_POSTERR]);
525
	    }
526
527
	    if(values[INDEX_REPLYUID]){
528
		if(reply)
529
		  *reply = build_reply_uid(values[INDEX_REPLYUID]);
530
531
		fs_give((void **) &values[INDEX_REPLYUID]);
532
533
		if(values[INDEX_REPLYMBOX] && reply && *reply)
534
		  (*reply)->origmbox = cpystr(values[INDEX_REPLYMBOX]);
535
		
536
		if(reply && *reply && !(*reply)->origmbox && (*reply)->mailbox)
537
		  (*reply)->origmbox = cpystr((*reply)->mailbox);
538
	    }
539
540
	    if(values[INDEX_REPLYMBOX])
541
	      fs_give((void **) &values[INDEX_REPLYMBOX]);
542
543
	    if(values[INDEX_SMTP]){
544
		char  *q;
545
		size_t cnt = 0;
546
547
		/*
548
		 * Turn the space delimited list of smtp servers into
549
		 * a char ** list.
550
		 */
551
		p = values[INDEX_SMTP];
552
		do{
553
		    if(!*p || isspace((unsigned char) *p))
554
		      cnt++;
555
		} while(*p++);
556
		
557
		smtp_servers = (char **) fs_get((cnt+1) * sizeof(char *));
558
		memset(smtp_servers, 0, (cnt+1) * sizeof(char *));
559
560
		cnt = 0;
561
		q = p = values[INDEX_SMTP];
562
		do{
563
		    if(!*p || isspace((unsigned char) *p)){
564
			if(*p){
565
			    *p = '\0';
566
			    smtp_servers[cnt++] = cpystr(q);
567
			    *p = ' ';
568
			    q = p+1;
569
			}
570
			else
571
			  smtp_servers[cnt++] = cpystr(q);
572
		    }
573
		} while(*p++);
574
575
		fs_give((void **) &values[INDEX_SMTP]);
576
	    }
577
578
	    if(values[INDEX_NNTP]){
579
		char  *q;
580
		size_t cnt = 0;
581
582
		/*
583
		 * Turn the space delimited list of smtp nntp into
584
		 * a char ** list.
585
		 */
586
		p = values[INDEX_NNTP];
587
		do{
588
		    if(!*p || isspace((unsigned char) *p))
589
		      cnt++;
590
		} while(*p++);
591
		
592
		nntp_servers = (char **) fs_get((cnt+1) * sizeof(char *));
593
		memset(nntp_servers, 0, (cnt+1) * sizeof(char *));
594
595
		cnt = 0;
596
		q = p = values[INDEX_NNTP];
597
		do{
598
		    if(!*p || isspace((unsigned char) *p)){
599
			if(*p){
600
			    *p = '\0';
601
			    nntp_servers[cnt++] = cpystr(q);
602
			    *p = ' ';
603
			    q = p+1;
604
			}
605
			else
606
			  nntp_servers[cnt++] = cpystr(q);
607
		    }
608
		} while(*p++);
609
610
		fs_give((void **) &values[INDEX_NNTP]);
611
	    }
612
613
	    if(values[INDEX_CURSORPOS]){
614
		/*
615
		 * The redraft cursor position is written as two fields
616
		 * separated by a space. First comes the name of the
617
		 * header field we're in, or just a ":" if we're in the
618
		 * body. Then comes the offset into that header or into
619
		 * the body.
620
		 */
621
		if(redraft_pos){
622
		    char *q1, *q2;
623
624
		    *redraft_pos
625
			      = (REDRAFT_POS_S *)fs_get(sizeof(REDRAFT_POS_S));
626
		    (*redraft_pos)->offset = 0L;
627
628
		    q1 = skip_white_space(values[INDEX_CURSORPOS]);
629
		    if(*q1 && (q2 = strindex(q1, SPACE))){
630
			*q2 = '\0';
631
			(*redraft_pos)->hdrname = cpystr(q1);
632
			q1 = skip_white_space(q2+1);
633
			if(*q1)
634
			  (*redraft_pos)->offset = atol(q1);
635
		    }
636
		    else
637
		      (*redraft_pos)->hdrname = cpystr(":");
638
		}
639
640
		fs_give((void **) &values[INDEX_CURSORPOS]);
641
	    }
642
643
	    if(lcc)
644
	      *lcc = values[INDEX_LCC];
645
	    else
646
	      fs_give((void **) &values[INDEX_LCC]);
647
	}
648
649
	fs_give((void **)&fields);
650
	fs_give((void **)&values);
651
652
	*outgoing = copy_envelope(e);
653
654
	/*
655
	 * If the postponed message has a From which is different from
656
	 * the default, it is either because allow-changing-from is on
657
	 * or because there was a role with a from that allowed it to happen.
658
	 * If allow-changing-from is not on, put this back in a role
659
	 * so that it will be allowed again in pine_send.
660
	 */
661
	if(role && *role == NULL &&
662
	   !ps_global->never_allow_changing_from &&
663
	   *outgoing){
664
	    /*
665
	     * Now check to see if the from is different from default from.
666
	     */
667
	    ADDRESS *deffrom;
668
669
	    deffrom = generate_from();
670
	    if(!((*outgoing)->from &&
671
		 address_is_same(deffrom, (*outgoing)->from) &&
672
	         ((!(deffrom->personal && deffrom->personal[0]) &&
673
		   !((*outgoing)->from->personal &&
674
		     (*outgoing)->from->personal[0])) ||
675
		  (deffrom->personal && (*outgoing)->from->personal &&
676
		   !strcmp(deffrom->personal, (*outgoing)->from->personal))))){
677
678
		*role = (ACTION_S *)fs_get(sizeof(**role));
679
		memset((void *)*role, 0, sizeof(**role));
680
		if(!(*outgoing)->from)
681
		  (*outgoing)->from = mail_newaddr();
682
683
		(*role)->from = (*outgoing)->from;
684
		(*outgoing)->from = NULL;
685
		added_to_role++;
686
	    }
687
688
	    mail_free_address(&deffrom);
689
	}
690
691
	/*
692
	 * Look at each empty address and see if the user has specified
693
	 * a default for that field or not.  If they have, that means
694
	 * they have erased it before postponing, so they won't want
695
	 * the default to come back.  If they haven't specified a default,
696
	 * then the default should be generated in pine_send.  We prevent
697
	 * the default from being assigned by assigning an empty address
698
	 * to the variable here.
699
	 *
700
	 * BUG: We should do this for custom Address headers, too, but
701
	 * there isn't such a thing yet.
702
	 */
703
	if(!(*outgoing)->to && hdr_is_in_list("to", *custom))
704
	  (*outgoing)->to = mail_newaddr();
705
	if(!(*outgoing)->cc && hdr_is_in_list("cc", *custom))
706
	  (*outgoing)->cc = mail_newaddr();
707
	if(!(*outgoing)->bcc && hdr_is_in_list("bcc", *custom))
708
	  (*outgoing)->bcc = mail_newaddr();
709
710
	if(our_replyto == 'E'){
711
	    /* user erased reply-to before postponing */
712
	    if((*outgoing)->reply_to)
713
	      mail_free_address(&(*outgoing)->reply_to);
714
715
	    /*
716
	     * If empty is not the normal default, make the outgoing
717
	     * reply_to be an emtpy address. If it is default, leave it
718
	     * as NULL and the default will be used.
719
	     */
720
	    if(hdr_is_in_list("reply-to", *custom)){
721
		PINEFIELD pf;
722
723
		pf.name = "reply-to";
724
		set_default_hdrval(&pf, *custom);
725
		if(pf.textbuf){
726
		    if(pf.textbuf[0])	/* empty is not default */
727
		      (*outgoing)->reply_to = mail_newaddr();
728
729
		    fs_give((void **)&pf.textbuf);
730
		}
731
	    }
732
	}
733
	else if(our_replyto == 'F'){
734
	    int add_to_role = 0;
735
736
	    /*
737
	     * The reply-to is real. If it is different from the default
738
	     * reply-to, put it in the role so that it will show up when
739
	     * the user edits.
740
	     */
741
	    if(hdr_is_in_list("reply-to", *custom)){
742
		PINEFIELD pf;
743
		char *str;
744
745
		pf.name = "reply-to";
746
		set_default_hdrval(&pf, *custom);
747
		if(pf.textbuf && pf.textbuf[0]){
1.1.3 by Asheesh Laroia
Import upstream version 0.9999+dfsg
748
		    if((str = addr_list_string((*outgoing)->reply_to,NULL,1)) != NULL){
1 by Asheesh Laroia
Import upstream version 0.82+dfsg
749
			if(!strcmp(str, pf.textbuf)){
750
			    /* standard value, leave it alone */
751
			    ;
752
			}
753
			else  /* not standard, put in role */
754
			  add_to_role++;
755
756
			fs_give((void **)&str);
757
		    }
758
		}
759
		else  /* not standard, put in role */
760
		  add_to_role++;
761
762
		if(pf.textbuf)
763
		  fs_give((void **)&pf.textbuf);
764
	    }
765
	    else  /* not standard, put in role */
766
	      add_to_role++;
767
768
	    if(add_to_role && role && (*role == NULL || added_to_role)){
769
		if(*role == NULL){
770
		    added_to_role++;
771
		    *role = (ACTION_S *)fs_get(sizeof(**role));
772
		    memset((void *)*role, 0, sizeof(**role));
773
		}
774
775
		(*role)->replyto = (*outgoing)->reply_to;
776
		(*outgoing)->reply_to = NULL;
777
	    }
778
	}
779
	else{
780
	    /* this is a bogus c-client generated replyto */
781
	    if((*outgoing)->reply_to)
782
	      mail_free_address(&(*outgoing)->reply_to);
783
	}
784
785
	if((smtp_servers || nntp_servers)
786
	   && role && (*role == NULL || added_to_role)){
787
	    if(*role == NULL){
788
		*role = (ACTION_S *)fs_get(sizeof(**role));
789
		memset((void *)*role, 0, sizeof(**role));
790
	    }
791
792
	    if(smtp_servers)
793
	      (*role)->smtp = smtp_servers;
794
	    if(nntp_servers)
795
	      (*role)->nntp = nntp_servers;
796
	}
797
798
	if(!(*outgoing)->subject && hdr_is_in_list("subject", *custom))
799
	  (*outgoing)->subject = cpystr("");
800
801
	if(!pine_generated){
802
	    /*
803
	     * Now, this is interesting.  We should have found 
804
	     * the "fcc:" field if pine wrote the message being
805
	     * redrafted.  Hence, we probably can't trust the 
806
	     * "originator" type fields, so we'll blast them and let
807
	     * them get set later in pine_send.  This should allow
808
	     * folks with custom or edited From's and such to still
809
	     * use redraft reasonably, without inadvertently sending
810
	     * messages that appear to be "From" others...
811
	     */
812
	    if((*outgoing)->from)
813
	      mail_free_address(&(*outgoing)->from);
814
815
	    /*
816
	     * Ditto for Reply-To and Sender...
817
		 */
818
	    if((*outgoing)->reply_to)
819
	      mail_free_address(&(*outgoing)->reply_to);
820
821
	    if((*outgoing)->sender)
822
	      mail_free_address(&(*outgoing)->sender);
823
	}
824
825
	if(!pine_generated || !(flags & REDRAFT_DEL)){
826
827
	    /*
828
	     * Generate a fresh message id for pretty much the same
829
	     * reason From and such got wacked...
830
	     * Also, if we're coming from a form letter, we need to
831
	     * generate a different id each time.
832
	     */
833
	    if((*outgoing)->message_id)
834
	      fs_give((void **)&(*outgoing)->message_id);
835
836
	    (*outgoing)->message_id = generate_message_id();
837
	}
838
1.1.3 by Asheesh Laroia
Import upstream version 0.9999+dfsg
839
	if(b && b->type != TYPETEXT){
1 by Asheesh Laroia
Import upstream version 0.82+dfsg
840
	  if(b->type == TYPEMULTIPART){
841
	      if(strucmp(b->subtype, "mixed")){
842
		  q_status_message1(SM_INFO, 3, 4, 
843
			     "Converting Multipart/%s to Multipart/Mixed", 
844
				 b->subtype);
845
		  fs_give((void **)&b->subtype);
846
		  b->subtype = cpystr("mixed");
847
	      }
848
	  }
849
	  else{
850
	      q_status_message2(SM_ORDER | SM_DING, 3, 4, 
851
				"Unable to resume type %s/%s message",
852
				body_types[b->type], b->subtype);
853
	      return(redraft_cleanup(streamp, TRUE, flags));
854
	  }
1.1.3 by Asheesh Laroia
Import upstream version 0.9999+dfsg
855
	}
1 by Asheesh Laroia
Import upstream version 0.82+dfsg
856
		
857
	gf_set_so_writec(&pc, so);
858
859
	if(b && b->type != TYPETEXT){	/* already TYPEMULTIPART */
860
	    *body			   = copy_body(NULL, b);
861
	    part			   = (*body)->nested.part;
862
	    part->body.contents.text.data = (void *)so;
1.1.1 by Asheesh Laroia
Import upstream version 0.98+dfsg
863
	    set_mime_type_by_grope(&part->body);
1 by Asheesh Laroia
Import upstream version 0.82+dfsg
864
	    if(part->body.type != TYPETEXT){
865
		q_status_message2(SM_ORDER | SM_DING, 3, 4,
866
		      "Unable to resume; first part is non-text: %s/%s",
867
				  body_types[part->body.type], 
868
				  part->body.subtype);
869
		return(redraft_cleanup(streamp, TRUE, flags));
870
	    }
871
		
1.1.7 by Asheesh Laroia
Import upstream version 2.00+dfsg
872
	    if((charset = parameter_val(part->body.parameter,"charset")) != NULL){
1.1.4 by Asheesh Laroia
Import upstream version 0.99999+dfsg
873
		/* let outgoing routines decide on charset */
874
		if(!strucmp(charset, "US-ASCII") || !strucmp(charset, "UTF-8"))
875
		  set_parameter(&part->body.parameter, "charset", NULL);
876
		  
877
		fs_give((void **) &charset);
878
	    }
879
1 by Asheesh Laroia
Import upstream version 0.82+dfsg
880
	    ps_global->postpone_no_flow = 1;
1.1.7 by Asheesh Laroia
Import upstream version 2.00+dfsg
881
1 by Asheesh Laroia
Import upstream version 0.82+dfsg
882
	    get_body_part_text(stream, &b->nested.part->body,
1.1.7 by Asheesh Laroia
Import upstream version 2.00+dfsg
883
			       cont_msg, "1", 0L, pc, NULL, NULL, gbpt_flags);
1 by Asheesh Laroia
Import upstream version 0.82+dfsg
884
	    ps_global->postpone_no_flow = 0;
885
886
	    if(!fetch_contents(stream, cont_msg, NULL, *body))
887
	      q_status_message(SM_ORDER | SM_DING, 3, 4,
888
			       _("Error including all message parts"));
889
	}
890
	else{
1.1.7 by Asheesh Laroia
Import upstream version 2.00+dfsg
891
	    *body	  = mail_newbody();
892
	    (*body)->type = TYPETEXT;
893
	    if(b->subtype)
894
	      (*body)->subtype = cpystr(b->subtype);
1 by Asheesh Laroia
Import upstream version 0.82+dfsg
895
1.1.7 by Asheesh Laroia
Import upstream version 2.00+dfsg
896
	    if((charset = parameter_val(b->parameter,"charset")) != NULL){
1.1.4 by Asheesh Laroia
Import upstream version 0.99999+dfsg
897
		/* let outgoing routines decide on charset */
898
		if(!strucmp(charset, "US-ASCII") || !strucmp(charset, "UTF-8"))
899
		  fs_give((void **) &charset);
900
		else{
901
		    (*body)->parameter	      = mail_newbody_parameter();
902
		    (*body)->parameter->attribute = cpystr("charset");
903
		    if(utf8_charset(charset)){
904
			fs_give((void **) &charset);
905
			(*body)->parameter->value = cpystr("UTF-8");
906
		    }
907
		    else
908
		      (*body)->parameter->value = charset;
1 by Asheesh Laroia
Import upstream version 0.82+dfsg
909
		}
910
	    }
911
912
	    (*body)->contents.text.data = (void *)so;
913
	    ps_global->postpone_no_flow = 1;
1.1.3 by Asheesh Laroia
Import upstream version 0.9999+dfsg
914
	    get_body_part_text(stream, b, cont_msg, "1", 0L, pc,
1.1.7 by Asheesh Laroia
Import upstream version 2.00+dfsg
915
			       NULL, NULL, gbpt_flags);
1 by Asheesh Laroia
Import upstream version 0.82+dfsg
916
	    ps_global->postpone_no_flow = 0;
917
	}
918
919
	gf_clear_so_writec(so);
920
921
	    /* We have what we want, blast this message... */
922
	if((flags & REDRAFT_DEL)
923
	   && cont_msg > 0L && stream && cont_msg <= stream->nmsgs
924
	   && (mc = mail_elt(stream, cont_msg)) && !mc->deleted)
925
	  mail_flag(stream, long2string(cont_msg), "\\DELETED", ST_SET);
926
    }
927
    else
928
      return(redraft_cleanup(streamp, TRUE, flags));
929
930
    return(redraft_cleanup(streamp, FALSE, flags));
931
}
932
933
934
/*----------------------------------------------------------------------
935
    Clear deleted messages from given stream and expunge if necessary
936
937
Args:	stream --
938
	problem --
939
940
 ----*/
941
int
942
redraft_cleanup(MAILSTREAM **streamp, int problem, int flags)
943
{
944
    MAILSTREAM *stream;
945
946
    if(!(streamp && *streamp))
947
      return(0);
948
949
    if(!problem && streamp && (stream = *streamp)){
950
	if(stream->nmsgs){
951
	    ps_global->expunge_in_progress = 1;
952
	    mail_expunge(stream);		/* clean out deleted */
953
	    ps_global->expunge_in_progress = 0;
954
	}
955
956
	if(!stream->nmsgs){			/* close and delete folder */
957
	    int do_the_broach = 0;
1.2.1 by Asheesh Laroia
Import upstream version 1.10+dfsg
958
	    char *mbox = NULL;
959
960
	    if(stream){
961
		if(stream->original_mailbox && stream->original_mailbox[0])
962
		  mbox = cpystr(stream->original_mailbox);
963
		else if(stream->mailbox && stream->mailbox[0])
964
		  mbox = cpystr(stream->mailbox);
965
	    }
1 by Asheesh Laroia
Import upstream version 0.82+dfsg
966
967
	    /* if it is current, we have to change folders */
968
	    if(stream == ps_global->mail_stream)
969
	      do_the_broach++;
970
971
	    /*
972
	     * This is the stream to the empty postponed-msgs folder.
973
	     * We are going to delete the folder in a second. It is
974
	     * probably preferable to unselect the mailbox and leave
975
	     * this stream open for re-use instead of actually closing it,
976
	     * so we do that if possible.
977
	     */
978
	    if(is_imap_stream(stream) && LEVELUNSELECT(stream)){
1.1.4 by Asheesh Laroia
Import upstream version 0.99999+dfsg
979
		/*
980
		 * This does the UNSELECT on the stream. A NULL
981
		 * return should mean that something went wrong and
982
		 * a mail_close already happened, so that should have
983
		 * cleaned things up in the callback.
984
		 */
985
		if((stream=mail_open(stream, stream->mailbox,
986
			  OP_HALFOPEN | (stream->debug ? OP_DEBUG : NIL))) != NULL){
987
		    /* now close it so it is put into the stream cache */
988
		    sp_set_flags(stream, sp_flags(stream) | SP_TEMPUSE);
989
		    pine_mail_close(stream);
990
		}
1 by Asheesh Laroia
Import upstream version 0.82+dfsg
991
	    }
992
	    else
993
	      pine_mail_actually_close(stream);
994
995
	    *streamp = NULL;
996
997
	    if(do_the_broach){
998
		ps_global->mail_stream = NULL;	/* already closed above */
999
	    }
1000
1.2.1 by Asheesh Laroia
Import upstream version 1.10+dfsg
1001
	    if(mbox && !pine_mail_delete(NULL, mbox))
1 by Asheesh Laroia
Import upstream version 0.82+dfsg
1002
	      q_status_message1(SM_ORDER|SM_DING, 3, 3,
1003
				/* TRANSLATORS: Arg is a mailbox name */
1004
				_("Can't delete %s"), mbox);
1005
1.2.1 by Asheesh Laroia
Import upstream version 1.10+dfsg
1006
	    if(mbox)
1007
	      fs_give((void **) &mbox);
1 by Asheesh Laroia
Import upstream version 0.82+dfsg
1008
	}
1009
    }
1010
1011
    return(!problem);
1012
}
1013
1014
1015
/*----------------------------------------------------------------------
1016
    Parse the given header text for any given fields
1017
1018
Args:  text -- Text to parse for fcc and attachments refs
1019
       fields -- array of field names to look for
1020
       values -- array of pointer to save values to, returned NULL if
1021
                 fields isn't in text.
1022
1023
This function simply looks for the given fields in the given header
1024
text string.
1025
NOTE: newlines are expected CRLF, and we'll ignore continuations
1026
 ----*/
1027
void
1028
simple_header_parse(char *text, char **fields, char **values)
1029
{
1030
    int   i, n;
1031
    char *p, *t;
1032
1033
    for(i = 0; fields[i]; i++)
1034
      values[i] = NULL;				/* clear values array */
1035
1036
    /*---- Loop until the end of the header ----*/
1037
    for(p = text; *p; ){
1038
	for(i = 0; fields[i]; i++)		/* find matching field? */
1039
	  if(!struncmp(p, fields[i], (n=strlen(fields[i]))) && p[n] == ':'){
1040
	      for(p += n + 1; *p; p++){		/* find start of value */
1041
		  if(*p == '\015' && *(p+1) == '\012'
1042
		     && !isspace((unsigned char) *(p+2)))
1043
		    break;
1044
1045
		  if(!isspace((unsigned char) *p))
1046
		    break;			/* order here is key... */
1047
	      }
1048
1049
	      if(!values[i]){			/* if we haven't already */
1050
		  values[i] = fs_get(strlen(text) + 1);
1051
		  values[i][0] = '\0';		/* alloc space for it */
1052
	      }
1053
1054
	      if(*p && *p != '\015'){		/* non-blank value. */
1055
		  t = values[i] + (values[i][0] ? strlen(values[i]) : 0);
1056
		  while(*p){			/* check for cont'n lines */
1057
		      if(*p == '\015' && *(p+1) == '\012'){
1058
			  if(isspace((unsigned char) *(p+2))){
1.2.1 by Asheesh Laroia
Import upstream version 1.10+dfsg
1059
			      p += 2;
1 by Asheesh Laroia
Import upstream version 0.82+dfsg
1060
			      continue;
1061
			  }
1062
			  else
1063
			    break;
1064
		      }
1065
1066
		      *t++ = *p++;
1067
		  }
1068
1069
		  *t = '\0';
1070
	      }
1071
1072
	      break;
1073
	  }
1074
1075
        /* Skip to end of line, what ever it was */
1076
        for(; *p ; p++)
1077
	  if(*p == '\015' && *(p+1) == '\012'){
1078
	      p += 2;
1079
	      break;
1080
	  }
1081
    }
1082
}
1083
1084
1085
/*----------------------------------------------------------------------
1086
    build a fresh REPLY_S from the given string (see pine_send for format)
1087
1088
Args:	s -- "X-Reply-UID" header value
1089
1090
Returns: filled in REPLY_S or NULL on parse error
1091
 ----*/
1092
REPLY_S *
1093
build_reply_uid(char *s)
1094
{
1.1.3 by Asheesh Laroia
Import upstream version 0.9999+dfsg
1095
    char    *p, *prefix = NULL, *val, *seq, *mbox;
1096
    int	     i, nseq, forwarded = 0;
1 by Asheesh Laroia
Import upstream version 0.82+dfsg
1097
    REPLY_S *reply = NULL;
1098
1099
    /* FORMAT: (n prefix)(n validity uidlist)mailbox */
1.1.3 by Asheesh Laroia
Import upstream version 0.9999+dfsg
1100
    /* if 'n prefix' is empty, uid list represents forwarded msgs */
1 by Asheesh Laroia
Import upstream version 0.82+dfsg
1101
    if(*s == '('){
1.1.3 by Asheesh Laroia
Import upstream version 0.9999+dfsg
1102
	if(*(p = s + 1) == ')'){
1103
	    forwarded = 1;
1104
	}
1105
	else{
1106
	    for(; isdigit(*p); p++)
1107
	      ;
1108
1109
	    if(*p == ' '){
1110
		*p++ = '\0';
1111
1112
		if((i = atoi(s+1)) && i < strlen(p)){
1113
		    prefix = p;
1114
		    *(p += i) = '\0';
1115
		}
1116
	    }
1117
	    else
1118
	      return(NULL);
1119
	}
1120
1121
	if(*++p == '(' && *++p){
1122
	    for(seq = p; isdigit(*p); p++)
1123
	      ;
1124
1125
	    if(*p == ' '){
1126
		*p++ = '\0';
1127
		for(val = p; isdigit(*p); p++)
1128
		  ;
1129
1130
		if(*p == ' '){
1 by Asheesh Laroia
Import upstream version 0.82+dfsg
1131
		    *p++ = '\0';
1.1.3 by Asheesh Laroia
Import upstream version 0.9999+dfsg
1132
1133
		    if((nseq = atoi(seq)) && isdigit(*(seq = p))
1134
		       && (p = strchr(p, ')')) && *(mbox = ++p)){
1135
			imapuid_t *uidl;
1136
1137
			uidl = (imapuid_t *) fs_get ((nseq+1)*sizeof(imapuid_t));
1138
			for(i = 0; i < nseq; i++)
1139
			  if((p = strchr(seq,',')) != NULL){
1140
			      *p = '\0';
1141
			      if((uidl[i]= strtoul(seq,NULL,10)) != 0)
1142
				seq = ++p;
1143
			      else
1144
				break;
1145
			  }
1146
			  else if((p = strchr(seq, ')')) != NULL){
1147
			      if((uidl[i] = strtoul(seq,NULL,10)) != 0)
1148
				i++;
1149
1150
			      break;
1151
			  }
1152
1153
			if(i == nseq){
1154
			    reply = (REPLY_S *)fs_get(sizeof(REPLY_S));
1155
			    memset(reply, 0, sizeof(REPLY_S));
1156
			    reply->uid		     = 1;
1157
			    reply->data.uid.validity = strtoul(val, NULL, 10);
1158
			    if(forwarded)
1159
			      reply->forwarded	     = 1;
1160
			    else
1161
			      reply->prefix	     = cpystr(prefix);
1162
1163
			    reply->mailbox	     = cpystr(mbox);
1164
			    uidl[nseq]	     = 0;
1165
			    reply->data.uid.msgs = uidl;
1 by Asheesh Laroia
Import upstream version 0.82+dfsg
1166
			}
1.1.3 by Asheesh Laroia
Import upstream version 0.9999+dfsg
1167
			else
1168
			  fs_give((void **) &uidl);
1 by Asheesh Laroia
Import upstream version 0.82+dfsg
1169
		    }
1170
		}
1171
	    }
1172
	}
1173
    }
1174
1175
    return(reply);
1176
}
1177
1178
1179
/*
1180
 * pine_new_env - allocate a new METAENV and fill it in.
1181
 */
1182
METAENV *
1183
pine_new_env(ENVELOPE *outgoing, char **fccp, char ***tobufpp, PINEFIELD *custom)
1184
{
1185
    int		        cnt, i, stdcnt;
1186
    char	       *p;
1187
    PINEFIELD	       *pfields, *pf, **sending_order;
1188
    METAENV	       *header;
1189
1190
    header = (METAENV *) fs_get(sizeof(METAENV));
1191
1192
    /* how many fields are there? */
1.1.3 by Asheesh Laroia
Import upstream version 0.9999+dfsg
1193
    for(cnt = 0; pf_template && pf_template[cnt].name; cnt++)
1194
      ;
1195
1196
    stdcnt = cnt;
1 by Asheesh Laroia
Import upstream version 0.82+dfsg
1197
1.1.7 by Asheesh Laroia
Import upstream version 2.00+dfsg
1198
    for(pf = custom; pf; pf = pf->next)
1199
      cnt++;	
1200
1 by Asheesh Laroia
Import upstream version 0.82+dfsg
1201
    /* temporary PINEFIELD array */
1202
    i = (cnt + 1) * sizeof(PINEFIELD);
1203
    pfields = (PINEFIELD *)fs_get((size_t) i);
1204
    memset(pfields, 0, (size_t) i);
1205
1206
    i             = (cnt + 1) * sizeof(PINEFIELD *);
1207
    sending_order = (PINEFIELD **)fs_get((size_t) i);
1208
    memset(sending_order, 0, (size_t) i);
1209
1210
    header->env           = outgoing;
1211
    header->local         = pfields;
1212
    header->custom        = custom;
1213
    header->sending_order = sending_order;
1214
1215
#if	!(defined(DOS) || defined(OS2)) || defined(NOAUTH)
1216
# define NN 4
1217
#else
1218
# define NN 3
1219
#endif
1220
1221
    /* initialize pfield */
1222
    pf = pfields;
1.1.7 by Asheesh Laroia
Import upstream version 2.00+dfsg
1223
    for(i=0; i < stdcnt; i++, pf++){
1 by Asheesh Laroia
Import upstream version 0.82+dfsg
1224
1225
        pf->name        = cpystr(pf_template[i].name);
1226
	if(i == N_SENDER && F_ON(F_USE_SENDER_NOT_X, ps_global))
1227
	  /* slide string over so it is Sender instead of X-X-Sender */
1228
	  for(p=pf->name; *(p+1); p++)
1229
	    *p = *(p+4);
1230
1231
        pf->type        = pf_template[i].type;
1232
	pf->canedit     = pf_template[i].canedit;
1233
	pf->rcptto      = pf_template[i].rcptto;
1234
	pf->writehdr    = pf_template[i].writehdr;
1235
	pf->localcopy   = pf_template[i].localcopy;
1236
        pf->extdata     = NULL; /* unused */
1237
        pf->next        = pf + 1;
1238
1239
        switch(pf->type){
1240
          case FreeText:
1241
            switch(i){
1242
              case N_AUTHRCVD:
1243
		sending_order[0]	= pf;
1244
                break;
1245
1246
              case N_NEWS:
1247
		pf->text		= &outgoing->newsgroups;
1248
		sending_order[1]	= pf;
1249
                break;
1250
1251
              case N_DATE:
1252
		pf->text		= (char **) &outgoing->date;
1253
		sending_order[2]	= pf;
1254
                break;
1255
1256
              case N_INREPLY:
1257
		pf->text		= &outgoing->in_reply_to;
1258
		sending_order[NN+9]	= pf;
1259
                break;
1260
1261
              case N_MSGID:
1262
		pf->text		= &outgoing->message_id;
1263
		sending_order[NN+10]	= pf;
1264
                break;
1265
1266
              case N_REF:			/* won't be used here */
1267
		sending_order[NN+11]	= pf;
1268
                break;
1269
1.1.3 by Asheesh Laroia
Import upstream version 0.9999+dfsg
1270
	      case N_PRIORITY:
1271
		sending_order[NN+12]    = pf;
1272
		break;
1273
1.1.4 by Asheesh Laroia
Import upstream version 0.99999+dfsg
1274
	      case N_USERAGENT:
1275
		pf->text		= &pf->textbuf;
1276
		pf->textbuf		= generate_user_agent();
1277
		sending_order[NN+13]    = pf;
1278
		break;
1279
1 by Asheesh Laroia
Import upstream version 0.82+dfsg
1280
              case N_POSTERR:			/* won't be used here */
1.1.4 by Asheesh Laroia
Import upstream version 0.99999+dfsg
1281
		sending_order[NN+14]	= pf;
1 by Asheesh Laroia
Import upstream version 0.82+dfsg
1282
                break;
1283
1284
              case N_RPLUID:			/* won't be used here */
1.1.4 by Asheesh Laroia
Import upstream version 0.99999+dfsg
1285
		sending_order[NN+15]	= pf;
1 by Asheesh Laroia
Import upstream version 0.82+dfsg
1286
                break;
1287
1288
              case N_RPLMBOX:			/* won't be used here */
1.1.4 by Asheesh Laroia
Import upstream version 0.99999+dfsg
1289
		sending_order[NN+16]	= pf;
1 by Asheesh Laroia
Import upstream version 0.82+dfsg
1290
                break;
1291
1292
              case N_SMTP:			/* won't be used here */
1.1.4 by Asheesh Laroia
Import upstream version 0.99999+dfsg
1293
		sending_order[NN+17]	= pf;
1 by Asheesh Laroia
Import upstream version 0.82+dfsg
1294
                break;
1295
1296
              case N_NNTP:			/* won't be used here */
1.1.4 by Asheesh Laroia
Import upstream version 0.99999+dfsg
1297
		sending_order[NN+18]	= pf;
1 by Asheesh Laroia
Import upstream version 0.82+dfsg
1298
                break;
1299
1300
              case N_CURPOS:			/* won't be used here */
1.1.4 by Asheesh Laroia
Import upstream version 0.99999+dfsg
1301
		sending_order[NN+19]	= pf;
1 by Asheesh Laroia
Import upstream version 0.82+dfsg
1302
                break;
1303
1304
              case N_OURREPLYTO:		/* won't be used here */
1.1.4 by Asheesh Laroia
Import upstream version 0.99999+dfsg
1305
		sending_order[NN+20]	= pf;
1 by Asheesh Laroia
Import upstream version 0.82+dfsg
1306
                break;
1307
1308
              case N_OURHDRS:			/* won't be used here */
1.1.4 by Asheesh Laroia
Import upstream version 0.99999+dfsg
1309
		sending_order[NN+21]	= pf;
1 by Asheesh Laroia
Import upstream version 0.82+dfsg
1310
                break;
1311
1312
              default:
1313
                q_status_message1(SM_ORDER,3,3,
1314
		    "Internal error: 1)FreeText header %s", comatose(i));
1315
                break;
1316
            }
1317
1318
            break;
1319
1320
          case Attachment:
1321
            break;
1322
1323
          case Address:
1324
            switch(i){
1325
	      case N_FROM:
1326
		sending_order[3]	= pf;
1327
		pf->addr		= &outgoing->from;
1328
		break;
1329
1330
	      case N_TO:
1331
		sending_order[NN+2]	= pf;
1332
		pf->addr		= &outgoing->to;
1333
		if(tobufpp)
1334
		  (*tobufpp)		= &pf->scratch;
1335
1336
		break;
1337
1338
	      case N_CC:
1339
		sending_order[NN+3]	= pf;
1340
		pf->addr		= &outgoing->cc;
1341
		break;
1342
1343
	      case N_BCC:
1344
		sending_order[NN+4]	= pf;
1345
		pf->addr		= &outgoing->bcc;
1346
		break;
1347
1348
	      case N_REPLYTO:
1349
		sending_order[NN+1]	= pf;
1350
		pf->addr		= &outgoing->reply_to;
1351
		break;
1352
1353
	      case N_LCC:		/* won't be used here */
1354
		sending_order[NN+7]	= pf;
1355
		break;
1356
1357
#if	!(defined(DOS) || defined(OS2)) || defined(NOAUTH)
1358
              case N_SENDER:
1359
		sending_order[4]	= pf;
1360
		pf->addr		= &outgoing->sender;
1361
                break;
1362
#endif
1363
1364
              case N_NOBODY: /* won't be used here */
1365
		sending_order[NN+5]	= pf;
1366
                break;
1367
1368
              default:
1369
                q_status_message1(SM_ORDER,3,3,
1370
		    "Internal error: Address header %s", comatose(i));
1371
                break;
1372
            }
1373
            break;
1374
1375
          case Fcc:
1376
	    sending_order[NN+8] = pf;
1377
	    pf->text		= fccp;
1378
            break;
1379
1380
	  case Subject:
1381
	    sending_order[NN+6]	= pf;
1382
	    pf->text		= &outgoing->subject;
1383
	    break;
1384
1385
          default:
1386
            q_status_message1(SM_ORDER,3,3,
1387
		"Unknown header type %d in pine_new_send", (void *)pf->type);
1388
            break;
1389
        }
1390
    }
1391
1.1.3 by Asheesh Laroia
Import upstream version 0.9999+dfsg
1392
    if(((--pf)->next = custom) != NULL){
1 by Asheesh Laroia
Import upstream version 0.82+dfsg
1393
	i--;
1394
1395
	/* 
1396
	 * NOTE: "i" is assumed to now index first custom field in sending
1397
	 *       order.
1398
	 */
1399
	for(pf = pf->next; pf && pf->name; pf = pf->next){
1400
	    if(pf->standard)
1401
	      continue;
1402
1403
	    pf->canedit     = 1;
1404
	    pf->rcptto      = 0;
1405
	    pf->writehdr    = 1;
1406
	    pf->localcopy   = 1;
1407
	
1408
	    switch(pf->type){
1409
	      case Address:
1410
		if(pf->addr){				/* better be set */
1411
		    char    *addr = NULL;
1412
		    BuildTo  bldto;
1413
1414
		    bldto.type    = Str;
1415
		    bldto.arg.str = pf->textbuf;
1416
		    
1417
		    sending_order[i++] = pf;
1418
		    /* change default text into an ADDRESS */
1419
		    /* strip quotes around whole default */
1420
		    removing_trailing_white_space(pf->textbuf);
1421
		    (void)removing_double_quotes(pf->textbuf);
1422
		    build_address_internal(bldto, &addr, NULL, NULL, NULL, NULL, NULL, 0, NULL);
1423
		    rfc822_parse_adrlist(pf->addr, addr, ps_global->maildomain);
1424
		    fs_give((void **)&addr);
1425
		    if(pf->textbuf)
1426
		      fs_give((void **)&pf->textbuf);
1427
		}
1428
1429
		break;
1430
1431
	      case FreeText:
1432
		sending_order[i++] = pf;
1433
		pf->text           = &pf->textbuf;
1434
		break;
1435
1436
	      default:
1437
		q_status_message1(SM_ORDER,0,7,"Unknown custom header type %d",
1438
				  (void *)pf->type);
1439
		break;
1440
	    }
1441
	}
1442
    }
1443
1444
1445
    return(header);
1446
}
1447
1448
1449
void
1450
pine_free_env(METAENV **menv)
1451
{
1452
    int cnt;
1453
1.1.3 by Asheesh Laroia
Import upstream version 0.9999+dfsg
1454
1 by Asheesh Laroia
Import upstream version 0.82+dfsg
1455
    if((*menv)->local){
1.1.3 by Asheesh Laroia
Import upstream version 0.9999+dfsg
1456
	for(cnt = 0; pf_template && pf_template[cnt].name; cnt++)
1457
	  ;
1458
1.1.4 by Asheesh Laroia
Import upstream version 0.99999+dfsg
1459
	for(; cnt >= 0; cnt--){
1460
	    if((*menv)->local[cnt].textbuf)
1461
	      fs_give((void **) &(*menv)->local[cnt].textbuf);
1462
1463
	    fs_give((void **) &(*menv)->local[cnt].name);
1464
	}
1 by Asheesh Laroia
Import upstream version 0.82+dfsg
1465
1466
	fs_give((void **) &(*menv)->local);
1467
    }
1468
1469
    if((*menv)->sending_order)
1470
      fs_give((void **) &(*menv)->sending_order);
1471
1472
    fs_give((void **) menv);
1473
}
1474
1475
1476
/*----------------------------------------------------------------------
1477
   Check for addresses the user is not permitted to send to, or probably
1478
   doesn't want to send to
1479
   
1480
Returns:  0 if OK
1481
          1 if there are only empty groups
1482
         -1 if the message shouldn't be sent
1483
1484
Queues a message indicating what happened
1485
  ---*/
1486
int
1487
check_addresses(METAENV *header)
1488
{
1489
    PINEFIELD *pf;
1490
    ADDRESS *a;
1491
    int	     send_daemon = 0, rv = CA_EMPTY;
1492
1493
    /*---- Is he/she trying to send mail to the mailer-daemon ----*/
1494
    for(pf = header->local; pf && pf->name; pf = pf->next)
1495
      if(pf->type == Address && pf->rcptto && pf->addr && *pf->addr)
1496
	for(a = *pf->addr; a != NULL; a = a->next){
1497
	    if(a->host && (a->host[0] == '.'
1498
			   || (F_ON(F_COMPOSE_REJECTS_UNQUAL, ps_global)
1499
			       && a->host[0] == '@'))){
1500
		q_status_message2(SM_ORDER, 4, 7,
1501
				  /* TRANSLATORS: First arg is the address we can't
1502
				     send to, second arg is "not in addressbook". */
1503
				  _("Can't send to address %s: %s"),
1504
				  a->mailbox,
1505
				  (a->host[0] == '.')
1506
				    ? a->host
1507
				    : _("not in addressbook"));
1508
		return(CA_BAD);
1509
	    }
1510
	    else if(ps_global->restricted
1511
		    && !address_is_us(*pf->addr, ps_global)){
1512
		q_status_message(SM_ORDER, 3, 3,
1513
	"Restricted demo version of Alpine. You may only send mail to yourself");
1514
		return(CA_BAD);
1515
	    }
1516
	    else if(a->mailbox && strucmp(a->mailbox, "mailer-daemon") == 0 && !send_daemon){
1517
		send_daemon = 1;
1518
		rv = (pith_opt_daemon_confirm && (*pith_opt_daemon_confirm)()) ? CA_OK : CA_BAD;
1519
	    }
1520
	    else if(a->mailbox && a->host){
1521
		rv = CA_OK;
1522
	    }
1523
	}
1524
1525
    return(rv);
1526
}
1527
1528
1.1.3 by Asheesh Laroia
Import upstream version 0.9999+dfsg
1529
/*
1530
 * If this isn't general enough we can modify it. The value passed in
1531
 * is expected to be one of the desc settings from the priorities array,
1532
 * like "High". The header value is X-Priority: 2 (High)
1533
 * or something similar. If value doesn't match any of the values then
1534
 * the actual value is used instead.
1535
 */
1.1.7 by Asheesh Laroia
Import upstream version 2.00+dfsg
1536
PINEFIELD *
1.1.3 by Asheesh Laroia
Import upstream version 0.9999+dfsg
1537
set_priority_header(METAENV *header, char *value)
1538
{
1539
    PINEFIELD *pf;
1540
1541
    for(pf = header->local; pf && pf->name; pf = pf->next)
1542
      if(pf->type == FreeText && !strcmp(pf->name, PRIORITYNAME))
1543
        break;
1544
1545
    if(pf){
1546
	if(pf->textbuf)
1547
	  fs_give((void **) &pf->textbuf);
1548
1549
	if(value){
1550
	    PRIORITY_S *p;
1551
1552
	    for(p = priorities; p && p->desc; p++)
1553
	      if(!strcmp(p->desc, value))
1554
		break;
1555
1556
	    if(p && p->desc){
1557
		char buf[100];
1558
1559
		snprintf(buf, sizeof(buf), "%d (%s)", p->val, p->desc);
1560
		pf->textbuf = cpystr(buf);
1561
	    }
1562
	    else
1563
	      pf->textbuf = cpystr(value);
1564
	}
1565
    }
1566
}
1567
1568
1 by Asheesh Laroia
Import upstream version 0.82+dfsg
1569
/*----------------------------------------------------------------------
1570
    Set answered flags for messages specified by reply structure
1571
     
1572
Args: reply -- 
1573
1574
Returns: with appropriate flags set and index cache entries suitably tweeked
1575
----*/      
1576
void
1577
update_answered_flags(REPLY_S *reply)
1578
{
1.2.1 by Asheesh Laroia
Import upstream version 1.10+dfsg
1579
    char       *seq = NULL, *p;
1 by Asheesh Laroia
Import upstream version 0.82+dfsg
1580
    long	i, ourstream = 0, we_cancel = 0;
1581
    MAILSTREAM *stream = NULL;
1582
1583
    /* nothing to flip in a pseudo reply */
1.1.3 by Asheesh Laroia
Import upstream version 0.9999+dfsg
1584
    if(reply && (reply->msgno || reply->uid)){
1 by Asheesh Laroia
Import upstream version 0.82+dfsg
1585
	int         j;
1586
	MAILSTREAM *m;
1587
1588
	/*
1589
	 * If an established stream will do, use it, else
1590
	 * build one unless we have an array of msgno's...
1591
	 *
1592
	 * I was just mimicking what was already here. I don't really
1593
	 * understand why we use strcmp instead of same_stream_and_mailbox().
1594
	 * Or sp_stream_get(reply->mailbox, SP_MATCH).
1595
	 * Hubert 2003-07-09
1596
	 */
1597
	for(j = 0; !stream && j < ps_global->s_pool.nstream; j++){
1598
	    m = ps_global->s_pool.streams[j];
1599
	    if(m && reply->mailbox && m->mailbox
1600
	       && !strcmp(reply->mailbox, m->mailbox))
1601
	      stream = m;
1602
	}
1603
1.1.3 by Asheesh Laroia
Import upstream version 0.9999+dfsg
1604
	if(!stream && reply->msgno)
1 by Asheesh Laroia
Import upstream version 0.82+dfsg
1605
	  return;
1606
1607
	/*
1608
	 * This is here only for people who ran pine4.42 and are
1609
	 * processing postponed mail from 4.42 now. Pine4.42 saved the
1610
	 * original mailbox name in the canonical name's position in
1611
	 * the postponed-msgs folder so it won't match the canonical
1612
	 * name from the stream.
1613
	 */
1614
	if(!stream && (!reply->origmbox ||
1615
		       (reply->mailbox &&
1616
		        !strcmp(reply->origmbox, reply->mailbox))))
1617
	  stream = sp_stream_get(reply->mailbox, SP_MATCH);
1618
1619
	/* TRANSLATORS: program is busy updating the Answered flags so warns user */
1620
	we_cancel = busy_cue(_("Updating \"Answered\" Flags"), NULL, 0);
1621
	if(!stream){
1.1.3 by Asheesh Laroia
Import upstream version 0.9999+dfsg
1622
	    if((stream = pine_mail_open(NULL,
1 by Asheesh Laroia
Import upstream version 0.82+dfsg
1623
				       reply->origmbox ? reply->origmbox
1624
						       : reply->mailbox,
1625
				       OP_SILENT | SP_USEPOOL | SP_TEMPUSE,
1.1.3 by Asheesh Laroia
Import upstream version 0.9999+dfsg
1626
				       NULL)) != NULL){
1 by Asheesh Laroia
Import upstream version 0.82+dfsg
1627
		ourstream++;
1628
	    }
1629
	    else{
1630
		if(we_cancel)
1631
		  cancel_busy_cue(0);
1632
1633
		return;
1634
	    }
1635
	}
1636
1637
	if(stream->uid_validity == reply->data.uid.validity){
1638
	    for(i = 0L, p = tmp_20k_buf; reply->data.uid.msgs[i]; i++){
1639
		if(i){
1640
		    sstrncpy(&p, ",", SIZEOF_20KBUF-(p-tmp_20k_buf));
1641
		    tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
1642
		}
1643
1644
		sstrncpy(&p, ulong2string(reply->data.uid.msgs[i]),
1645
			 SIZEOF_20KBUF-(p-tmp_20k_buf));
1646
		tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
1647
	    }
1648
1.2.1 by Asheesh Laroia
Import upstream version 1.10+dfsg
1649
	    if(reply->forwarded){
1650
		/*
1651
		 * $Forwarded is a regular keyword so we only try to
1652
		 * set it if the stream allows keywords.
1653
		 * We could mess up if the stream has keywords but just
1654
		 * isn't allowing anymore and $Forwarded already exists,
1655
		 * but what are the odds?
1656
		 */
1657
		if(stream && stream->kwd_create)
1658
		  mail_flag(stream, seq = cpystr(tmp_20k_buf),
1659
			    FORWARDED_FLAG,
1660
			    ST_SET | ((reply->uid) ? ST_UID : 0L));
1661
	    }
1662
	    else
1663
	      mail_flag(stream, seq = cpystr(tmp_20k_buf),
1664
		        "\\ANSWERED",
1665
		        ST_SET | ((reply->uid) ? ST_UID : 0L));
1666
1667
	    if(seq)
1668
	      fs_give((void **)&seq);
1 by Asheesh Laroia
Import upstream version 0.82+dfsg
1669
	}
1670
1671
	if(ourstream)
1672
	  pine_mail_close(stream);	/* clean up dangling stream */
1673
1674
	if(we_cancel)
1675
	  cancel_busy_cue(0);
1676
    }
1677
}
1678
1679
1680
/*----------------------------------------------------------------------
1681
     Call the mailer, SMTP, sendmail or whatever
1682
     
1683
Args: header -- full header (envelope and local parts) of message to send
1684
      body -- The full body of the message including text
1685
      alt_smtp_servers --
1686
      verbosefile -- non-null means caller wants verbose interaction and the resulting
1687
                     output file name to be returned
1688
1689
Returns: -1 if failed, 1 if succeeded
1690
----*/      
1691
int
1692
call_mailer(METAENV *header, struct mail_bodystruct *body, char **alt_smtp_servers,
1.1.3 by Asheesh Laroia
Import upstream version 0.9999+dfsg
1693
	    int flags, void (*bigresult_f)(char *, int),
1694
	    void (*pipecb_f)(PIPE_S *, int, void *))
1 by Asheesh Laroia
Import upstream version 0.82+dfsg
1695
{
1696
    char         error_buf[200], *error_mess = NULL, *postcmd;
1697
    ADDRESS     *a;
1698
    ENVELOPE	*fake_env = NULL;
1699
    int          addr_error_count, we_cancel = 0;
1700
    long	 smtp_opts = 0L;
1701
    char	*verbose_file = NULL;
1702
    BODY	*bp = NULL;
1703
    PINEFIELD	*pf;
1.1.7 by Asheesh Laroia
Import upstream version 2.00+dfsg
1704
    BODY	*origBody = body;
1 by Asheesh Laroia
Import upstream version 0.82+dfsg
1705
1706
    dprint((4, "Sending mail...\n"));
1707
1708
    /* Check for any recipients */
1709
    for(pf = header->local; pf && pf->name; pf = pf->next)
1710
      if(pf->type == Address && pf->rcptto && pf->addr && *pf->addr)
1711
	break;
1712
1713
    if(!pf){
1714
	q_status_message(SM_ORDER,3,3,
1715
	    _("Can't send message. No recipients specified!"));
1716
	return(0);
1717
    }
1718
1.1.7 by Asheesh Laroia
Import upstream version 2.00+dfsg
1719
#ifdef SMIME
1720
    if(ps_global->smime && (ps_global->smime->do_encrypt || ps_global->smime->do_sign)){
1721
    	int result;
1722
	
1723
    	STORE_S *so = lmc.so;
1724
	lmc.so = NULL;
1725
    
1726
    	result = 1;
1727
    
1728
    	if(ps_global->smime->do_encrypt)
1729
    	  result = encrypt_outgoing_message(header, &body);
1730
	
1731
	/* need to free new body from encrypt if sign fails? */
1732
	if(result && ps_global->smime->do_sign)
1733
	  result = sign_outgoing_message(header, &body, ps_global->smime->do_encrypt);
1734
	
1735
	lmc.so = so;
1736
	
1737
	if(!result)
1738
	  return 0;
1739
    }
1740
#endif
1741
1 by Asheesh Laroia
Import upstream version 0.82+dfsg
1742
    /* set up counts and such to keep track sent percentage */
1743
    send_bytes_sent = 0;
1744
    gf_filter_init();				/* zero piped byte count, 'n */
1745
    send_bytes_to_send = send_body_size(body);	/* count body bytes	     */
1746
    ps_global->c_client_error[0] = error_buf[0] = '\0';
1747
    we_cancel = busy_cue(_("Sending mail"),
1748
			 send_bytes_to_send ? sent_percent : NULL, 0);
1749
1750
#ifndef	_WINDOWS
1751
1752
    /* try posting via local "<mta> <-t>" if specified */
1.1.3 by Asheesh Laroia
Import upstream version 0.9999+dfsg
1753
    if(mta_handoff(header, body, error_buf, sizeof(error_buf), bigresult_f, pipecb_f)){
1 by Asheesh Laroia
Import upstream version 0.82+dfsg
1754
	if(error_buf[0])
1755
	  error_mess = error_buf;
1756
1757
	goto done;
1758
    }
1759
1760
#endif
1761
1762
    /*
1763
     * If the user's asked for it, and we find that the first text
1764
     * part (attachments all get b64'd) is non-7bit, ask for 8BITMIME.
1765
     */
1766
    if(F_ON(F_ENABLE_8BIT, ps_global) && (bp = first_text_8bit(body)))
1767
       smtp_opts |= SOP_8BITMIME;
1768
1769
#ifdef	DEBUG
1770
#ifndef DEBUGJOURNAL
1771
    if(debug > 5 || (flags & CM_VERBOSE))
1772
#endif
1773
      smtp_opts |= SOP_DEBUG;
1774
#endif
1775
1776
    if(flags & (CM_DSN_NEVER | CM_DSN_DELAY | CM_DSN_SUCCESS | CM_DSN_FULL)){
1777
	smtp_opts |= SOP_DSN;
1778
	if(!(flags & CM_DSN_NEVER)){ /* if never, don't turn others on */
1779
	    if(flags & CM_DSN_DELAY)
1780
	      smtp_opts |= SOP_DSN_NOTIFY_DELAY;
1781
	    if(flags & CM_DSN_SUCCESS)
1782
	      smtp_opts |= SOP_DSN_NOTIFY_SUCCESS;
1783
1784
	    /*
1785
	     * If it isn't Never, then we're always going to let them
1786
	     * know about failures.  This means we don't allow for the
1787
	     * possibility of setting delay or success without failure.
1788
	     */
1789
	    smtp_opts |= SOP_DSN_NOTIFY_FAILURE;
1790
1791
	    if(flags & CM_DSN_FULL)
1792
	      smtp_opts |= SOP_DSN_RETURN_FULL;
1793
	}
1794
    }
1795
1796
1797
    /*
1798
     * Set global header pointer so post_rfc822_output can get at it when
1799
     * it's called back from c-client's sending routine...
1800
     */
1801
    send_header = header;
1802
1803
    /*
1804
     * Fabricate a fake ENVELOPE to hand c-client's SMTP engine.
1805
     * The purpose is to give smtp_mail the list for SMTP RCPT when
1806
     * there are recipients in pine's METAENV that are outside c-client's
1807
     * envelope.
1808
     *  
1809
     * NOTE: If there aren't any, don't bother.  Dealt with it below.
1810
     */
1811
    for(pf = header->local; pf && pf->name; pf = pf->next)
1812
      if(pf->type == Address && pf->rcptto && pf->addr && *pf->addr
1813
	 && !(*pf->addr == header->env->to || *pf->addr == header->env->cc
1814
	      || *pf->addr == header->env->bcc))
1815
	break;
1816
1817
    if(pf && pf->name){
1818
	ADDRESS **tail;
1819
1820
	fake_env = (ENVELOPE *)fs_get(sizeof(ENVELOPE));
1821
	memset(fake_env, 0, sizeof(ENVELOPE));
1822
	fake_env->return_path = rfc822_cpy_adr(header->env->return_path);
1823
	tail = &(fake_env->to);
1824
	for(pf = header->local; pf && pf->name; pf = pf->next)
1825
	  if(pf->type == Address && pf->rcptto && pf->addr && *pf->addr){
1826
	      *tail = rfc822_cpy_adr(*pf->addr);
1827
	      while(*tail)
1828
		tail = &((*tail)->next);
1829
	  }
1830
    }
1831
1832
    /*
1833
     * Install our rfc822 output routine 
1834
     */
1835
    sending_hooks.rfc822_out = mail_parameters(NULL, GET_RFC822OUTPUT, NULL);
1836
    (void)mail_parameters(NULL, SET_RFC822OUTPUT, (void *)post_rfc822_output);
1837
1838
    /*
1839
     * Allow for verbose posting
1840
     */
1841
    (void) mail_parameters(NULL, SET_SMTPVERBOSE,
1842
			   (void *) pine_smtp_verbose_out);
1843
1844
    /*
1845
     * We do this because we want mm_log to put the error message into
1846
     * c_client_error instead of showing it itself.
1847
     */
1848
    ps_global->noshow_error = 1;
1849
1850
    /*
1851
     * OK, who posts what?  We tried an mta_handoff above, but there
1852
     * was either none specified or we decided not to use it.  So,
1853
     * if there's an smtp-server defined anywhere, 
1854
     */
1855
    if(alt_smtp_servers && alt_smtp_servers[0] && alt_smtp_servers[0][0]){
1856
	/*---------- SMTP ----------*/
1857
	dprint((4, "call_mailer: via TCP (%s)\n",
1858
		alt_smtp_servers[0]));
1859
	TIME_STAMP("smtp-open start (tcp)", 1);
1860
	sending_stream = smtp_open(alt_smtp_servers, smtp_opts);
1861
    }
1862
    else if(ps_global->VAR_SMTP_SERVER && ps_global->VAR_SMTP_SERVER[0]
1863
	    && ps_global->VAR_SMTP_SERVER[0][0]){
1864
	/*---------- SMTP ----------*/
1865
	dprint((4, "call_mailer: via TCP\n"));
1866
	TIME_STAMP("smtp-open start (tcp)", 1);
1867
	sending_stream = smtp_open(ps_global->VAR_SMTP_SERVER, smtp_opts);
1868
    }
1.1.3 by Asheesh Laroia
Import upstream version 0.9999+dfsg
1869
    else if((postcmd = smtp_command(ps_global->c_client_error, sizeof(ps_global->c_client_error))) != NULL){
1 by Asheesh Laroia
Import upstream version 0.82+dfsg
1870
	char *cmdlist[2];
1871
1872
	/*----- Send via LOCAL SMTP agent ------*/
1873
	dprint((4, "call_mailer: via \"%s\"\n", postcmd));
1874
1875
	TIME_STAMP("smtp-open start (pipe)", 1);
1876
	fs_give((void **) &postcmd);
1877
	cmdlist[0] = "localhost";
1878
	cmdlist[1] = NULL;
1879
	sending_stream = smtp_open_full(&piped_io, cmdlist, "smtp",
1880
					SMTPTCPPORT, smtp_opts);
1881
/* BUG: should provide separate stderr output! */
1882
    }
1883
1884
    ps_global->noshow_error = 0;
1885
1886
    TIME_STAMP("smtp open", 1);
1887
    if(sending_stream){
1.1.4 by Asheesh Laroia
Import upstream version 0.99999+dfsg
1888
	unsigned short save_encoding, added_encoding;
1 by Asheesh Laroia
Import upstream version 0.82+dfsg
1889
1890
	dprint((1, "Opened SMTP server \"%s\"\n",
1891
	       net_host(sending_stream->netstream)
1892
	         ? net_host(sending_stream->netstream) : "?"));
1893
1894
	if(flags & CM_VERBOSE){
1895
	    TIME_STAMP("verbose start", 1);
1.1.4 by Asheesh Laroia
Import upstream version 0.99999+dfsg
1896
	    if((verbose_file = temp_nam(NULL, "sd")) != NULL){
1.1.3 by Asheesh Laroia
Import upstream version 0.9999+dfsg
1897
		if((verbose_send_output = our_fopen(verbose_file, "w")) != NULL){
1898
		    if(!smtp_verbose(sending_stream)){
1 by Asheesh Laroia
Import upstream version 0.82+dfsg
1899
			snprintf(error_mess = error_buf, sizeof(error_buf),
1900
			      "Mail not sent.  VERBOSE mode error%s%.50s.",
1901
			      (sending_stream && sending_stream->reply)
1902
			        ? ": ": "",
1903
			      (sending_stream && sending_stream->reply)
1904
			        ? sending_stream->reply : "");
1905
			error_buf[sizeof(error_buf)-1] = '\0';
1906
		    }
1907
		}
1908
		else{
1909
		    our_unlink(verbose_file);
1910
		    strncpy(error_mess = error_buf,
1911
			    "Can't open tmp file for VERBOSE mode.", sizeof(error_buf));
1912
		    error_buf[sizeof(error_buf)-1] = '\0';
1913
		}
1914
	    }
1915
	    else{
1916
		strncpy(error_mess = error_buf,
1917
		        "Can't create tmp file name for VERBOSE mode.", sizeof(error_buf));
1918
		error_buf[sizeof(error_buf)-1] = '\0';
1919
	    }
1920
1921
	    TIME_STAMP("verbose end", 1);
1922
	}
1923
1924
	/*
1925
	 * Before we actually send data, see if we have to protect
1926
	 * the first text body part from getting encoded.  We protect
1927
	 * it from getting encoded in "pine_rfc822_output_body" by
1928
	 * temporarily inventing a synonym for ENC8BIT...
1.1.5 by Asheesh Laroia
Import upstream version 1.0+dfsg
1929
	 * This works like so:
1930
	 *   Suppose bp->encoding is set to ENC8BIT.
1931
	 *   We change that here to some unused value (added_encoding) and
1932
	 *    set body_encodings[added_encoding] to "8BIT".
1933
	 *   Then post_rfc822_output is called which calls
1934
	 *    pine_rfc822_output_body. Inside that routine
1935
	 *    pine_write_body_header writes out the encoding for the
1936
	 *    part. Normally it would see encoding == ENC8BIT and it would
1937
	 *    change that to QUOTED-PRINTABLE, but since encoding has been
1938
	 *    set to added_encoding it uses body_encodings[added_encoding]
1939
	 *    which is "8BIT" instead. Then the actual body is written by
1940
	 *    pine_write_body_header which does not do the gf_8bit_qp
1941
	 *    filtering because encoding != ENC8BIT (instead it's equal
1942
	 *    to added_encoding).
1 by Asheesh Laroia
Import upstream version 0.82+dfsg
1943
	 */
1944
	if(bp && sending_stream->protocol.esmtp.eightbit.ok
1945
	      && sending_stream->protocol.esmtp.eightbit.want){
1946
	    int i;
1947
1948
	    for(i = 0; (i <= ENCMAX) && body_encodings[i]; i++)
1949
	      ;
1950
1951
	    if(i > ENCMAX){		/* no empty encoding slots! */
1952
		bp = NULL;
1953
	    }
1954
	    else {
1.1.4 by Asheesh Laroia
Import upstream version 0.99999+dfsg
1955
		added_encoding = i;
1956
		body_encodings[added_encoding] = body_encodings[ENC8BIT];
1 by Asheesh Laroia
Import upstream version 0.82+dfsg
1957
		save_encoding = bp->encoding;
1.1.4 by Asheesh Laroia
Import upstream version 0.99999+dfsg
1958
		bp->encoding = added_encoding;
1 by Asheesh Laroia
Import upstream version 0.82+dfsg
1959
	    }
1960
	}
1961
1962
	if(sending_stream->protocol.esmtp.ok
1963
	   && sending_stream->protocol.esmtp.dsn.want
1964
	   && !sending_stream->protocol.esmtp.dsn.ok)
1965
	  q_status_message(SM_ORDER,3,3,
1966
	    _("Delivery Status Notification not available from this server."));
1967
1968
	TIME_STAMP("smtp start", 1);
1969
	if(!error_mess && !smtp_mail(sending_stream, "MAIL",
1970
				     fake_env ? fake_env : header->env, body)){
1971
1972
	    snprintf(error_buf, sizeof(error_buf),
1973
		    _("Mail not sent. Sending error%s%s"),
1974
		    (sending_stream && sending_stream->reply) ? ": ": ".",
1975
		    (sending_stream && sending_stream->reply)
1976
		      ? sending_stream->reply : "");
1977
	    error_buf[sizeof(error_buf)-1] = '\0';
1978
	    dprint((1, error_buf));
1979
	    addr_error_count = 0;
1980
	    if(fake_env){
1981
		for(a = fake_env->to; a != NULL; a = a->next)
1982
		  if(a->error != NULL){
1983
		      if(addr_error_count++ < MAX_ADDR_ERROR){
1984
1985
			  /*
1986
			   * Too complicated to figure out which header line
1987
			   * has the error in the fake_env case, so just
1988
			   * leave cursor at default.
1989
			   */
1990
1991
1992
			  if(error_mess)	/* previous error? */
1993
			    q_status_message(SM_ORDER, 4, 7, error_mess);
1994
1995
			  error_mess = tidy_smtp_mess(a->error,
1996
						      _("Mail not sent: %.80s"),
1997
						      error_buf, sizeof(error_buf));
1998
		      }
1999
2000
		      dprint((1, "Send Error: \"%s\"\n",
2001
				 a->error));
2002
		  }
2003
	    }
2004
	    else{
2005
	      for(pf = header->local; pf && pf->name; pf = pf->next)
2006
	        if(pf->type == Address && pf->rcptto && pf->addr && *pf->addr)
2007
		  for(a = *pf->addr; a != NULL; a = a->next)
2008
		    if(a->error != NULL){
2009
		      if(addr_error_count++ < MAX_ADDR_ERROR){
2010
2011
			  if(error_mess)	/* previous error? */
2012
			    q_status_message(SM_ORDER, 4, 7, error_mess);
2013
2014
			  error_mess = tidy_smtp_mess(a->error,
2015
						      _("Mail not sent: %.80s"),
2016
						      error_buf, sizeof(error_buf));
2017
		      }
2018
2019
		      dprint((1, "Send Error: \"%s\"\n",
2020
				 a->error));
2021
		    }
2022
	    }
2023
2024
	    if(!error_mess)
2025
	      error_mess = error_buf;
2026
	}
2027
2028
	/* repair modified "body_encodings" array? */
2029
	if(bp && sending_stream->protocol.esmtp.eightbit.ok
2030
	      && sending_stream->protocol.esmtp.eightbit.want){
1.1.4 by Asheesh Laroia
Import upstream version 0.99999+dfsg
2031
	    body_encodings[added_encoding] = NULL;
1 by Asheesh Laroia
Import upstream version 0.82+dfsg
2032
	    bp->encoding = save_encoding;
2033
	}
2034
2035
	TIME_STAMP("smtp closing", 1);
2036
	smtp_close(sending_stream);
2037
	sending_stream = NULL;
2038
	TIME_STAMP("smtp done", 1);
2039
    }
2040
    else if(!error_mess){
2041
	snprintf(error_mess = error_buf, sizeof(error_buf), _("Error sending%.2s%.80s"),
2042
	        ps_global->c_client_error[0] ? ": " : "",
2043
		ps_global->c_client_error);
2044
	error_buf[sizeof(error_buf)-1] = '\0';
2045
    }
2046
2047
    if(verbose_file){
2048
	if(verbose_send_output){
2049
	    TIME_STAMP("verbose start", 1);
2050
	    fclose(verbose_send_output);
2051
	    verbose_send_output = NULL;
2052
	    q_status_message(SM_ORDER, 0, 3, "Verbose SMTP output received");
2053
2054
	    if(bigresult_f)
2055
	      (*bigresult_f)(verbose_file, CM_BR_VERBOSE);
2056
2057
	    TIME_STAMP("verbose end", 1);
2058
	}
2059
2060
	fs_give((void **)&verbose_file);
2061
    }
2062
2063
    /*
2064
     * Restore original 822 emitter...
2065
     */
2066
    (void) mail_parameters(NULL, SET_RFC822OUTPUT, sending_hooks.rfc822_out);
2067
2068
    if(fake_env)
2069
      mail_free_envelope(&fake_env);
2070
2071
  done:
1.1.7 by Asheesh Laroia
Import upstream version 2.00+dfsg
2072
2073
#ifdef SMIME
2074
    /* Free replacement encrypted body */
2075
    if(F_OFF(F_DONT_DO_SMIME, ps_global) && body != origBody){
2076
    
2077
    	if(body->type == TYPEMULTIPART){
2078
	    /* Just get rid of first part, it's actually origBody */
2079
	    void *x = body->nested.part;
2080
	    
2081
	    body->nested.part = body->nested.part->next;
2082
	    
2083
	    fs_give(&x);
2084
	}
2085
    
2086
    	pine_free_body(&body);
2087
    }
2088
#endif
2089
1 by Asheesh Laroia
Import upstream version 0.82+dfsg
2090
    if(we_cancel)
2091
      cancel_busy_cue(0);
2092
2093
    TIME_STAMP("call_mailer done", 1);
2094
    /*-------- Did message make it ? ----------*/
2095
    if(error_mess){
2096
        /*---- Error sending mail -----*/
2097
	if(lmc.so && !lmc.all_written)
2098
	  so_give(&lmc.so);
2099
2100
	if(error_mess){
2101
	    q_status_message(SM_ORDER | SM_DING, 4, 7, error_mess);
2102
	    dprint((1, "call_mailer ERROR: %s\n", error_mess));
2103
	}
2104
2105
	return(-1);
2106
    }
2107
    else{
2108
	lmc.all_written = 1;
2109
	return(1);
2110
    }
2111
}
2112
2113
2114
/*
2115
 * write_postponed - exported method to write the given message
2116
 *		     to the postponed folder
2117
 */
2118
int
2119
write_postponed(METAENV *header, struct mail_bodystruct *body)
2120
{
2121
    char      **pp, *folder;
2122
    int		rv = 0, sz;
2123
    CONTEXT_S  *fcc_cntxt = NULL;
2124
    PINEFIELD *pf;
2125
    static char *writem[] = {"To", "References", "Fcc", "X-Reply-UID", NULL};
2126
2127
    if(!ps_global->VAR_POSTPONED_FOLDER
2128
       || !ps_global->VAR_POSTPONED_FOLDER[0]){
2129
	q_status_message(SM_ORDER | SM_DING, 3, 3, 
2130
			 _("No postponed file defined"));
2131
	return(-1);
2132
    }
2133
2134
    folder = cpystr(ps_global->VAR_POSTPONED_FOLDER);
2135
2136
    lmc.all_written = lmc.text_written = lmc.text_only = 0;
2137
2138
    lmc.so = open_fcc(folder, &fcc_cntxt, 1, NULL, NULL);
2139
2140
    if(lmc.so){
2141
	/* BUG: writem sufficient ? */
2142
	for(pf = header->local; pf && pf->name; pf = pf->next)
2143
	  for(pp = writem; *pp; pp++)
2144
	    if(!strucmp(pf->name, *pp)){
2145
		pf->localcopy = 1;
2146
		pf->writehdr = 1;
2147
		break;
2148
	    }
2149
2150
	/*
2151
	 * Work around c-client reply-to bug. C-client will
2152
	 * return a reply_to in an envelope even if there is
2153
	 * no reply-to header field. We want to note here whether
2154
	 * the reply-to is real or not.
2155
	 */
2156
	if(header->env->reply_to || hdr_is_in_list("reply-to", header->custom))
2157
	  for(pf = header->local; pf; pf = pf->next)
2158
	    if(!strcmp(pf->name, "Reply-To")){
2159
		pf->writehdr = 1;
2160
		pf->localcopy = 1;
2161
		if(header->env->reply_to)
2162
		  pf->textbuf = cpystr("Full");
2163
		else
2164
		  pf->textbuf = cpystr("Empty");
2165
	    }
2166
2167
	/*
2168
	 * Write the list of custom headers to the
2169
	 * X-Our-Headers header so that we can recover the
2170
	 * list in redraft.
2171
	 */
2172
	sz = 0;
2173
	for(pf = header->custom; pf && pf->name; pf = pf->next)
2174
	  sz += strlen(pf->name) + 1;
2175
2176
	if(sz){
2177
	    int i;
1.1.8 by Asheesh Laroia
Import upstream version 2.02
2178
	    char *pstart, *pend;
1 by Asheesh Laroia
Import upstream version 0.82+dfsg
2179
2180
	    for(i = 0, pf = header->local; i != N_OURHDRS; i++, pf = pf->next)
2181
	      ;
2182
2183
	    pf->writehdr  = 1;
2184
	    pf->localcopy = 1;
1.1.8 by Asheesh Laroia
Import upstream version 2.02
2185
	    pf->textbuf = pstart = pend = (char *) fs_get(sz + 1);
1 by Asheesh Laroia
Import upstream version 0.82+dfsg
2186
	    pf->text = &pf->textbuf;
1.1.8 by Asheesh Laroia
Import upstream version 2.02
2187
	    pf->textbuf[sz] = '\0';			/* tie off overflow */
2188
            /* note: "pf" overloaded */
1 by Asheesh Laroia
Import upstream version 0.82+dfsg
2189
	    for(pf = header->custom; pf && pf->name; pf = pf->next){
1.1.8 by Asheesh Laroia
Import upstream version 2.02
2190
		int r = sz - (pend - pstart);		/* remaining buffer */
2191
2192
		if(r > 0 && r != sz){
2193
		    r--;
2194
		    *pend++ = ',';
1 by Asheesh Laroia
Import upstream version 0.82+dfsg
2195
		}
2196
1.1.8 by Asheesh Laroia
Import upstream version 2.02
2197
		sstrncpy(&pend, pf->name, r);
1 by Asheesh Laroia
Import upstream version 0.82+dfsg
2198
	    }
2199
	}
2200
2201
	if(pine_rfc822_output(header, body, NULL, NULL) < 0
2202
	   || write_fcc(folder, fcc_cntxt, lmc.so, NULL, "postponed message", NULL) < 0)
2203
	  rv = -1;
2204
2205
	so_give(&lmc.so);
2206
    }
2207
    else {
2208
	q_status_message1(SM_ORDER | SM_DING, 3, 3, 
2209
			  "Can't allocate internal storage: %s ",
2210
			  error_description(errno));
2211
	rv = -1;
2212
    }
2213
2214
    fs_give((void **) &folder);
2215
    return(rv);
2216
}
2217
2218
2219
int
2220
commence_fcc(char *fcc, CONTEXT_S **fcc_cntxt, int forced)
2221
{
2222
    if(fcc && *fcc){
2223
	lmc.all_written = lmc.text_written = 0;
2224
	lmc.text_only = F_ON(F_NO_FCC_ATTACH, ps_global) != 0;
2225
	return((lmc.so = open_fcc(fcc, fcc_cntxt, 0, NULL, NULL)) != NULL);
2226
    }
2227
    else
2228
      lmc.so = NULL;
2229
2230
    return(TRUE);
2231
}
2232
2233
2234
int
2235
wrapup_fcc(char *fcc, CONTEXT_S *fcc_cntxt, METAENV *header, struct mail_bodystruct *body)
2236
{
2237
    int rv = TRUE;
2238
2239
    if(lmc.so){
2240
	if(!header || pine_rfc822_output(header, body, NULL, NULL)){
2241
	    char label[50];
2242
2243
	    strncpy(label, "Fcc", sizeof(label));
2244
	    label[sizeof(label)-1] = '\0';
2245
	    if(strcmp(fcc, ps_global->VAR_DEFAULT_FCC)){
2246
		snprintf(label + 3, sizeof(label)-3, " to %.40s", fcc);
2247
		label[sizeof(label)-1] = '\0';
2248
	    }
2249
2250
	    rv = write_fcc(fcc,fcc_cntxt,lmc.so,NULL,NULL,NULL);
2251
	}
2252
	else{
2253
	    rv = FALSE;
2254
	}
2255
2256
	so_give(&lmc.so);
2257
    }
2258
2259
    return(rv);
2260
}
2261
2262
2263
/*----------------------------------------------------------------------
2264
    Checks to make sure the fcc is available and can be opened
2265
2266
Args: fcc -- the name of the fcc to create.  It can't be NULL.
2267
      fcc_cntxt -- Returns the context the fcc is in.
2268
      force -- supress user option prompt
2269
2270
Returns allocated storage object on success, NULL on failure
2271
  ----*/
2272
STORE_S *
2273
open_fcc(char *fcc, CONTEXT_S **fcc_cntxt, int force, char *err_prefix, char *err_suffix)
2274
{
2275
    int		exists, ok = 0;
2276
2277
    ps_global->mm_log_error = 0;
2278
2279
    /* 
2280
     * check for fcc's existance...
2281
     */
2282
    TIME_STAMP("open_fcc start", 1);
2283
    if(!is_absolute_path(fcc) && context_isambig(fcc)
2284
       && (strucmp(ps_global->inbox_name, fcc) != 0)){
2285
	int flip_dot = 0;
2286
2287
	/*
2288
	 * Don't want to preclude a user from Fcc'ing a .name'd folder
2289
	 */
2290
	if(F_OFF(F_ENABLE_DOT_FOLDERS, ps_global)){
2291
	    flip_dot = 1;
2292
	    F_TURN_ON(F_ENABLE_DOT_FOLDERS, ps_global);
2293
	}
2294
2295
	/*
2296
	 * We only want to set the "context" if fcc is an ambiguous
2297
	 * name.  Otherwise, our "relativeness" rules for contexts 
2298
	 * (implemented in context.c) might cause the name to be
2299
	 * interpreted in the wrong context...
2300
	 */
2301
	if(!(*fcc_cntxt || (*fcc_cntxt = default_save_context(ps_global->context_list))))
2302
	  *fcc_cntxt = ps_global->context_list;
2303
2304
        build_folder_list(NULL, *fcc_cntxt, fcc, NULL, BFL_FLDRONLY);
2305
        if(folder_index(fcc, *fcc_cntxt, FI_FOLDER) < 0){
2306
	    if(force
2307
	       || (pith_opt_save_create_prompt
2308
		   && (*pith_opt_save_create_prompt)(*fcc_cntxt, fcc, 0) > 0)){
2309
2310
		ps_global->noshow_error = 1;
2311
2312
		if(context_create(*fcc_cntxt, NULL, fcc))
2313
		  ok++;
2314
2315
		ps_global->noshow_error = 0;
2316
	    }
2317
	    else
2318
	      ok--;			/* declined! */
2319
        }
2320
	else
2321
	  ok++;				/* found! */
2322
2323
	if(flip_dot)
2324
	  F_TURN_OFF(F_ENABLE_DOT_FOLDERS, ps_global);
2325
2326
        free_folder_list(*fcc_cntxt);
2327
    }
2328
    else if((exists = folder_exists(NULL, fcc)) != FEX_ERROR){
2329
	if(exists & (FEX_ISFILE | FEX_ISDIR)){
2330
	    ok++;
2331
	}
2332
	else{
2333
	    if(force
2334
	       || (pith_opt_save_create_prompt
2335
		   && (*pith_opt_save_create_prompt)(NULL, fcc, 0) > 0)){
2336
2337
		ps_global->mm_log_error = 0;
2338
		ps_global->noshow_error = 1;
2339
2340
		ok = pine_mail_create(NULL, fcc) != 0L;
2341
2342
		ps_global->noshow_error = 0;
2343
	    }
2344
	    else
2345
	      ok--;			/* declined! */
2346
	}
2347
    }
2348
2349
    TIME_STAMP("open_fcc done.", 1);
2350
    if(ok > 0){
2351
	return(so_get(FCC_SOURCE, NULL, WRITE_ACCESS));
2352
    }
2353
    else{
2354
	int   l1, l2, l3, wid, w;
2355
	char *errstr, tmp[MAILTMPLEN];
2356
	char *s1, *s2;
2357
2358
	if(ok == 0){
2359
	    if(ps_global->mm_log_error){
2360
		s1 = err_prefix ? err_prefix : "Fcc Error: ";
2361
		s2 = err_suffix ? err_suffix : " Message NOT sent or copied.";
2362
2363
		l1 = strlen(s1);
2364
		l2 = strlen(s2);
2365
		l3 = strlen(ps_global->c_client_error);
2366
		wid = (ps_global->ttyo && ps_global->ttyo->screen_cols > 0)
2367
		       ? ps_global->ttyo->screen_cols : 80;
2368
		w = wid - l1 - l2 - 5;
2369
2370
		snprintf(errstr = tmp, sizeof(tmp),
2371
			"%.99s\"%.*s%.99s\".%.99s",
2372
			s1,
2373
			(l3 > w) ? MAX(w-3,0) : MAX(w,0),
2374
			ps_global->c_client_error,
2375
			(l3 > w) ? "..." : "",
2376
			s2);
2377
		tmp[sizeof(tmp)-1] = '\0';
2378
2379
	    }
2380
	    else
2381
	      errstr = _("Fcc creation error. Message NOT sent or copied.");
2382
	}
2383
	else
2384
	  errstr = _("Fcc creation rejected. Message NOT sent or copied.");
2385
2386
	q_status_message(SM_ORDER | SM_DING, 3, 3, errstr);
2387
    }
2388
2389
    return(NULL);
2390
}
2391
2392
2393
/*----------------------------------------------------------------------
2394
   mail_append() the fcc accumulated in temp_storage to proper destination
2395
2396
Args:  fcc -- name of folder
2397
       fcc_cntxt -- context for folder
2398
       temp_storage -- String of file where Fcc has been accumulated
2399
2400
This copies the string of file to the actual folder, which might be IMAP
2401
or a disk folder.  The temp_storage is freed after it is written.
2402
An error message is produced if this fails.
2403
  ----*/
2404
int
1.1.1 by Asheesh Laroia
Import upstream version 0.98+dfsg
2405
write_fcc(char *fcc, CONTEXT_S *fcc_cntxt, STORE_S *tmp_storage,
2406
	  MAILSTREAM *stream, char *label, char *flags)
1 by Asheesh Laroia
Import upstream version 0.82+dfsg
2407
{
2408
    STRING      msg;
2409
    CONTEXT_S  *cntxt;
2410
    int         we_cancel = 0;
2411
2412
    if(!tmp_storage)
2413
      return(0);
2414
2415
    TIME_STAMP("write_fcc start.", 1);
2416
    dprint((4, "Writing %s\n", (label && *label) ? label : ""));
2417
    if(label && *label){
2418
	char msg_buf[80];
2419
1.1.3 by Asheesh Laroia
Import upstream version 0.9999+dfsg
2420
	strncpy(msg_buf, "Writing ", sizeof(msg_buf));
1 by Asheesh Laroia
Import upstream version 0.82+dfsg
2421
	msg_buf[sizeof(msg_buf)-1] = '\0';
1.1.3 by Asheesh Laroia
Import upstream version 0.9999+dfsg
2422
	strncat(msg_buf, label, sizeof(msg_buf)-10);
1 by Asheesh Laroia
Import upstream version 0.82+dfsg
2423
	we_cancel = busy_cue(msg_buf, NULL, 0);
2424
    }
2425
    else
2426
      we_cancel = busy_cue(NULL, NULL, 1);
2427
2428
    so_seek(tmp_storage, 0L, 0);
2429
2430
/*
2431
 * Before changing this note that these lines depend on the
2432
 * definition of FCC_SOURCE.
2433
 */
2434
    INIT(&msg, mail_string, (void *)so_text(tmp_storage), 
2435
	     strlen((char *)so_text(tmp_storage)));
2436
2437
    cntxt      = fcc_cntxt;
2438
2439
    if(!context_append_full(cntxt, stream, fcc, flags, NULL, &msg)){
2440
	cancel_busy_cue(-1);
2441
	we_cancel = 0;
2442
2443
	q_status_message1(SM_ORDER | SM_DING, 3, 5,
2444
			  "Write to \"%s\" FAILED!!!", fcc);
2445
	dprint((1, "ERROR appending %s in \"%s\"",
2446
	       fcc ? fcc : "?",
2447
	       (cntxt && cntxt->context) ? cntxt->context : "NULL"));
2448
	return(0);
2449
    }
2450
2451
    if(we_cancel)
2452
      cancel_busy_cue(label ? 0 : -1);
2453
2454
    dprint((4, "done.\n"));
2455
    TIME_STAMP("write_fcc done.", 1);
2456
    return(1);
2457
}
2458
2459
2460
/*
2461
 * first_text_8bit - return TRUE if somewhere in the body 8BIT data's
2462
 *		     contained.
2463
 */
2464
BODY *
2465
first_text_8bit(struct mail_bodystruct *body)
2466
{
2467
    if(body->type == TYPEMULTIPART)	/* advance to first contained part */
2468
      body = &body->nested.part->body;
2469
2470
    return((body->type == TYPETEXT && body->encoding != ENC7BIT)
2471
	     ? body : NULL);
2472
}
2473
2474
2475
/*
2476
 * Build and return the "From:" address for outbound messages from
2477
 * global data...
2478
 */
2479
ADDRESS *
2480
generate_from(void)
2481
{
2482
    ADDRESS *addr = mail_newaddr();
2483
    if(ps_global->VAR_PERSONAL_NAME){
2484
	addr->personal = cpystr(ps_global->VAR_PERSONAL_NAME);
2485
	removing_leading_and_trailing_white_space(addr->personal);
2486
	if(addr->personal[0] == '\0')
2487
	  fs_give((void **)&addr->personal);
2488
    }
2489
2490
    addr->mailbox = cpystr(ps_global->VAR_USER_ID);
2491
    addr->host    = cpystr(ps_global->maildomain);
2492
    removing_leading_and_trailing_white_space(addr->mailbox);
2493
    removing_leading_and_trailing_white_space(addr->host);
2494
    return(addr);
2495
}
2496
2497
2498
/*
2499
 * set_mime_type_by_grope - sniff the given storage object to determine its 
2500
 *                  type, subtype, encoding, and charset
2501
 *
2502
 *		"Type" and "encoding" must be set before calling this routine.
2503
 *		If "type" is set to something other than TYPEOTHER on entry,
2504
 *		then that is the "type" we wish to use.  Same for "encoding"
2505
 *		using ENCOTHER instead of TYPEOTHER.  Otherwise, we
2506
 *		figure them out here.  If "type" is already set, we also
2507
 *		leave subtype alone.  If not, we figure out subtype here.
2508
 *		There is a chance that we will upgrade encoding to a "higher"
2509
 *		level.  For example, if it comes in as 7BIT we may change
2510
 *		that to 8BIT if we find a From_ we want to escape.
2511
 *		We may also set the charset attribute if the type is TEXT.
2512
 *
2513
 * NOTE: this is rather inefficient if the store object is a CharStar
2514
 *       but the win is all types are handled the same
2515
 */
2516
void
1.1.1 by Asheesh Laroia
Import upstream version 0.98+dfsg
2517
set_mime_type_by_grope(struct mail_bodystruct *body)
1 by Asheesh Laroia
Import upstream version 0.82+dfsg
2518
{
2519
#define RBUFSZ	(8193)
2520
    unsigned char   *buf, *p, *bol;
2521
    register size_t  n;
2522
    long             max_line = 0L,
2523
                     eight_bit_chars = 0L,
2524
                     line_so_far = 0L,
2525
                     len = 0L;
2526
    STORE_S         *so = (STORE_S *)body->contents.text.data;
2527
    unsigned short   new_encoding = ENCOTHER;
1.1.1 by Asheesh Laroia
Import upstream version 0.98+dfsg
2528
    int              we_cancel = 0;
1 by Asheesh Laroia
Import upstream version 0.82+dfsg
2529
#ifdef ENCODE_FROMS
2530
    short            froms = 0, dots = 0,
2531
                     bmap  = 0x1, dmap = 0x1;
2532
#endif
2533
2534
    we_cancel = busy_cue(NULL, NULL, 1);
2535
2536
    buf = (unsigned char *)fs_get(RBUFSZ);
2537
    so_seek(so, 0L, 0);
2538
2539
    for(n = 0; n < RBUFSZ-1 && so_readc(&buf[n], so) != 0; n++)
2540
      ;
2541
2542
    buf[n] = '\0';
2543
2544
    if(n){    /* check first few bytes to look for magic numbers */
2545
	if(body->type == TYPEOTHER){
2546
	    if(buf[0] == 'G' && buf[1] == 'I' && buf[2] == 'F'){
2547
		body->type    = TYPEIMAGE;
2548
		body->subtype = cpystr("GIF");
2549
	    }
2550
	    else if((n > 9) && buf[0] == 0xFF && buf[1] == 0xD8
2551
		    && buf[2] == 0xFF && buf[3] == 0xE0
2552
		    && !strncmp((char *)&buf[6], "JFIF", 4)){
2553
	        body->type    = TYPEIMAGE;
2554
	        body->subtype = cpystr("JPEG");
2555
	    }
2556
	    else if((buf[0] == 'M' && buf[1] == 'M')
2557
		    || (buf[0] == 'I' && buf[1] == 'I')){
2558
		body->type    = TYPEIMAGE;
2559
		body->subtype = cpystr("TIFF");
2560
	    }
2561
	    else if((buf[0] == '%' && buf[1] == '!')
2562
		     || (buf[0] == '\004' && buf[1] == '%' && buf[2] == '!')){
2563
		body->type    = TYPEAPPLICATION;
2564
		body->subtype = cpystr("PostScript");
2565
	    }
2566
	    else if(buf[0] == '%' && !strncmp((char *)buf+1, "PDF-", 4)){
2567
		body->type = TYPEAPPLICATION;
2568
		body->subtype = cpystr("PDF");
2569
	    }
2570
	    else if(buf[0] == '.' && !strncmp((char *)buf+1, "snd", 3)){
2571
		body->type    = TYPEAUDIO;
2572
		body->subtype = cpystr("Basic");
2573
	    }
2574
	    else if((n > 3) && buf[0] == 0x00 && buf[1] == 0x05
2575
		            && buf[2] == 0x16 && buf[3] == 0x00){
2576
	        body->type    = TYPEAPPLICATION;
2577
		body->subtype = cpystr("APPLEFILE");
2578
	    }
2579
	    else if((n > 3) && buf[0] == 0x50 && buf[1] == 0x4b
2580
		            && buf[2] == 0x03 && buf[3] == 0x04){
2581
	        body->type    = TYPEAPPLICATION;
2582
		body->subtype = cpystr("ZIP");
2583
	    }
2584
2585
	    /*
2586
	     * if type was set above, but no encoding specified, go
2587
	     * ahead and make it BASE64...
2588
	     */
2589
	    if(body->type != TYPEOTHER && body->encoding == ENCOTHER)
2590
	      body->encoding = ENCBINARY;
2591
	}
2592
    }
2593
    else{
2594
	/* PROBLEM !!! */
2595
	if(body->type == TYPEOTHER){
2596
	    body->type = TYPEAPPLICATION;
2597
	    body->subtype = cpystr("octet-stream");
2598
	    if(body->encoding == ENCOTHER)
2599
		body->encoding = ENCBINARY;
2600
	}
2601
    }
2602
2603
    if (body->encoding == ENCOTHER || body->type == TYPEOTHER){
2604
#if defined(DOS) || defined(OS2) /* for binary file detection */
2605
	int lastchar = '\0';
2606
#define BREAKOUT 300   /* a value that a character can't be */
2607
#endif
2608
2609
	p   = bol = buf;
2610
	len = n;
2611
	while (n--){
2612
/* Some people don't like quoted-printable caused by leading Froms */
2613
#ifdef ENCODE_FROMS
2614
	    Find_Froms(froms, dots, bmap, dmap, *p);
2615
#endif
2616
	    if(*p == '\n'){
2617
		max_line    = MAX(max_line, line_so_far + p - bol);
2618
		bol	    = NULL;		/* clear beginning of line */
2619
		line_so_far = 0L;		/* clear line count */
2620
#if	defined(DOS) || defined(OS2)
2621
		/* LF with no CR!! */
2622
		if(lastchar != '\r')		/* must be non-text data! */
2623
		  lastchar = BREAKOUT;
2624
#endif
2625
	    }
2626
	    else if(*p & 0x80){
2627
		eight_bit_chars++;
2628
	    }
2629
	    else if(!*p){
2630
		/* NULL found. Unless we're told otherwise, must be binary */
2631
		if(body->type == TYPEOTHER){
2632
		    body->type    = TYPEAPPLICATION;
2633
		    body->subtype = cpystr("octet-stream");
2634
		}
2635
2636
		/*
2637
		 * The "TYPETEXT" here handles the case that the NULL
2638
		 * comes from imported text generated by some external
2639
		 * editor that permits or inserts NULLS.  Otherwise,
2640
		 * assume it's a binary segment...
2641
		 */
2642
		new_encoding = (body->type==TYPETEXT) ? ENC8BIT : ENCBINARY;
2643
2644
		/*
2645
		 * Since we've already set encoding, count this as a 
2646
		 * hi bit char and continue.  The reason is that if this
2647
		 * is text, there may be a high percentage of encoded 
2648
		 * characters, so base64 may get set below...
2649
		 */
2650
		if(body->type == TYPETEXT)
2651
		  eight_bit_chars++;
2652
		else
2653
		  break;
2654
	    }
2655
2656
#if defined(DOS) || defined(OS2) /* for binary file detection */
2657
	    if(lastchar != BREAKOUT)
2658
	      lastchar = *p;
2659
#endif
2660
2661
	    /* read another buffer in */
2662
	    if(n == 0){
2663
		if(bol)
2664
		  line_so_far += p - bol;
2665
2666
		for (n = 0; n < RBUFSZ-1 && so_readc(&buf[n], so) != 0; n++)
2667
		  ;
2668
2669
		len += n;
2670
		p = buf;
2671
	    }
2672
	    else
2673
	      p++;
2674
2675
	    /*
2676
	     * If there's no beginning-of-line pointer, then we must
2677
	     * have seen an end-of-line.  Set bol to the start of the
2678
	     * new line...
2679
	     */
2680
	    if(!bol)
2681
	      bol = p;
2682
2683
#if defined(DOS) || defined(OS2) /* for binary file detection */
2684
	    /* either a lone \r or lone \n indicate binary file */
2685
	    if(lastchar == '\r' || lastchar == BREAKOUT){
2686
		if(lastchar == BREAKOUT || n == 0 || *p != '\n'){
2687
		    if(body->type == TYPEOTHER){
2688
			body->type    = TYPEAPPLICATION;
2689
			body->subtype = cpystr("octet-stream");
2690
		    }
2691
2692
		    new_encoding = ENCBINARY;
2693
		    break;
2694
		}
2695
	    }
2696
#endif
2697
	}
2698
    }
2699
2700
    /* stash away for later */
2701
    so_attr(so, "maxline", long2string(max_line));
2702
2703
    if(body->encoding == ENCOTHER || body->type == TYPEOTHER){
2704
	/*
2705
	 * Since the type or encoding aren't set yet, fall thru a 
2706
	 * series of tests to make sure an adequate type and 
2707
	 * encoding are set...
2708
	 */
2709
2710
	if(max_line >= 1000L){ 		/* 1000 comes from rfc821 */
2711
	    if(body->type == TYPEOTHER){
2712
		/*
2713
		 * Since the types not set, then we didn't find a NULL.
2714
		 * If there's no NULL, then this is likely text.  However,
2715
		 * since we can't be *completely* sure, we set it to
2716
		 * the generic type.
2717
		 */
2718
		body->type    = TYPEAPPLICATION;
2719
		body->subtype = cpystr("octet-stream");
2720
	    }
2721
2722
	    if(new_encoding != ENCBINARY)
2723
	      /*
2724
	       * As with NULL handling, if we're told it's text, 
2725
	       * qp-encode it, else it gets base 64...
2726
	       */
2727
	      new_encoding = (body->type == TYPETEXT) ? ENC8BIT : ENCBINARY;
2728
	}
2729
2730
	if(eight_bit_chars == 0L){
2731
	    if(body->type == TYPEOTHER)
2732
	      body->type = TYPETEXT;
2733
2734
	    if(new_encoding == ENCOTHER)
2735
	      new_encoding = ENC7BIT;  /* short lines, no 8 bit */
2736
	}
2737
	else if(len <= 3000L || (eight_bit_chars * 100L)/len < 30L){
2738
	    /*
2739
	     * The 30% threshold is based on qp encoded readability
2740
	     * on non-MIME UA's.
2741
	     */
2742
	    if(body->type == TYPEOTHER)
2743
	      body->type = TYPETEXT;
2744
2745
	    if(new_encoding != ENCBINARY)
2746
	      new_encoding = ENC8BIT;  /* short lines, < 30% 8 bit chars */
2747
	}
2748
	else{
2749
	    if(body->type == TYPEOTHER){
2750
		body->type    = TYPEAPPLICATION;
2751
		body->subtype = cpystr("octet-stream");
2752
	    }
2753
2754
	    /*
2755
	     * Apply maximal encoding regardless of previous
2756
	     * setting.  This segment's either not text, or is 
2757
	     * unlikely to be readable with > 30% of the
2758
	     * text encoded anyway, so we might as well save space...
2759
	     */
2760
	    new_encoding = ENCBINARY;   /*  > 30% 8 bit chars */
2761
	}
2762
    }
2763
2764
#ifdef ENCODE_FROMS
2765
    /* If there were From_'s at the beginning of a line or standalone dots */
2766
    if((froms || dots) && new_encoding != ENCBINARY)
2767
      new_encoding = ENC8BIT;
2768
#endif
2769
2770
    /* Set the subtype */
2771
    if(body->subtype == NULL)
2772
      body->subtype = cpystr(rfc822_default_subtype(body->type));
2773
2774
    if(body->encoding == ENCOTHER)
2775
      body->encoding = new_encoding;
2776
2777
    fs_give((void **)&buf);
2778
2779
    if(we_cancel)
2780
      cancel_busy_cue(-1);
2781
}
2782
2783
1.1.1 by Asheesh Laroia
Import upstream version 0.98+dfsg
2784
/*
2785
 * Call this to set the charset of an attachment we have
2786
 * created. If the attachment contains any non-ascii characters
2787
 * then we'll set the charset to the passed in charset, otherwise
2788
 * we'll make it us-ascii.
2789
 */
1 by Asheesh Laroia
Import upstream version 0.82+dfsg
2790
void
1.1.1 by Asheesh Laroia
Import upstream version 0.98+dfsg
2791
set_charset_possibly_to_ascii(struct mail_bodystruct *body, char *charset)
1 by Asheesh Laroia
Import upstream version 0.82+dfsg
2792
{
2793
    unsigned char c;
2794
    int           can_be_ascii = 1;
2795
    STORE_S      *so = (STORE_S *)body->contents.text.data;
2796
    int           we_cancel = 0;
2797
1.1.1 by Asheesh Laroia
Import upstream version 0.98+dfsg
2798
    if(!body || body->type != TYPETEXT)
1 by Asheesh Laroia
Import upstream version 0.82+dfsg
2799
      return;
2800
2801
    we_cancel = busy_cue(NULL, NULL, 1);
2802
2803
    so_seek(so, 0L, 0);
2804
2805
    while(can_be_ascii && so_readc(&c, so))
1.1.1 by Asheesh Laroia
Import upstream version 0.98+dfsg
2806
      if(!c || c & 0x80)
1 by Asheesh Laroia
Import upstream version 0.82+dfsg
2807
	can_be_ascii--;
2808
1.1.1 by Asheesh Laroia
Import upstream version 0.98+dfsg
2809
    if(can_be_ascii)
2810
      set_parameter(&body->parameter, "charset", "US-ASCII");
2811
    else if(charset && *charset && strucmp(charset, "US-ASCII"))
2812
      set_parameter(&body->parameter, "charset", charset);
1 by Asheesh Laroia
Import upstream version 0.82+dfsg
2813
    else{
2814
	/*
2815
	 * Else we don't know. There are non ascii characters but we either
2816
	 * don't have a charset to set it to or that charset is just us_ascii,
2817
	 * which is impossible. So we label it unknown. An alternative would
2818
	 * have been to strip the high bits instead and label it ascii.
2819
	 */
1.1.1 by Asheesh Laroia
Import upstream version 0.98+dfsg
2820
      set_parameter(&body->parameter, "charset", UNKNOWN_CHARSET);
1 by Asheesh Laroia
Import upstream version 0.82+dfsg
2821
    }
1.1.1 by Asheesh Laroia
Import upstream version 0.98+dfsg
2822
2823
    if(we_cancel)
2824
      cancel_busy_cue(-1);
1 by Asheesh Laroia
Import upstream version 0.82+dfsg
2825
}
2826
2827
2828
/*
2829
 * since encoding happens on the way out the door, this is basically
2830
 * just needed to handle TYPEMULTIPART
2831
 */
2832
void
2833
pine_encode_body (struct mail_bodystruct *body)
2834
{
2835
  PART *part;
2836
2837
  dprint((4, "-- pine_encode_body: %d\n", body ? body->type : 0));
2838
  if (body) switch (body->type) {
1.1.7 by Asheesh Laroia
Import upstream version 2.00+dfsg
2839
    char *freethis;
2840
1 by Asheesh Laroia
Import upstream version 0.82+dfsg
2841
    case TYPEMULTIPART:		/* multi-part */
1.1.7 by Asheesh Laroia
Import upstream version 2.00+dfsg
2842
      if(!(freethis=parameter_val(body->parameter, "BOUNDARY"))){
1 by Asheesh Laroia
Import upstream version 0.82+dfsg
2843
	  char tmp[MAILTMPLEN];	/* make cookie not in BASE64 or QUOTEPRINT*/
2844
2845
	  snprintf (tmp,sizeof(tmp),"%ld-%ld-%ld=:%ld",gethostid (),random (),(long) time (0),
2846
		    (long) getpid ());
2847
	  tmp[sizeof(tmp)-1] = '\0';
1.1.1 by Asheesh Laroia
Import upstream version 0.98+dfsg
2848
	  set_parameter(&body->parameter, "BOUNDARY", tmp);
1 by Asheesh Laroia
Import upstream version 0.82+dfsg
2849
      }
1.1.7 by Asheesh Laroia
Import upstream version 2.00+dfsg
2850
2851
      if(freethis)
2852
        fs_give((void **) &freethis);
2853
1 by Asheesh Laroia
Import upstream version 0.82+dfsg
2854
      part = body->nested.part;	/* encode body parts */
2855
      do pine_encode_body (&part->body);
1.1.3 by Asheesh Laroia
Import upstream version 0.9999+dfsg
2856
      while ((part = part->next) != NULL);	/* until done */
1 by Asheesh Laroia
Import upstream version 0.82+dfsg
2857
      break;
2858
2859
    case TYPETEXT :
2860
	/*
2861
	 * If the part is text we edited, then it is UTF-8.
2862
	 * The user may be asking us to send it as something else
2863
	 * or we may want to downconvert to a more-specific characterset.
2864
	 * Mark it for conversion here so the right MIME header's written.
2865
	 * Do conversion pine_rfc822_output_body.
2866
	 * Attachments are left as is.
2867
	 */
2868
      if(body->contents.text.data
2869
	 && so_attr((STORE_S *) body->contents.text.data, "edited", NULL)){
2870
	  char *charset, *posting_charset, *lp;
2871
1.1.7 by Asheesh Laroia
Import upstream version 2.00+dfsg
2872
	  if(!((charset = parameter_val(body->parameter, "charset"))
1.1.3 by Asheesh Laroia
Import upstream version 0.9999+dfsg
2873
	        && !strucmp(charset, UNKNOWN_CHARSET))
2874
	     && (posting_charset = posting_characterset(body, charset, MsgBody))){
2875
1.1.1 by Asheesh Laroia
Import upstream version 0.98+dfsg
2876
	      set_parameter(&body->parameter, "charset", posting_charset);
1 by Asheesh Laroia
Import upstream version 0.82+dfsg
2877
		
1.1.5 by Asheesh Laroia
Import upstream version 1.0+dfsg
2878
	      /*
2879
	       * Fix iso-2022-jp encoding to ENC7BIT since it's escape based
2880
	       * and doesn't use anything but ASCII characters.
2881
	       * Why is it not ENC7BIT already? Because when we set the encoding
2882
	       * in set_mime_type_by_grope we were groping through UTF-8 text
2883
	       * not 2022 text. Not only that, but we didn't know at that point
2884
	       * that it wouldn't stay UTF-8 when we sent it, which would require
2885
	       * encoding.
2886
	       */
1 by Asheesh Laroia
Import upstream version 0.82+dfsg
2887
	      if(!strucmp(posting_charset, "iso-2022-jp")
2888
		 && (lp = so_attr((STORE_S *) body->contents.text.data, "maxline", NULL))
2889
		 && strlen(lp) < 4)
2890
		body->encoding = ENC7BIT;
2891
	  }
2892
2893
	  if(charset)
2894
	    fs_give((void **)&charset);
2895
      }	  
2896
2897
      break;
2898
2899
/* case MESSAGE:	*/	/* here for documentation */
2900
      /* Encapsulated messages are always treated as text objects at this point.
2901
	 This means that you must replace body->contents.msg with
2902
	 body->contents.text, which probably involves copying
2903
	 body->contents.msg.text to body->contents.text */
2904
    default:			/* all else has some encoding */
2905
      /*
2906
       * but we'll delay encoding it until the message is on the way
2907
       * into the mail slot...
2908
       */
2909
      break;
2910
  }
2911
}
2912
2913
2914
/*
2915
 * pine_header_line - simple wrapper around c-client call to contain
2916
 *                    repeated code, and to write fcc if required.
2917
 */
2918
int
2919
pine_header_line(char *field, METAENV *header, char *text, soutr_t f, void *s,
2920
		 int writehdr, int localcopy)
2921
{
2922
    int   ret = 1;
2923
    int   big = 10000;
2924
    char *value, *folded = NULL, *cs;
2925
    char *converted;
2926
2927
    if(!text)
2928
      return 1;
2929
1.1.3 by Asheesh Laroia
Import upstream version 0.9999+dfsg
2930
    converted = utf8_to_charset(text, cs = posting_characterset(text, NULL, HdrText), 0);
1 by Asheesh Laroia
Import upstream version 0.82+dfsg
2931
2932
    if(converted){
1.1.1 by Asheesh Laroia
Import upstream version 0.98+dfsg
2933
	if(cs && !strucmp(cs, "us-ascii"))
2934
	  value = converted;
2935
	else
2936
	  value = encode_header_value(tmp_20k_buf, SIZEOF_20KBUF,
2937
				      (unsigned char *) converted, cs,
2938
				      encode_whole_header(field, header));
1 by Asheesh Laroia
Import upstream version 0.82+dfsg
2939
2940
	if(value && value == converted){	/* no encoding was done, have to fold */
2941
	    int   fold_by, len;
2942
	    char *actual_field;
2943
2944
	    len = ((header && header->env && header->env->remail)
2945
			? strlen("ReSent-") : 0) +
2946
		  (field ? strlen(field) : 0) + 2;
2947
		  
2948
	    actual_field = (char *)fs_get((len+1) * sizeof(char));
2949
	    snprintf(actual_field, len+1, "%s%s: ",
2950
		    (header && header->env && header->env->remail) ? "ReSent-" : "",
2951
		    field ? field : "");
2952
	    actual_field[len] = '\0';
2953
2954
	    /*
2955
	     * We were folding everything except message-id, but that wasn't
2956
	     * sufficient. Since 822 only allows folding where linear-white-space
2957
	     * is allowed we'd need a smarter folder than "fold" to do it. So,
2958
	     * instead of inventing that smarter folder (which would have to
1.1.3 by Asheesh Laroia
Import upstream version 0.9999+dfsg
2959
	     * know 822 syntax)
1 by Asheesh Laroia
Import upstream version 0.82+dfsg
2960
	     *
2961
	     * We could just alloc space and copy the actual_field followed by
2962
	     * the value into it, but since that's what fold does anyway we'll
2963
	     * waste some cpu time and use fold with a big fold parameter.
1.1.3 by Asheesh Laroia
Import upstream version 0.9999+dfsg
2964
	     *
2965
	     * We upped the references folding from 75 to 256 because we were
2966
	     * encountering longer-than-75 message ids, and to break one line
2967
	     * in references is to break them all.
1 by Asheesh Laroia
Import upstream version 0.82+dfsg
2968
	     */
1.1.3 by Asheesh Laroia
Import upstream version 0.9999+dfsg
2969
	    if(field && !strucmp("Subject", field))
1 by Asheesh Laroia
Import upstream version 0.82+dfsg
2970
	      fold_by = 75;
1.1.3 by Asheesh Laroia
Import upstream version 0.9999+dfsg
2971
	    else if(field && !strucmp("References", field))
2972
	      fold_by = 256;
1 by Asheesh Laroia
Import upstream version 0.82+dfsg
2973
	    else
2974
	      fold_by = big;
2975
2976
	    folded = fold(value, fold_by, big, actual_field, " ", FLD_CRLF);
2977
2978
	    if(actual_field)
2979
	      fs_give((void **)&actual_field);
2980
	}
2981
	else if(value){			/* encoding was done */
2982
	    RFC822BUFFER rbuf;
2983
	    size_t ll;
2984
2985
	    /*
2986
	     * rfc1522_encode already inserted continuation lines and did
2987
	     * the necessary folding so we don't have to do it. Let
2988
	     * rfc822_header_line add the trailing crlf and the resent- if
2989
	     * necessary. The 20 could actually be a 12.
2990
	     */
2991
	    ll = strlen(field) + strlen(value) + 20;
2992
	    folded = (char *) fs_get(ll * sizeof(char));
2993
	    *folded = '\0';
2994
	    rbuf.f   = dummy_soutr;
2995
	    rbuf.s   = NULL;
2996
	    rbuf.beg = folded;
2997
	    rbuf.cur = folded;
2998
	    rbuf.end = folded+ll-1;
2999
	    rfc822_output_header_line(&rbuf, field,
3000
		(header && header->env && header->env->remail) ? LONGT : 0L, value);
3001
	    *rbuf.cur = '\0';
3002
	}
3003
1.1.1 by Asheesh Laroia
Import upstream version 0.98+dfsg
3004
	if(value && folded){
1 by Asheesh Laroia
Import upstream version 0.82+dfsg
3005
	    if(writehdr && f)
3006
	      ret = (*f)(s, folded);
3007
	    
3008
	    if(ret && localcopy && lmc.so && !lmc.all_written)
3009
	      ret = so_puts(lmc.so, folded);
3010
	}
3011
1.1.1 by Asheesh Laroia
Import upstream version 0.98+dfsg
3012
	if(folded)
3013
	  fs_give((void **)&folded);
3014
3015
	if(converted && converted != text)
1 by Asheesh Laroia
Import upstream version 0.82+dfsg
3016
	  fs_give((void **) &converted);
3017
    }
3018
    else
3019
      ret = 0;
3020
    
3021
    return(ret);
3022
}
3023
3024
3025
/*
3026
 * Do appropriate encoding of text header lines.
3027
 * For some field types (those that consist of 822 *text) we just encode
3028
 * the whole thing. For structured fields we encode only within comments
3029
 * if possible.
3030
 *
3031
 * Args      d -- Destination buffer if needed. (tmp_20k_buf)
3032
 *           s -- Source string.
3033
 *     charset -- Charset to encode with.
3034
 *  encode_all -- If set, encode the whole string. If not, try to encode
3035
 *                only within comments if possible.
3036
 *
3037
 * Returns   S is returned if no encoding is done. D is returned if encoding
3038
 *           was needed.
3039
 */
3040
char *
3041
encode_header_value(char *d, size_t dlen, unsigned char *s, char *charset, int encode_all)
3042
{
3043
    char *p, *q, *r, *start_of_comment = NULL, *value = NULL;
3044
    int   in_comment = 0;
3045
3046
    if(!s)
3047
      return((char *)s);
3048
    
3049
    if(dlen < SIZEOF_20KBUF)
3050
      panic("bad call to encode_header_value");
3051
3052
    if(!encode_all){
3053
	/*
3054
	 * We don't have to worry about keeping track of quoted-strings because
3055
	 * none of these fields which aren't addresses contain quoted-strings.
3056
	 * We do keep track of escaped parens inside of comments and comment
3057
	 * nesting.
3058
	 */
3059
	p = d+7000;
3060
	for(q = (char *)s; *q; q++){
3061
	    switch(*q){
3062
	      case LPAREN:
3063
		if(in_comment++ == 0)
3064
		  start_of_comment = q;
3065
3066
		break;
3067
3068
	      case RPAREN:
3069
		if(--in_comment == 0){
3070
		    /* encode the comment, excluding the outer parens */
3071
		    if(p-d < dlen-1)
3072
		      *p++ = LPAREN;
3073
3074
		    *q = '\0';
3075
		    r = rfc1522_encode(d+14000, dlen-14000,
3076
				       (unsigned char *)start_of_comment+1,
3077
				       charset);
3078
		    if(r != start_of_comment+1)
3079
		      value = d+7000;  /* some encoding was done */
3080
3081
		    start_of_comment = NULL;
3082
		    if(r)
3083
		      sstrncpy(&p, r, dlen-1-(p-d));
3084
3085
		    *q = RPAREN;
3086
		    if(p-d < dlen-1)
3087
		      *p++ = *q;
3088
		}
3089
		else if(in_comment < 0){
3090
		    in_comment = 0;
3091
		    if(p-d < dlen-1)
3092
		      *p++ = *q;
3093
		}
3094
3095
		break;
3096
3097
	      case BSLASH:
3098
		if(!in_comment && *(q+1)){
3099
		    if(p-d < dlen-2){
3100
			*p++ = *q++;
3101
			*p++ = *q;
3102
		    }
3103
		}
3104
3105
		break;
3106
3107
	      default:
3108
		if(!in_comment && p-d < dlen-1)
3109
		  *p++ = *q;
3110
3111
		break;
3112
	    }
3113
	}
3114
3115
	if(value){
3116
	    /* Unterminated comment (wasn't really a comment) */
3117
	    if(start_of_comment)
3118
	      sstrncpy(&p, start_of_comment, dlen-1-(p-d));
3119
	    
3120
	    *p = '\0';
3121
	}
3122
    }
3123
3124
    /*
3125
     * We have to check if there is anything that needs to be encoded that
3126
     * wasn't in a comment. If there is, we'd better just start over and
3127
     * encode the whole thing. So, if no encoding has been done within
3128
     * comments, or if encoding is needed both within and outside of
3129
     * comments, then we encode the whole thing. Otherwise, go with
3130
     * the version that has only comments encoded.
3131
     */
3132
    if(!value || rfc1522_encode(d, dlen,
3133
				(unsigned char *)value, charset) != value)
3134
      return(rfc1522_encode(d, dlen, s, charset));
3135
    else{
3136
	strncpy(d, value, dlen-1);
3137
	d[dlen-1] = '\0';
3138
	return(d);
3139
    }
3140
}
3141
3142
3143
/*
3144
 * pine_address_line - write a header field containing addresses,
3145
 *                     one by one (so there's no buffer limit), and
3146
 *                     wrapping where necessary.
3147
 * Note: we use c-client functions to properly build the text string,
3148
 *       but have to screw around with pointers to fool c-client functions
3149
 *       into not blatting all the text into a single buffer.  Yeah, I know.
3150
 */
3151
int
3152
pine_address_line(char *field, METAENV *header, struct mail_address *alist,
3153
		  soutr_t f, void *s, int writehdr, int localcopy)
3154
{
3155
    char     tmp[MAX_SINGLE_ADDR], *tmpptr = NULL;
3156
    size_t   alloced = 0, sz;
3157
    char    *delim, *ptmp, *mtmp, buftmp[MAILTMPLEN];
3158
    char    *converted, *cs;
3159
    ADDRESS *atmp;
3160
    int      i, count;
3161
    int      in_group = 0, was_start_of_group = 0, fix_lcc = 0, failed = 0;
3162
    RFC822BUFFER rbuf;
3163
    static   char comma[]     = ", ";
3164
    static   char end_group[] = ";";
3165
#define	no_comma	(&comma[1])
3166
3167
    if(!alist)				/* nothing in field! */
3168
      return(1);
3169
3170
    if(!alist->host && alist->mailbox){ /* c-client group convention */
3171
        in_group++;
3172
	was_start_of_group++;
3173
	/* encode mailbox of group */
3174
	mtmp	    = alist->mailbox;
3175
	if(mtmp){
3176
	    snprintf(buftmp, sizeof(buftmp), "%s", mtmp);
3177
	    buftmp[sizeof(buftmp)-1] = '\0';
1.1.3 by Asheesh Laroia
Import upstream version 0.9999+dfsg
3178
	    converted = utf8_to_charset(buftmp, cs = posting_characterset(buftmp, NULL, HdrText), 0);
1 by Asheesh Laroia
Import upstream version 0.82+dfsg
3179
	    if(converted){
3180
		alist->mailbox = cpystr(rfc1522_encode(tmp_20k_buf, SIZEOF_20KBUF,
3181
							(unsigned char *) converted, cs));
1.1.1 by Asheesh Laroia
Import upstream version 0.98+dfsg
3182
		if(converted && converted != buftmp)
1 by Asheesh Laroia
Import upstream version 0.82+dfsg
3183
		  fs_give((void **) &converted);
3184
	    }
3185
	    else{
3186
		failed++;
3187
		goto bail_out;
3188
	    }
3189
	}
3190
    }
3191
    else
3192
      mtmp = NULL;
3193
3194
    ptmp	    = alist->personal;	/* remember personal name */
3195
    /* make sure personal name is encoded */
3196
    if(ptmp){
3197
	snprintf(buftmp, sizeof(buftmp), "%s", ptmp);
3198
	buftmp[sizeof(buftmp)-1] = '\0';
1.1.3 by Asheesh Laroia
Import upstream version 0.9999+dfsg
3199
	converted = utf8_to_charset(buftmp, cs = posting_characterset(buftmp, NULL, HdrText), 0);
1 by Asheesh Laroia
Import upstream version 0.82+dfsg
3200
	if(converted){
3201
	    alist->personal = cpystr(rfc1522_encode(tmp_20k_buf, SIZEOF_20KBUF,
3202
						    (unsigned char *) converted, cs));
1.1.1 by Asheesh Laroia
Import upstream version 0.98+dfsg
3203
	    if(converted && converted != buftmp)
1 by Asheesh Laroia
Import upstream version 0.82+dfsg
3204
	      fs_give((void **) &converted);
3205
	}
3206
	else{
3207
	    failed++;
3208
	    goto bail_out;
3209
	}
3210
    }
3211
3212
    atmp            = alist->next;
3213
    alist->next	    = NULL;		/* digest only first address! */
3214
3215
    /* use automatic buffer unless it isn't big enough */
3216
    if((alloced = est_size(alist)) > sizeof(tmp)){
3217
      tmpptr = (char *)fs_get(alloced);
3218
      sz = alloced;
3219
    }
3220
    else{
3221
      tmpptr = tmp;
3222
      sz = sizeof(tmp);
3223
    }
3224
3225
    rbuf.f   = dummy_soutr;
3226
    rbuf.s   = NULL;
3227
    rbuf.beg = tmpptr;
3228
    rbuf.cur = tmpptr;
3229
    rbuf.end = tmpptr+sz-1;
3230
    rfc822_output_address_line(&rbuf, field,
3231
	(header && header->env && header->env->remail) ? LONGT : 0L, alist, NULL);
3232
    *rbuf.cur = '\0';
3233
3234
    alist->next	    = atmp;		/* restore pointer to next addr */
3235
3236
    if(alist->personal && alist->personal != ptmp)
3237
      fs_give((void **) &alist->personal);
3238
3239
    alist->personal = ptmp;		/* in case it changed, restore name */
3240
3241
    if(mtmp){
3242
	if(alist->mailbox && alist->mailbox != mtmp)
3243
	  fs_give((void **) &alist->mailbox);
3244
3245
	alist->mailbox = mtmp;
3246
    }
3247
3248
    if((count = strlen(tmpptr)) > 2){	/* back over CRLF */
3249
	count -= 2;
3250
	tmpptr[count] = '\0';
3251
    }
3252
3253
    /*
3254
     * If there is no sending_stream and we are writing the Lcc header,
3255
     * then we are piping it to sendmail -t which expects it to be a bcc,
3256
     * not lcc.
3257
     *
3258
     * When we write it to the fcc or postponed (the lmc.so),
3259
     * we want it to be lcc, not bcc, so we put it back.
3260
     */
3261
    if(!sending_stream && writehdr && struncmp("lcc:", tmpptr, 4) == 0)
3262
      fix_lcc = 1;
3263
3264
    if(writehdr && f && *tmpptr){
3265
	if(fix_lcc)
3266
	  tmpptr[0] = 'b';
3267
	
3268
	failed = !(*f)(s, tmpptr);
3269
	if(fix_lcc)
3270
	  tmpptr[0] = 'L';
3271
3272
	if(failed)
3273
	  goto bail_out;
3274
    }
3275
3276
    if(localcopy && lmc.so &&
3277
       !lmc.all_written && *tmpptr && !so_puts(lmc.so, tmpptr))
3278
      goto bail_out;
3279
3280
    for(alist = atmp; alist; alist = alist->next){
3281
	delim = comma;
3282
	/* account for c-client's representation of group names */
3283
	if(in_group){
3284
	    if(!alist->host){  /* end of group */
3285
		in_group = 0;
3286
		was_start_of_group = 0;
3287
		/*
3288
		 * Rfc822_write_address no longer writes out the end of group
3289
		 * unless the whole group address is passed to it, so we do
3290
		 * it ourselves.
3291
		 */
3292
		delim = end_group;
3293
	    }
3294
	    else if(!localcopy || !lmc.so || lmc.all_written)
3295
	      continue;
3296
	}
3297
	/* start of new group, print phrase below */
3298
        else if(!alist->host && alist->mailbox){
3299
	    in_group++;
3300
	    was_start_of_group++;
3301
	}
3302
3303
	/* no comma before first address in group syntax */
3304
	if(was_start_of_group && alist->host){
3305
	    delim = no_comma;
3306
	    was_start_of_group = 0;
3307
	}
3308
3309
	/* write delimiter */
3310
	if(((!in_group||was_start_of_group) && writehdr && f && !(*f)(s, delim))
3311
	   || (localcopy && lmc.so && !lmc.all_written
3312
	       && !so_puts(lmc.so, delim)))
3313
	  goto bail_out;
3314
3315
	ptmp		= alist->personal; /* remember personal name */
3316
	snprintf(buftmp, sizeof(buftmp), "%.200s", ptmp ? ptmp : "");
3317
	buftmp[sizeof(buftmp)-1] = '\0';
1.1.3 by Asheesh Laroia
Import upstream version 0.9999+dfsg
3318
	converted = utf8_to_charset(buftmp, cs = posting_characterset(buftmp, NULL, HdrText), 0);
1 by Asheesh Laroia
Import upstream version 0.82+dfsg
3319
	if(converted){
3320
	    alist->personal = cpystr(rfc1522_encode(tmp_20k_buf, SIZEOF_20KBUF,
3321
						    (unsigned char *) converted, cs));
1.1.1 by Asheesh Laroia
Import upstream version 0.98+dfsg
3322
	    if(converted && converted != buftmp)
1 by Asheesh Laroia
Import upstream version 0.82+dfsg
3323
	      fs_give((void **) &converted);
3324
	}
3325
	else{
3326
	    failed++;
3327
	    goto bail_out;
3328
	}
3329
3330
	atmp		= alist->next;
3331
	alist->next	= NULL;		/* tie off linked list */
3332
	if((i = est_size(alist)) > MAX(sizeof(tmp), alloced)){
3333
	    alloced = i;
3334
	    sz = alloced;
3335
	    fs_resize((void **)&tmpptr, alloced);
3336
	}
3337
3338
	*tmpptr  = '\0';
3339
	/* make sure we don't write out group end with rfc822_write_address */
3340
        if(alist->host || alist->mailbox){
3341
	    rbuf.f   = dummy_soutr;
3342
	    rbuf.s   = NULL;
3343
	    rbuf.beg = tmpptr;
3344
	    rbuf.cur = tmpptr;
3345
	    rbuf.end = tmpptr+sz-1;
3346
	    rfc822_output_address_list(&rbuf, alist, 0L, NULL);
3347
	    *rbuf.cur = '\0';
3348
	}
3349
3350
	alist->next	= atmp;		/* restore next pointer */
3351
3352
	if(alist->personal && alist->personal != ptmp)
3353
	  fs_give((void **) &alist->personal);
3354
3355
	alist->personal = ptmp;		/* in case it changed, restore name */
3356
3357
	/*
3358
	 * BUG
3359
	 * With group syntax addresses we no longer have two identical
3360
	 * streams of output.  Instead, for the fcc/postpone copy we include
3361
	 * all of the addresses inside the :; of the group, and for the
3362
	 * mail we're sending we don't include them.  That means we aren't
3363
	 * correctly keeping track of the column to wrap in, below.  That is,
3364
	 * we are keeping track of the fcc copy but we aren't keeping track
3365
	 * of the regular copy.  It could result in too long or too short
3366
	 * lines.  Should almost never come up since group addresses are almost
3367
	 * never followed by other addresses in the same header, and even
3368
	 * when they are, you have to go out of your way to get the headers
3369
	 * messed up.
3370
	 */
3371
	if(count + 2 + (i = strlen(tmpptr)) > 78){ /* wrap long lines... */
3372
	    count = i + 4;
3373
	    if((!in_group && writehdr && f && !(*f)(s, "\015\012    "))
3374
	       || (localcopy && lmc.so && !lmc.all_written &&
3375
				       !so_puts(lmc.so, "\015\012    ")))
3376
	      goto bail_out;
3377
	}
3378
	else
3379
	  count += i + 2;
3380
3381
	if(((!in_group || was_start_of_group)
3382
	    && writehdr && *tmpptr && f && !(*f)(s, tmpptr))
3383
	   || (localcopy && lmc.so && !lmc.all_written
3384
	       && *tmpptr && !so_puts(lmc.so, tmpptr)))
3385
	  goto bail_out;
3386
    }
3387
3388
bail_out:
3389
    if(tmpptr && tmpptr != tmp)
3390
      fs_give((void **)&tmpptr);
3391
3392
    if(failed)
3393
      return(0);
3394
3395
    return((writehdr && f ? (*f)(s, "\015\012") : 1)
3396
	   && ((localcopy && lmc.so
3397
	       && !lmc.all_written) ? so_puts(lmc.so, "\015\012") : 1));
3398
}
3399
3400
3401
/*
3402
 * mutated pine version of c-client's rfc822_header() function. 
3403
 * changed to call pine-wrapped header and address functions
3404
 * so we don't have to limit the header size to a fixed buffer.
3405
 * This function also calls pine's body_header write function
3406
 * because encoding is delayed until output_body() is called.
3407
 */
3408
long
3409
pine_rfc822_header(METAENV *header, struct mail_bodystruct *body, soutr_t f, void *s)
3410
{
3411
    PINEFIELD  *pf;
3412
    int         j;
3413
3414
    if(header->env->remail){			/* if remailing */
3415
	long i = strlen (header->env->remail);
3416
	if(i > 4 && header->env->remail[i-4] == '\015')
3417
	  header->env->remail[i-2] = '\0'; /* flush extra blank line */
3418
3419
	if((f && !(*f)(s, header->env->remail)) 
3420
	  || (lmc.so && !lmc.all_written
3421
	      && !so_puts(lmc.so, header->env->remail)))
3422
	  return(0L);				/* start with remail header */
3423
    }
3424
3425
    j = 0;
3426
    for(pf = header->sending_order[j]; pf; pf = header->sending_order[++j]){
3427
	switch(pf->type){
3428
	  /*
3429
	   * Warning:  This is confusing.  The 2nd to last argument used to
3430
	   * be just pf->writehdr.  We want Bcc lines to be written out
3431
	   * if we are handing off to a sendmail temp file but not if we
3432
	   * are talking smtp, so bcc's writehdr is set to 0 and
3433
	   * pine_address_line was sending if writehdr OR !sending_stream.
3434
	   * That works as long as we want to write everything when
3435
	   * !sending_stream (an mta handoff to sendmail).  But then we
3436
	   * added the undisclosed recipients line which should only get
3437
	   * written if writehdr is set, and not when we pass to a
3438
	   * sendmail temp file.  So pine_address_line has been changed
3439
	   * so it bases its decision solely on the writehdr passed to it,
3440
	   * and the logic that worries about Bcc and sending_stream
3441
	   * was moved up to the caller (here) to decide when to set it.
3442
	   *
3443
	   * So we have:
3444
	   *   undisclosed recipients:;  This will just be written
3445
	   *                             if writehdr was set and not
3446
	   *                             otherwise, nothing magical.
3447
	   *** We may want to change this, because sendmail -t doesn't handle
3448
	   *** the empty group syntax well unless it has been configured to
3449
	   *** do so.  It isn't configured by default, or in any of the
3450
	   *** sendmail v8 configs.  So we may want to not write this line
3451
	   *** if we're doing an mta_handoff (!sending_stream).
3452
	   *
3453
	   *   !sending_stream (which means a handoff to a sendmail -t)
3454
	   *           bcc or lcc both set the arg so they'll get written
3455
	   *             (There is also Lcc hocus pocus in pine_address_line
3456
	   *              which converts the Lcc: to Bcc: for sendmail
3457
	   *              processing.)
3458
	   *   sending_stream (which means an smtp handoff)
3459
	   *           bcc and lcc will never have writehdr set, so
3460
	   *             will never be written (They both do have rcptto set,
3461
	   *             so they both do cause RCPT TO commands.)
3462
	   *
3463
	   *   The localcopy is independent of sending_stream and is just
3464
	   *   written if it is set for all of these.
3465
	   */
3466
	  case Address:
3467
	    if(!pine_address_line(pf->name,
3468
				  header,
3469
				  pf->addr ? *pf->addr : NULL,
3470
				  f,
3471
				  s,
3472
				  (!strucmp("bcc",pf->name ? pf->name : "")
3473
				    || !strucmp("Lcc",pf->name ? pf->name : ""))
3474
					   ? !sending_stream
3475
					   : pf->writehdr,
3476
				  pf->localcopy))
3477
	      return(0L);
3478
3479
	    break;
3480
3481
	  case Fcc:
3482
	  case FreeText:
3483
	  case Subject:
3484
	    if(!pine_header_line(pf->name, header,
3485
				 pf->text ? *pf->text : NULL,
3486
				 f, s, pf->writehdr, pf->localcopy))
3487
	      return(0L);
3488
3489
	    break;
3490
3491
	  default:
3492
	    q_status_message1(SM_ORDER,3,7,"Unknown header type: %.200s",
3493
			      pf->name);
3494
	    break;
3495
	}
3496
    }
3497
3498
3499
#if	(defined(DOS) || defined(OS2)) && !defined(NOAUTH)
3500
    /*
3501
     * Add comforting "X-" header line indicating what sort of 
3502
     * authenticity the receiver can expect...
3503
     */
3504
    if(F_OFF(F_DISABLE_SENDER, ps_global)){
3505
	 NETMBX	     netmbox;
3506
	 char	     sstring[MAILTMPLEN], *label;	/* place to write  */
3507
	 MAILSTREAM *m;
3508
	 int         i, anonymous = 1;
3509
3510
	 for(i = 0; anonymous && i < ps_global->s_pool.nstream; i++){
3511
	     m = ps_global->s_pool.streams[i];
3512
	     if(m && sp_flagged(m, SP_LOCKED)
3513
		&& mail_valid_net_parse(m->mailbox, &netmbox)
3514
		&& !netmbox.anoflag)
3515
	       anonymous = 0;
3516
	 }
3517
3518
	 if(!anonymous){
3519
	     char  last_char = netmbox.host[strlen(netmbox.host) - 1],
3520
		  *user = (*netmbox.user)
3521
			    ? netmbox.user
3522
			    : cached_user_name(netmbox.mailbox);
3523
	     snprintf(sstring, sizeof(sstring), "%.300s@%s%.300s%s", user ? user : "NULL", 
3524
		     isdigit((unsigned char)last_char) ? "[" : "",
3525
		     netmbox.host,
3526
		     isdigit((unsigned char) last_char) ? "]" : "");
3527
	     sstring[sizeof(sstring)-1] = '\0';
3528
	     label = "X-X-Sender";		/* Jeez. */
3529
	     if(F_ON(F_USE_SENDER_NOT_X,ps_global))
3530
	       label += 4;
3531
	 }
3532
	 else{
3533
	     strncpy(sstring,"UNAuthenticated Sender", sizeof(sstring));
3534
	     sstring[sizeof(sstring)-1] = '\0';
3535
	     label = "X-Warning";
3536
	 }
3537
3538
	 if(!pine_header_line(label, header, sstring, f, s, 1, 1))
3539
	   return(0L);
3540
     }
3541
#endif
3542
3543
    if(body && !header->env->remail){	/* not if remail or no body */
3544
	if((f && !(*f)(s, MIME_VER))
3545
	   || (lmc.so && !lmc.all_written && !so_puts(lmc.so, MIME_VER))
3546
	   || !pine_write_body_header(body, f, s))
3547
	  return(0L);
3548
    }
3549
    else{					/* write terminating newline */
3550
	if((f && !(*f)(s, "\015\012"))
3551
	   || (lmc.so && !lmc.all_written && !so_puts(lmc.so, "\015\012")))
3552
	  return(0L);
3553
    }
3554
3555
    return(1L);
3556
}
3557
3558
3559
/*
3560
 * pine_rfc822_output - pine's version of c-client call.  Necessary here
3561
 *			since we're not using its structures as intended!
3562
 */
3563
long
3564
pine_rfc822_output(METAENV *header, struct mail_bodystruct *body, soutr_t f, void *s)
3565
{
3566
    int we_cancel = 0;
3567
    long retval;
3568
3569
    dprint((4, "-- pine_rfc822_output\n"));
3570
3571
    we_cancel = busy_cue(NULL, NULL, 1);
3572
    pine_encode_body(body);		/* encode body as necessary */
3573
    /* build and output RFC822 header, output body */
3574
    retval = pine_rfc822_header(header, body, f, s)
3575
	   && (body ? pine_rfc822_output_body(body, f, s) : 1L);
3576
3577
    if(we_cancel)
3578
      cancel_busy_cue(-1);
3579
3580
    return(retval);
3581
}
3582
3583
3584
/*
3585
 * post_rfc822_output - cloak for pine's 822 output routine.  Since
3586
 *			we can't pass opaque envelope thru c-client posting
3587
 *			logic, we need to wrap the real output inside
3588
 *			something that c-client knows how to call.
3589
 */
3590
long
3591
post_rfc822_output(char *tmp,
3592
		   ENVELOPE *env,
3593
		   struct mail_bodystruct *body,
3594
		   soutr_t f,
3595
		   void *s,
3596
		   long int ok8bit)
3597
{
3598
    return(pine_rfc822_output(send_header, body, f, s));
3599
}
3600
3601
3602
/*
3603
 * posting_characterset- determine what transliteration is reasonable
3604
 *                       for posting the given non-ascii messsage data.
3605
 *
1.1.3 by Asheesh Laroia
Import upstream version 0.9999+dfsg
3606
 *       preferred_charset is the charset the original data was labeled in.
3607
 *                         If we can keep that we do.
3608
 *
1 by Asheesh Laroia
Import upstream version 0.82+dfsg
3609
 *  Returns: always returns the preferred character set.
3610
 */
3611
char *
1.1.3 by Asheesh Laroia
Import upstream version 0.9999+dfsg
3612
posting_characterset(void *data, char *preferred_charset, MsgPart mp)
1 by Asheesh Laroia
Import upstream version 0.82+dfsg
3613
{
1.1.5 by Asheesh Laroia
Import upstream version 1.0+dfsg
3614
    unsigned long *charsetmap = NULL;
3615
    unsigned long validbitmap;
3616
    static char *ascii = "US-ASCII";
3617
    static char *utf8 = "UTF-8";
3618
    int notcjk = 0;
3619
1 by Asheesh Laroia
Import upstream version 0.82+dfsg
3620
    if(!ps_global->post_utf8){
1.1.5 by Asheesh Laroia
Import upstream version 1.0+dfsg
3621
	validbitmap = 0;
3622
3623
	if(mp == HdrText){
3624
	    char *text = NULL;
3625
	    UCS *ucs = NULL, *ucsp;
3626
3627
	    text = (char *) data;
3628
3629
	    /* convert text in header to UCS characters */
3630
	    if(text)
3631
	      ucsp = ucs = utf8_to_ucs4_cpystr(text);
3632
3633
	    if(!(ucs && *ucs))
3634
	      return(ascii);
3635
3636
	    /*
3637
	     * After the while loop is done the validbitmap has
3638
	     * a 1 bit for all the character sets that can
3639
	     * represent all of the characters of this header.
3640
	     */
3641
	    charsetmap = init_charsetchecker(preferred_charset);
3642
3643
	    if(!charsetmap)
3644
	      return(utf8);
3645
3646
	    validbitmap = ~0;
3647
	    while((validbitmap & ~0x1) && (*ucsp)){
3648
		if(*ucsp > 0xffff){
3649
		    fs_give((void **) &ucs);
3650
		    return(utf8);
3651
		}
3652
3653
		validbitmap &= charsetmap[(unsigned long) (*ucsp++)];
3654
	    }
3655
3656
	    fs_give((void **) &ucs);
3657
3658
	    notcjk = validbitmap & 0x1;
3659
	    validbitmap &= ~0x1;
3660
3661
	    if(!validbitmap)
3662
	      return(utf8);
3663
	}
3664
	else{
3665
	    struct mail_bodystruct *body = NULL;
3666
	    STORE_S *the_text = NULL;
3667
	    int outchars;
3668
	    unsigned char c;
3669
	    UCS ucs;
3670
	    CBUF_S cbuf;
3671
3672
	    cbuf.cbuf[0] = '\0';
3673
	    cbuf.cbufp = cbuf.cbuf;
3674
	    cbuf.cbufend = cbuf.cbuf;
3675
3676
	    body = (struct mail_bodystruct *) data;
3677
3678
	    if(body && body->type == TYPEMULTIPART)
3679
	      body = &body->nested.part->body;
3680
3681
	    if(body && body->type == TYPETEXT)
3682
	      the_text = (STORE_S *) body->contents.text.data;
3683
3684
	    if(!the_text)
3685
	      return(ascii);
3686
3687
	    so_seek(the_text, 0L, 0);		/* rewind */
3688
3689
	    charsetmap = init_charsetchecker(preferred_charset);
3690
3691
	    if(!charsetmap)
3692
	      return(utf8);
3693
3694
	    validbitmap = ~0;
3695
3696
	    /*
3697
	     * Read a stream of UTF-8 characters from the_text
3698
	     * and convert them to UCS-4 characters for the translatable
3699
	     * test.
3700
	     */
3701
	    while((validbitmap & ~0x1) && so_readc(&c, the_text)){
3702
		if((outchars = utf8_to_ucs4_oneatatime(c, &cbuf, &ucs, NULL)) > 0){
3703
		    /* got a ucs character */
3704
		    if(ucs > 0xffff)
3705
		      return(utf8);
3706
3707
		    validbitmap &= charsetmap[(unsigned long) ucs];
3708
		}
3709
	    }
3710
3711
	    notcjk = validbitmap & 0x1;
3712
	    validbitmap &= ~0x1;
3713
3714
	    if(!validbitmap)
3715
	      return(utf8);
3716
	}
3717
3718
	/* user chooses something other than UTF-8 */
3719
	if(strucmp(ps_global->posting_charmap, utf8)){
1 by Asheesh Laroia
Import upstream version 0.82+dfsg
3720
	    /*
3721
	     * If we're to post in other than UTF-8, and it can be
3722
	     * transliterated without losing fidelity, do it.
1.1.3 by Asheesh Laroia
Import upstream version 0.9999+dfsg
3723
	     * Else, use UTF-8.
1 by Asheesh Laroia
Import upstream version 0.82+dfsg
3724
	     */
1.1.3 by Asheesh Laroia
Import upstream version 0.9999+dfsg
3725
1.1.5 by Asheesh Laroia
Import upstream version 1.0+dfsg
3726
	    /* if ascii works, always use that */
3727
	    if(representable_in_charset(validbitmap, ascii))
3728
	      return(ascii);
1.1.1 by Asheesh Laroia
Import upstream version 0.98+dfsg
3729
1.1.5 by Asheesh Laroia
Import upstream version 1.0+dfsg
3730
	    /* does the user's posting character set work? */
3731
	    if(representable_in_charset(validbitmap, ps_global->posting_charmap))
1 by Asheesh Laroia
Import upstream version 0.82+dfsg
3732
	      return(ps_global->posting_charmap);
1.1.3 by Asheesh Laroia
Import upstream version 0.9999+dfsg
3733
1.1.5 by Asheesh Laroia
Import upstream version 1.0+dfsg
3734
	    /* this is the charset the message we are replying to was in */
1.1.3 by Asheesh Laroia
Import upstream version 0.9999+dfsg
3735
	    if(preferred_charset
1.1.5 by Asheesh Laroia
Import upstream version 1.0+dfsg
3736
	       && strucmp(preferred_charset, ascii)
3737
	       && representable_in_charset(validbitmap, preferred_charset))
1.1.3 by Asheesh Laroia
Import upstream version 0.9999+dfsg
3738
	      return(preferred_charset);
1.1.5 by Asheesh Laroia
Import upstream version 1.0+dfsg
3739
3740
	    /* else, use UTF-8 */
3741
1 by Asheesh Laroia
Import upstream version 0.82+dfsg
3742
	}
1.1.5 by Asheesh Laroia
Import upstream version 1.0+dfsg
3743
	/* user chooses nothing, going with the default */
1 by Asheesh Laroia
Import upstream version 0.82+dfsg
3744
	else if(ps_global->vars[V_POST_CHAR_SET].main_user_val.p == NULL
3745
		&& ps_global->vars[V_POST_CHAR_SET].post_user_val.p == NULL
3746
		&& ps_global->vars[V_POST_CHAR_SET].fixed_val.p == NULL){
1.1.5 by Asheesh Laroia
Import upstream version 1.0+dfsg
3747
	    char *most_preferred;
3748
1.2.1 by Asheesh Laroia
Import upstream version 1.10+dfsg
3749
	    /*
1.1.5 by Asheesh Laroia
Import upstream version 1.0+dfsg
3750
	     * In this case the user didn't specify a posting character set
3751
	     * and we will choose the most-specific one from our list.
1.2.1 by Asheesh Laroia
Import upstream version 1.10+dfsg
3752
	     */
1.1.5 by Asheesh Laroia
Import upstream version 1.0+dfsg
3753
3754
	    /* ascii is best */
3755
	    if(representable_in_charset(validbitmap, ascii))
3756
	      return(ascii);
3757
3758
	    /* Can we keep the original from the message we're replying to?  */
1.1.3 by Asheesh Laroia
Import upstream version 0.9999+dfsg
3759
	    if(preferred_charset
1.1.5 by Asheesh Laroia
Import upstream version 1.0+dfsg
3760
	       && strucmp(preferred_charset, ascii)
3761
	       && representable_in_charset(validbitmap, preferred_charset))
1.1.3 by Asheesh Laroia
Import upstream version 0.9999+dfsg
3762
	      return(preferred_charset);
3763
1.1.5 by Asheesh Laroia
Import upstream version 1.0+dfsg
3764
	    /* choose the best of the rest */
3765
	    most_preferred = most_preferred_charset(validbitmap);
3766
	    if(!most_preferred)
3767
	      return(utf8);
3768
3769
	    /*
3770
	     * If the text we're labeling contains something like
3771
	     * smart quotes but no CJK characters, then instead of
3772
	     * labeling it as ISO-2022-JP we want to use UTF-8.
3773
	     */
3774
	    if(notcjk){
3775
		const CHARSET *cs;
3776
3777
		cs = utf8_charset(most_preferred);
3778
		if(!cs
3779
		   || cs->script == SC_CHINESE_SIMPLIFIED
3780
		   || cs->script == SC_CHINESE_TRADITIONAL
3781
		   || cs->script == SC_JAPANESE
3782
		   || cs->script == SC_KOREAN)
3783
		  return(utf8);
3784
	    }
3785
3786
	    return(most_preferred);
3787
	}
3788
	/* user explicitly chooses UTF-8 */
3789
	else{
3790
	    /* if ascii works, always use that */
3791
	    if(representable_in_charset(validbitmap, ascii))
3792
	      return(ascii);
3793
3794
	    /* else, use UTF-8 */
3795
3796
	}
3797
    }
3798
3799
    return(utf8);
3800
}
3801
3802
3803
static char **charsetlist = NULL;
3804
static int items_in_charsetlist = 0;
3805
static unsigned long *charsetmap = NULL;
3806
3807
static char *downgrades[] = {
3808
    "US-ASCII",
3809
    "ISO-8859-15",
3810
    "ISO-8859-1",
3811
    "ISO-8859-2",
3812
    "VISCII",
3813
    "KOI8-R",
3814
    "KOI8-U",
3815
    "ISO-8859-7",
3816
    "ISO-8859-6",
3817
    "ISO-8859-8",
3818
    "TIS-620",
3819
    "ISO-2022-JP",
3820
    "GB2312",
3821
    "BIG5",
3822
    "EUC-KR"
3823
};
3824
3825
3826
unsigned long *
3827
init_charsetchecker(char *preferred_charset)
3828
{
3829
    int i, count = 0, reset = 0;
3830
    char *ascii = "US-ASCII";
3831
    char *utf8 = "UTF-8";
3832
3833
    /*
3834
     * When user doesn't set a posting character set posting_charmap ends up
3835
     * set to UTF-8. That also happens if user sets UTF-8 explicitly.
3836
     * That's where the strange set of if-else's come from.
3837
     */
3838
3839
    /* user chooses something other than UTF-8 */
3840
    if(strucmp(ps_global->posting_charmap, utf8)){
3841
	count++;	/* US-ASCII */
3842
	if(items_in_charsetlist < 1 || strucmp(charsetlist[0], ascii))
3843
	  reset++;
3844
3845
	/* if posting_charmap is valid, include it in list */
3846
	if(ps_global->posting_charmap && ps_global->posting_charmap[0]
3847
	   && strucmp(ps_global->posting_charmap, ascii)
3848
	   && strucmp(ps_global->posting_charmap, utf8)
3849
	   && utf8_charset(ps_global->posting_charmap)){
3850
	    count++;
3851
	    if(!reset
3852
	       && (items_in_charsetlist < count
3853
	           || strucmp(charsetlist[count-1], ps_global->posting_charmap)))
3854
	      reset++;
3855
	}
3856
3857
	if(preferred_charset && preferred_charset[0]
3858
	   && strucmp(preferred_charset, ascii)
3859
	   && strucmp(preferred_charset, utf8)
3860
	   && (count < 2 || strucmp(preferred_charset, ps_global->posting_charmap))){
3861
	    count++;
3862
	    if(!reset
3863
	       && (items_in_charsetlist < count
3864
	           || strucmp(charsetlist[count-1], preferred_charset)))
3865
	      reset++;
3866
	}
3867
3868
	if(items_in_charsetlist != count)
3869
	  reset++;
3870
3871
	if(reset){
3872
	    if(charsetlist)
3873
	      free_list_array(&charsetlist);
3874
3875
	    items_in_charsetlist = count;
3876
	    charsetlist = (char **) fs_get((count + 1) * sizeof(char *));
3877
3878
	    i = 0;
3879
	    charsetlist[i++] = cpystr(ascii);
3880
3881
	    if(ps_global->posting_charmap && ps_global->posting_charmap[0]
3882
	       && strucmp(ps_global->posting_charmap, ascii)
3883
	       && strucmp(ps_global->posting_charmap, utf8)
3884
	       && utf8_charset(ps_global->posting_charmap))
3885
	      charsetlist[i++] = cpystr(ps_global->posting_charmap);
3886
3887
	    if(preferred_charset && preferred_charset[0]
3888
	       && strucmp(preferred_charset, ascii)
3889
	       && strucmp(preferred_charset, utf8)
3890
	       && (i < 2 || strucmp(preferred_charset, ps_global->posting_charmap)))
3891
	      charsetlist[i++] = cpystr(preferred_charset);
3892
3893
	    charsetlist[i] = NULL;
3894
	}
3895
    }
3896
    /* user chooses nothing, going with the default */
3897
    else if(ps_global->vars[V_POST_CHAR_SET].main_user_val.p == NULL
3898
	    && ps_global->vars[V_POST_CHAR_SET].post_user_val.p == NULL
3899
	    && ps_global->vars[V_POST_CHAR_SET].fixed_val.p == NULL){
3900
	int add_preferred = 0;
3901
3902
	/* does preferred_charset have to be added to the list?  */
3903
	if(preferred_charset && preferred_charset[0] && strucmp(preferred_charset, utf8)){
3904
	    add_preferred = 1;
3905
	    for(i = 0; add_preferred && i < sizeof(downgrades)/sizeof(downgrades[0]); i++)
3906
	      if(!strucmp(downgrades[i], preferred_charset))
3907
		add_preferred = 0;
3908
	}
3909
3910
	if(add_preferred){
3911
	    /* existing list is right size already */
3912
	    if(items_in_charsetlist == sizeof(downgrades)/sizeof(downgrades[0]) + 1){
3913
		/* just check to see if last list item is the preferred_charset */
3914
		if(strucmp(preferred_charset, charsetlist[items_in_charsetlist-1])){
3915
		    /* no, fix it */
3916
		    reset++;
3917
		    fs_give((void **) &charsetlist[items_in_charsetlist-1]);
3918
		    charsetlist[items_in_charsetlist-1] = cpystr(preferred_charset);
3919
		}
3920
	    }
3921
	    else{
3922
		reset++;
3923
		if(charsetlist)
3924
		  free_list_array(&charsetlist);
3925
3926
		count = sizeof(downgrades)/sizeof(downgrades[0]) + 1;
3927
		items_in_charsetlist = count;
3928
		charsetlist = (char **) fs_get((count + 1) * sizeof(char *));
3929
		for(i = 0; i < sizeof(downgrades)/sizeof(downgrades[0]); i++)
3930
		  charsetlist[i] = cpystr(downgrades[i]);
3931
3932
		charsetlist[i++] = cpystr(preferred_charset);
3933
		charsetlist[i] = NULL;
3934
	    }
3935
	}
3936
	else{
3937
	    /* if list is same size as downgrades, consider it good */
3938
	    if(items_in_charsetlist != sizeof(downgrades)/sizeof(downgrades[0]))
3939
	      reset++;
3940
3941
	    if(reset){
3942
		if(charsetlist)
3943
		  free_list_array(&charsetlist);
3944
3945
		count = sizeof(downgrades)/sizeof(downgrades[0]);
3946
		items_in_charsetlist = count;
3947
		charsetlist = (char **) fs_get((count + 1) * sizeof(char *));
3948
		for(i = 0; i < sizeof(downgrades)/sizeof(downgrades[0]); i++)
3949
		  charsetlist[i] = cpystr(downgrades[i]);
3950
3951
		charsetlist[i] = NULL;
3952
	    }
3953
	}
3954
    }
3955
    /* user explicitly chooses UTF-8 */
3956
    else{
3957
	/* include possibility of ascii even if they explicitly ask for UTF-8 */
3958
	count++;	/* US-ASCII */
3959
	if(items_in_charsetlist < 1 || strucmp(charsetlist[0], ascii))
3960
	  reset++;
3961
3962
	if(items_in_charsetlist != count)
3963
	  reset++;
3964
3965
	if(reset){
3966
	    if(charsetlist)
3967
	      free_list_array(&charsetlist);
3968
3969
	    /* the list is just ascii and nothing else */
3970
	    items_in_charsetlist = count;
3971
	    charsetlist = (char **) fs_get((count + 1) * sizeof(char *));
3972
3973
	    i = 0;
3974
	    charsetlist[i++] = cpystr(ascii);
3975
	    charsetlist[i] = NULL;
3976
	}
3977
    }
3978
3979
3980
    if(reset){
3981
	if(charsetmap)
3982
	  fs_give((void **) &charsetmap);
3983
3984
	if(charsetlist)
3985
	  charsetmap = utf8_csvalidmap(charsetlist);
3986
    }
3987
3988
    return(charsetmap);
3989
}
3990
3991
3992
/* total reset */
3993
void
3994
free_charsetchecker(void)
3995
{
3996
    if(charsetlist)
3997
      free_list_array(&charsetlist);
3998
3999
    items_in_charsetlist = 0;
4000
4001
    if(charsetmap)
4002
      fs_give((void **) &charsetmap);
4003
}
4004
4005
4006
int
4007
representable_in_charset(unsigned long validbitmap, char *charset)
4008
{
4009
    int i, done = 0, ret = 0;
4010
    unsigned long j;
4011
4012
    if(!(charset && charset[0]))
4013
      return ret;
4014
4015
    if(!strucmp(charset, "UTF-8"))
4016
      return 1;
4017
4018
    for(i = 0; !done && i < items_in_charsetlist; i++){
4019
	if(!strucmp(charset, charsetlist[i])){
4020
	    j = 1;
4021
	    j <<= (i+1);
4022
	    done++;
4023
	    if(validbitmap & j)
4024
	      ret = 1;
4025
	}
4026
    }
4027
4028
    return ret;
4029
}
4030
4031
4032
char *
4033
most_preferred_charset(unsigned long validbitmap)
4034
{
4035
    unsigned long bm;
4036
    unsigned long rm;
4037
    int index;
4038
4039
    if(!(validbitmap && items_in_charsetlist > 0))
4040
      return("UTF-8");
4041
4042
    /* careful, find_rightmost_bit modifies the bitmap */
4043
    bm = validbitmap;
4044
    rm = find_rightmost_bit(&bm);
4045
    index = MIN(MAX(rm-1,0), items_in_charsetlist-1);
4046
4047
    return(charsetlist[index]);
1.2.1 by Asheesh Laroia
Import upstream version 1.10+dfsg
4048
}
4049
4050
4051
/*
1.1.1 by Asheesh Laroia
Import upstream version 0.98+dfsg
4052
 * Set parameter to new value.
4053
 */
4054
void
4055
set_parameter(PARAMETER **param, char *paramname, char *new_value)
4056
{
4057
    PARAMETER *pm;
4058
4059
    if(!param || !(paramname && *paramname))
4060
      return;
4061
4062
    if(*param == NULL){
4063
	pm = (*param) = mail_newbody_parameter();
4064
	pm->attribute = cpystr(paramname);
4065
    }
4066
    else{
4067
	int nomatch;
4068
4069
	for(pm = *param;
4070
	    (nomatch=strucmp(pm->attribute, paramname)) && pm->next != NULL;
4071
	    pm = pm->next)
4072
	  ;/* searching for paramname parameter */
4073
4074
	if(nomatch){			/* add charset parameter */
4075
	    pm->next = mail_newbody_parameter();
4076
	    pm = pm->next;
4077
	    pm->attribute = cpystr(paramname);
4078
	}
4079
	/* else pm is existing paramname parameter */
4080
    }
4081
4082
    if(pm){
1.1.3 by Asheesh Laroia
Import upstream version 0.9999+dfsg
4083
	if(!(pm->value && new_value && !strcmp(pm->value, new_value))){
4084
	    if(pm->value)
4085
	      fs_give((void **) &pm->value);
1.1.1 by Asheesh Laroia
Import upstream version 0.98+dfsg
4086
1.1.3 by Asheesh Laroia
Import upstream version 0.9999+dfsg
4087
	    if(new_value)
4088
	      pm->value = cpystr(new_value);
4089
	}
1.1.1 by Asheesh Laroia
Import upstream version 0.98+dfsg
4090
    }
4091
}
4092
4093
1 by Asheesh Laroia
Import upstream version 0.82+dfsg
4094
/*----------------------------------------------------------------------
4095
    Remove the leading digits from SMTP error messages
4096
 -----*/
4097
char *
4098
tidy_smtp_mess(char *error, char *printstring, char *outbuf, size_t outbuflen)
4099
{
4100
    while(isdigit((unsigned char)*error) || isspace((unsigned char)*error) ||
4101
	  (*error == '.' && isdigit((unsigned char)*(error+1))))
4102
      error++;
4103
4104
    snprintf(outbuf, outbuflen, printstring, error);
4105
    outbuf[outbuflen-1] = '\0';
4106
    return(outbuf);
4107
}
4108
4109
4110
/*
4111
 * Local globals pine's body output routine needs
4112
 */
4113
static soutr_t    l_f;
4114
static TCPSTREAM *l_stream;
4115
static unsigned   c_in_buf = 0;
4116
4117
/*
4118
 * def to make our pipe write's more friendly
4119
 */
4120
#ifdef	PIPE_MAX
4121
#if	PIPE_MAX > 20000
4122
#undef	PIPE_MAX
4123
#endif
4124
#endif
4125
4126
#ifndef	PIPE_MAX
4127
#define	PIPE_MAX	1024
4128
#endif
4129
4130
4131
/*
4132
 * l_flust_net - empties gf_io terminal function's buffer
4133
 */
4134
int
4135
l_flush_net(int force)
4136
{
4137
    if(c_in_buf && c_in_buf < SIZEOF_20KBUF){
4138
	char *p = &tmp_20k_buf[0], *lp = NULL, c = '\0';
4139
4140
	tmp_20k_buf[c_in_buf] = '\0';
4141
	if(!force){
4142
	    /*
4143
	     * The start of each write is expected to be the start of a
4144
	     * "record" (i.e., a CRLF terminated line).  Make sure that is true
4145
	     * else we might screw up SMTP dot quoting...
4146
	     */
4147
	    for(p = tmp_20k_buf, lp = NULL;
1.1.3 by Asheesh Laroia
Import upstream version 0.9999+dfsg
4148
		(p = strstr(p, "\015\012")) != NULL;
1 by Asheesh Laroia
Import upstream version 0.82+dfsg
4149
		lp = (p += 2))
4150
	      ;
4151
	      
4152
4153
	    if(!lp && c_in_buf > 2)			/* no CRLF! */
4154
	      for(p = &tmp_20k_buf[c_in_buf] - 2;
4155
		  p > &tmp_20k_buf[0] && *p == '.';
4156
		  p--)					/* find last non-dot */
4157
		;
4158
4159
	    if(lp && *lp && lp >= tmp_20k_buf && lp < tmp_20k_buf + SIZEOF_20KBUF){
4160
		/* snippet remains */
4161
		c = *lp;
4162
		*lp = '\0';
4163
	    }
4164
	}
4165
4166
	if((l_f && !(*l_f)(l_stream, tmp_20k_buf))
4167
	   || (lmc.so && !lmc.all_written
4168
	       && !(lmc.text_only && lmc.text_written)
4169
	       && !so_puts(lmc.so, tmp_20k_buf)))
4170
	  return(0);
4171
4172
	c_in_buf = 0;
4173
	if(lp && lp >= tmp_20k_buf && lp < tmp_20k_buf + SIZEOF_20KBUF && (*lp = c))				/* Shift text left? */
4174
	  while(c_in_buf < SIZEOF_20KBUF && (tmp_20k_buf[c_in_buf] = *lp))
4175
	    c_in_buf++, lp++;
4176
    }
4177
4178
    return(1);
4179
}
4180
4181
4182
/*
4183
 * l_putc - gf_io terminal function that calls smtp's soutr_t function.
4184
 *
4185
 */
4186
int
4187
l_putc(int c)
4188
{
4189
    if(c_in_buf >= 0 && c_in_buf < SIZEOF_20KBUF)
4190
      tmp_20k_buf[c_in_buf++] = (char) c;
4191
4192
    return((c_in_buf >= PIPE_MAX) ? l_flush_net(FALSE) : TRUE);
4193
}
4194
4195
4196
4197
/*
4198
 * pine_rfc822_output_body - pine's version of c-client call.  Again, 
4199
 *                necessary since c-client doesn't know about how
4200
 *                we're treating attachments
4201
 */
4202
long
4203
pine_rfc822_output_body(struct mail_bodystruct *body, soutr_t f, void *s)
4204
{
4205
    PART *part;
4206
    PARAMETER *param;
4207
    char *t, *cookie = NIL, *encode_error;
4208
    char tmp[MAILTMPLEN];
4209
    int                add_trailing_crlf;
4210
    LOC_2022_JP ljp;
4211
    gf_io_t            gc;
4212
4213
    dprint((4, "-- pine_rfc822_output_body: %d\n",
4214
	       body ? body->type : 0));
4215
    if(body->type == TYPEMULTIPART) {   /* multipart gets special handling */
4216
	part = body->nested.part;	/* first body part */
4217
					/* find cookie */
4218
	for (param = body->parameter; param && !cookie; param = param->next)
4219
	  if (!strucmp (param->attribute,"BOUNDARY")) cookie = param->value;
4220
	if (!cookie) cookie = "-";	/* yucky default */
4221
4222
	/*
4223
	 * Output a bit of text before the first multipart delimiter
4224
	 * to warn unsuspecting users of non-mime-aware ua's that
4225
	 * they should expect weirdness...
4226
	 */
4227
	if(f && !(*f)(s, "  This message is in MIME format.  The first part should be readable text,\015\012  while the remaining parts are likely unreadable without MIME-aware tools.\015\012\015\012"))
4228
	  return(0);
4229
4230
	do {				/* for each part */
4231
					/* build cookie */
4232
	    snprintf (tmp, sizeof(tmp), "--%s\015\012", cookie);
4233
	    tmp[sizeof(tmp)-1] = '\0';
4234
					/* append cookie,mini-hdr,contents */
4235
	    if((f && !(*f)(s, tmp))
4236
	       || (lmc.so && !lmc.all_written && !so_puts(lmc.so, tmp))
4237
	       || !pine_write_body_header(&part->body,f,s)
4238
	       || !pine_rfc822_output_body (&part->body,f,s))
4239
	      return(0);
1.1.3 by Asheesh Laroia
Import upstream version 0.9999+dfsg
4240
	} while ((part = part->next) != NULL);	/* until done */
1 by Asheesh Laroia
Import upstream version 0.82+dfsg
4241
					/* output trailing cookie */
4242
	snprintf (t = tmp, sizeof(tmp), "--%s--",cookie);
4243
	tmp[sizeof(tmp)-1] = '\0';
4244
	if(lmc.so && !lmc.all_written){
4245
	    so_puts(lmc.so, t);
4246
	    so_puts(lmc.so, "\015\012");
4247
	}
4248
4249
	return(f ? ((*f) (s,t) && (*f) (s,"\015\012")) : 1);
4250
    }
4251
4252
    l_f      = f;			/* set up for writing chars...  */
4253
    l_stream = s;			/* out other end of pipe...     */
4254
    gf_filter_init();
4255
    dprint((4, "-- pine_rfc822_output_body: segment %ld bytes\n",
4256
	       body->size.bytes));
4257
4258
    if(body->contents.text.data)
4259
      gf_set_so_readc(&gc, (STORE_S *) body->contents.text.data);
4260
    else
4261
      return(1);
4262
4263
    /*
4264
     * Don't add trailing line if it is ExternalText, which already guarantees
4265
     * a trailing newline.
4266
     */
4267
    add_trailing_crlf = !(((STORE_S *) body->contents.text.data)->src == ExternalText);
4268
4269
    so_seek((STORE_S *) body->contents.text.data, 0L, 0);
4270
4271
    if(body->type != TYPEMESSAGE){ 	/* NOT encapsulated message */
4272
	char *charset;
4273
4274
	if(body->type == TYPETEXT
4275
	   && so_attr((STORE_S *) body->contents.text.data, "edited", NULL)
1.1.7 by Asheesh Laroia
Import upstream version 2.00+dfsg
4276
	   && (charset = parameter_val(body->parameter, "charset"))){
1 by Asheesh Laroia
Import upstream version 0.82+dfsg
4277
	    if(strucmp(charset, "utf-8") && strucmp(charset, "us-ascii")){
4278
		if(!strucmp(charset, "iso-2022-jp")){
4279
		    ljp.report_err = 0;
4280
		    gf_link_filter(gf_line_test,
4281
				   gf_line_test_opt(translate_utf8_to_2022_jp,&ljp));
4282
		}
4283
		else{
4284
		    void *table = utf8_rmap(charset);
4285
4286
		    if(table){
4287
			gf_link_filter(gf_convert_utf8_charset,
4288
				       gf_convert_utf8_charset_opt(table,0));
4289
		    }
4290
		    else{
4291
			/* else, just send it? */
1.1.1 by Asheesh Laroia
Import upstream version 0.98+dfsg
4292
			set_parameter(&body->parameter, "charset", "UTF-8");
1 by Asheesh Laroia
Import upstream version 0.82+dfsg
4293
		    }
4294
		}
4295
	    }
4296
4297
	    fs_give((void **)&charset);
4298
	}
4299
4300
	/*
4301
	 * Convert text pieces to canonical form
4302
	 * BEFORE applying any encoding (rfc1341: appendix G)...
4303
	 * NOTE: almost all filters expect CRLF newlines 
4304
	 */
4305
	if(body->type == TYPETEXT
4306
	   && body->encoding != ENCBASE64
4307
	   && !so_attr((STORE_S *) body->contents.text.data, "rawbody", NULL)){
4308
	    gf_link_filter(gf_local_nvtnl, NULL);
4309
	}
4310
4311
	switch (body->encoding) {	/* all else needs filtering */
4312
	  case ENC8BIT:			/* encode 8BIT into QUOTED-PRINTABLE */
4313
	    gf_link_filter(gf_8bit_qp, NULL);
4314
	    break;
4315
4316
	  case ENCBINARY:		/* encode binary into BASE64 */
4317
	    gf_link_filter(gf_binary_b64, NULL);
4318
	    break;
4319
4320
	  default:			/* otherwise text */
4321
	    break;
4322
	}
4323
    }
4324
1.1.3 by Asheesh Laroia
Import upstream version 0.9999+dfsg
4325
    if((encode_error = gf_pipe(gc, l_putc)) != NULL){ /* shove body part down pipe */
1 by Asheesh Laroia
Import upstream version 0.82+dfsg
4326
	q_status_message1(SM_ORDER | SM_DING, 3, 4,
4327
			  _("Encoding Error \"%s\""), encode_error);
4328
	display_message('x');
4329
    }
4330
4331
    gf_clear_so_readc((STORE_S *) body->contents.text.data);
4332
4333
    if(encode_error || !l_flush_net(TRUE))
4334
      return(0);
4335
4336
    send_bytes_sent += gf_bytes_piped();
4337
    so_release((STORE_S *)body->contents.text.data);
4338
4339
    if(lmc.so && !lmc.all_written && lmc.text_only){
4340
	if(lmc.text_written){	/* we have some splainin' to do */
4341
	    char tmp[MAILTMPLEN];
4342
	    char *name = NULL;
4343
4344
	    if(!(so_puts(lmc.so,_("The following attachment was sent,\015\012"))
4345
		 && so_puts(lmc.so,_("but NOT saved in the Fcc copy:\015\012"))))
4346
	      return(0);
4347
4348
	    /*
4349
	     * BUG: If this name is not ascii it's going to cause trouble.
4350
	     */
1.1.7 by Asheesh Laroia
Import upstream version 2.00+dfsg
4351
	    name = parameter_val(body->parameter, "name");
1 by Asheesh Laroia
Import upstream version 0.82+dfsg
4352
	    snprintf(tmp, sizeof(tmp),
4353
    "    A %s/%s%s%s%s segment of about %s bytes.\015\012",
4354
		    body_type_names(body->type), 
4355
		    body->subtype ? body->subtype : "Unknown",
4356
		    name ? " (Name=\"" : "",
4357
		    name ? name : "",
4358
		    name ? "\")" : "",
4359
		    comatose(body->size.bytes));
4360
	    tmp[sizeof(tmp)-1] = '\0';
4361
	    if(name)
4362
	      fs_give((void **)&name);
4363
4364
	    if(!so_puts(lmc.so, tmp))
4365
	      return(0);
4366
	}
4367
	else			/* suppress everything after first text part */
4368
	  lmc.text_written = (body->type == TYPETEXT
4369
			      && (!body->subtype
4370
				  || !strucmp(body->subtype, "plain")));
4371
    }
4372
4373
    if(add_trailing_crlf)
4374
      return((f ? (*f)(s, "\015\012") : 1)	/* output final stuff */
4375
	   && ((lmc.so && !lmc.all_written) ? so_puts(lmc.so,"\015\012") : 1));
4376
    else
4377
      return(1);
4378
}
4379
4380
4381
/*
4382
 * pine_write_body_header - another c-client clone.  This time
4383
 *                          so the final encoding labels get set 
4384
 *                          correctly since it hasn't happened yet,
4385
 *			    and to be paranoid about line lengths.
4386
 *
4387
 * Returns: TRUE/nonzero on success, zero on error
4388
 */
4389
int
4390
pine_write_body_header(struct mail_bodystruct *body, soutr_t f, void *s)
4391
{
4392
    char tmp[MAILTMPLEN];
4393
    RFC822BUFFER rbuf;
4394
    int  i;
4395
    unsigned char c;
4396
    STRINGLIST *stl;
4397
    STORE_S    *so;
4398
    extern const char *tspecials;
4399
1.1.3 by Asheesh Laroia
Import upstream version 0.9999+dfsg
4400
    if((so = so_get(CharStar, NULL, WRITE_ACCESS)) != NULL){
1 by Asheesh Laroia
Import upstream version 0.82+dfsg
4401
	if(!(so_puts(so, "Content-Type: ")
4402
	     && so_puts(so, body_types[body->type])
4403
	     && so_puts(so, "/")
4404
	     && so_puts(so, body->subtype
4405
			      ? body->subtype
4406
			      : rfc822_default_subtype (body->type))))
4407
	  return(pwbh_finish(0, so));
4408
	    
4409
	if(body->parameter){
4410
	    if(!pine_write_params(body->parameter, so))
4411
	      return(pwbh_finish(0, so));
4412
	}
4413
	else if(!so_puts(so, "; CHARSET=US-ASCII"))
4414
	  return(pwbh_finish(0, so));
4415
	    
4416
	if(!so_puts(so, "\015\012"))
4417
	  return(pwbh_finish(0, so));
4418
4419
	if ((body->encoding		/* note: encoding 7BIT never output! */
4420
	     && !(so_puts(so, "Content-Transfer-Encoding: ")
4421
		  && so_puts(so, body_encodings[(body->encoding==ENCBINARY)
4422
						? ENCBASE64
4423
						: (body->encoding == ENC8BIT)
4424
						  ? ENCQUOTEDPRINTABLE
4425
						  : (body->encoding <= ENCMAX)
4426
						    ? body->encoding
4427
						    : ENCOTHER])
4428
		  && so_puts(so, "\015\012")))
4429
	    /*
4430
	     * If requested, strip Content-ID headers that don't look like they
4431
	     * are needed. Microsoft's Outlook XP has a bug that causes it to
4432
	     * not show that there is an attachment when there is a Content-ID
4433
	     * header present on that attachment.
4434
	     *
4435
	     * If user has quell-content-id turned on, don't output content-id
4436
	     * unless it is of type message/external-body.
4437
	     * Since this code doesn't look inside messages being forwarded
4438
	     * type message content-ids will remain as is and type multipart
4439
	     * alternative will remain as is. We don't create those on our
4440
	     * own. If we did, we'd have to worry about getting this right.
4441
	     */
4442
	    || (body->id && (F_OFF(F_QUELL_CONTENT_ID, ps_global)
4443
	                     || (body->type == TYPEMESSAGE
4444
			         && body->subtype
4445
				 && !strucmp(body->subtype, "external-body")))
4446
		&& !(so_puts(so, "Content-ID: ") && so_puts(so, body->id)
4447
		     && so_puts(so, "\015\012")))
4448
	    || (body->description
4449
		&& strlen(body->description) < 5000	/* arbitrary! */
4450
		&& !pine_write_header_line("Content-Description: ", body->description, so))
4451
	    || (body->md5
4452
		&& !(so_puts(so, "Content-MD5: ")
4453
		     && so_puts(so, body->md5)
4454
		     && so_puts(so, "\015\012"))))
4455
	  return(pwbh_finish(0, so));
4456
1.1.3 by Asheesh Laroia
Import upstream version 0.9999+dfsg
4457
	if ((stl = body->language) != NULL) {
1 by Asheesh Laroia
Import upstream version 0.82+dfsg
4458
	    if(!so_puts(so, "Content-Language: "))
4459
	      return(pwbh_finish(0, so));
4460
4461
	    do {
4462
		if(strlen((char *)stl->text.data) > 500) /* arbitrary! */
4463
		  return(pwbh_finish(0, so));
4464
4465
		tmp[0] = '\0';
4466
		rbuf.f   = dummy_soutr;
4467
		rbuf.s   = NULL;
4468
		rbuf.beg = tmp;
4469
		rbuf.cur = tmp;
4470
		rbuf.end = tmp+sizeof(tmp)-1;
4471
		rfc822_output_cat(&rbuf, (char *)stl->text.data, tspecials);
4472
		*rbuf.cur = '\0';
4473
4474
		if(!so_puts(so, tmp)
4475
		   || ((stl = stl->next) && !so_puts(so, ", ")))
4476
		  return(pwbh_finish(0, so));
4477
	    }
4478
	    while (stl);
4479
4480
	    if(!so_puts(so, "\015\012"))
4481
	      return(pwbh_finish(0, so));
4482
	}
4483
4484
	if (body->disposition.type) {
4485
	    if(!(so_puts(so, "Content-Disposition: ")
4486
		 && so_puts(so, body->disposition.type)))
4487
	      return(pwbh_finish(0, so));
4488
4489
	    if(!pine_write_params(body->disposition.parameter, so))
4490
	      return(pwbh_finish(0, so));	      
4491
4492
	    if(!so_puts(so, "\015\012"))
4493
	      return(pwbh_finish(0, so));
4494
	}
4495
4496
	/* copy out of so, a line at a time (or less than a K)
4497
	 * and send it down the pike
4498
	 */
4499
	so_seek(so, 0L, 0);
4500
	i = 0;
4501
	while(so_readc(&c, so))
4502
	  if((tmp[i++] = c) == '\012' || i > sizeof(tmp) - 3){
4503
	      tmp[i] = '\0';
4504
	      if((f && !(*f)(s, tmp))
4505
		 || !lmc_body_header_line(tmp, i <= sizeof(tmp) - 3))
4506
		return(pwbh_finish(0, so));
4507
4508
	      i = 0;
4509
	  }
4510
4511
	/* Finally, write blank line */
4512
	if((f && !(*f)(s, "\015\012")) || !lmc_body_header_finish())
4513
	  return(pwbh_finish(0, so));
4514
	
4515
	return(pwbh_finish(i == 0, so)); /* better of ended on LF */
4516
    }
4517
4518
    return(0);
4519
}
4520
4521
4522
/*
4523
 * pine_write_header - convert, encode (if needed) and
4524
 *                     write "header-name: field-body"
4525
 */
4526
int
4527
pine_write_header_line(char *hdr, char *val, STORE_S *so)
4528
{
4529
    char *cv, *cs, *vp;
4530
    int   rv;
4531
1.1.3 by Asheesh Laroia
Import upstream version 0.9999+dfsg
4532
    cs = posting_characterset(val, NULL, HdrText);
1 by Asheesh Laroia
Import upstream version 0.82+dfsg
4533
    cv = utf8_to_charset(val, cs, 0);
4534
    vp = rfc1522_encode(tmp_20k_buf, SIZEOF_20KBUF,
4535
			(unsigned char *) cv, cs);
4536
4537
    rv = (so_puts(so, hdr) && so_puts(so, vp) && so_puts(so, "\015\012"));
4538
1.1.1 by Asheesh Laroia
Import upstream version 0.98+dfsg
4539
    if(cv && cv != val)
1 by Asheesh Laroia
Import upstream version 0.82+dfsg
4540
      fs_give((void **) &cv);
4541
4542
4543
    return(rv);
4544
}
4545
4546
4547
/*
4548
 * pine_write_param - convert, encode and write MIME header-field parameters
4549
 */
4550
int
4551
pine_write_params(PARAMETER *param, STORE_S *so)
4552
{	      
4553
    for(; param; param = param->next){
4554
	int   rv;
4555
	char *cv, *cs;
4556
	extern const char *tspecials;
4557
1.1.3 by Asheesh Laroia
Import upstream version 0.9999+dfsg
4558
	cs = posting_characterset(param->value, NULL, HdrText);
1 by Asheesh Laroia
Import upstream version 0.82+dfsg
4559
	cv = utf8_to_charset(param->value, cs, 0);
4560
	rv = (so_puts(so, "; ")
4561
	      && rfc2231_output(so, param->attribute, cv, (char *) tspecials, cs));
4562
1.1.1 by Asheesh Laroia
Import upstream version 0.98+dfsg
4563
	if(cv && cv != param->value)
1 by Asheesh Laroia
Import upstream version 0.82+dfsg
4564
	  fs_give((void **) &cv);
4565
4566
	if(!rv)
4567
	  return(0);
4568
    }
4569
4570
    return(1);
4571
}
4572
4573
4574
4575
int
4576
lmc_body_header_line(char *line, int beginning)
4577
{
4578
    if(lmc.so && !lmc.all_written){
4579
	if(beginning && lmc.text_only && lmc.text_written
4580
	   && (!struncmp(line, "content-type:", 13)
4581
	       || !struncmp(line, "content-transfer-encoding:", 26)
4582
	       || !struncmp(line, "content-disposition:", 20))){
4583
	    /*
4584
	     * "comment out" the real values since our comment isn't
4585
	     * likely the same type, disposition nor encoding...
4586
	     */
4587
	    if(!so_puts(lmc.so, "X-"))
4588
	      return(FALSE);
4589
	}
4590
4591
	return(so_puts(lmc.so, line));
4592
    }
4593
4594
    return(TRUE);
4595
}
4596
4597
4598
int
4599
lmc_body_header_finish(void)
4600
{
4601
    if(lmc.so && !lmc.all_written){
4602
	if(lmc.text_only && lmc.text_written
4603
	   && !so_puts(lmc.so, "Content-Type: TEXT/PLAIN\015\012"))
4604
	  return(FALSE);
4605
4606
	return(so_puts(lmc.so, "\015\012"));
4607
    }
4608
4609
    return(TRUE);
4610
}
4611
4612
4613
4614
int
4615
pwbh_finish(int rv, STORE_S *so)
4616
{
4617
    if(so)
4618
      so_give(&so);
4619
4620
    return(rv);
4621
}
4622
4623
4624
/*
4625
 * pine_free_body - c-client call wrapper so the body data pointer we
4626
 *		    we're using in a way c-client doesn't know about
4627
 *		    gets free'd appropriately.
4628
 */
4629
void
4630
pine_free_body(struct mail_bodystruct **body)
4631
{
4632
    /*
4633
     * Preempt c-client's contents.text.data clean up since we've
4634
     * usurped it's meaning for our own purposes...
4635
     */
4636
    pine_free_body_data (*body);
4637
4638
    /* Then let c-client handle the rest... */
4639
    mail_free_body(body);
4640
}
4641
4642
4643
/* 
4644
 * pine_free_body_data - free pine's interpretations of the body part's
4645
 *			 data pointer.
4646
 */
4647
void
4648
pine_free_body_data(struct mail_bodystruct *body)
4649
{
4650
    if(body){
4651
	if(body->type == TYPEMULTIPART){
4652
	    PART *part = body->nested.part;
4653
	    do				/* for each part */
4654
	      pine_free_body_data(&part->body);
1.1.3 by Asheesh Laroia
Import upstream version 0.9999+dfsg
4655
	    while ((part = part->next) != NULL);	/* until done */
1 by Asheesh Laroia
Import upstream version 0.82+dfsg
4656
	}
4657
	else if(body->contents.text.data)
4658
	  so_give((STORE_S **) &body->contents.text.data);
4659
    }
4660
}
4661
4662
4663
long
4664
send_body_size(struct mail_bodystruct *body)
4665
{
4666
    long  l = 0L;
4667
    PART *part;
4668
4669
    if(body->type == TYPEMULTIPART) {   /* multipart gets special handling */
4670
	part = body->nested.part;	/* first body part */
4671
	do				/* for each part */
4672
	  l += send_body_size(&part->body);
1.1.3 by Asheesh Laroia
Import upstream version 0.9999+dfsg
4673
	while ((part = part->next) != NULL);	/* until done */
1 by Asheesh Laroia
Import upstream version 0.82+dfsg
4674
	return(l);
4675
    }
4676
4677
    return(l + body->size.bytes);
4678
}
4679
4680
4681
int
4682
sent_percent(void)
4683
{
4684
    int i = (int) (((send_bytes_sent + gf_bytes_piped()) * 100)
4685
							/ send_bytes_to_send);
4686
    return(MIN(i, 100));
4687
}
4688
4689
4690
/*
4691
 * pine_smtp_verbose_out - write 
4692
 */
4693
void
4694
pine_smtp_verbose_out(char *s)
4695
{
4696
#ifdef _WINDOWS
4697
    LPTSTR slpt;
4698
#endif
4699
    if(verbose_send_output && s){
4700
	char *p, last = '\0';
4701
4702
	for(p = s; *p; p++)
4703
	  if(*p == '\015')
4704
	    *p = ' ';
4705
	  else
4706
	    last = *p;
4707
4708
#ifdef _WINDOWS
4709
	/*
4710
	 * The stream is opened in Unicode mode, so we need to fix the
4711
	 * argument to fputs.
4712
	 */
4713
	slpt = utf8_to_lptstr((LPSTR) s);
4714
	if(slpt){
4715
	    _fputts(slpt, verbose_send_output);
4716
	    fs_give((void **) &slpt);
4717
	}
4718
4719
	if(last != '\012')
4720
	  _fputtc(L'\n', verbose_send_output);
4721
#else
4722
	fputs(s, verbose_send_output);
4723
	if(last != '\012')
4724
	  fputc('\n', verbose_send_output);
4725
#endif
4726
    }
4727
4728
}
4729
4730
4731
/*
4732
 * pine_header_forbidden - is this name a "forbidden" header?
4733
 *
4734
 *          name - the header name to check
4735
 *  We don't allow user to change these.
4736
 */
4737
int
4738
pine_header_forbidden(char *name)
4739
{
4740
    char **p;
4741
    static char *forbidden_headers[] = {
4742
	"sender",
4743
	"x-sender",
4744
	"x-x-sender",
4745
	"date",
4746
	"received",
4747
	"message-id",
4748
	"in-reply-to",
4749
	"path",
4750
	"resent-message-id",
4751
	"resent-date",
4752
	"resent-from",
4753
	"resent-sender",
4754
	"resent-to",
4755
	"resent-cc",
4756
	"resent-reply-to",
4757
	"mime-version",
4758
	"content-type",
1.2.1 by Asheesh Laroia
Import upstream version 1.10+dfsg
4759
	"x-priority",
4760
	"user-agent",
1 by Asheesh Laroia
Import upstream version 0.82+dfsg
4761
	NULL
4762
    };
4763
4764
    for(p = forbidden_headers; *p; p++)
4765
      if(!strucmp(name, *p))
4766
	break;
4767
4768
    return((*p) ? 1 : 0);
4769
}
4770
4771
4772
/*
4773
 * hdr_is_in_list - is there a custom value for this header?
4774
 *
4775
 *          hdr - the header name to check
4776
 *       custom - the list to check in
4777
 *  Returns 1 if there is a custom value, 0 otherwise.
4778
 */
4779
int
4780
hdr_is_in_list(char *hdr, PINEFIELD *custom)
4781
{
4782
    PINEFIELD *pf;
4783
4784
    for(pf = custom; pf && pf->name; pf = pf->next)
4785
      if(strucmp(pf->name, hdr) == 0)
4786
	return 1;
4787
    
4788
    return 0;
4789
}
4790
4791
4792
/*
4793
 * count_custom_hdrs_pf - returns number of custom headers in arg
4794
 *            custom -- the list to be counted
4795
 *  only_nonstandard -- only count headers which aren't standard pine headers
4796
 */
4797
int
4798
count_custom_hdrs_pf(PINEFIELD *custom, int only_nonstandard)
4799
{
4800
    int ret = 0;
4801
4802
    for(; custom && custom->name; custom = custom->next)
4803
      if(!only_nonstandard || !custom->standard)
4804
        ret++;
4805
4806
    return(ret);
4807
}
4808
4809
4810
/*
4811
 * count_custom_hdrs_list - returns number of custom headers in arg
4812
 */
4813
int
4814
count_custom_hdrs_list(char **list)
4815
{
4816
    char **p;
4817
    char  *q     = NULL;
4818
    char  *name;
4819
    char  *t;
4820
    char   save;
4821
    int    ret   = 0;
4822
4823
    if(list){
4824
	for(p = list; (q = *p) != NULL; p++){
4825
	    if(q[0]){
4826
		/* remove leading whitespace */
4827
		name = skip_white_space(q);
4828
		
4829
		/* look for colon or space or end */
4830
		for(t = name;
4831
		    *t && !isspace((unsigned char)*t) && *t != ':'; t++)
4832
		  ;/* do nothing */
4833
4834
		save = *t;
4835
		*t = '\0';
4836
		if(!pine_header_forbidden(name))
4837
		  ret++;
4838
4839
		*t = save;
4840
	    }
4841
	}
4842
    }
4843
4844
    return(ret);
4845
}
4846
4847
4848
/*
4849
 * set_default_hdrval - put the user's default value for this header
4850
 *                      into pf->textbuf.
4851
 *             setthis - the pinefield to be set
4852
 *              custom - where to look for the default
4853
 */
4854
CustomType
4855
set_default_hdrval(PINEFIELD *setthis, PINEFIELD *custom)
4856
{
4857
    PINEFIELD *pf;
4858
    CustomType ret = NoMatch;
4859
4860
    if(!setthis || !setthis->name){
4861
	q_status_message(SM_ORDER,3,7,"Internal error setting default header");
4862
	return(ret);
4863
    }
4864
4865
    setthis->textbuf = NULL;
4866
4867
    for(pf = custom; pf && pf->name; pf = pf->next){
4868
	if(strucmp(pf->name, setthis->name) != 0)
4869
	  continue;
4870
4871
	ret = pf->cstmtype;
4872
4873
	/* turn on editing */
4874
	if(strucmp(pf->name, "From") == 0 || strucmp(pf->name, "Reply-To") == 0)
4875
	  setthis->canedit = 1;
4876
4877
	if(pf->val)
4878
	  setthis->textbuf = cpystr(pf->val);
4879
    }
4880
4881
    if(!setthis->textbuf)
4882
      setthis->textbuf = cpystr("");
4883
    
4884
    return(ret);
4885
}
4886
4887
4888
/*
4889
 * pine_header_standard - is this name a "standard" header?
4890
 *
4891
 *          name - the header name to check
4892
 */
4893
FieldType
4894
pine_header_standard(char *name)
4895
{
4896
    int    i;
4897
4898
    /* check to see if this is a standard header */
4899
    for(i = 0; pf_template[i].name; i++)
4900
      if(!strucmp(name, pf_template[i].name))
4901
	return(pf_template[i].type);
4902
4903
    return(TypeUnknown);
4904
}
4905
4906
4907
/*
4908
 * customized_hdr_setup - setup the PINEFIELDS for all the customized headers
4909
 *                    Allocates space for each name and addr ptr.
4910
 *                    Allocates space for default in textbuf, even if empty.
4911
 *
4912
 *              head - the first PINEFIELD to fill in
4913
 *              list - the list to parse
4914
 */
4915
void
4916
customized_hdr_setup(PINEFIELD *head, char **list, CustomType cstmtype)
4917
{
4918
    char **p, *q, *t, *name, *value, save;
4919
    PINEFIELD *pf;
4920
4921
    pf = head;
4922
4923
    if(list){
4924
        for(p = list; (q = *p) != NULL; p++){
4925
4926
	    if(q[0]){
4927
4928
		/* anything after leading whitespace? */
4929
		if(!*(name = skip_white_space(q)))
4930
		  continue;
4931
4932
		/* look for colon or space or end */
4933
	        for(t = name;
4934
		    *t && !isspace((unsigned char)*t) && *t != ':'; t++)
4935
	    	  ;/* do nothing */
4936
4937
		/* if there is a space in the field-name, skip it */
4938
		if(isspace((unsigned char)*t)){
4939
		    q_status_message1(SM_ORDER, 3, 3,
4940
				    _("Space not allowed in header name (%s)"),
4941
				      name);
4942
		    continue;
4943
		}
4944
4945
		save = *t;
4946
		*t = '\0';
4947
4948
		/* Don't allow any of the forbidden headers. */
4949
		if(pine_header_forbidden(name)){
4950
		    q_status_message1(SM_ORDER | SM_DING, 3, 3,
4951
				      _("Not allowed to change header \"%s\""),
4952
				      name);
4953
4954
		    *t = save;
4955
		    continue;
4956
		}
4957
4958
		if(pf){
4959
		    if(pine_header_standard(name) != TypeUnknown)
4960
		      pf->standard = 1;
4961
4962
		    pf->name     = cpystr(name);
4963
		    pf->type     = FreeText;
4964
		    pf->cstmtype = cstmtype;
4965
		    pf->next     = pf+1;
4966
4967
#ifdef OLDWAY
4968
		    /*
4969
		     * Some mailers apparently break if we change
4970
		     * user@domain into Fred <user@domain> for
4971
		     * return-receipt-to,
4972
		     * so we'll just call this a FreeText field, too.
4973
		     */
4974
		    /*
4975
		     * For now, all custom headers are FreeText except for
4976
		     * this one that we happen to know about.  We might
4977
		     * have to add some syntax to the config option so that
4978
		     * people can tell us their custom header takes addresses.
4979
		     */
4980
		    if(!strucmp(pf->name, "Return-Receipt-to")){
4981
			pf->type = Address;
4982
			pf->addr = (ADDRESS **)fs_get(sizeof(ADDRESS *));
4983
			*pf->addr = (ADDRESS *)NULL;
4984
		    }
4985
#endif /* OLDWAY */
4986
4987
		    *t = save;
4988
4989
		    /* remove space between name and colon */
4990
		    value = skip_white_space(t);
4991
4992
		    /* give them an alloc'd default, even if empty */
4993
		    pf->textbuf = cpystr((*value == ':')
4994
					   ? skip_white_space(++value) : "");
4995
		    if(pf->textbuf && pf->textbuf[0])
4996
		      pf->val = cpystr(pf->textbuf);
4997
4998
		    pf++;
4999
		}
5000
		else
5001
		  *t = save;
5002
	    }
5003
	}
5004
    }
5005
5006
    /* fix last next pointer */
5007
    if(head && pf != head)
5008
      (pf-1)->next = NULL;
5009
}
5010
5011
5012
/*
5013
 * add_defaults_from_list - the PINEFIELDS list given by "head" is already
5014
 *                          setup except that it doesn't have default values.
5015
 *                          Add those defaults if they exist in "list".
5016
 *
5017
 *              head - the first PINEFIELD to add a default to
5018
 *              list - the list to get the defaults from
5019
 */
5020
void
5021
add_defaults_from_list(PINEFIELD *head, char **list)
5022
{
5023
    char **p, *q, *t, *name, *value, save;
5024
    PINEFIELD *pf;
5025
5026
    for(pf = head; pf && list; pf = pf->next){
5027
	if(!pf->name)
5028
	  continue;
5029
5030
        for(p = list; (q = *p) != NULL; p++){
5031
5032
	    if(q[0]){
5033
5034
		/* anything after leading whitespace? */
5035
		if(!*(name = skip_white_space(q)))
5036
		  continue;
5037
5038
		/* look for colon or space or end */
5039
	        for(t = name;
5040
		    *t && !isspace((unsigned char)*t) && *t != ':'; t++)
5041
	    	  ;/* do nothing */
5042
5043
		/* if there is a space in the field-name, skip it */
5044
		if(isspace((unsigned char)*t))
5045
		  continue;
5046
5047
		save = *t;
5048
		*t = '\0';
5049
5050
		if(strucmp(name, pf->name) != 0){
5051
		    *t = save;
5052
		    continue;
5053
		}
5054
5055
		*t = save;
5056
5057
		/*
5058
		 * Found the right header. See if it has a default
5059
		 * value set.
5060
		 */
5061
5062
		/* remove space between name and colon */
5063
		value = skip_white_space(t);
5064
5065
		if(*value == ':'){
5066
		    char *defval;
5067
5068
		    defval = skip_white_space(++value);
5069
		    if(defval && *defval){
5070
			if(pf->val)
5071
			  fs_give((void **)&pf->val);
5072
			
5073
			pf->val = cpystr(defval);
5074
		    }
5075
		}
5076
5077
		break;		/* on to next pf */
5078
	    }
5079
	}
5080
    }
5081
}
5082
5083
5084
/*
5085
 * parse_custom_hdrs - allocate PINEFIELDS for custom headers
5086
 *                     fill in the defaults
5087
 * Args -     list -- The list to parse.
5088
 *        cstmtype -- Fill the in cstmtype field with this value
5089
 */
5090
PINEFIELD *
5091
parse_custom_hdrs(char **list, CustomType cstmtype)
5092
{
5093
    PINEFIELD          *pfields;
5094
    int			i;
5095
5096
    /*
5097
     * add one for possible use by fcc
5098
     *   What is this "possible use"? I don't see it. I don't
5099
     *   think it exists anymore. I think +1 would be ok.   hubert 2000-08-02
5100
     */
5101
    i       = (count_custom_hdrs_list(list) + 2) * sizeof(PINEFIELD);
5102
    pfields = (PINEFIELD *)fs_get((size_t) i);
5103
    memset(pfields, 0, (size_t) i);
5104
5105
    /* set up the custom header pfields */
5106
    customized_hdr_setup(pfields, list, cstmtype);
5107
5108
    return(pfields);
5109
}
5110
5111
5112
/*
5113
 * Combine the two lists of headers into one list which is allocated here
5114
 * and freed by the caller. Eliminate duplicates with values from the role
5115
 * taking precedence over values from the default.
5116
 */
5117
PINEFIELD *
5118
combine_custom_headers(PINEFIELD *dflthdrs, PINEFIELD *rolehdrs)
5119
{
5120
    PINEFIELD *pfields, *pf, *pf2;
5121
    int        max_hdrs, i;
5122
5123
    max_hdrs = count_custom_hdrs_pf(rolehdrs,0) +
5124
               count_custom_hdrs_pf(dflthdrs,0);
5125
5126
    pfields = (PINEFIELD *)fs_get((size_t)(max_hdrs+1)*sizeof(PINEFIELD));
5127
    memset(pfields, 0, (size_t)(max_hdrs+1)*sizeof(PINEFIELD));
5128
5129
    pf = pfields;
5130
    for(pf2 = rolehdrs; pf2 && pf2->name; pf2 = pf2->next){
5131
	pf->name     = pf2->name ? cpystr(pf2->name) : NULL;
5132
	pf->type     = pf2->type;
5133
	pf->cstmtype = pf2->cstmtype;
5134
	pf->textbuf  = pf2->textbuf ? cpystr(pf2->textbuf) : NULL;
5135
	pf->val      = pf2->val ? cpystr(pf2->val) : NULL;
5136
	pf->standard = pf2->standard;
5137
	pf->next     = pf+1;
5138
	pf++;
5139
    }
5140
5141
    /* if these aren't already there, add them */
5142
    for(pf2 = dflthdrs; pf2 && pf2->name; pf2 = pf2->next){
5143
	/* check for already there */
5144
	for(i = 0;
5145
	    pfields[i].name && strucmp(pfields[i].name, pf2->name);
5146
	    i++)
5147
	  ;
5148
	
5149
	if(!pfields[i].name){			/* this is a new one */
5150
	    pf->name     = pf2->name ? cpystr(pf2->name) : NULL;
5151
	    pf->type     = pf2->type;
5152
	    pf->cstmtype = pf2->cstmtype;
5153
	    pf->textbuf  = pf2->textbuf ? cpystr(pf2->textbuf) : NULL;
5154
	    pf->val      = pf2->val ? cpystr(pf2->val) : NULL;
5155
	    pf->standard = pf2->standard;
5156
	    pf->next     = pf+1;
5157
	    pf++;
5158
	}
5159
    }
5160
5161
    /* fix last next pointer */
5162
    if(pf != pfields)
5163
      (pf-1)->next = NULL;
5164
5165
    return(pfields);
5166
}
5167
5168
5169
/*
5170
 * free_customs - free misc. resources associated with custom header fields
5171
 *
5172
 *           pf - pointer to first custom field
5173
 */
5174
void
5175
free_customs(PINEFIELD *head)
5176
{
5177
    PINEFIELD *pf;
5178
5179
    for(pf = head; pf && pf->name; pf = pf->next){
5180
5181
	fs_give((void **)&pf->name);
5182
5183
	if(pf->val)
5184
	  fs_give((void **)&pf->val);
5185
5186
	/* only true for FreeText */
5187
	if(pf->textbuf)
5188
	  fs_give((void **)&pf->textbuf);
5189
5190
	/* only true for Address */
1.1.4 by Asheesh Laroia
Import upstream version 0.99999+dfsg
5191
	if(pf->addr && *pf->addr)
5192
	  mail_free_address(pf->addr);
1 by Asheesh Laroia
Import upstream version 0.82+dfsg
5193
    }
5194
5195
    fs_give((void **)&head);
5196
}
5197
5198
5199
/*
5200
 * encode_whole_header
5201
 *
5202
 * Returns     1 if whole value should be encoded
5203
 *             0 to encode only within comments
5204
 */
5205
int
5206
encode_whole_header(char *field, METAENV *header)
5207
{
5208
    int        retval = 0;
5209
    PINEFIELD *pf;
5210
5211
    if(field && (!strucmp(field, "Subject") ||
5212
		 !strucmp(field, "Comment") ||
5213
		 !struncmp(field, "X-", 2)))
5214
      retval++;
5215
    else if(field && *field && header && header->custom){
5216
	for(pf = header->custom; pf && pf->name; pf = pf->next){
5217
5218
	    if(!pf->standard && !strucmp(pf->name, field)){
5219
		retval++;
5220
		break;
5221
	    }
5222
	}
5223
    }
5224
5225
    return(retval);
5226
}
5227
5228
5229
/*----------------------------------------------------------------------
5230
      Post news via NNTP or inews
5231
5232
Args: env -- envelope of message to post
5233
       body -- body of message to post
5234
5235
Returns: -1 if failed or cancelled, 1 if succeeded
5236
5237
WARNING: This call function has the side effect of writing the message
5238
    to the lmc.so object.   
5239
  ----*/
1.1.3 by Asheesh Laroia
Import upstream version 0.9999+dfsg
5240
int
5241
news_poster(METAENV *header, struct mail_bodystruct *body, char **alt_nntp_servers,
5242
	    void (*pipecb_f)(PIPE_S *, int, void *))
1 by Asheesh Laroia
Import upstream version 0.82+dfsg
5243
{
5244
    char *error_mess, error_buf[200], **news_servers;
5245
    char **servers_to_use;
5246
    int	  we_cancel = 0, server_no = 0, done_posting = 0;
5247
    void *orig_822_output;
5248
    BODY *bp = NULL;
5249
5250
    error_buf[0] = '\0';
5251
    we_cancel = busy_cue("Posting news", NULL, 0);
5252
5253
    dprint((4, "Posting: [%s]\n",
5254
	   (header && header->env && header->env->newsgroups)
5255
	     ? header->env->newsgroups : "?"));
5256
5257
    if((alt_nntp_servers && alt_nntp_servers[0] && alt_nntp_servers[0][0])
5258
       || (ps_global->VAR_NNTP_SERVER && ps_global->VAR_NNTP_SERVER[0]
5259
       && ps_global->VAR_NNTP_SERVER[0][0])){
5260
       /*---------- NNTP server defined ----------*/
5261
	error_mess = NULL;
5262
	servers_to_use = (alt_nntp_servers && alt_nntp_servers[0]
5263
			  && alt_nntp_servers[0][0]) 
5264
	  ? alt_nntp_servers : ps_global->VAR_NNTP_SERVER;
5265
5266
	/*
5267
	 * Install our rfc822 output routine 
5268
	 */
5269
	orig_822_output = mail_parameters(NULL, GET_RFC822OUTPUT, NULL);
5270
	(void) mail_parameters(NULL, SET_RFC822OUTPUT,
5271
			       (void *)post_rfc822_output);
5272
5273
	server_no = 0;
5274
	news_servers = (char **)fs_get(2 * sizeof(char *));
5275
	news_servers[1] = NULL;
5276
	while(!done_posting && servers_to_use[server_no] && 
5277
	      servers_to_use[server_no][0]){
5278
	    news_servers[0] = servers_to_use[server_no];
5279
	    ps_global->noshow_error = 1;
5280
#ifdef DEBUG
5281
	    sending_stream = nntp_open(news_servers, debug ? NOP_DEBUG : 0L);
5282
#else
5283
	    sending_stream = nntp_open(news_servers, 0L);
5284
#endif
5285
	    ps_global->noshow_error = 0;
5286
	    
5287
	    if(sending_stream != NULL) {
1.1.4 by Asheesh Laroia
Import upstream version 0.99999+dfsg
5288
		unsigned short save_encoding, added_encoding;
1 by Asheesh Laroia
Import upstream version 0.82+dfsg
5289
5290
		/*
5291
		 * Fake that we've got clearance from the transport agent
5292
		 * for 8bit transport for the benefit of our output routines...
5293
		 */
5294
		if(F_ON(F_ENABLE_8BIT_NNTP, ps_global)
5295
		   && (bp = first_text_8bit(body))){
5296
		    int i;
5297
5298
		    for(i = 0; (i <= ENCMAX) && body_encodings[i]; i++)
5299
		        ;
5300
5301
		    if(i > ENCMAX){		/* no empty encoding slots! */
5302
		        bp = NULL;
5303
		    }
5304
		    else {
1.1.4 by Asheesh Laroia
Import upstream version 0.99999+dfsg
5305
			added_encoding = i;
5306
			body_encodings[added_encoding] = body_encodings[ENC8BIT];
1 by Asheesh Laroia
Import upstream version 0.82+dfsg
5307
			save_encoding = bp->encoding;
1.1.4 by Asheesh Laroia
Import upstream version 0.99999+dfsg
5308
			bp->encoding = added_encoding;
1 by Asheesh Laroia
Import upstream version 0.82+dfsg
5309
		    }
5310
		}
5311
5312
		/*
5313
		 * Set global header pointer so we can get at it later...
5314
		 */
5315
		send_header = header;
5316
		ps_global->noshow_error = 1;
5317
		if(nntp_mail(sending_stream, header->env, body) == 0)
5318
		    snprintf(error_mess = error_buf, sizeof(error_buf),
5319
			    _("Error posting message: %s"),
5320
			    sending_stream->reply);
5321
		else{
5322
		    done_posting = 1;
5323
		    error_buf[0] = '\0';
5324
		    error_mess = NULL;
5325
		}
5326
5327
		error_buf[sizeof(error_buf)-1] = '\0';
5328
		smtp_close(sending_stream);
5329
		ps_global->noshow_error = 0;
5330
		sending_stream = NULL;
5331
		if(F_ON(F_ENABLE_8BIT_NNTP, ps_global) && bp){
1.1.4 by Asheesh Laroia
Import upstream version 0.99999+dfsg
5332
		    body_encodings[added_encoding] = NULL;
1 by Asheesh Laroia
Import upstream version 0.82+dfsg
5333
		    bp->encoding = save_encoding;
5334
		}
5335
		
5336
	    } else {
5337
	        /*---- Open of NNTP connection failed ------ */
5338
	        snprintf(error_mess = error_buf, sizeof(error_buf),
5339
			_("Error connecting to news server: %s"),
5340
			ps_global->c_client_error);
5341
		error_buf[sizeof(error_buf)-1] = '\0';
5342
		dprint((1, error_buf));
5343
	    }
5344
	    server_no++;
5345
	}
5346
	fs_give((void **)&news_servers);
5347
	(void) mail_parameters (NULL, SET_RFC822OUTPUT, orig_822_output);
5348
    } else {
5349
        /*----- Post via local mechanism -------*/
5350
#ifdef	_WINDOWS
5351
	snprintf(error_mess = error_buf, sizeof(error_buf),
5352
		 _("Can't post, NNTP-server must be defined!"));
5353
#else  /* UNIX */
1.1.3 by Asheesh Laroia
Import upstream version 0.9999+dfsg
5354
        error_mess = post_handoff(header, body, error_buf, sizeof(error_buf), NULL,
5355
				  pipecb_f);
1 by Asheesh Laroia
Import upstream version 0.82+dfsg
5356
#endif
5357
    }
5358
5359
    if(we_cancel)
5360
      cancel_busy_cue(0);
5361
5362
    if(error_mess){
5363
	if(lmc.so && !lmc.all_written)
5364
	  so_give(&lmc.so);	/* clean up any fcc data */
5365
5366
        q_status_message(SM_ORDER | SM_DING, 4, 5, error_mess);
5367
        return(-1);
5368
    }
5369
5370
    lmc.all_written = 1;
5371
    return(1);
5372
}
5373
5374
5375
/* ----------------------------------------------------------------------
5376
   Figure out command to start local SMTP agent
5377
5378
  Args: errbuf   -- buffer for reporting errors (assumed non-NULL)
5379
5380
  Returns an alloc'd copy of the local SMTP agent invocation or NULL
5381
5382
  ----*/
5383
char *
5384
smtp_command(char *errbuf, size_t errbuflen)
5385
{
5386
#ifdef	_WINDOWS
5387
    if(!(ps_global->VAR_SMTP_SERVER && ps_global->VAR_SMTP_SERVER[0]
5388
	 && ps_global->VAR_SMTP_SERVER[0][0]))
5389
      strncpy(errbuf,_("SMTP-server must be defined!"),errbuflen);
5390
5391
    errbuf[errbuflen-1] = '\0';
5392
#else	/* UNIX */
5393
# if	defined(SENDMAIL) && defined(SENDMAILFLAGS)
5394
    char tmp[256];
5395
5396
    snprintf(tmp, sizeof(tmp), "%.*s %.*s", (sizeof(tmp)-3)/2, SENDMAIL,
5397
	    (sizeof(tmp)-3)/2, SENDMAILFLAGS);
5398
    return(cpystr(tmp));
5399
# else
5400
    strncpy(errbuf, _("No default posting command."), errbuflen);
5401
    errbuf[errbuflen-1] = '\0';
5402
# endif
5403
#endif
5404
5405
    return(NULL);
5406
}
5407
5408
5409
5410
#ifndef	_WINDOWS
5411
/*----------------------------------------------------------------------
5412
   Hand off given message to local posting agent
5413
5414
  Args: envelope -- The envelope for the BCC and debugging
5415
        header   -- The text of the message header
5416
        errbuf   -- buffer for reporting errors (assumed non-NULL)
5417
	len      -- Length of errbuf
5418
     
5419
   ----*/
5420
int
1.1.3 by Asheesh Laroia
Import upstream version 0.9999+dfsg
5421
mta_handoff(METAENV *header, struct mail_bodystruct *body,
5422
	    char *errbuf, size_t len,
5423
	    void (*bigresult_f) (char *, int),
5424
	    void (*pipecb_f)(PIPE_S *, int, void *))
1 by Asheesh Laroia
Import upstream version 0.82+dfsg
5425
{
5426
#ifdef	DF_SENDMAIL_PATH
5427
    char  cmd_buf[256];
5428
#endif
5429
    char *cmd = NULL;
5430
5431
    /*
5432
     * A bit of complicated policy implemented here.
5433
     * There are two posting variables sendmail-path and smtp-server.
5434
     * Precedence is in that order.
5435
     * They can be set one of 4 ways: fixed, command-line, user, or globally.
5436
     * Precedence is in that order.
5437
     * Said differently, the order goes something like what's below.
5438
     * 
5439
     * NOTE: the fixed/command-line/user precendence handling is also
5440
     *	     indicated by what's pointed to by ps_global->VAR_*, but since
5441
     *	     that also includes the global defaults, it's not sufficient.
5442
     */
5443
5444
    if(ps_global->FIX_SENDMAIL_PATH
5445
       && ps_global->FIX_SENDMAIL_PATH[0]){
5446
	cmd = ps_global->FIX_SENDMAIL_PATH;
5447
    }
5448
    else if(!(ps_global->FIX_SMTP_SERVER
5449
	      && ps_global->FIX_SMTP_SERVER[0])){
5450
	if(ps_global->COM_SENDMAIL_PATH
5451
	   && ps_global->COM_SENDMAIL_PATH[0]){
5452
	    cmd = ps_global->COM_SENDMAIL_PATH;
5453
	}
5454
	else if(!(ps_global->COM_SMTP_SERVER
5455
		  && ps_global->COM_SMTP_SERVER[0])){
5456
	    if((ps_global->vars[V_SENDMAIL_PATH].post_user_val.p
5457
	        && ps_global->vars[V_SENDMAIL_PATH].post_user_val.p[0]) ||
5458
	       (ps_global->vars[V_SENDMAIL_PATH].main_user_val.p
5459
	        && ps_global->vars[V_SENDMAIL_PATH].main_user_val.p[0])){
5460
		if(ps_global->vars[V_SENDMAIL_PATH].post_user_val.p
5461
		   && ps_global->vars[V_SENDMAIL_PATH].post_user_val.p[0])
5462
		  cmd = ps_global->vars[V_SENDMAIL_PATH].post_user_val.p;
5463
		else
5464
		  cmd = ps_global->vars[V_SENDMAIL_PATH].main_user_val.p;
5465
	    }
5466
	    else if(!((ps_global->vars[V_SMTP_SERVER].post_user_val.l
5467
	               && ps_global->vars[V_SMTP_SERVER].post_user_val.l[0]) ||
5468
		      (ps_global->vars[V_SMTP_SERVER].main_user_val.l
5469
	               && ps_global->vars[V_SMTP_SERVER].main_user_val.l[0]))){
5470
		if(ps_global->GLO_SENDMAIL_PATH
5471
		   && ps_global->GLO_SENDMAIL_PATH[0]){
5472
		    cmd = ps_global->GLO_SENDMAIL_PATH;
5473
		}
5474
#ifdef	DF_SENDMAIL_PATH
5475
		/*
5476
		 * This defines the default method of posting.  So,
5477
		 * unless we're told otherwise use it...
5478
		 */
5479
		else if(!(ps_global->GLO_SMTP_SERVER
5480
			  && ps_global->GLO_SMTP_SERVER[0])){
5481
		    strncpy(cmd = cmd_buf, DF_SENDMAIL_PATH, sizeof(cmd_buf)-1);
5482
		    cmd_buf[sizeof(cmd_buf)-1] = '\0';
5483
		}
5484
#endif
5485
	    }
5486
	}
5487
    }
5488
5489
    *errbuf = '\0';
5490
    if(cmd){
5491
	dprint((4, "call_mailer via cmd: %s\n", cmd ? cmd : "?"));
5492
1.1.3 by Asheesh Laroia
Import upstream version 0.9999+dfsg
5493
	(void) mta_parse_post(header, body, cmd, errbuf, len, bigresult_f, pipecb_f);
1 by Asheesh Laroia
Import upstream version 0.82+dfsg
5494
	return(1);
5495
    }
5496
    else
5497
      return(0);
5498
}
5499
5500
5501
5502
/*----------------------------------------------------------------------
5503
   Hand off given message to local posting agent
5504
5505
  Args: envelope -- The envelope for the BCC and debugging
5506
        header   -- The text of the message header
5507
        errbuf   -- buffer for reporting errors (assumed non-NULL)
5508
	errbuflen -- Length of errbuf
5509
     
5510
  Fork off mailer process and pipe the message into it
5511
  Called to post news via Inews when NNTP is unavailable
5512
  
5513
   ----*/
5514
char *
5515
post_handoff(METAENV *header, struct mail_bodystruct *body, char *errbuf,
1.1.3 by Asheesh Laroia
Import upstream version 0.9999+dfsg
5516
	     size_t errbuflen,
5517
	     void (*bigresult_f) (char *, int),
5518
	     void (*pipecb_f)(PIPE_S *, int, void *))
1 by Asheesh Laroia
Import upstream version 0.82+dfsg
5519
{
5520
    char *err = NULL;
5521
#ifdef	SENDNEWS
5522
    char *s;
5523
    char  tmp[200];
5524
5525
    if(s = strstr(header->env->date," (")) /* fix the date format for news */
5526
      *s = '\0';
5527
1.1.3 by Asheesh Laroia
Import upstream version 0.9999+dfsg
5528
    if(err = mta_parse_post(header, body, SENDNEWS, errbuf, errbuflen, bigresult_f, pipecb_f)){
1 by Asheesh Laroia
Import upstream version 0.82+dfsg
5529
	strncpy(tmp, err, sizeof(tmp)-1);
5530
	tmp[sizeof(tmp)-1] = '\0';
5531
	snprintf(err = errbuf, errbuflen, _("News not posted: \"%s\": %s"),
5532
		 SENDNEWS, tmp);
5533
    }
5534
5535
    if(s)
5536
      *s = ' ';				/* restore the date */
5537
5538
#else /* !SENDNEWS */  /* this is the default case */
5539
    strncpy(err = errbuf, _("Can't post, NNTP-server must be defined!"), errbuflen-1);
5540
    err[errbuflen-1] = '\0';
5541
#endif /* !SENDNEWS */
5542
    return(err);
5543
}
5544
5545
5546
5547
/*----------------------------------------------------------------------
5548
   Hand off message to local MTA; it parses recipients from 822 header
5549
5550
  Args: header -- struct containing header data
5551
        body  -- struct containing message body data
5552
	cmd -- command to use for handoff (%s says where file should go)
5553
	errs -- pointer to buf to hold errors
5554
5555
   ----*/
5556
char *
5557
mta_parse_post(METAENV *header, struct mail_bodystruct *body, char *cmd,
1.1.3 by Asheesh Laroia
Import upstream version 0.9999+dfsg
5558
	       char *errs, size_t errslen, void (*bigresult_f)(char *, int),
5559
	       void (*pipecb_f)(PIPE_S *, int, void *))
1 by Asheesh Laroia
Import upstream version 0.82+dfsg
5560
{
5561
    char   *result = NULL;
5562
    PIPE_S *pipe;
5563
5564
    dprint((1, "=== mta_parse_post(%s) ===\n", cmd ? cmd : "?"));
5565
1.1.3 by Asheesh Laroia
Import upstream version 0.9999+dfsg
5566
    if((pipe = open_system_pipe(cmd, &result, NULL,
1 by Asheesh Laroia
Import upstream version 0.82+dfsg
5567
			       PIPE_STDERR|PIPE_WRITE|PIPE_PROT|PIPE_NOSHELL|PIPE_DESC,
1.1.3 by Asheesh Laroia
Import upstream version 0.9999+dfsg
5568
			       0, pipecb_f, pipe_report_error)) != NULL){
1 by Asheesh Laroia
Import upstream version 0.82+dfsg
5569
	if(!pine_rfc822_output(header, body, pine_pipe_soutr_nl,
5570
			       (TCPSTREAM *) pipe)){
5571
	  strncpy(errs, _("Error posting."), errslen-1);
5572
	  errs[errslen-1] = '\0';
5573
	}
5574
1.1.3 by Asheesh Laroia
Import upstream version 0.9999+dfsg
5575
	if(close_system_pipe(&pipe, NULL, pipecb_f) && !*errs){
1 by Asheesh Laroia
Import upstream version 0.82+dfsg
5576
	    snprintf(errs, errslen, _("Posting program %s returned error"), cmd);
5577
	    if(result && bigresult_f)
5578
	      (*bigresult_f)(result, CM_BR_ERROR);
5579
	}
5580
    }
5581
    else
5582
      snprintf(errs, errslen, _("Error running \"%s\""), cmd);
5583
5584
    if(result){
5585
	our_unlink(result);
5586
	fs_give((void **)&result);
5587
    }
5588
5589
    return(*errs ? errs : NULL);
5590
}
5591
5592
5593
/* 
5594
 * pine_pipe_soutr - Replacement for tcp_soutr that writes one of our
5595
 *		     pipes rather than a tcp stream
5596
 */
5597
long
5598
pine_pipe_soutr_nl (void *stream, char *s)
5599
{
5600
    long    rv = T;
5601
    char   *p;
5602
    size_t  n;
5603
5604
    while(*s && rv){
1.1.3 by Asheesh Laroia
Import upstream version 0.9999+dfsg
5605
	if((n = (p = strstr(s, "\015\012")) ? p - s : strlen(s)) != 0)
1 by Asheesh Laroia
Import upstream version 0.82+dfsg
5606
	  while((rv = write(((PIPE_S *)stream)->out.d, s, n)) != n)
5607
	    if(rv < 0){
5608
		if(errno != EINTR){
5609
		    rv = 0;
5610
		    break;
5611
		}
5612
	    }
5613
	    else{
5614
		s += rv;
5615
		n -= rv;
5616
	    }
5617
5618
	if(p && rv){
5619
	    s = p + 2;			/* write UNIX EOL */
5620
	    while((rv = write(((PIPE_S *)stream)->out.d,"\n",1)) != 1)
5621
	      if(rv < 0 && errno != EINTR){
5622
		  rv = 0;
5623
		  break;
5624
	      }
5625
	}
5626
	else
5627
	  break;
5628
    }
5629
5630
    return(rv);
5631
}
5632
#endif
5633
5634
5635
/**************** "PIPE" READING POSTING I/O ROUTINES ****************/
5636
5637
5638
/*
5639
 * helpful def's
5640
 */
5641
#define	S(X)		((PIPE_S *)(X))
5642
#define	GETBUFLEN	(4 * MAILTMPLEN)
5643
5644
5645
void *
5646
piped_smtp_open (char *host, char *service, long unsigned int port)
5647
{
5648
    char *postcmd;
5649
    void *rv = NULL;
5650
5651
    if(strucmp(host, "localhost")){
5652
	char tmp[MAILTMPLEN];
5653
5654
	snprintf(tmp, sizeof(tmp), _("Unexpected hostname for piped SMTP: %.*s"), 
5655
		sizeof(tmp)-50, host);
5656
	tmp[sizeof(tmp)-1] = '\0';
5657
	mm_log(tmp, ERROR);
5658
    }
1.1.3 by Asheesh Laroia
Import upstream version 0.9999+dfsg
5659
    else if((postcmd = smtp_command(ps_global->c_client_error, sizeof(ps_global->c_client_error))) != NULL){
1 by Asheesh Laroia
Import upstream version 0.82+dfsg
5660
	rv = open_system_pipe(postcmd, NULL, NULL,
5661
			      PIPE_READ|PIPE_STDERR|PIPE_WRITE|PIPE_PROT|PIPE_NOSHELL|PIPE_DESC,
5662
			      0, NULL, pipe_report_error);
5663
	fs_give((void **) &postcmd);
5664
    }
5665
    else
5666
      mm_log(ps_global->c_client_error, ERROR);
5667
5668
    return(rv);
5669
}
5670
5671
5672
void *
5673
piped_aopen (NETMBX *mb, char *service, char *user)
5674
{
5675
    return(NULL);
5676
}
5677
5678
5679
/* 
5680
 * piped_soutr - Replacement for tcp_soutr that writes one of our
5681
 *		     pipes rather than a tcp stream
5682
 */
5683
long
5684
piped_soutr (void *stream, char *s)
5685
{
5686
    return(piped_sout(stream, s, strlen(s)));
5687
}
5688
5689
5690
/* 
5691
 * piped_sout - Replacement for tcp_soutr that writes one of our
5692
 *		     pipes rather than a tcp stream
5693
 */
5694
long
5695
piped_sout (void *stream, char *s, long unsigned int size)
5696
{
5697
    int i, o;
5698
5699
    if(S(stream)->out.d < 0)
5700
      return(0L);
5701
1.1.3 by Asheesh Laroia
Import upstream version 0.9999+dfsg
5702
    if((i = (int) size) != 0){
1 by Asheesh Laroia
Import upstream version 0.82+dfsg
5703
	while((o = write(S(stream)->out.d, s, i)) != i)
5704
	  if(o < 0){
5705
	      if(errno != EINTR){
5706
		  piped_abort(stream);
5707
		  return(0L);
5708
	      }
5709
	  }
5710
	  else{
5711
	      s += o;			/* try again, fix up counts */
5712
	      i -= o;
5713
	  }
5714
    }
5715
5716
    return(1L);
5717
}
5718
5719
5720
/* 
5721
 * piped_getline - Replacement for tcp_getline that reads one
5722
 *		       of our pipes rather than a tcp pipe
5723
 *
5724
 *                     C-client expects that the \r\n will be stripped off.
5725
 */
5726
char *
5727
piped_getline (void *stream)
5728
{
5729
    static int   cnt;
5730
    static char *ptr;
5731
    int		 n, m;
5732
    char	*ret, *s, *sp, c = '\0', d;
5733
5734
    if(S(stream)->in.d < 0)
5735
      return(NULL);
5736
5737
    if(!S(stream)->tmp){		/* initialize! */
5738
	/* alloc space to collect input (freed in close_system_pipe) */
5739
	S(stream)->tmp = (char *) fs_get(GETBUFLEN);
5740
	memset(S(stream)->tmp, 0, GETBUFLEN);
5741
	cnt = -1;
5742
    }
5743
5744
    while(cnt < 0){
5745
	while((cnt = read(S(stream)->in.d, S(stream)->tmp, GETBUFLEN)) < 0)
5746
	  if(errno != EINTR){
5747
	      piped_abort(stream);
5748
	      return(NULL);
5749
	  }
5750
5751
	if(cnt == 0){
5752
	    piped_abort(stream);
5753
	    return(NULL);
5754
	}
5755
5756
	ptr = S(stream)->tmp;
5757
    }
5758
5759
    s = ptr;
5760
    n = 0;
5761
    while(cnt--){
5762
	d = *ptr++;
5763
	if((c == '\015') && (d == '\012')){
5764
	    ret = (char *)fs_get (n--);
5765
	    memcpy(ret, s, n);
5766
	    ret[n] = '\0';
5767
	    return(ret);
5768
	}
5769
5770
	n++;
5771
	c = d;
5772
    }
5773
					/* copy partial string from buffer */
5774
    memcpy((ret = sp = (char *) fs_get (n)), s, n);
5775
					/* get more data */
5776
    while(cnt < 0){
5777
	memset(S(stream)->tmp, 0, GETBUFLEN);
5778
	while((cnt = read(S(stream)->in.d, S(stream)->tmp, GETBUFLEN)) < 0)
5779
	  if(errno != EINTR){
5780
	      fs_give((void **) &ret);
5781
	      piped_abort(stream);
5782
	      return(NULL);
5783
	  }
5784
5785
	if(cnt == 0){
5786
	    if(n > 0)
5787
	      ret[n-1] = '\0';  /* to try to get error message logged */
5788
	    else{
5789
		piped_abort(stream);
5790
		return(NULL);
5791
	    }
5792
	}
5793
5794
	ptr = S(stream)->tmp;
5795
    }
5796
5797
    if(c == '\015' && *ptr == '\012'){
5798
	ptr++;
5799
	cnt--;
5800
	ret[n - 1] = '\0';		/* tie off string with null */
5801
    }
1.1.3 by Asheesh Laroia
Import upstream version 0.9999+dfsg
5802
    else if ((s = piped_getline(stream)) != NULL) {
1 by Asheesh Laroia
Import upstream version 0.82+dfsg
5803
	ret = (char *) fs_get(n + 1 + (m = strlen (s)));
5804
	memcpy(ret, sp, n);		/* copy first part */
5805
	memcpy(ret + n, s, m);		/* and second part */
5806
	fs_give((void **) &sp);		/* flush first part */
5807
	fs_give((void **) &s);		/* flush second part */
5808
	ret[n + m] = '\0';		/* tie off string with null */
5809
    }
5810
5811
    return(ret);
5812
}
5813
5814
5815
/* 
5816
 * piped_close - Replacement for tcp_close that closes pipes to our
5817
 *		     child rather than a tcp connection
5818
 */
5819
void
5820
piped_close(void *stream)
5821
{
5822
    /*
5823
     * Uninstall our hooks into smtp_send since it's being used by
5824
     * the nntp driver as well...
5825
     */
5826
    (void) close_system_pipe((PIPE_S **) &stream, NULL, NULL);
5827
}
5828
5829
5830
/*
5831
 * piped_abort - close down the pipe we were using to post
5832
 */
5833
void
5834
piped_abort(void *stream)
5835
{
5836
    if(S(stream)->in.d >= 0){
5837
	close(S(stream)->in.d);
5838
	S(stream)->in.d = -1;
5839
    }
5840
5841
    if(S(stream)->out.d){
5842
	close(S(stream)->out.d);
5843
	S(stream)->out.d = -1;
5844
    }
5845
}
5846
5847
5848
char *
5849
piped_host(void *stream)
5850
{
5851
    return(ps_global->hostname ? ps_global->hostname : "localhost");
5852
}
5853
5854
5855
unsigned long
5856
piped_port(void *stream)
5857
{
5858
    return(0L);
5859
}