1 
2 static char rcsid[] = "@(#)$Id: answer.c,v 1.5 1999/03/24 14:04:12 wfp5p Exp $";
3 
4 /*******************************************************************************
5  *  The Elm Mail System  -  $Revision: 1.5 $   $State: Exp $
6  *
7  *                      Copyright (c) 1988-1995 USENET Community Trust
8  * 			Copyright (c) 1986,1987 Dave Taylor
9  *******************************************************************************
10  * Bug reports, patches, comments, suggestions should be sent to:
11  *
12  *      Bill Pemberton, Elm Coordinator
13  *      flash@virginia.edu
14  *
15  *******************************************************************************
16  * $Log: answer.c,v $
17  * Revision 1.5  1999/03/24  14:04:12  wfp5p
18  * elm 2.5PL0
19  *
20  * Revision 1.4  1996/03/14  17:30:06  wfp5p
21  * Alpha 9
22  *
23  * Revision 1.3  1995/09/29  17:42:41  wfp5p
24  * Alpha 8 (Chip's big changes)
25  *
26  * Revision 1.2  1995/04/20  21:02:04  wfp5p
27  * Added the showreply feature and emacs key bindings.
28  *
29  * Revision 1.1.1.1  1995/04/19  20:38:40  wfp5p
30  * Initial import of elm 2.4 PL0 as base for elm 2.5.
31  *
32  ******************************************************************************/
33 
34 /** This program is a phone message transcription system, and
35     is designed for secretaries and the like, to allow them to
36     painlessly generate electronic mail instead of paper forms.
37 
38     Note: this program ONLY uses the local alias file, and does not
39 	  even read in the system alias file at all.
40 
41 **/
42 
43 #define INTERN
44 #include "elm_defs.h"
45 #include "s_answer.h"
46 #include "ndbz.h"
47 
48 #define  ELM		"elm"		/* where the elm program lives */
49 
50 int user_data;		/* fileno of user data file   */
51 DBZ *hash;		/* dbz file for same */
52 
53 char *get_alias_address(), *get_token(), *strip_parens(), *shift_lower();
54 static void open_alias_file(void);
55 
56 static const char *quit_word, *exit_word, *done_word, *bye_word;
57 
main(argc,argv)58 main(argc, argv)
59 int argc;
60 char *argv[];
61 {
62 	FILE *fd;
63 	char *address, buffer[LONG_STRING], tempfile[SLEN], *cp;
64 	char  name[SLEN], recip_name[SLEN], in_line[SLEN];
65 	int   msgnum = 0, eof, allow_name = 0, phone_slip = 0;
66 	int   ans_pid = getpid();
67 
68 	initialize_common();
69 
70 	quit_word = catgets(elm_msg_cat, AnswerSet, AnswerQuitWord, "quit");
71 	exit_word = catgets(elm_msg_cat, AnswerSet, AnswerExitWord, "exit");
72 	done_word = catgets(elm_msg_cat, AnswerSet, AnswerDoneWord, "done");
73 	bye_word = catgets(elm_msg_cat, AnswerSet, AnswerByeWord, "bye");
74 /*
75  *	simplistic crack arguments, looking for -u/-p
76  *	-u = allow user names not in alias table
77  *	-p = prompt for phone slip messages
78  */
79 	for (msgnum = 1; msgnum < argc; msgnum++) {
80 	  if (istrcmp(argv[msgnum], "-u") == 0)
81 	    allow_name = 1;
82 	  if (istrcmp(argv[msgnum], "-p") == 0)
83 	    phone_slip = 1;
84 	  if (istrcmp(argv[msgnum], "-pu") == 0) {
85 	    allow_name = 1;
86 	    phone_slip = 1;
87 	  }
88 	  if (istrcmp(argv[msgnum], "-up") == 0) {
89 	    allow_name = 1;
90 	    phone_slip = 1;
91 	  }
92 	}
93 
94 	open_alias_file();
95 
96 	while (1) {
97 	  if (msgnum > 9999) msgnum = 0;
98 
99 	  printf("\n-------------------------------------------------------------------------------\n");
100 
101 prompt:   printf(catgets(elm_msg_cat, AnswerSet, AnswerMessageTo, "\nMessage to: "));
102 	  if (fgets(recip_name, SLEN, stdin) == NULL) {
103 		putchar('\n');
104 		exit(0);
105 	  }
106 	  if(recip_name[0] == '\0')
107 	    goto prompt;
108 
109 	  cp = &recip_name[strlen(recip_name)-1];
110 	  if(*cp == '\n') *cp = '\0';
111 	  if(recip_name[0] == '\0')
112 		goto prompt;
113 
114 	  if ((istrcmp(recip_name, quit_word) == 0) ||
115 	      (istrcmp(recip_name, exit_word) == 0) ||
116 	      (istrcmp(recip_name, done_word) == 0) ||
117 	      (istrcmp(recip_name, bye_word)  == 0))
118 	     exit(0);
119 
120 	  if (translate(recip_name, name) == 0)
121 	    goto prompt;
122 
123 	  address = get_alias_address(name, 1, 0);
124 
125 	  if (address == NULL || strlen(address) == 0) {
126 	    if (allow_name)
127 	      address =  name;
128 	    else {
129 	      printf(catgets(elm_msg_cat, AnswerSet, AnswerSorryNotFound,
130 		     "Sorry, could not find '%s' [%s] in list!\n"),
131 		     recip_name, name);
132 	      goto prompt;
133 	    }
134 	  }
135 
136 	  printf("address '%s'\n", address);
137 
138 	  sprintf(tempfile, "%sans.%d.%d", default_temp, ans_pid, msgnum++);
139 
140 	  if ((fd = fopen(tempfile,"w")) == NULL)
141 	    exit(printf(catgets(elm_msg_cat, AnswerSet, AnswerCouldNotOpenWrite,
142 			"** Fatal Error: could not open %s to write\n"),
143 		 tempfile));
144 
145 
146 	/** Enter standard phone message fields **/
147 	  if (phone_slip) {
148 	    strcpy(buffer, catgets(elm_msg_cat, AnswerSet, AnswerCaller, "Caller: "));
149 	    printf("\n%s",buffer);
150 	    fflush(stdout);
151 	    fgets(in_line, SLEN, stdin);
152 	    if (strlen(in_line) > 1)
153 	      fprintf(fd,"%s%s",buffer,in_line);
154 
155 	    strcpy(buffer, catgets(elm_msg_cat, AnswerSet, AnswerOf, "of:     "));
156 	    printf("%s",buffer);
157 	    fflush(stdout);
158 	    fgets(in_line, SLEN, stdin);
159 	    if (strlen(in_line) > 1)
160 	      fprintf(fd,"%s%s",buffer,in_line);
161 
162 	    strcpy(buffer, catgets(elm_msg_cat, AnswerSet, AnswerPhone, "Phone:  "));
163 	    printf("%s",buffer);
164 	    fflush(stdout);
165 	    fgets(in_line, SLEN, stdin);
166 	    if (strlen(in_line) > 1)
167 	      fprintf(fd,"%s%s\n",buffer,in_line);
168 
169 	    strcpy(buffer, catgets(elm_msg_cat, AnswerSet, AnswerTelephoned, "TELEPHONED         - "));
170 	    printf("\n%s",buffer);
171 	    fflush(stdout);
172 	    fgets(in_line, SLEN, stdin);
173 	    if (strlen(in_line) > 1)
174 	      fprintf(fd,"%s%s",buffer,in_line);
175 
176 	    strcpy(buffer, catgets(elm_msg_cat, AnswerSet, AnswerCalledToSeeYou, "CALLED TO SEE YOU  - "));
177 	    printf("%s",buffer);
178 	    fflush(stdout);
179 	    fgets(in_line, SLEN, stdin);
180 	    if (strlen(in_line) > 1)
181 	      fprintf(fd,"%s%s",buffer,in_line);
182 
183 	    strcpy(buffer, catgets(elm_msg_cat, AnswerSet, AnswerWantsToSeeYou, "WANTS TO SEE YOU   - "));
184 	    printf("%s",buffer);
185 	    fflush(stdout);
186 	    fgets(in_line, SLEN, stdin);
187 	    if (strlen(in_line) > 1)
188 	      fprintf(fd,"%s%s",buffer,in_line);
189 
190 	    strcpy(buffer, catgets(elm_msg_cat, AnswerSet, AnswerReturnedYourCall, "RETURNED YOUR CALL - "));
191 	    printf("%s",buffer);
192 	    fflush(stdout);
193 	    fgets(in_line, SLEN, stdin);
194 	    if (strlen(in_line) > 1)
195 	      fprintf(fd,"%s%s",buffer,in_line);
196 
197 	    strcpy(buffer, catgets(elm_msg_cat, AnswerSet, AnswerPleaseCall, "PLEASE CALL        - "));
198 	    printf("%s",buffer);
199 	    fflush(stdout);
200 	    fgets(in_line, SLEN, stdin);
201 	    if (strlen(in_line) > 1)
202 	      fprintf(fd,"%s%s",buffer,in_line);
203 
204 	    strcpy(buffer, catgets(elm_msg_cat, AnswerSet, AnswerWillCallAgain, "WILL CALL AGAIN    - "));
205 	    printf("%s",buffer);
206 	    fflush(stdout);
207 	    fgets(in_line, SLEN, stdin);
208 	    if (strlen(in_line) > 1)
209 	      fprintf(fd,"%s%s",buffer,in_line);
210 
211 	    strcpy(buffer, catgets(elm_msg_cat, AnswerSet, AnswerUrgent, "*****URGENT******  - "));
212 	    printf("%s",buffer);
213 	    fflush(stdout);
214 	    fgets(in_line, SLEN, stdin);
215 	    if (strlen(in_line) > 1)
216 	      fprintf(fd,"%s%s",buffer,in_line);
217 	  }
218 
219 	  printf(catgets(elm_msg_cat, AnswerSet, AnswerEnterMessage,
220 		"\n\nEnter message for %s ending with a blank line.\n\n"),
221 		 recip_name);
222 
223 	  fprintf(fd,"\n\n");
224 
225 	  do {
226 	   printf("> ");
227 	   if (! (eof = (fgets(buffer, SLEN, stdin) == NULL)))
228 	     fprintf(fd, "%s", buffer);
229 	  } while (! eof && strlen(buffer) > 1);
230 
231 	  fclose(fd);
232 
233 	  sprintf(buffer, catgets(elm_msg_cat, AnswerSet, AnswerElmCommand,
234 	     "( ( %s -s \"While You Were Out\" %s < %s ; %s %s) & ) > /dev/null"),
235 	     ELM, strip_parens(address), tempfile, remove_cmd, tempfile);
236 
237 	  system(buffer);
238 	}
239 }
240 
241 int
translate(fullname,name)242 translate(fullname, name)
243 char *fullname, *name;
244 {
245 	/** translate fullname into name..
246 	       'first last'  translated to first_initial - underline - last
247 	       'initial last' translated to initial - underline - last
248 	    Return 0 if error.
249 	**/
250 	register int i, lastname = 0, len;
251 
252 	for (i=0, len = strlen(fullname); i < len; i++) {
253 
254 	  fullname[i] = tolower(fullname[i]);
255 
256 	  if (fullname[i] == ' ')
257 	    if (lastname) {
258 	      printf(catgets(elm_msg_cat, AnswerSet, AnswerCannotHaveMoreNames,
259 	      "** Can't have more than 'FirstName LastName' as address!\n"));
260 	      return(0);
261 	    }
262 	    else
263 	      lastname = i+1;
264 
265 	}
266 
267 	if (lastname)
268 	  sprintf(name, "%c_%s", fullname[0], (char *) fullname + lastname);
269 	else
270 	  strcpy(name, fullname);
271 
272 	return(1);
273 }
274 
275 static void
open_alias_file()276 open_alias_file()
277 {
278 	/** open the user alias file **/
279 
280 	char fname[SLEN];
281 
282 	sprintf(fname,  "%s/.elm/aliases", getenv("HOME"));
283 
284 	if ((hash = dbz_open(fname, O_RDONLY, 0)) == NULL)
285 	  exit(printf("** Fatal Error: Could not open %s!\n", fname));
286 
287 	if ((user_data = open(fname, O_RDONLY)) == -1)
288 	  return;
289 }
290 
get_alias_address(name,mailing,depth)291 char *get_alias_address(name, mailing, depth)
292 char *name;
293 int   mailing, depth;
294 {
295 	/** return the line from either datafile that corresponds
296 	    to the specified name.  If 'mailing' specified, then
297 	    fully expand group names.  Returns NULL if not found.
298 	    Depth is the nesting depth, and varies according to the
299 	    nesting level of the routine.  **/
300 
301 	static char buffer[VERY_LONG_STRING];
302 	static char sprbuffer[VERY_LONG_STRING];
303 	datum  key, value;
304 	int    loc;
305 	struct alias_disk_rec entry;
306 
307 	name = shift_lower(name);
308 	key.dptr = name;
309 	key.dsize = strlen(name);
310 	value = dbz_fetch(hash, key);
311 	if (value.dptr == NULL)
312 	    return( (char *) NULL); /* not found */
313 
314 	bcopy(value.dptr, (char *) &loc, sizeof(loc));
315 	loc -= sizeof(entry);
316 	lseek(user_data, loc, SEEK_SET);
317 	read(user_data, (char *) &entry, sizeof(entry));
318 	read(user_data, buffer, entry.length > VERY_LONG_STRING ? VERY_LONG_STRING : entry.length);
319 	if ((entry.type & GROUP) != 0 && mailing) {
320 	    if (expand_group(sprbuffer, buffer + entry.address,
321 			     depth) < 0)
322 		return NULL;
323 	} else {
324 	    sprintf(sprbuffer, "%s (%s)", buffer + entry.address,
325 		    buffer + entry.name);
326 	}
327 	return sprbuffer;
328 }
329 
expand_group(target,members,depth)330 int expand_group(target, members, depth)
331 char *target;
332 char *members;
333 int   depth;
334 {
335 	/** given a group of names separated by commas, this routine
336 	    will return a string that is the full addresses of each
337 	    member separated by spaces.  Depth is the current recursion
338 	    depth of the expansion (for the 'get_token' routine) **/
339 
340 	char   buf[VERY_LONG_STRING], *word, *address, *bufptr;
341 
342 	strcpy(buf, members); 	/* parameter safety! */
343 	target[0] = '\0';	/* nothing in yet!   */
344 	bufptr = (char *) buf;	/* grab the address  */
345 	depth++;		/* one more deeply into stack */
346 
347 	while ((word = (char *) get_token(bufptr, "!, ", depth)) != NULL) {
348 	  if ((address = (char *) get_alias_address(word, 1, depth)) == NULL) {
349 	    fprintf(stderr, catgets(elm_msg_cat, AnswerSet, AnswerNotFoundForGroup,
350 		"Alias %s not found for group expansion!\n"), word);
351 	    return -1;
352 	  }
353 	  else if (strcmp(target,address) != 0) {
354 	    sprintf(target + strlen(target), " %s", address);
355 	  }
356 
357 	  bufptr = NULL;
358 	}
359 	return 0;
360 }
361 
print_long(buffer,init_len)362 print_long(buffer, init_len)
363 char *buffer;
364 int   init_len;
365 {
366 	/** print buffer out, 80 characters (or less) per line, for
367 	    as many lines as needed.  If 'init_len' is specified,
368 	    it is the length that the first line can be.
369 	**/
370 
371 	register int i, loc=0, space, length, len;
372 
373 	/* In general, go to 80 characters beyond current character
374 	   being processed, and then work backwards until space found! */
375 
376 	length = init_len;
377 
378 	do {
379 	  if (strlen(buffer) > loc + length) {
380 	    space = loc + length;
381 	    while (buffer[space] != ' ' && space > loc + 50) space--;
382 	    for (i=loc;i <= space;i++)
383 	      putchar(buffer[i]);
384 	    putchar('\n');
385 	    loc = space;
386 	  }
387 	  else {
388 	    for (i=loc, len = strlen(buffer);i < len;i++)
389 	      putchar(buffer[i]);
390 	    putchar('\n');
391 	    loc = len;
392 	  }
393 	  length = 80;
394 	} while (loc < strlen(buffer));
395 }
396 
397 /****
398      The following is a newly chopped version of the 'strtok' routine
399   that can work in a recursive way (up to 20 levels of recursion) by
400   changing the character buffer to an array of character buffers....
401 ****/
402 
403 #define MAX_RECURSION		20		/* up to 20 deep recursion */
404 
405 #undef  NULL
406 #define NULL			(char *) 0	/* for this routine only   */
407 
408 
get_token(string,sepset,depth)409 char *get_token(string, sepset, depth)
410 char *string, *sepset;
411 int  depth;
412 {
413 
414 	/** string is the string pointer to break up, sepstr are the
415 	    list of characters that can break the line up and depth
416 	    is the current nesting/recursion depth of the call **/
417 
418 	register char	*p, *q, *r;
419 	static char	*savept[MAX_RECURSION];
420 
421 	/** is there space on the recursion stack? **/
422 
423 	if (depth >= MAX_RECURSION) {
424 	 fprintf(stderr, catgets(elm_msg_cat, AnswerSet, AnswerRecursionTooDeep,
425 		"Error: Get_token calls nested greater than %d deep!\n"),
426 			MAX_RECURSION);
427 	 exit(1);
428 	}
429 
430 	/* set up the pointer for the first or subsequent call */
431 	p = (string == NULL)? savept[depth]: string;
432 
433 	if(p == 0)		/* return if no tokens remaining */
434 		return(NULL);
435 
436 	q = p + strspn(p, sepset);	/* skip leading separators */
437 
438 	if (*q == '\0')		/* return if no tokens remaining */
439 		return(NULL);
440 
441 	if ((r = strpbrk(q, sepset)) == NULL)	/* move past token */
442 		savept[depth] = 0;	/* indicate this is last token */
443 	else {
444 		*r = '\0';
445 		savept[depth] = ++r;
446 	}
447 	return(q);
448 }
449