@@ -369,24 +369,111 @@ static int send_one_mail(CURL *curl, MailConfig *mail, struct tm *p, MailNode *m
369369 strftime (message_id , sizeof (message_id ), "%Y%m%d%H%M%S.%z" , p );
370370 strftime (date_buf , sizeof (date_buf ), "%a, %d %b %Y %T %z" , p );
371371
372- sanitize_header_value (mailmsg -> mail -> subject ? mailmsg -> mail -> subject : "" , sanitized_subject , sizeof (sanitized_subject ));
372+ /* Subject: allow global override (_g_subject) to match legacy behavior. */
373+ {
374+ const char * raw_subject ;
375+
376+ if (_g_subject && _g_subject [0 ] != '\0' ) {
377+ raw_subject = _g_subject ;
378+ } else if (mailmsg -> mail -> subject ) {
379+ raw_subject = mailmsg -> mail -> subject ;
380+ } else {
381+ raw_subject = "" ;
382+ }
383+
384+ sanitize_header_value (raw_subject , sanitized_subject , sizeof (sanitized_subject ));
385+ }
386+
373387 sanitize_header_value (mail -> from , sanitized_from , sizeof (sanitized_from ));
374388 sanitize_header_value (mail -> to [0 ], sanitized_to , sizeof (sanitized_to ));
375389
390+ /* Optional headers: CC, Reply-To, and X-IDS-OSSEC. */
376391 {
377- int n = snprintf (header_buf , sizeof (header_buf ),
378- "Date: %s\r\n"
379- "To: %s\r\n"
380- "From: %s\r\n"
381- "Message-ID: <%s@%s>\r\n"
382- "Subject: %s\r\n"
383- "\r\n" ,
384- date_buf ,
385- sanitized_to ,
386- sanitized_from ,
387- message_id ,
388- hostname ,
389- sanitized_subject );
392+ char cc_header_line [HEADER_MAX ];
393+ char replyto_header_line [HEADER_MAX ];
394+ char sanitized_idsname [HEADER_MAX ];
395+ int n ;
396+ size_t i ;
397+
398+ cc_header_line [0 ] = '\0' ;
399+ replyto_header_line [0 ] = '\0' ;
400+
401+ /* Build CC header from additional recipients (mail->to[1..]). */
402+ if (mail -> to [1 ]) {
403+ char cc_value [HEADER_MAX ];
404+ size_t cc_len = 0 ;
405+
406+ cc_value [0 ] = '\0' ;
407+
408+ for (i = 1 ; mail -> to [i ] != NULL ; ++ i ) {
409+ char sanitized_cc [HEADER_MAX ];
410+ size_t addr_len ;
411+
412+ sanitize_header_value (mail -> to [i ], sanitized_cc , sizeof (sanitized_cc ));
413+ addr_len = strlen (sanitized_cc );
414+
415+ if (addr_len == 0 ) {
416+ continue ;
417+ }
418+
419+ /* Add comma+space between multiple CC addresses. */
420+ if (cc_len > 0 ) {
421+ if (cc_len + 2 >= sizeof (cc_value )) {
422+ break ;
423+ }
424+ cc_value [cc_len ++ ] = ',' ;
425+ cc_value [cc_len ++ ] = ' ' ;
426+ }
427+
428+ if (cc_len + addr_len >= sizeof (cc_value )) {
429+ break ;
430+ }
431+
432+ memcpy (cc_value + cc_len , sanitized_cc , addr_len );
433+ cc_len += addr_len ;
434+ cc_value [cc_len ] = '\0' ;
435+ }
436+
437+ if (cc_len > 0 ) {
438+ (void )snprintf (cc_header_line , sizeof (cc_header_line ),
439+ "Cc: %s\r\n" , cc_value );
440+ }
441+ }
442+
443+ /* Optional Reply-To header, if configured in the mail structure. */
444+ if (mail -> reply_to ) {
445+ char sanitized_reply_to [HEADER_MAX ];
446+
447+ sanitize_header_value (mail -> reply_to , sanitized_reply_to , sizeof (sanitized_reply_to ));
448+ if (sanitized_reply_to [0 ] != '\0' ) {
449+ (void )snprintf (replyto_header_line , sizeof (replyto_header_line ),
450+ "Reply-To: %s\r\n" , sanitized_reply_to );
451+ }
452+ }
453+
454+ /* X-IDS-OSSEC header from mail->idsname, matching legacy behavior. */
455+ sanitize_header_value (mail -> idsname ? mail -> idsname : "" ,
456+ sanitized_idsname , sizeof (sanitized_idsname ));
457+
458+ n = snprintf (header_buf , sizeof (header_buf ),
459+ "Date: %s\r\n"
460+ "To: %s\r\n"
461+ "%s"
462+ "From: %s\r\n"
463+ "%s"
464+ "Message-ID: <%s@%s>\r\n"
465+ "X-IDS-OSSEC: %s\r\n"
466+ "Subject: %s\r\n"
467+ "\r\n" ,
468+ date_buf ,
469+ sanitized_to ,
470+ cc_header_line ,
471+ sanitized_from ,
472+ replyto_header_line ,
473+ message_id ,
474+ hostname ,
475+ sanitized_idsname ,
476+ sanitized_subject );
390477 if (n < 0 || (size_t )n >= sizeof (header_buf )) {
391478 merror ("%s: Email header truncated (subject/from/to too long)." , ARGV0 );
392479 goto done ;
0 commit comments