1 /*
2  *  This file is part of libESMTP, a library for submission of RFC 2822
3  *  formatted electronic mail messages using the SMTP protocol described
4  *  in RFC 2821.
5  *
6  *  Copyright (C) 2001,2002  Brian Stafford  <brian@stafford.uklinux.net>
7  *
8  *  This library is free software; you can redistribute it and/or
9  *  modify it under the terms of the GNU Lesser General Public
10  *  License as published by the Free Software Foundation; either
11  *  version 2.1 of the License, or (at your option) any later version.
12  *
13  *  This library is distributed in the hope that it will be useful,
14  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
15  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  *  Lesser General Public License for more details.
17  *
18  *  You should have received a copy of the GNU Lesser General Public
19  *  License along with this library; if not, write to the Free Software
20  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
21  */
22 
23 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif
26 
27 #include <assert.h>
28 
29 #include <stdio.h>
30 #include <stdarg.h>
31 #include <string.h>
32 #include <stdlib.h>
33 #include <unistd.h>
34 #include <time.h>
35 #ifdef HAVE_GETTIMEOFDAY
36 # include <sys/time.h>
37 #endif
38 #include <errno.h>
39 
40 #include <missing.h>
41 
42 #include "libesmtp-private.h"
43 #include "headers.h"
44 #include "htable.h"
45 #include "rfc2822date.h"
46 #include "api.h"
47 
48 struct rfc2822_header
49   {
50     struct rfc2822_header *next;
51     struct header_info *info;		/* Info for setting and printing */
52     char *header;			/* Header name */
53     void *value;			/* Header value */
54   };
55 
56 typedef int (*hdrset_t) (struct rfc2822_header *, va_list);
57 typedef void (*hdrprint_t) (smtp_message_t, struct rfc2822_header *);
58 typedef void (*hdrdestroy_t) (struct rfc2822_header *);
59 
60 struct header_actions
61   {
62     const char *name;		/* Header for which the action is specified */
63     unsigned int flags;		/* Header flags */
64     hdrset_t set;		/* Function to set header value from API */
65     hdrprint_t print;		/* Function to print the header value */
66     hdrdestroy_t destroy;	/* Function to destroy the header value */
67   };
68 
69 struct header_info
70   {
71     const struct header_actions *action;
72     struct rfc2822_header *hdr;	/* Pointer to most recently defined header */
73     unsigned int seen : 1;	/* Header has been seen in the message */
74     unsigned int override : 1;	/* LibESMTP is overriding the message */
75     unsigned int prohibit : 1;	/* Header may not appear in the message */
76   };
77 
78 #define NELT(x)		((int) (sizeof x / sizeof x[0]))
79 
80 #define OPTIONAL	0
81 #define SHOULD		1
82 #define REQUIRE		2
83 #define PROHIBIT	4
84 #define PRESERVE	8
85 #define LISTVALUE	16
86 #define MULTIPLE	32
87 
88 static struct rfc2822_header *create_header (smtp_message_t message,
89 					    const char *header,
90 					    struct header_info *info);
91 void destroy_string (struct rfc2822_header *header);
92 void destroy_mbox_list (struct rfc2822_header *header);
93 struct header_info *find_header (smtp_message_t message,
94 				 const char *name, int len);
95 struct header_info *insert_header (smtp_message_t message, const char *name);
96 
97 /* RFC 2822 headers processing */
98 
99 /****************************************************************************
100  * Functions for setting and printing header values
101  ****************************************************************************/
102 
103 static int
set_string(struct rfc2822_header * header,va_list alist)104 set_string (struct rfc2822_header *header, va_list alist)
105 {
106   const char *value;
107 
108   assert (header != NULL);
109 
110   if (header->value != NULL)		/* Already set */
111     return 0;
112 
113   value = va_arg (alist, const char *);
114   if (value == NULL)
115     return 0;
116   header->value = strdup (value);
117   return header->value != NULL;
118 }
119 
120 static int
set_string_null(struct rfc2822_header * header,va_list alist)121 set_string_null (struct rfc2822_header *header, va_list alist)
122 {
123   const char *value;
124 
125   assert (header != NULL);
126 
127   if (header->value != NULL)		/* Already set */
128     return 0;
129 
130   value = va_arg (alist, const char *);
131   if (value == NULL)
132     return 1;
133   header->value = strdup (value);
134   return header->value != NULL;
135 }
136 
137 /* Print header-name ": " header-value "\r\n" */
138 static void
print_string(smtp_message_t message,struct rfc2822_header * header)139 print_string (smtp_message_t message, struct rfc2822_header *header)
140 {
141   assert (message != NULL && header != NULL);
142 
143   /* TODO: implement line folding at white spaces */
144   vconcatenate (&message->hdr_buffer, header->header, ": ",
145                 (header->value != NULL) ? header->value : "", "\r\n", NULL);
146 }
147 
148 void
destroy_string(struct rfc2822_header * header)149 destroy_string (struct rfc2822_header *header)
150 {
151   assert (header != NULL);
152 
153   if (header->value != NULL)
154     free (header->value);
155 }
156 
157 /* Print header-name ": <" message-id ">\r\n" */
158 static void
print_message_id(smtp_message_t message,struct rfc2822_header * header)159 print_message_id (smtp_message_t message, struct rfc2822_header *header)
160 {
161   const char *message_id;
162   char buf[64];
163 #ifdef HAVE_GETTIMEOFDAY
164   struct timeval tv;
165 #endif
166 
167   assert (message != NULL && header != NULL);
168 
169   message_id = header->value;
170   if (message_id == NULL)
171     {
172 #ifdef HAVE_GETTIMEOFDAY
173       if (gettimeofday (&tv, NULL) != -1) /* This shouldn't fail ... */
174 	snprintf (buf, sizeof buf, "%ld.%ld.%d@%s", tv.tv_sec, tv.tv_usec,
175 		  getpid (), message->session->localhost);
176       else /* ... but if it does fall back to using time() */
177 #endif
178       snprintf (buf, sizeof buf, "%ld.%d@%s", time (NULL),
179       	        getpid (), message->session->localhost);
180       message_id = buf;
181     }
182   /* TODO: implement line folding at white spaces */
183   vconcatenate (&message->hdr_buffer,
184                 header->header, ": <", message_id, ">\r\n",
185                 NULL);
186 }
187 
188 /****/
189 
190 static int
set_date(struct rfc2822_header * header,va_list alist)191 set_date (struct rfc2822_header *header, va_list alist)
192 {
193   const time_t *value;
194 
195   assert (header != NULL);
196 
197   if ((time_t) header->value != (time_t) 0)		/* Already set */
198     return 0;
199 
200   value = va_arg (alist, const time_t *);
201   header->value = (void *) *value;
202   return 1;
203 }
204 
205 /* Print header-name ": " formatted-date "\r\n" */
206 static void
print_date(smtp_message_t message,struct rfc2822_header * header)207 print_date (smtp_message_t message, struct rfc2822_header *header)
208 {
209   char buf[64];
210   time_t when;
211 
212   assert (message != NULL && header != NULL);
213 
214   when = (time_t) header->value;
215   if (when == (time_t) 0)
216     time (&when);
217   vconcatenate (&message->hdr_buffer, header->header, ": ",
218                 rfc2822date (buf, sizeof buf, &when), "\r\n", NULL);
219 }
220 
221 /****/
222 
223 struct mbox
224   {
225     struct mbox *next;
226     char *mailbox;
227     char *phrase;
228   };
229 
230 void
destroy_mbox_list(struct rfc2822_header * header)231 destroy_mbox_list (struct rfc2822_header *header)
232 {
233   struct mbox *mbox, *next;
234 
235   assert (header != NULL);
236 
237   mbox = header->value;
238   while (mbox != NULL)
239     {
240       next = mbox->next;
241       if (mbox->phrase != NULL)
242         free ((void *) mbox->phrase);
243       if (mbox->mailbox != NULL)
244         free ((void *) mbox->mailbox);
245       free (mbox);
246       mbox = next;
247     }
248 }
249 
250 static int
set_from(struct rfc2822_header * header,va_list alist)251 set_from (struct rfc2822_header *header, va_list alist)
252 {
253   struct mbox *mbox;
254   const char *mailbox;
255   const char *phrase;
256 
257   assert (header != NULL);
258 
259   phrase = va_arg (alist, const char *);
260   mailbox = va_arg (alist, const char *);
261 
262   /* Allow this to succeed as a special case.  Effectively requesting
263      default action in print_from().   Fails if explicit values have
264      already been set.  */
265   if (phrase == NULL && mailbox == NULL)
266     return header->value == NULL;
267 
268   mbox = malloc (sizeof (struct mbox));
269   if (mbox == NULL)
270     return 0;
271   mbox->phrase = (phrase != NULL) ? strdup (phrase) : NULL;
272   mbox->mailbox = strdup (mailbox);
273 
274   mbox->next = header->value;
275   header->value = mbox;
276   return 1;
277 }
278 
279 /* Print header-name ": " mailbox "\r\n"
280       or header-name ": \"" phrase "\" <" mailbox ">\r\n" */
281 static void
print_from(smtp_message_t message,struct rfc2822_header * header)282 print_from (smtp_message_t message, struct rfc2822_header *header)
283 {
284   struct mbox *mbox;
285   const char *mailbox;
286 
287   assert (message != NULL && header != NULL);
288 
289   vconcatenate (&message->hdr_buffer, header->header, ": ", NULL);
290   /* TODO: implement line folding at white spaces */
291   if (header->value == NULL)
292     {
293       mailbox = message->reverse_path_mailbox;
294       vconcatenate (&message->hdr_buffer,
295 		    (mailbox != NULL && *mailbox != '\0') ? mailbox : "<>",
296 		    "\r\n", NULL);
297     }
298   else
299     for (mbox = header->value; mbox != NULL; mbox = mbox->next)
300       {
301 	mailbox = mbox->mailbox;
302 	if (mbox->phrase == NULL)
303 	  vconcatenate (&message->hdr_buffer,
304 			(mailbox != NULL && *mailbox != '\0') ? mailbox : "<>",
305 			NULL);
306 	else
307 	  vconcatenate (&message->hdr_buffer, "\"", mbox->phrase, "\""
308 			" <", (mailbox != NULL) ? mailbox : "", ">", NULL);
309 	vconcatenate (&message->hdr_buffer,
310 		      (mbox->next != NULL) ? ",\r\n    " : "\r\n", NULL);
311       }
312 }
313 
314 /* Same arguments and syntax as from: except that only one value is
315    allowed.  */
316 static int
set_sender(struct rfc2822_header * header,va_list alist)317 set_sender (struct rfc2822_header *header, va_list alist)
318 {
319   struct mbox *mbox;
320   const char *mailbox;
321   const char *phrase;
322 
323   assert (header != NULL);
324 
325   if (header->value != NULL)
326     return 0;
327 
328   phrase = va_arg (alist, const char *);
329   mailbox = va_arg (alist, const char *);
330   if (phrase == NULL && mailbox == NULL)
331     return 0;
332 
333   mbox = malloc (sizeof (struct mbox));
334   if (mbox == NULL)
335     return 0;
336   mbox->phrase = (phrase != NULL) ? strdup (phrase) : NULL;
337   mbox->mailbox = strdup (mailbox);
338   mbox->next = NULL;
339 
340   mbox->next = header->value;
341   return 1;
342 }
343 
344 /* TODO: do nothing if the mailbox is NULL.  Check this doesn't fool
345          the protocol engine into thinking it has seen end of file. */
346 /* Print header-name ": " mailbox "\r\n"
347       or header-name ": \"" phrase "\" <" mailbox ">\r\n"
348  */
349 static void
print_sender(smtp_message_t message,struct rfc2822_header * header)350 print_sender (smtp_message_t message, struct rfc2822_header *header)
351 {
352   struct mbox *mbox;
353   const char *mailbox;
354 
355   assert (message != NULL && header != NULL);
356 
357   vconcatenate (&message->hdr_buffer, header->header, ": ", NULL);
358   mbox = header->value;
359   mailbox = mbox->mailbox;
360   if (mbox->phrase == NULL)
361     vconcatenate (&message->hdr_buffer,
362 		  (mailbox != NULL && *mailbox != '\0') ? mailbox : "<>",
363 		  "\r\n", NULL);
364   else
365     vconcatenate (&message->hdr_buffer, "\"", mbox->phrase, "\""
366 		  " <", (mailbox != NULL) ? mailbox : "", ">\r\n", NULL);
367 }
368 
369 static int
set_to(struct rfc2822_header * header,va_list alist)370 set_to (struct rfc2822_header *header, va_list alist)
371 {
372   struct mbox *mbox;
373   const char *mailbox;
374   const char *phrase;
375 
376   assert (header != NULL);
377 
378   phrase = va_arg (alist, const char *);
379   mailbox = va_arg (alist, const char *);
380   if (phrase == NULL && mailbox == NULL)
381     mbox = NULL;
382   else
383     {
384       mbox = malloc (sizeof (struct mbox));
385       if (mbox == NULL)
386 	return 0;
387       mbox->phrase = (phrase != NULL) ? strdup (phrase) : NULL;
388       mbox->mailbox = strdup (mailbox);
389 
390       mbox->next = header->value;
391     }
392   header->value = mbox;
393   return 1;
394 }
395 
396 static int
set_cc(struct rfc2822_header * header,va_list alist)397 set_cc (struct rfc2822_header *header, va_list alist)
398 {
399   struct mbox *mbox;
400   const char *mailbox;
401   const char *phrase;
402 
403   assert (header != NULL);
404 
405   phrase = va_arg (alist, const char *);
406   mailbox = va_arg (alist, const char *);
407   if (mailbox == NULL)
408     return 0;
409   mbox = malloc (sizeof (struct mbox));
410   if (mbox == NULL)
411     return 0;
412   mbox->phrase = (phrase != NULL) ? strdup (phrase) : NULL;
413   mbox->mailbox = strdup (mailbox);
414 
415   mbox->next = header->value;
416   header->value = mbox;
417   return 1;
418 }
419 
420 /* Print header-name ": " mailbox "\r\n"
421       or header-name ": \"" phrase "\" <" mailbox ">\r\n"
422    ad nauseum. */
423 static void
print_cc(smtp_message_t message,struct rfc2822_header * header)424 print_cc (smtp_message_t message, struct rfc2822_header *header)
425 {
426   struct mbox *mbox;
427 
428   assert (message != NULL && header != NULL);
429 
430   vconcatenate (&message->hdr_buffer, header->header, ": ", NULL);
431   for (mbox = header->value; mbox != NULL; mbox = mbox->next)
432     {
433       if (mbox->phrase == NULL)
434 	vconcatenate (&message->hdr_buffer, mbox->mailbox, NULL);
435       else
436 	vconcatenate (&message->hdr_buffer,
437 		      "\"", mbox->phrase, "\" <", mbox->mailbox, ">",
438 		      NULL);
439       vconcatenate (&message->hdr_buffer,
440 		    (mbox->next != NULL) ? ",\r\n    " : "\r\n", NULL);
441     }
442 }
443 
444 /* As above but generate a default value from the recipient list.
445  */
446 static void
print_to(smtp_message_t message,struct rfc2822_header * header)447 print_to (smtp_message_t message, struct rfc2822_header *header)
448 {
449   smtp_recipient_t recipient;
450 
451   assert (header != NULL);
452 
453   if (header->value != NULL)
454     {
455       print_cc (message, header);
456       return;
457     }
458 
459   /* TODO: implement line folding at white spaces */
460   vconcatenate (&message->hdr_buffer, header->header, ": ", NULL);
461   for (recipient = message->recipients;
462        recipient != NULL;
463        recipient = recipient->next)
464     vconcatenate (&message->hdr_buffer, recipient->mailbox,
465 		  (recipient->next != NULL) ? ",\r\n    " : "\r\n",
466 		  NULL);
467 }
468 
469 
470 /* Header actions placed here to avoid the need for many akward forward
471    declarations for set_xxx/print_xxx.  */
472 
473 static const struct header_actions header_actions[] =
474   {
475     /* This is the default header info for a simple string value.
476      */
477     { NULL,		OPTIONAL,
478       set_string,	print_string,		destroy_string},
479     /* A number of headers should be present in every message
480      */
481     { "Date",		REQUIRE,
482       set_date,		print_date, NULL, },
483     { "From",		REQUIRE | LISTVALUE,
484       set_from,		print_from,		destroy_mbox_list, },
485     /* Certain headers are added when a message is delivered and
486        should not be present in a message being posted or which
487        is in transit.  If present in the message they will be stripped
488        and if specified by the API, the relevant APIs will fail. */
489     { "Return-Path",	PROHIBIT, NULL, NULL, NULL, },
490     /* RFC 2298 - Delivering MTA may add the Original-Recipient: header
491                   using DSN ORCPT parameter and may discard
492                   Original-Recipient: headers present in the message.
493                   No point in sending it then. */
494     { "Original-Recipient", PROHIBIT, NULL, NULL, NULL, },
495     /* MIME-*: and Content-*: are MIME headers and must not be generated
496        or processed by libESMTP.  Similarly, Resent-*: and Received: must
497        be retained unaltered. */
498     { "Content-",	PRESERVE, NULL, NULL, NULL, },
499     { "MIME-",		PRESERVE, NULL, NULL, NULL, },
500     { "Resent-",	PRESERVE, NULL, NULL, NULL, },
501     { "Received",	PRESERVE, NULL, NULL, NULL, },
502     /* Headers which are optional but which are recommended to be
503        present.  Default action is to provide a default unless the
504        application explicitly requests not to. */
505     { "Message-Id",	SHOULD,
506       set_string_null,print_message_id,		destroy_string, },
507     /* Remaining headers are known to libESMTP to simplify handling them
508        for the application.   All other headers are reaated as simple
509        string values. */
510     { "Sender",		OPTIONAL,
511       set_sender,	print_sender,		destroy_mbox_list, },
512     { "To",		OPTIONAL | LISTVALUE,
513       set_to,		print_to,		destroy_mbox_list, },
514     { "Cc",		OPTIONAL | LISTVALUE,
515       set_cc,		print_cc,		destroy_mbox_list, },
516     { "Bcc",		OPTIONAL | LISTVALUE,
517       set_cc,		print_cc,		destroy_mbox_list, },
518     { "Reply-To",	OPTIONAL | LISTVALUE,
519       set_cc,		print_cc,		destroy_mbox_list, },
520     /* RFC 2298 - MDN request.  Syntax is the same as the From: header and
521                   default when set to NULL is the same as From: */
522     { "Disposition-Notification-To", OPTIONAL,
523       set_from,		print_from,		destroy_mbox_list, },
524     /* TODO:
525        In-Reply-To:	*(phrase / msgid)
526        References:	*(phrase / msgid)
527        Keywords:	#phrase
528 
529        Handle Resent- versions of
530        		To Cc Bcc Message-ID Date Reply-To From Sender
531      */
532   };
533 
534 static int
init_header_table(smtp_message_t message)535 init_header_table (smtp_message_t message)
536 {
537   int i;
538   struct header_info *hi;
539 
540   assert (message != NULL);
541 
542   if (message->hdr_action != NULL)
543     return -1;
544 
545   message->hdr_action = h_create ();
546   if (message->hdr_action == NULL)
547     return 0;
548   for (i = 0; i < NELT (header_actions); i++)
549     if (header_actions[i].name != NULL)
550       {
551 	hi = h_insert (message->hdr_action, header_actions[i].name, -1,
552 		       sizeof (struct header_info));
553 	if (hi == NULL)
554 	  return 0;
555 	hi->action = &header_actions[i];
556 
557 	/* REQUIREd headers must be present in the message.  SHOULD
558 	   means the header is optional but its presence is recommended.
559 	   Create a NULL valued header.  This will either be set later
560 	   with the API, or the print_xxx function will handle the NULL
561 	   value as a special case, e.g, the To: header is generated
562 	   from the recipient_t list. */
563 	if (hi->action->flags & (REQUIRE | SHOULD))
564 	  {
565 	    if (create_header (message, header_actions[i].name, hi) == NULL)
566 	      return 0;
567 	  }
568       }
569   return 1;
570 }
571 
572 void
destroy_header_table(smtp_message_t message)573 destroy_header_table (smtp_message_t message)
574 {
575   struct rfc2822_header *header, *next;
576 
577   assert (message != NULL);
578 
579   /* Take out the linked list */
580   for (header = message->headers; header!= NULL; header = next)
581     {
582       next = header->next;
583       if (header->info->action->destroy != NULL)
584 	(*header->info->action->destroy) (header);
585       free (header->header);
586       free (header);
587     }
588 
589   /* Take out the hash table */
590   if (message->hdr_action != NULL)
591     {
592       h_destroy (message->hdr_action, NULL, NULL);
593       message->hdr_action = NULL;
594     }
595 
596   message->headers = message->end_headers = NULL;
597 }
598 
599 struct header_info *
find_header(smtp_message_t message,const char * name,int len)600 find_header (smtp_message_t message, const char *name, int len)
601 {
602   struct header_info *info;
603   const char *p;
604 
605   assert (message != NULL && name != NULL);
606 
607   if (len < 0)
608     len = strlen (name);
609   if (len == 0)
610     return NULL;
611   info = h_search (message->hdr_action, name, len);
612   if (info == NULL && (p = memchr (name, '-', len)) != NULL)
613     info = h_search (message->hdr_action, name, p - name + 1);
614   return info;
615 }
616 
617 struct header_info *
insert_header(smtp_message_t message,const char * name)618 insert_header (smtp_message_t message, const char *name)
619 {
620   struct header_info *info;
621 
622   assert (message != NULL && name != NULL);
623 
624   info = h_insert (message->hdr_action, name, -1, sizeof (struct header_info));
625   if (info == NULL)
626     return NULL;
627   info->action = &header_actions[0];
628   return info;
629 }
630 
631 static struct rfc2822_header *
create_header(smtp_message_t message,const char * header,struct header_info * info)632 create_header (smtp_message_t message, const char *header,
633                struct header_info *info)
634 {
635   struct rfc2822_header *hdr;
636 
637   assert (message != NULL && header != NULL && info != NULL);
638 
639   if ((hdr = malloc (sizeof (struct rfc2822_header))) == NULL)
640     return NULL;
641 
642   memset (hdr, 0, sizeof (struct rfc2822_header));
643   hdr->header = strdup (header);
644   hdr->info = info;
645   info->hdr = hdr;
646   APPEND_LIST (message->headers, message->end_headers, hdr);
647   return hdr;
648 }
649 
650 /****************************************************************************
651  * Header processing
652  ****************************************************************************/
653 
654 /* Called just before copying the messge from the application.
655    Resets the seen flag for headers libESMTP is interested in */
656 
657 static void
reset_headercb(const char * name,void * data,void * arg)658 reset_headercb (const char *name __attribute__ ((unused)),
659                 void *data, void *arg __attribute__ ((unused)))
660 {
661   struct header_info *info = data;
662 
663   assert (info != NULL);
664 
665   info->seen = 0;
666 }
667 
668 int
reset_header_table(smtp_message_t message)669 reset_header_table (smtp_message_t message)
670 {
671   int status;
672 
673   assert (message != NULL);
674 
675   if ((status = init_header_table (message)) < 0)
676     h_enumerate (message->hdr_action, reset_headercb, NULL);
677   return status;
678 }
679 
680 /* This is called to process headers present in the application supplied
681    message.  */
682 const char *
process_header(smtp_message_t message,const char * header,int * len)683 process_header (smtp_message_t message, const char *header, int *len)
684 {
685   const char *p;
686   struct header_info *info;
687   const struct header_actions *action;
688   hdrprint_t print;
689 
690   assert (message != NULL && header != NULL && len != NULL);
691 
692   if (*len > 0
693       && (p = memchr (header, ':', *len)) != NULL
694       && (info = find_header (message, header, p - header)) != NULL)
695     {
696       if ((action = info->action) != NULL)
697         {
698 	  /* RFC 2822 states that headers may only appear once in a
699 	     message with the exception of a few special headers.
700 	     This restriction is enforced here. */
701           if (info->seen && !(action->flags & (MULTIPLE | PRESERVE)))
702             header = NULL;
703           if (info->prohibit || (action->flags & PROHIBIT))
704             header = NULL;
705 
706 	  /* When libESMTP is overriding headers in the message with
707 	     ones supplied in the API, the substitution is done here
708 	     to preserve the original ordering of the headers.  */
709 	  if (header != NULL && info->override)
710             {
711 	      if ((print = action->print) == NULL)
712 		print = print_string;
713 	      cat_reset (&message->hdr_buffer, 0);
714 	      (*print) (message, info->hdr);
715 	      header = cat_buffer (&message->hdr_buffer, len);
716             }
717         }
718       else if (info->seen)
719 	header = NULL;
720       info->seen = 1;
721     }
722   return header;
723 }
724 
725 /* This is called to supply headers not present in the application supplied
726    message.  */
727 const char *
missing_header(smtp_message_t message,int * len)728 missing_header (smtp_message_t message, int *len)
729 {
730   struct header_info *info;
731   hdrprint_t print;
732 
733   assert (message != NULL && len != NULL);
734 
735   /* Move on to the next header */
736   if (message->current_header == NULL)
737     message->current_header = message->headers;
738   else
739     message->current_header = message->current_header->next;
740 
741   /* Look for the next header that is actually required */
742   print = NULL;
743   while (message->current_header != NULL)
744     {
745       info = message->current_header->info;
746       if (info == NULL)		/* shouldn't happen */
747         break;
748       if (!info->seen)
749         {
750 	  if (info->action != NULL)
751 	    print = info->action->print;
752 	  break;
753         }
754       message->current_header = message->current_header->next;
755     }
756   if (message->current_header == NULL)
757     {
758       /* Free the buffer created by concatenate() and return NULL to
759          mark the end of the headers */
760       cat_free (&message->hdr_buffer);
761       return NULL;
762     }
763 
764   if (print == NULL)
765     print = print_string;
766 
767   cat_reset (&message->hdr_buffer, 0);
768   (*print) (message, message->current_header);
769   return cat_buffer (&message->hdr_buffer, len);
770 }
771 
772 /****************************************************************************
773  * Header API
774  ****************************************************************************/
775 
776 int
smtp_set_header(smtp_message_t message,const char * header,...)777 smtp_set_header (smtp_message_t message, const char *header, ...)
778 {
779   va_list alist;
780   struct rfc2822_header *hdr;
781   struct header_info *info;
782   hdrset_t set;
783 
784   SMTPAPI_CHECK_ARGS (message != NULL && header != NULL, 0);
785 
786   if (!init_header_table (message))
787     {
788       set_errno (ENOMEM);
789       return 0;
790     }
791 
792   info = find_header (message, header, -1);
793   if (info == NULL && (info = insert_header (message, header)) == NULL)
794     {
795       set_errno (ENOMEM);
796       return 0;
797     }
798 
799   /* Cannot specify a value for headers that must pass unchanged (MIME)
800      or which may not appear in a posted message (Return-Path:).  */
801   if (info->prohibit || (info->action->flags & (PROHIBIT | PRESERVE)))
802     {
803       set_error (SMTP_ERR_INVAL);
804       return 0;
805     }
806 
807   set = info->action->set;
808   if (set == NULL)
809     {
810       set_error (SMTP_ERR_INVAL);
811       return 0;
812     }
813 
814   if (info->hdr == NULL)
815     hdr = create_header (message, header, info);
816   else if (info->hdr->value == NULL)
817     hdr = info->hdr;
818   else
819     {
820       /* Header has a previous value.  If multiple headers are permitted,
821          create a new value.  If the header has a list value, the value
822          is appended to the iost.  If neither condition applies, this
823          is an error. */
824       if (info->action->flags & MULTIPLE)
825 	hdr = create_header (message, header, info);
826       else if (info->action->flags & LISTVALUE)
827 	hdr = info->hdr;
828       else
829         {
830 	  set_error (SMTP_ERR_INVAL);
831 	  return 0;
832         }
833   }
834 
835   /* Set its value */
836   va_start (alist, header);
837   (*set) (hdr, alist);
838   va_end (alist);
839 
840   return 1;
841 }
842 
843 int
smtp_set_header_option(smtp_message_t message,const char * header,enum header_option option,...)844 smtp_set_header_option (smtp_message_t message, const char *header,
845 			enum header_option option, ...)
846 {
847   va_list alist;
848   struct header_info *info;
849 
850   SMTPAPI_CHECK_ARGS (message != NULL && header != NULL, 0);
851 
852   if (!init_header_table (message))
853     {
854       set_errno (ENOMEM);
855       return 0;
856     }
857 
858   info = find_header (message, header, -1);
859   if (info == NULL && (info = insert_header (message, header)) == NULL)
860     {
861       set_errno (ENOMEM);
862       return 0;
863     }
864 
865   /* Don't permit options to be set on headers that must pass intact or
866      which are prohibited. */
867   if (info->action->flags & (PROHIBIT | PRESERVE))
868     {
869       set_error (SMTP_ERR_INVAL);
870       return 0;
871     }
872 
873   /* There is an odd quirk when setting options.  Setting an option for
874      the OPTIONAL headers known to libESMTP causes default values to be
875      generated automatically when not found in the message, so long as
876      there is no other reason to prevent them appearing in the message! */
877 
878   /* Don't allow the user to set override on prohibited headers. */
879   if (option == Hdr_OVERRIDE && !info->prohibit)
880     {
881       va_start (alist, option);
882       info->override = !!va_arg (alist, int);
883       va_end (alist);
884       return 1;
885     }
886   /* Don't allow the user to prohibit required headers. */
887   if (option == Hdr_PROHIBIT && !(info->action->flags & REQUIRE))
888     {
889       va_start (alist, option);
890       info->prohibit = !!va_arg (alist, int);
891       va_end (alist);
892       return 1;
893     }
894 
895   set_error (SMTP_ERR_INVAL);
896   return 0;
897 }
898 
899 int
smtp_set_resent_headers(smtp_message_t message,int onoff)900 smtp_set_resent_headers (smtp_message_t message, int onoff)
901 {
902   SMTPAPI_CHECK_ARGS (message != NULL, 0);
903 
904   /* TODO: place holder, implement real functionality here.
905            For now, succeed if the onoff argument is zero. */
906   SMTPAPI_CHECK_ARGS (onoff == 0, 0);
907 
908   return 1;
909 }
910