Postfix3.3.1
dsn_util.c
[詳解]
1 /*++
2 /* NAME
3 /* dsn_util 3
4 /* SUMMARY
5 /* DSN status parsing routines
6 /* SYNOPSIS
7 /* #include <dsn_util.h>
8 /*
9 /* #define DSN_SIZE ...
10 /*
11 /* typedef struct { ... } DSN_BUF;
12 /*
13 /* typedef struct {
14 /* .in +4
15 /* DSN_STAT dsn; /* RFC 3463 status */
16 /* const char *text; /* Free text */
17 /* .in -4
18 /* } DSN_SPLIT;
19 /*
20 /* DSN_SPLIT *dsn_split(dp, def_dsn, text)
21 /* DSN_SPLIT *dp;
22 /* const char *def_dsn;
23 /* const char *text;
24 /*
25 /* char *dsn_prepend(def_dsn, text)
26 /* const char *def_dsn;
27 /* const char *text;
28 /*
29 /* size_t dsn_valid(text)
30 /* const char *text;
31 /*
32 /* void DSN_UPDATE(dsn_buf, dsn, len)
33 /* DSN_BUF dsn_buf;
34 /* const char *dsn;
35 /* size_t len;
36 /*
37 /* const char *DSN_CODE(dsn_buf)
38 /* DSN_BUF dsn_buf;
39 /*
40 /* char *DSN_CLASS(dsn_buf)
41 /* DSN_BUF dsn_buf;
42 /* DESCRIPTION
43 /* The functions in this module manipulate pairs of RFC 3463
44 /* status codes and descriptive free text.
45 /*
46 /* dsn_split() splits text into an RFC 3463 status code and
47 /* descriptive free text. When the text does not start with
48 /* a status code, the specified default status code is used
49 /* instead. Whitespace before the optional status code or
50 /* text is skipped. dsn_split() returns a copy of the RFC
51 /* 3463 status code, and returns a pointer to (not copy of)
52 /* the remainder of the text. The result value is the first
53 /* argument.
54 /*
55 /* dsn_prepend() prepends the specified default RFC 3463 status
56 /* code to the specified text if no status code is present in
57 /* the text. This function produces the same result as calling
58 /* concatenate() with the results from dsn_split(). The result
59 /* should be passed to myfree(). Whitespace before the optional
60 /* status code or text is skipped.
61 /*
62 /* dsn_valid() returns the length of the RFC 3463 status code
63 /* at the beginning of text, or zero. It does not skip initial
64 /* whitespace.
65 /*
66 /* Arguments:
67 /* .IP def_dsn
68 /* Null-terminated default RFC 3463 status code that will be
69 /* used when the free text does not start with one.
70 /* .IP dp
71 /* Pointer to storage for copy of DSN status code, and for
72 /* pointer to free text.
73 /* .IP dsn
74 /* Null-terminated RFC 3463 status code.
75 /* .IP text
76 /* Null-terminated free text.
77 /* SEE ALSO
78 /* msg(3) diagnostics interface
79 /* DIAGNOSTICS
80 /* Panic: invalid default DSN code.
81 /* LICENSE
82 /* .ad
83 /* .fi
84 /* The Secure Mailer license must be distributed with this software.
85 /* AUTHOR(S)
86 /* Wietse Venema
87 /* IBM T.J. Watson Research
88 /* P.O. Box 704
89 /* Yorktown Heights, NY 10598, USA
90 /*--*/
91 
92 /* System library. */
93 
94 #include <sys_defs.h>
95 #include <stdarg.h>
96 #include <string.h>
97 #include <ctype.h>
98 
99 /* Utility library. */
100 
101 #include <msg.h>
102 #include <mymalloc.h>
103 #include <vstring.h>
104 #include <stringops.h>
105 
106 /* Global library. */
107 
108 #include <dsn_util.h>
109 
110 /* dsn_valid - check RFC 3463 enhanced status code, return length or zero */
111 
112 size_t dsn_valid(const char *text)
113 {
114  const unsigned char *cp = (unsigned char *) text;
115  size_t len;
116 
117  /* First portion is one digit followed by dot. */
118  if ((cp[0] != '2' && cp[0] != '4' && cp[0] != '5') || cp[1] != '.')
119  return (0);
120 
121  /* Second portion is 1-3 digits followed by dot. */
122  cp += 2;
123  if ((len = strspn((char *) cp, "0123456789")) < 1 || len > DSN_DIGS2
124  || cp[len] != '.')
125  return (0);
126 
127  /* Last portion is 1-3 digits followed by end-of-string or whitespace. */
128  cp += len + 1;
129  if ((len = strspn((char *) cp, "0123456789")) < 1 || len > DSN_DIGS3
130  || (cp[len] != 0 && !ISSPACE(cp[len])))
131  return (0);
132 
133  return (((char *) cp - text) + len);
134 }
135 
136 /* dsn_split - split text into DSN and text */
137 
138 DSN_SPLIT *dsn_split(DSN_SPLIT *dp, const char *def_dsn, const char *text)
139 {
140  const char *myname = "dsn_split";
141  const char *cp = text;
142  size_t len;
143 
144  /*
145  * Look for an optional RFC 3463 enhanced status code.
146  *
147  * XXX If we want to enforce that the first digit of the status code in the
148  * text matches the default status code, then pipe_command() needs to be
149  * changed. It currently auto-detects the reply code without knowing in
150  * advance if the result will start with '4' or '5'.
151  */
152  while (ISSPACE(*cp))
153  cp++;
154  if ((len = dsn_valid(cp)) > 0) {
155  strncpy(dp->dsn.data, cp, len);
156  dp->dsn.data[len] = 0;
157  cp += len + 1;
158  } else if ((len = dsn_valid(def_dsn)) > 0) {
159  strncpy(dp->dsn.data, def_dsn, len);
160  dp->dsn.data[len] = 0;
161  } else {
162  msg_panic("%s: bad default status \"%s\"", myname, def_dsn);
163  }
164 
165  /*
166  * The remainder is free text.
167  */
168  while (ISSPACE(*cp))
169  cp++;
170  dp->text = cp;
171 
172  return (dp);
173 }
174 
175 /* dsn_prepend - prepend optional status to text, result on heap */
176 
177 char *dsn_prepend(const char *def_dsn, const char *text)
178 {
179  DSN_SPLIT dp;
180 
181  dsn_split(&dp, def_dsn, text);
182  return (concatenate(DSN_STATUS(dp.dsn), " ", dp.text, (char *) 0));
183 }
#define DSN_DIGS3
Definition: dsn_util.h:24
DSN_SPLIT * dsn_split(DSN_SPLIT *dp, const char *def_dsn, const char *text)
Definition: dsn_util.c:138
size_t dsn_valid(const char *text)
Definition: dsn_util.c:112
#define DSN_DIGS2
Definition: dsn_util.h:23
DSN_SPLIT dp
Definition: dsn_filter.c:96
NORETURN msg_panic(const char *fmt,...)
Definition: msg.c:295
#define DSN_STATUS(dsn_buf)
Definition: dsn_util.h:46
char * dsn_prepend(const char *def_dsn, const char *text)
Definition: dsn_util.c:177
const char * text
Definition: dsn_util.h:55
DSN_STAT dsn
Definition: dsn_util.h:54
char * concatenate(const char *arg0,...)
Definition: concatenate.c:42
#define ISSPACE(c)
Definition: sys_defs.h:1753
char data[DSN_SIZE]
Definition: dsn_util.h:35