summaryrefslogtreecommitdiffstats
path: root/src/global/dsn_util.c
blob: 52b997a3320593bc2ab577442d38e7290e11a18f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
/*++
/* NAME
/*	dsn_util 3
/* SUMMARY
/*	DSN status parsing routines
/* SYNOPSIS
/*	#include <dsn_util.h>
/*
/*	#define DSN_SIZE ...
/*
/*	typedef struct { ... } DSN_BUF;
/*
/*	typedef struct {
/* .in +4
/*		DSN_STAT dsn;		/* RFC 3463 status */
/*		const char *text;	/* Free text */
/* .in -4
/*	} DSN_SPLIT;
/*
/*	DSN_SPLIT *dsn_split(dp, def_dsn, text)
/*	DSN_SPLIT *dp;
/*	const char *def_dsn;
/*	const char *text;
/*
/*	char	*dsn_prepend(def_dsn, text)
/*	const char *def_dsn;
/*	const char *text;
/*
/*	size_t	dsn_valid(text)
/*	const char *text;
/*
/*	void	DSN_UPDATE(dsn_buf, dsn, len)
/*	DSN_BUF	dsn_buf;
/*	const char *dsn;
/*	size_t	len;
/*
/*	const char *DSN_CODE(dsn_buf)
/*	DSN_BUF dsn_buf;
/*
/*	char	*DSN_CLASS(dsn_buf)
/*	DSN_BUF	dsn_buf;
/* DESCRIPTION
/*	The functions in this module manipulate pairs of RFC 3463
/*	status codes and descriptive free text.
/*
/*	dsn_split() splits text into an RFC 3463 status code and
/*	descriptive free text.  When the text does not start with
/*	a status code, the specified default status code is used
/*	instead.  Whitespace before the optional status code or
/*	text is skipped.  dsn_split() returns a copy of the RFC
/*	3463 status code, and returns a pointer to (not copy of)
/*	the remainder of the text.  The result value is the first
/*	argument.
/*
/*	dsn_prepend() prepends the specified default RFC 3463 status
/*	code to the specified text if no status code is present in
/*	the text. This function produces the same result as calling
/*	concatenate() with the results from dsn_split().  The result
/*	should be passed to myfree(). Whitespace before the optional
/*	status code or text is skipped.
/*
/*	dsn_valid() returns the length of the RFC 3463 status code
/*	at the beginning of text, or zero. It does not skip initial
/*	whitespace.
/*
/*	Arguments:
/* .IP def_dsn
/*	Null-terminated default RFC 3463 status code that will be
/*	used when the free text does not start with one.
/* .IP dp
/*	Pointer to storage for copy of DSN status code, and for
/*	pointer to free text.
/* .IP dsn
/*	Null-terminated RFC 3463 status code.
/* .IP text
/*	Null-terminated free text.
/* SEE ALSO
/*	msg(3) diagnostics interface
/* DIAGNOSTICS
/*	Panic: invalid default DSN code.
/* LICENSE
/* .ad
/* .fi
/*	The Secure Mailer license must be distributed with this software.
/* AUTHOR(S)
/*	Wietse Venema
/*	IBM T.J. Watson Research
/*	P.O. Box 704
/*	Yorktown Heights, NY 10598, USA
/*--*/

/* System library. */

#include <sys_defs.h>
#include <stdarg.h>
#include <string.h>
#include <ctype.h>

/* Utility library. */

#include <msg.h>
#include <mymalloc.h>
#include <vstring.h>
#include <stringops.h>

/* Global library. */

#include <dsn_util.h>

/* dsn_valid - check RFC 3463 enhanced status code, return length or zero */

size_t  dsn_valid(const char *text)
{
    const unsigned char *cp = (unsigned char *) text;
    size_t  len;

    /* First portion is one digit followed by dot. */
    if ((cp[0] != '2' && cp[0] != '4' && cp[0] != '5') || cp[1] != '.')
	return (0);

    /* Second portion is 1-3 digits followed by dot. */
    cp += 2;
    if ((len = strspn((char *) cp, "0123456789")) < 1 || len > DSN_DIGS2
	|| cp[len] != '.')
	return (0);

    /* Last portion is 1-3 digits followed by end-of-string or whitespace. */
    cp += len + 1;
    if ((len = strspn((char *) cp, "0123456789")) < 1 || len > DSN_DIGS3
	|| (cp[len] != 0 && !ISSPACE(cp[len])))
	return (0);

    return (((char *) cp - text) + len);
}

/* dsn_split - split text into DSN and text */

DSN_SPLIT *dsn_split(DSN_SPLIT *dp, const char *def_dsn, const char *text)
{
    const char *myname = "dsn_split";
    const char *cp = text;
    size_t  len;

    /*
     * Look for an optional RFC 3463 enhanced status code.
     * 
     * XXX If we want to enforce that the first digit of the status code in the
     * text matches the default status code, then pipe_command() needs to be
     * changed. It currently auto-detects the reply code without knowing in
     * advance if the result will start with '4' or '5'.
     */
    while (ISSPACE(*cp))
	cp++;
    if ((len = dsn_valid(cp)) > 0) {
	strncpy(dp->dsn.data, cp, len);
	dp->dsn.data[len] = 0;
	cp += len + 1;
    } else if ((len = dsn_valid(def_dsn)) > 0) {
	strncpy(dp->dsn.data, def_dsn, len);
	dp->dsn.data[len] = 0;
    } else {
	msg_panic("%s: bad default status \"%s\"", myname, def_dsn);
    }

    /*
     * The remainder is free text.
     */
    while (ISSPACE(*cp))
	cp++;
    dp->text = cp;

    return (dp);
}

/* dsn_prepend - prepend optional status to text, result on heap */

char   *dsn_prepend(const char *def_dsn, const char *text)
{
    DSN_SPLIT dp;

    dsn_split(&dp, def_dsn, text);
    return (concatenate(DSN_STATUS(dp.dsn), " ", dp.text, (char *) 0));
}