1 /*        $NetBSD: is_header.c,v 1.2 2025/02/25 19:15:45 christos Exp $         */
2 
3 /*++
4 /* NAME
5 /*        is_header 3
6 /* SUMMARY
7 /*        message header classification
8 /* SYNOPSIS
9 /*        #include <is_header.h>
10 /*
11 /*        ssize_t   is_header(string)
12 /*        const char *string;
13 /*
14 /*        ssize_t   is_header_buf(string, len)
15 /*        const char *string;
16 /*        ssize_t   len;
17 /* DESCRIPTION
18 /*        is_header() examines the given string and returns non-zero (true)
19 /*        when it begins with a mail header name + optional space + colon.
20 /*        The result is the length of the mail header name.
21 /*
22 /*        is_header_buf() is a more elaborate interface for use with strings
23 /*        that may not be null terminated.
24 /* STANDARDS
25 /*        RFC 822 (ARPA Internet Text Messages)
26 /* LICENSE
27 /* .ad
28 /* .fi
29 /*        The Secure Mailer license must be distributed with this software.
30 /* AUTHOR(S)
31 /*        Wietse Venema
32 /*        IBM T.J. Watson Research
33 /*        P.O. Box 704
34 /*        Yorktown Heights, NY 10598, USA
35 /*--*/
36 
37 /* System library. */
38 
39 #include "sys_defs.h"
40 #include <ctype.h>
41 
42 /* Global library. */
43 
44 #include "is_header.h"
45 
46 /* is_header_buf - determine if this can be a header line */
47 
is_header_buf(const char * str,ssize_t str_len)48 ssize_t is_header_buf(const char *str, ssize_t str_len)
49 {
50     const unsigned char *cp;
51     int     state;
52     int     c;
53     ssize_t len;
54 
55 #define INIT                  0
56 #define IN_CHAR               1
57 #define IN_CHAR_SPACE         2
58 #define CU_CHAR_PTR(x)        ((const unsigned char *) (x))
59 
60     /*
61      * XXX RFC 2822 Section 4.5, Obsolete header fields: whitespace may
62      * appear between header label and ":" (see: RFC 822, Section 3.4.2.).
63      *
64      * XXX Don't run off the end in case some non-standard iscntrl()
65      * implementation considers null a non-control character...
66      */
67     for (len = 0, state = INIT, cp = CU_CHAR_PTR(str); /* see below */ ; cp++) {
68           if (str_len != IS_HEADER_NULL_TERMINATED && str_len-- <= 0)
69               return (0);
70           switch (c = *cp) {
71           default:
72               if (c == 0 || !ISASCII(c) || ISCNTRL(c))
73                     return (0);
74               if (state == INIT)
75                     state = IN_CHAR;
76               if (state == IN_CHAR) {
77                     len++;
78                     continue;
79               }
80               return (0);
81           case ' ':
82           case '\t':
83               if (state == IN_CHAR)
84                     state = IN_CHAR_SPACE;
85               if (state == IN_CHAR_SPACE)
86                     continue;
87               return (0);
88           case ':':
89               return ((state == IN_CHAR || state == IN_CHAR_SPACE) ? len : 0);
90           }
91     }
92     /* Redundant return for future proofing. */
93     return (0);
94 }
95