1 /*
2  *	(c) Copyright 1990, Kim Fabricius Storm.  All rights reserved.
3  *      Copyright (c) 1996-2005 Michael T Pins.  All rights reserved.
4  *
5  *	Pack sender's name into something sensible, return in packed:
6  * 	pack_name(packed, name, length)
7  *
8  */
9 
10 #include <string.h>
11 #include "config.h"
12 #include "global.h"
13 
14  /* #define NAME_TEST *//* Never define this ! */
15 
16 #define	SEP_DOT		0	/* . */
17 #define	SEP_PERCENT	1	/* % */
18 #define	SEP_SCORE	2	/* _ */
19 #define	SEP_AMPERSAND	3	/* @ */
20 #define	SEP_BANG	4	/* ! */
21 #define	SEP_MAXIMUM	5
22 
23 
24 #define CL_OK		0x0100	/* letter or digit */
25 #define CL_SPACE	0x0200	/* cvt to space */
26 #define CL_IGNORE	0x0400	/* ignore */
27 #define CL_RANGE(c)	0x0800+c/* space range, end with c */
28 #define CL_HYPHEN	0x1000	/* convert to - */
29 #define CL_STOP		0x2000	/* discard rest of name */
30 #define	CL_SEP		| 0x4000 +	/* address separator */
31 
32 #define	IS_OK(c)	(Class[c] & CL_OK)
33 #define IS_SPACE(c)	(Class[c] & CL_SPACE)
34 #define IGNORE(c)	(c & 0x80 || Class[c] & CL_IGNORE)
35 #define BEGIN_RANGE(c)	(Class[c] & CL_RANGE(0))
36 #define END_RANGE(c)	(Class[c] & 0xff)
37 #define IS_HYPHEN(c)	(Class[c] & CL_HYPHEN)
38 #define IS_STOP(c)	(Class[c] & CL_STOP)
39 #define	IS_SEPARATOR(c)	(Class[c] & (0 CL_SEP 0))
40 
41 int             old_packname = 0;	/* Default to new behavior */
42 
43 static short    Class[128] = {
44      /* NUL */ CL_STOP,
45      /* SOH */ CL_IGNORE,
46      /* STX */ CL_IGNORE,
47      /* ETX */ CL_IGNORE,
48      /* EOT */ CL_IGNORE,
49      /* ENQ */ CL_IGNORE,
50      /* ACK */ CL_IGNORE,
51      /* BEL */ CL_IGNORE,
52      /* BS  */ CL_IGNORE,
53      /* TAB */ CL_SPACE,
54      /* NL  */ CL_IGNORE,
55      /* VT  */ CL_IGNORE,
56      /* FF  */ CL_IGNORE,
57      /* CR  */ CL_IGNORE,
58      /* SO  */ CL_IGNORE,
59      /* SI  */ CL_IGNORE,
60      /* DLE */ CL_IGNORE,
61      /* DC1 */ CL_IGNORE,
62      /* DC2 */ CL_IGNORE,
63      /* DC3 */ CL_IGNORE,
64      /* DC4 */ CL_IGNORE,
65      /* NAK */ CL_IGNORE,
66      /* SYN */ CL_IGNORE,
67      /* ETB */ CL_IGNORE,
68      /* CAN */ CL_IGNORE,
69      /* EM  */ CL_IGNORE,
70      /* SUB */ CL_IGNORE,
71      /* ESC */ CL_IGNORE,
72      /* FS  */ CL_IGNORE,
73      /* GS  */ CL_IGNORE,
74      /* RS  */ CL_IGNORE,
75      /* US  */ CL_IGNORE,
76 
77      /* space */ CL_SPACE,
78      /* !   */ CL_SPACE CL_SEP SEP_BANG,
79      /* "   */ CL_RANGE('"'),
80      /* #   */ CL_OK,
81      /* $   */ CL_OK,
82      /* %   */ CL_OK CL_SEP SEP_PERCENT,
83      /* &   */ CL_OK,
84      /* '   */ CL_OK,
85      /* (   */ CL_OK,
86      /* )   */ CL_OK,
87      /* *   */ CL_HYPHEN,
88      /* +   */ CL_HYPHEN,
89      /* ,   */ CL_STOP,
90      /* -   */ CL_HYPHEN,
91      /* .   */ CL_SPACE CL_SEP SEP_DOT,
92      /* /   */ CL_OK,
93      /* 0   */ CL_OK,
94      /* 1   */ CL_OK,
95      /* 2   */ CL_OK,
96      /* 3   */ CL_OK,
97      /* 4   */ CL_OK,
98      /* 5   */ CL_OK,
99      /* 6   */ CL_OK,
100      /* 7   */ CL_OK,
101      /* 8   */ CL_OK,
102      /* 9   */ CL_OK,
103      /* :   */ CL_IGNORE,
104      /* ;   */ CL_STOP,
105      /* <   */ CL_IGNORE,
106      /* =   */ CL_HYPHEN,
107      /* >   */ CL_IGNORE,
108      /* ?   */ CL_IGNORE,
109      /* @   */ CL_OK CL_SEP SEP_AMPERSAND,
110      /* A   */ CL_OK,
111      /* B   */ CL_OK,
112      /* C   */ CL_OK,
113      /* D   */ CL_OK,
114      /* E   */ CL_OK,
115      /* F   */ CL_OK,
116      /* G   */ CL_OK,
117      /* H   */ CL_OK,
118      /* I   */ CL_OK,
119      /* J   */ CL_OK,
120      /* K   */ CL_OK,
121      /* L   */ CL_OK,
122      /* M   */ CL_OK,
123      /* N   */ CL_OK,
124      /* O   */ CL_OK,
125      /* P   */ CL_OK,
126      /* Q   */ CL_OK,
127      /* R   */ CL_OK,
128      /* S   */ CL_OK,
129      /* T   */ CL_OK,
130      /* U   */ CL_OK,
131      /* V   */ CL_OK,
132      /* W   */ CL_OK,
133      /* X   */ CL_OK,
134      /* Y   */ CL_OK,
135      /* Z   */ CL_OK,
136      /* [   */ CL_OK,
137      /* \   */ CL_OK,
138      /* ]   */ CL_OK,
139      /* ^   */ CL_IGNORE,
140      /* _   */ CL_SPACE CL_SEP SEP_SCORE,
141      /* `   */ CL_IGNORE,
142      /* a   */ CL_OK,
143      /* b   */ CL_OK,
144      /* c   */ CL_OK,
145      /* d   */ CL_OK,
146      /* e   */ CL_OK,
147      /* f   */ CL_OK,
148      /* g   */ CL_OK,
149      /* h   */ CL_OK,
150      /* i   */ CL_OK,
151      /* j   */ CL_OK,
152      /* k   */ CL_OK,
153      /* l   */ CL_OK,
154      /* m   */ CL_OK,
155      /* n   */ CL_OK,
156      /* o   */ CL_OK,
157      /* p   */ CL_OK,
158      /* q   */ CL_OK,
159      /* r   */ CL_OK,
160      /* s   */ CL_OK,
161      /* t   */ CL_OK,
162      /* u   */ CL_OK,
163      /* v   */ CL_OK,
164      /* w   */ CL_OK,
165      /* x   */ CL_OK,
166      /* y   */ CL_OK,
167      /* z   */ CL_OK,
168      /* {   */ CL_OK,
169      /* |   */ CL_OK,
170      /* }   */ CL_OK,
171      /* ~   */ CL_HYPHEN,
172      /* DEL  */ CL_IGNORE
173 };
174 
175 
176 int
pack_name(char * dest,char * source,int length)177 pack_name(char *dest, char *source, int length)
178 {
179     register char  *p, *q, *r, c;
180     register int    n;
181     register int    i;
182     register int    sep;
183     register int    ignore_braces = 0;
184     register int    ignore_comment = 0;
185     register int    rfc_addr = 0;
186     register char  *name;
187     register int    lname;
188     register int    drop_space, prev_space;
189     char           *maxq;
190     int             lfirst, lmiddle, llast;
191     char            namebuf[129];
192     char           *separator[SEP_MAXIMUM];
193 
194     dest[0] = NUL;
195 
196     if (source == NULL || source[0] == NUL)
197 	return 0;
198 
199     p = source;
200     if (old_packname == 0) {
201 	while ((c = *p++)) {
202 	    if (c == '<') {
203 		ignore_comment = 1;
204 		rfc_addr = 1;
205 		break;
206 	    }
207 	}
208     }
209 full_scan:
210     p = source, q = namebuf, n = 0;
211     maxq = namebuf + sizeof namebuf - 1;
212 
213 new_partition:
214     for (i = SEP_MAXIMUM; --i >= 0; separator[i] = NULL);
215 
216     while ((c = *p++)) {
217 	if (c == '<') {
218 	    while (q > namebuf && q[-1] == SP)
219 		q--;
220 	    if (q == namebuf)
221 		continue;
222 	    break;
223 	}
224 	if (c == '(' && ignore_comment)
225 	    break;
226 	if (!rfc_addr) {
227 	    if (c == ')') {
228 		if (--n == 0 && !ignore_braces)
229 		    break;
230 		continue;
231 	    }
232 	}
233 	if (IGNORE((int8) c))
234 	    continue;
235 	if (q == namebuf && IS_SPACE((int8) c))
236 	    continue;
237 
238 	if (!rfc_addr) {
239 	    if (c == '(') {
240 		if (n++ == 0 && !ignore_braces) {
241 		    q = namebuf;
242 		    goto new_partition;
243 		}
244 		continue;
245 	    }
246 	}
247 	if (n > 0)
248 	    if (ignore_braces || n > 1)
249 		continue;
250 	if (q >= maxq)
251 	    break;
252 	*q++ = c;
253 	if (IS_SEPARATOR((int8) c)) {
254 	    switch ((sep = (Class[(int8) c] & 0xff))) {
255 
256 		case SEP_DOT:
257 		    if (separator[SEP_AMPERSAND] && q - namebuf <= length)
258 			break;
259 		    continue;
260 
261 		case SEP_BANG:
262 		    if (separator[SEP_AMPERSAND])
263 			continue;
264 		    break;
265 
266 		default:
267 		    if (separator[sep])
268 			continue;
269 		    break;
270 	    }
271 
272 	    separator[sep] = q - 1;
273 	}
274     }
275 
276     *q = NUL;
277 
278     if (namebuf[0] == NUL && !ignore_braces) {
279 	ignore_braces = 1;
280 	goto full_scan;
281     }
282     if (namebuf[0] == NUL)
283 	return 0;
284 
285     name = namebuf;
286 
287     if (name[0] == '"') {
288 	name++;
289 	if (q[-1] == '"')
290 	    *--q = NUL;
291     }
292     if (q - name <= length)
293 	goto name_ok;
294 
295     /* sorry for the many goto's -- the 3B2 C compiler does not */
296     /* make correct code for complicated logical expressions!!  */
297     /* not even without -O					 */
298 
299     /* We must pack the name to make it fit */
300 
301     /* Name_of_person%... -> Name_of_person */
302 
303     if ((r = separator[SEP_PERCENT])) {
304 	if (!(q = separator[SEP_SCORE]) || q > r)
305 	    goto no_percent;
306 	if ((q = separator[SEP_AMPERSAND]) && q < r)
307 	    goto no_percent;
308 	if ((q = separator[SEP_BANG]) && q < r)
309 	    goto no_percent;
310 	*r = NUL;
311 	goto parse_name;
312     }
313 no_percent:
314 
315     /* name@site.domain -> name@site */
316 
317     if ((r = separator[SEP_AMPERSAND])) {
318 
319 	if ((q = separator[SEP_PERCENT]) && q < r) {
320 	    *r = NUL;
321 	    if (r - name <= length)
322 		goto name_ok;
323 
324 	    *q = NUL;
325 
326 	    if (((p = separator[SEP_BANG]) && p < q)
327 		|| ((p = strrchr(name, '!')) && p < q)) {
328 		name = p + 1;
329 	    }
330 	    if (strchr(name, '.'))
331 		goto parse_name;
332 
333 	    goto name_ok;
334 	}
335 	if ((q = separator[SEP_DOT])) {
336 	    *q = NUL;
337 	    goto name_ok;
338 	}
339 	*r = NUL;
340 	if (r - name <= length)
341 	    goto name_ok;
342 
343 	if ((q = separator[SEP_BANG]) && q < r) {
344 	    name = q + 1;
345 	    goto name_ok;
346 	}
347 
348 #ifdef NOTDEF
349 	if (strchr(name, '!') == NULL)
350 	    goto parse_name;	/* take the chance ... */
351 #endif
352 
353 	goto name_ok;		/* can't do it any better */
354     }
355     /* Phase 1: Normalization (remove superfluous characters) */
356 
357 parse_name:
358 
359     for (p = name, lname = 0, prev_space = 0; (c = *p); p++) {
360 
361 /*
362 	if (IGNORE(c)) {
363 	    *p = TAB;
364 	    if (p == name) name++;
365 	    continue;
366 	}
367 */
368 
369 	if (IS_OK((int8) c)) {
370 	    lname++;
371 	    prev_space = 0;
372 	    continue;
373 	}
374 	if (IS_HYPHEN((int8) c)) {
375 	    if (p == name) {
376 		name++;
377 		continue;
378 	    }
379 	    if (prev_space) {
380 		*p = NUL;
381 		break;		/* strip from " -" */
382 		/* *p = TAB; *//* do not strip from " -" */
383 	    } else {
384 		*p = '-';
385 		lname++;
386 	    }
387 	    continue;
388 	}
389 	if (BEGIN_RANGE((int8) c)) {
390 
391 	    if (p == name) {
392 		name++;
393 		continue;
394 	    }
395 	    c = END_RANGE((int8) c);
396 	    for (q = p + 1; *q && *q != c; q++);
397 	    if (*q) {
398 		if (p[-1] != ' ')
399 		    lname++;
400 		while (p <= q)
401 		    *p++ = ' ';
402 		p--;
403 		prev_space++;
404 		continue;
405 	    }
406 	    c = ' ';
407 	}
408 	if (IS_SPACE((int8) c)) {
409 	    *p = ' ';
410 	    if (p == name)
411 		name++;
412 	    else if (!prev_space) {
413 		lname++;
414 		prev_space++;
415 	    }
416 	    continue;
417 	}
418 	if (IS_STOP((int8) c)) {
419 	    *p = NUL;
420 	    break;
421 	}
422     }
423 drop_last_name:
424     while (p > name && (*--p == ' ' || *p == TAB))
425 	*p = NUL;
426 
427     if (lname < length)
428 	goto name_ok;
429 
430 
431     /* Phase 2: Reduce middle names */
432 
433     for (r = p, llast = 0; r > name && *r != ' '; r--)
434 	if (*r != TAB)
435 	    llast++;
436 
437     /* r points to space before last name */
438 
439     if (strncmp(r, " Jr", 3) == 0 || strncmp(r, " II", 3) == 0) {
440 	p = r + 1;
441 	lname -= llast;
442 	goto drop_last_name;
443     }
444     if (r == name)
445 	goto phase6;		/* only last name */
446 
447     for (q = name, lfirst = 0; *q && *q != ' '; q++)
448 	if (*q != TAB)
449 	    lfirst++;
450 
451     /* q points at space after first name */
452 
453     for (p = q, lmiddle = 0; p < r;) {
454 	/* find next middle name */
455 	while (p < r && (*p == ' ' || *p == TAB))
456 	    p++;
457 
458 	if (p >= r)
459 	    break;		/* found last name */
460 
461 	p++;			/* skip first char of middle name */
462 	for (; *p != ' '; p++) {/* remove rest */
463 	    if (*p == TAB)
464 		continue;
465 	    *p = TAB;
466 	    lname--;
467 	}
468 	lmiddle += 2;		/* initial + space */
469     }
470 
471     if (lname < length)
472 	goto name_ok;
473 
474     /*
475      * If removing middle names is not enough, but reducing first name
476      * instead is, do it that way
477      */
478 
479     if (lname - lmiddle >= length && lname - lfirst + 1 < length)
480 	goto phase4;
481 
482 
483     /* Phase 3: Remove middle names */
484 
485     for (p = q; p < r; p++) {
486 	if (*p == TAB)
487 	    continue;
488 	if (*p == ' ')
489 	    continue;
490 	*p = TAB;
491 	lname -= 2;
492     }
493 
494     if (lname < length)
495 	goto name_ok;
496 
497 
498     /* Phase 4: Reduce first name */
499 
500 phase4:
501     for (p = name + 1; p < q; p++) {
502 	if (*p == TAB)
503 	    continue;
504 	if (*p == ' ')
505 	    continue;
506 	if (*p == '-' && (p + 1) < q) {
507 	    p++;
508 	    continue;
509 	}
510 	*p = TAB;
511 	lname--;
512     }
513 
514     if (lname < length)
515 	goto name_ok;
516 
517     /* Phase 5: Remove first name */
518 
519     name = r + 1;
520     lname--;
521 
522     if (lname < length)
523 	goto name_ok;
524 
525     /* Phase 6: Cut last name */
526 phase6:
527     goto name_ok;
528 
529 name_ok:
530 
531     q = dest;
532     maxq = q + length;
533 
534     drop_space = 1;
535 
536     for (p = name; *p && q < maxq; p++) {
537 	if (*p == TAB)
538 	    continue;
539 
540 	if (*p == ' ') {
541 	    if (!drop_space) {
542 		drop_space = 1;
543 		*q++ = ' ';
544 	    }
545 	    continue;
546 	}
547 	drop_space = 0;
548 	*q++ = *p;
549     }
550 
551     *q = NUL;
552 
553     return strlen(dest);
554 }
555 
556 #ifdef NAME_TEST
main()557 main()
558 {
559     char            in[512], out[512];
560 
561     while (fgets(in, sizeof(in), stdin)) {
562 	printf("'%s' -> (%d) ", in, pack_name(out, in, Name_Length));
563 	puts(out);
564     }
565     exit(0);
566 }
567 
568 #endif
569