1/*	$NetBSD$	*/
2
3/*++
4/* NAME
5/*	been_here 3
6/* SUMMARY
7/*	detect repeated occurrence of string
8/* SYNOPSIS
9/*	#include <been_here.h>
10/*
11/*	BH_TABLE *been_here_init(size, flags)
12/*	int	size;
13/*	int	flags;
14/*
15/*	int	been_here_fixed(dup_filter, string)
16/*	BH_TABLE *dup_filter;
17/*	char	*string;
18/*
19/*	int	been_here(dup_filter, format, ...)
20/*	BH_TABLE *dup_filter;
21/*	char	*format;
22/*
23/*	int	been_here_check_fixed(dup_filter, string)
24/*	BH_TABLE *dup_filter;
25/*	char	*string;
26/*
27/*	int	been_here_check(dup_filter, format, ...)
28/*	BH_TABLE *dup_filter;
29/*	char	*format;
30/*
31/*	void	been_here_free(dup_filter)
32/*	BH_TABLE *dup_filter;
33/* DESCRIPTION
34/*	This module implements a simple filter to detect repeated
35/*	occurrences of character strings.
36/*
37/*	been_here_init() creates an empty duplicate filter.
38/*
39/*	been_here_fixed() looks up a fixed string in the given table, and
40/*	makes an entry in the table if the string was not found. The result
41/*	is non-zero (true) if the string was found, zero (false) otherwise.
42/*
43/*	been_here() formats its arguments, looks up the result in the
44/*	given table, and makes an entry in the table if the string was
45/*	not found. The result is non-zero (true) if the formatted result was
46/*	found, zero (false) otherwise.
47/*
48/*	been_here_check_fixed() and been_here_check() are similar
49/*	but do not update the duplicate filter.
50/*
51/*	been_here_free() releases storage for a duplicate filter.
52/*
53/*	Arguments:
54/* .IP size
55/*	Upper bound on the table size; at most \fIsize\fR strings will
56/*	be remembered.  Specify a value <= 0 to disable the upper bound.
57/* .IP flags
58/*	Requests for special processing. Specify the bitwise OR of zero
59/*	or more flags:
60/* .RS
61/* .IP BH_FLAG_FOLD
62/*	Enable case-insensitive lookup.
63/* .IP BH_FLAG_NONE
64/*	A manifest constant that requests no special processing.
65/* .RE
66/* .IP dup_filter
67/*	The table with remembered names
68/* .IP string
69/*	Fixed search string.
70/* .IP format
71/*	Format for building the search string.
72/* LICENSE
73/* .ad
74/* .fi
75/*	The Secure Mailer license must be distributed with this software.
76/* AUTHOR(S)
77/*	Wietse Venema
78/*	IBM T.J. Watson Research
79/*	P.O. Box 704
80/*	Yorktown Heights, NY 10598, USA
81/*--*/
82
83/* System library. */
84
85#include "sys_defs.h"
86#include <stdlib.h>			/* 44BSD stdarg.h uses abort() */
87#include <stdarg.h>
88
89/* Utility library. */
90
91#include <msg.h>
92#include <mymalloc.h>
93#include <htable.h>
94#include <vstring.h>
95#include <stringops.h>
96
97/* Global library. */
98
99#include "been_here.h"
100
101/* been_here_init - initialize duplicate filter */
102
103BH_TABLE *been_here_init(int limit, int flags)
104{
105    BH_TABLE *dup_filter;
106
107    dup_filter = (BH_TABLE *) mymalloc(sizeof(*dup_filter));
108    dup_filter->limit = limit;
109    dup_filter->flags = flags;
110    dup_filter->table = htable_create(0);
111    return (dup_filter);
112}
113
114/* been_here_free - destroy duplicate filter */
115
116void    been_here_free(BH_TABLE *dup_filter)
117{
118    htable_free(dup_filter->table, (void (*) (char *)) 0);
119    myfree((char *) dup_filter);
120}
121
122/* been_here - duplicate detector with finer control */
123
124int     been_here(BH_TABLE *dup_filter, const char *fmt,...)
125{
126    VSTRING *buf = vstring_alloc(100);
127    int     status;
128    va_list ap;
129
130    /*
131     * Construct the string to be checked.
132     */
133    va_start(ap, fmt);
134    vstring_vsprintf(buf, fmt, ap);
135    va_end(ap);
136
137    /*
138     * Do the duplicate check.
139     */
140    status = been_here_fixed(dup_filter, vstring_str(buf));
141
142    /*
143     * Cleanup.
144     */
145    vstring_free(buf);
146    return (status);
147}
148
149/* been_here_fixed - duplicate detector */
150
151int     been_here_fixed(BH_TABLE *dup_filter, const char *string)
152{
153    char   *folded_string;
154    const char *lookup_key;
155    int     status;
156
157    /*
158     * Special processing: case insensitive lookup.
159     */
160    if (dup_filter->flags & BH_FLAG_FOLD) {
161	folded_string = mystrdup(string);
162	lookup_key = lowercase(folded_string);
163    } else {
164	folded_string = 0;
165	lookup_key = string;
166    }
167
168    /*
169     * Do the duplicate check.
170     */
171    if (htable_locate(dup_filter->table, lookup_key) != 0) {
172	status = 1;
173    } else {
174	if (dup_filter->limit <= 0
175	    || dup_filter->limit > dup_filter->table->used)
176	    htable_enter(dup_filter->table, lookup_key, (char *) 0);
177	status = 0;
178    }
179    if (msg_verbose)
180	msg_info("been_here: %s: %d", string, status);
181
182    /*
183     * Cleanup.
184     */
185    if (folded_string)
186	myfree(folded_string);
187
188    return (status);
189}
190
191/* been_here_check - query duplicate detector with finer control */
192
193int     been_here_check(BH_TABLE *dup_filter, const char *fmt,...)
194{
195    VSTRING *buf = vstring_alloc(100);
196    int     status;
197    va_list ap;
198
199    /*
200     * Construct the string to be checked.
201     */
202    va_start(ap, fmt);
203    vstring_vsprintf(buf, fmt, ap);
204    va_end(ap);
205
206    /*
207     * Do the duplicate check.
208     */
209    status = been_here_check_fixed(dup_filter, vstring_str(buf));
210
211    /*
212     * Cleanup.
213     */
214    vstring_free(buf);
215    return (status);
216}
217
218/* been_here_check_fixed - query duplicate detector */
219
220int     been_here_check_fixed(BH_TABLE *dup_filter, const char *string)
221{
222    char   *folded_string;
223    const char *lookup_key;
224    int     status;
225
226    /*
227     * Special processing: case insensitive lookup.
228     */
229    if (dup_filter->flags & BH_FLAG_FOLD) {
230	folded_string = mystrdup(string);
231	lookup_key = lowercase(folded_string);
232    } else {
233	folded_string = 0;
234	lookup_key = string;
235    }
236
237    /*
238     * Do the duplicate check.
239     */
240    status = (htable_locate(dup_filter->table, lookup_key) != 0);
241    if (msg_verbose)
242	msg_info("been_here_check: %s: %d", string, status);
243
244    /*
245     * Cleanup.
246     */
247    if (folded_string)
248	myfree(folded_string);
249
250    return (status);
251}
252