1 #include <ctype.h>
2 #include <stdio.h>
3 #include <pwd.h>
4 #include <utmp.h>
5 #include <signal.h>
6 #include <sys/types.h>
7 #include <sys/stat.h>
8 #include <setjmp.h>
9 #include <sysexits.h>
10 
11 static char SccsId[] = "@(#)mail.local.c	4.15	04/12/83";
12 
13 #define SENDMAIL	"/usr/lib/sendmail"
14 
15 
16 /*copylet flags */
17 	/*remote mail, add rmtmsg */
18 #define REMOTE	1
19 	/* zap header and trailing empty line */
20 #define ZAP	3
21 #define ORDINARY 2
22 #define	FORWARD	4
23 #define	LSIZE	256
24 #define	MAXLET	300	/* maximum number of letters */
25 #define	MAILMODE (~0600)		/* mode of created mail */
26 
27 char	line[LSIZE];
28 char	resp[LSIZE];
29 struct let {
30 	long	adr;
31 	char	change;
32 } let[MAXLET];
33 int	nlet	= 0;
34 char	lfil[50];
35 long	iop, time();
36 char	*getenv();
37 char	*index();
38 char	lettmp[] = "/tmp/maXXXXX";
39 char	maildir[] = "/usr/spool/mail/";
40 char	mailfile[] = "/usr/spool/mail/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
41 char	dead[] = "dead.letter";
42 char	*netname = "vax";
43 char	forwmsg[] = " forwarded\n";
44 FILE	*tmpf;
45 FILE	*malf;
46 char	*my_name;
47 char	*getlogin();
48 struct	passwd	*getpwuid();
49 int	error;
50 int	changed;
51 int	forward;
52 char	from[] = "From ";
53 long	ftell();
54 int	delete();
55 char	*ctime();
56 int	flgf;
57 int	flgp;
58 int	delflg = 1;
59 int	hseqno;
60 jmp_buf	sjbuf;
61 int	rmail;
62 
63 main(argc, argv)
64 char **argv;
65 {
66 	register i;
67 	char sobuf[BUFSIZ];
68 
69 	setbuf(stdout, sobuf);
70 	mktemp(lettmp);
71 	unlink(lettmp);
72 	my_name = getlogin();
73 	if (my_name == NULL || strlen(my_name) == 0) {
74 		struct passwd *pwent;
75 		pwent = getpwuid(getuid());
76 		if (pwent==NULL)
77 			my_name = "???";
78 		else
79 			my_name = pwent->pw_name;
80 	}
81 	if(setjmp(sjbuf)) done();
82 	for (i=0; i<20; i++)
83 		setsig(i, delete);
84 	tmpf = fopen(lettmp, "w");
85 	if (tmpf == NULL) {
86 		fprintf(stderr, "mail: cannot open %s for writing\n", lettmp);
87 		done();
88 	}
89 	if (argv[0][0] == 'r')
90 		rmail++;
91 	if (argv[0][0] != 'r' &&	/* no favors for rmail*/
92 	   (argc == 1 || argv[1][0] == '-' && !any(argv[1][1], "rhd")))
93 		printmail(argc, argv);
94 	else
95 		bulkmail(argc, argv);
96 	done();
97 }
98 
99 setsig(i, f)
100 int i;
101 int (*f)();
102 {
103 	if(signal(i, SIG_IGN)!=SIG_IGN)
104 		signal(i, f);
105 }
106 
107 any(c, str)
108 	register int c;
109 	register char *str;
110 {
111 
112 	while (*str)
113 		if (c == *str++)
114 			return(1);
115 	return(0);
116 }
117 
118 printmail(argc, argv)
119 char **argv;
120 {
121 	int flg, i, j, print;
122 	char *p, *getarg();
123 	struct stat statb;
124 
125 	setuid(getuid());
126 	cat(mailfile, maildir, my_name);
127 	if (stat(mailfile, &statb) >= 0
128 	    && (statb.st_mode & S_IFMT) == S_IFDIR) {
129 		strcat(mailfile, "/");
130 		strcat(mailfile, my_name);
131 	}
132 	for (; argc>1; argv++, argc--) {
133 		if (argv[1][0]=='-') {
134 			if (argv[1][1]=='q')
135 				delflg = 0;
136 			else if (argv[1][1]=='p') {
137 				flgp++;
138 				delflg = 0;
139 			} else if (argv[1][1]=='f') {
140 				if (argc>=3) {
141 					strcpy(mailfile, argv[2]);
142 					argv++;
143 					argc--;
144 				}
145 			} else if (argv[1][1]=='r') {
146 				forward = 1;
147 			} else if (argv[1][1]=='h') {
148 				forward = 1;
149 			} else {
150 				fprintf(stderr, "mail: unknown option %c\n", argv[1][1]);
151 				done();
152 			}
153 		} else
154 			break;
155 	}
156 	malf = fopen(mailfile, "r");
157 	if (malf == NULL) {
158 		fprintf(stdout, "No mail.\n");
159 		return;
160 	}
161 	lock(mailfile);
162 	copymt(malf, tmpf);
163 	fclose(malf);
164 	fclose(tmpf);
165 	unlock();
166 	tmpf = fopen(lettmp, "r");
167 
168 	changed = 0;
169 	print = 1;
170 	for (i = 0; i < nlet; ) {
171 		j = forward ? i : nlet - i - 1;
172 		if(setjmp(sjbuf)) {
173 			print=0;
174 		} else {
175 			if (print)
176 				copylet(j, stdout, ORDINARY);
177 			print = 1;
178 		}
179 		if (flgp) {
180 			i++;
181 			continue;
182 		}
183 		setjmp(sjbuf);
184 		fprintf(stdout, "? ");
185 		fflush(stdout);
186 		if (fgets(resp, LSIZE, stdin) == NULL)
187 			break;
188 		switch (resp[0]) {
189 
190 		default:
191 			fprintf(stderr, "usage\n");
192 		case '?':
193 			print = 0;
194 			fprintf(stderr, "q\tquit\n");
195 			fprintf(stderr, "x\texit without changing mail\n");
196 			fprintf(stderr, "p\tprint\n");
197 			fprintf(stderr, "s[file]\tsave (default mbox)\n");
198 			fprintf(stderr, "w[file]\tsame without header\n");
199 			fprintf(stderr, "-\tprint previous\n");
200 			fprintf(stderr, "d\tdelete\n");
201 			fprintf(stderr, "+\tnext (no delete)\n");
202 			fprintf(stderr, "m user\tmail to user\n");
203 			fprintf(stderr, "! cmd\texecute cmd\n");
204 			break;
205 
206 		case '+':
207 		case 'n':
208 		case '\n':
209 			i++;
210 			break;
211 		case 'x':
212 			changed = 0;
213 		case 'q':
214 			goto donep;
215 		case 'p':
216 			break;
217 		case '^':
218 		case '-':
219 			if (--i < 0)
220 				i = 0;
221 			break;
222 		case 'y':
223 		case 'w':
224 		case 's':
225 			flg = 0;
226 			if (resp[1] != '\n' && resp[1] != ' ') {
227 				printf("illegal\n");
228 				flg++;
229 				print = 0;
230 				continue;
231 			}
232 			if (resp[1] == '\n' || resp[1] == '\0') {
233 				p = getenv("HOME");
234 				if(p != 0)
235 					cat(resp+1, p, "/mbox");
236 				else
237 					cat(resp+1, "", "mbox");
238 			}
239 			for (p = resp+1; (p = getarg(lfil, p)) != NULL; ) {
240 				malf = fopen(lfil, "a");
241 				if (malf == NULL) {
242 					fprintf(stdout, "mail: cannot append to %s\n", lfil);
243 					flg++;
244 					continue;
245 				}
246 				copylet(j, malf, resp[0]=='w'? ZAP: ORDINARY);
247 				fclose(malf);
248 			}
249 			if (flg)
250 				print = 0;
251 			else {
252 				let[j].change = 'd';
253 				changed++;
254 				i++;
255 			}
256 			break;
257 		case 'm':
258 			flg = 0;
259 			if (resp[1] == '\n' || resp[1] == '\0') {
260 				i++;
261 				continue;
262 			}
263 			if (resp[1] != ' ') {
264 				printf("invalid command\n");
265 				flg++;
266 				print = 0;
267 				continue;
268 			}
269 			for (p = resp+1; (p = getarg(lfil, p)) != NULL; )
270 				if (!sendrmt(j, lfil, "/bin/mail"))	/* couldn't send it */
271 					flg++;
272 			if (flg)
273 				print = 0;
274 			else {
275 				let[j].change = 'd';
276 				changed++;
277 				i++;
278 			}
279 			break;
280 		case '!':
281 			system(resp+1);
282 			printf("!\n");
283 			print = 0;
284 			break;
285 		case 'd':
286 			let[j].change = 'd';
287 			changed++;
288 			i++;
289 			if (resp[1] == 'q')
290 				goto donep;
291 			break;
292 		}
293 	}
294    donep:
295 	if (changed)
296 		copyback();
297 }
298 
299 copyback()	/* copy temp or whatever back to /usr/spool/mail */
300 {
301 	register i, n, c;
302 	int new = 0;
303 	struct stat stbuf;
304 
305 	signal(SIGINT, SIG_IGN);
306 	signal(SIGHUP, SIG_IGN);
307 	signal(SIGQUIT, SIG_IGN);
308 	lock(mailfile);
309 	stat(mailfile, &stbuf);
310 	if (stbuf.st_size != let[nlet].adr) {	/* new mail has arrived */
311 		malf = fopen(mailfile, "r");
312 		if (malf == NULL) {
313 			fprintf(stdout, "mail: can't re-read %s\n", mailfile);
314 			done();
315 		}
316 		fseek(malf, let[nlet].adr, 0);
317 		fclose(tmpf);
318 		tmpf = fopen(lettmp, "a");
319 		fseek(tmpf, let[nlet].adr, 0);
320 		while ((c = fgetc(malf)) != EOF)
321 			fputc(c, tmpf);
322 		fclose(malf);
323 		fclose(tmpf);
324 		tmpf = fopen(lettmp, "r");
325 		let[++nlet].adr = stbuf.st_size;
326 		new = 1;
327 	}
328 	malf = fopen(mailfile, "w");
329 	if (malf == NULL) {
330 		fprintf(stderr, "mail: can't rewrite %s\n", lfil);
331 		done();
332 	}
333 	n = 0;
334 	for (i = 0; i < nlet; i++)
335 		if (let[i].change != 'd') {
336 			copylet(i, malf, ORDINARY);
337 			n++;
338 		}
339 	fclose(malf);
340 	if (new)
341 		fprintf(stdout, "new mail arrived\n");
342 	unlock();
343 }
344 
345 copymt(f1, f2)	/* copy mail (f1) to temp (f2) */
346 FILE *f1, *f2;
347 {
348 	long nextadr;
349 
350 	nlet = nextadr = 0;
351 	let[0].adr = 0;
352 	while (fgets(line, LSIZE, f1) != NULL) {
353 		if (isfrom(line))
354 			let[nlet++].adr = nextadr;
355 		nextadr += strlen(line);
356 		fputs(line, f2);
357 	}
358 	let[nlet].adr = nextadr;	/* last plus 1 */
359 }
360 
361 copylet(n, f, type)
362 	FILE *f;
363 {
364 	int ch;
365 	long k;
366 
367 	fseek(tmpf, let[n].adr, 0);
368 	k = let[n+1].adr - let[n].adr;
369 	while(k-- > 1 && (ch=fgetc(tmpf))!='\n')
370 		if(type!=ZAP) fputc(ch,f);
371 	if(type==REMOTE) {
372 		char hostname[32];
373 		gethostname(hostname, sizeof (hostname));
374 		fprintf(f, " remote from %s\n", hostname);
375 	} else if (type==FORWARD)
376 		fprintf(f, forwmsg);
377 	else if(type==ORDINARY)
378 		fputc(ch,f);
379 	while(k-->1)
380 		fputc(ch=fgetc(tmpf), f);
381 	if(type!=ZAP || ch!= '\n')
382 		fputc(fgetc(tmpf), f);
383 }
384 
385 isfrom(lp)
386 register char *lp;
387 {
388 	register char *p;
389 
390 	for (p = from; *p; )
391 		if (*lp++ != *p++)
392 			return(0);
393 	return(1);
394 }
395 
396 bulkmail(argc, argv)
397 char **argv;
398 {
399 	char truename[100];
400 	int first;
401 	register char *cp;
402 	int gaver = 0;
403 	char *newargv[1000];
404 	register char **ap;
405 	register char **vp;
406 	int dflag;
407 
408 	dflag = 0;
409 	if (argc < 1)
410 		fprintf(stderr, "puke\n");
411 	for (vp = argv, ap = newargv + 1; (*ap = *vp++) != 0; ap++)
412 	{
413 		if (ap[0][0] == '-' && ap[0][1] == 'd')
414 			dflag++;
415 	}
416 	if (!dflag)
417 	{
418 		/* give it to sendmail, rah rah! */
419 		unlink(lettmp);
420 		ap = newargv+1;
421 		if (rmail)
422 			*ap-- = "-s";
423 		*ap = "-sendmail";
424 		setuid(getuid());
425 		execv(SENDMAIL, ap);
426 		perror(SENDMAIL);
427 		exit(EX_UNAVAILABLE);
428 	}
429 
430 	truename[0] = 0;
431 	line[0] = '\0';
432 
433 	/*
434 	 * When we fall out of this, argv[1] should be first name,
435 	 * argc should be number of names + 1.
436 	 */
437 
438 	while (argc > 1 && *argv[1] == '-') {
439 		cp = *++argv;
440 		argc--;
441 		switch (cp[1]) {
442 		case 'r':
443 			if (argc <= 0) {
444 				usage();
445 				done();
446 			}
447 			gaver++;
448 			strcpy(truename, argv[1]);
449 			fgets(line, LSIZE, stdin);
450 			if (strcmpn("From", line, 4) == 0)
451 				line[0] = '\0';
452 			argv++;
453 			argc--;
454 			break;
455 
456 		case 'h':
457 			if (argc <= 0) {
458 				usage();
459 				done();
460 			}
461 			hseqno = atoi(argv[1]);
462 			argv++;
463 			argc--;
464 			break;
465 
466 		case 'd':
467 			break;
468 
469 		default:
470 			usage();
471 			done();
472 		}
473 	}
474 	if (argc <= 1) {
475 		usage();
476 		done();
477 	}
478 	if (gaver == 0)
479 		strcpy(truename, my_name);
480 	/*
481 	if (argc > 4 && strcmp(argv[1], "-r") == 0) {
482 		strcpy(truename, argv[2]);
483 		argc -= 2;
484 		argv += 2;
485 		fgets(line, LSIZE, stdin);
486 		if (strcmpn("From", line, 4) == 0)
487 			line[0] = '\0';
488 	} else
489 		strcpy(truename, my_name);
490 	*/
491 	time(&iop);
492 	fprintf(tmpf, "%s%s %s", from, truename, ctime(&iop));
493 	iop = ftell(tmpf);
494 	flgf = 1;
495 	for (first = 1;; first = 0) {
496 		if (first && line[0] == '\0' && fgets(line, LSIZE, stdin) == NULL)
497 			break;
498 		if (!first && fgets(line, LSIZE, stdin) == NULL)
499 			break;
500 		if (line[0] == '.' && line[1] == '\n' && isatty(fileno(stdin)))
501 			break;
502 		if (isfrom(line))
503 			fputs(">", tmpf);
504 		fputs(line, tmpf);
505 		flgf = 0;
506 	}
507 	fputs("\n", tmpf);
508 	nlet = 1;
509 	let[0].adr = 0;
510 	let[1].adr = ftell(tmpf);
511 	fclose(tmpf);
512 	if (flgf)
513 		return;
514 	tmpf = fopen(lettmp, "r");
515 	if (tmpf == NULL) {
516 		fprintf(stderr, "mail: cannot reopen %s for reading\n", lettmp);
517 		return;
518 	}
519 	while (--argc > 0) {
520 		if (!sendmail(0, *++argv, truename))
521 			error++;
522 	}
523 	if (error && safefile(dead)) {
524 		setuid(getuid());
525 		malf = fopen(dead, "w");
526 		if (malf == NULL) {
527 			fprintf(stdout, "mail: cannot open %s\n", dead);
528 			fclose(tmpf);
529 			return;
530 		}
531 		copylet(0, malf, ZAP);
532 		fclose(malf);
533 		fprintf(stdout, "Mail saved in %s\n", dead);
534 	}
535 	fclose(tmpf);
536 }
537 
538 sendrmt(n, name, rcmd)
539 char *name;
540 char *rcmd;
541 {
542 	FILE *rmf, *popen();
543 	register char *p;
544 	char rsys[64], cmd[64];
545 	register local, pid;
546 	int sts;
547 
548 	local = 0;
549 	if (index(name, '^')) {
550 		while (p = index(name, '^'))
551 			*p = '!';
552 		if (strncmp(name, "researc", 7)) {
553 			strcpy(rsys, "research");
554 			if (*name != '!')
555 				--name;
556 			goto skip;
557 		}
558 	}
559 	if (*name=='!')
560 		name++;
561 	for(p=rsys; *name!='!'; *p++ = *name++)
562 		if (*name=='\0') {
563 			local++;
564 			break;
565 		}
566 	*p = '\0';
567 	if ((!local && *name=='\0') || (local && *rsys=='\0')) {
568 		fprintf(stdout, "null name\n");
569 		return(0);
570 	}
571 skip:
572 	if ((pid = fork()) == -1) {
573 		fprintf(stderr, "mail: can't create proc for remote\n");
574 		return(0);
575 	}
576 	if (pid) {
577 		while (wait(&sts) != pid) {
578 			if (wait(&sts)==-1)
579 				return(0);
580 		}
581 		return(!sts);
582 	}
583 	setuid(getuid());
584 	if (local)
585 		sprintf(cmd, "%s %s", rcmd, rsys);
586 	else {
587 		if (index(name+1, '!'))
588 			sprintf(cmd, "uux - %s!rmail \\(%s\\)", rsys, name+1);
589 		else
590 			sprintf(cmd, "uux - %s!rmail %s", rsys, name+1);
591 	}
592 	if ((rmf=popen(cmd, "w")) == NULL)
593 		exit(1);
594 	copylet(n, rmf, local ? !strcmp(rcmd, "/bin/mail") ? FORWARD : ORDINARY : REMOTE);
595 	exit(pclose(rmf) != 0);
596 }
597 
598 usage()
599 {
600 
601 	fprintf(stderr, "Usage: mail [ -f ] people . . .\n");
602 }
603 
604 #include <sys/socket.h>
605 #include <netinet/in.h>
606 #include <netdb.h>
607 struct sockaddr_in biffaddr;
608 
609 sendmail(n, name, fromaddr)
610 int n;
611 char *name;
612 char *fromaddr;
613 {
614 	char file[100];
615 	register char *p;
616 	register mask;
617 	struct passwd *pw, *getpwnam();
618 	struct stat statb;
619 	char buf[128];
620 	int f;
621 	struct hostent *hp = NULL;
622 	struct servent *sp = NULL;
623 
624 	for(p=name; *p!='!'&&*p!='^' &&*p!='\0'; p++)
625 		;
626 	if (*p == '!'|| *p=='^')
627 		return(sendrmt(n, name, 0));
628 	if ((pw = getpwnam(name)) == NULL) {
629 		fprintf(stdout, "mail: can't send to %s\n", name);
630 		return(0);
631 	}
632 	cat(file, maildir, name);
633 	if (stat(file, &statb) >= 0 && (statb.st_mode & S_IFMT) == S_IFDIR) {
634 		strcat(file, "/");
635 		strcat(file, name);
636 	}
637 	mask = umask(MAILMODE);
638 	if (!safefile(file))
639 		return(0);
640 	lock(file);
641 	malf = fopen(file, "a");
642 	umask(mask);
643 	if (malf == NULL) {
644 		unlock();
645 		fprintf(stdout, "mail: cannot append to %s\n", file);
646 		return(0);
647 	}
648 	chown(file, pw->pw_uid, pw->pw_gid);
649 	{
650 		hp = gethostbyname("localhost");
651 		sp = getservbyname("biff", "udp");
652 		if (hp && sp) {
653 			f = socket(AF_INET, SOCK_DGRAM, 0, 0);
654 			sprintf(buf, "%s@%d\n", name, ftell(malf));
655 		}
656 	}
657 	copylet(n, malf, ORDINARY);
658 	fclose(malf);
659 	if (hp && sp) {
660 		biffaddr.sin_family = hp->h_addrtype;
661 		bcopy(hp->h_addr, &biffaddr.sin_addr, hp->h_length);
662 		biffaddr.sin_port = sp->s_port;
663 		sendto(f, buf, strlen(buf)+1, 0, &biffaddr, sizeof (biffaddr));
664 		close(f);
665 	}
666 	unlock();
667 	return(1);
668 }
669 
670 delete(i)
671 {
672 	setsig(i, delete);
673 	fprintf(stderr, "\n");
674 	if(delflg)
675 		longjmp(sjbuf, 1);
676 	done();
677 }
678 
679 /*
680  * Lock the specified mail file by setting the file mailfile.lock.
681  * We must, of course, be careful to unlink the lock file by a call
682  * to unlock before we stop.  The algorithm used here is to see if
683  * the lock exists, and if it does, to check its modify time.  If it
684  * is older than 30 seconds, we assume error and set our own file.
685  * Otherwise, we wait for 5 seconds and try again.
686  */
687 
688 char	*maillock	= ".lock";		/* Lock suffix for mailname */
689 char	*lockname	= "/usr/spool/mail/tmXXXXXX";
690 char	locktmp[30];				/* Usable lock temporary */
691 char	curlock[50];				/* Last used name of lock */
692 int	locked;					/* To note that we locked it */
693 
694 lock(file)
695 char *file;
696 {
697 	register int f;
698 	struct stat sbuf;
699 	long curtime;
700 	int statfailed;
701 
702 	if (locked || flgf)
703 		return(0);
704 	strcpy(curlock, file);
705 	strcat(curlock, maillock);
706 	strcpy(locktmp, lockname);
707 	mktemp(locktmp);
708 	unlink(locktmp);
709 	statfailed = 0;
710 	for (;;) {
711 		f = lock1(locktmp, curlock);
712 		if (f == 0) {
713 			locked = 1;
714 			return(0);
715 		}
716 		if (stat(curlock, &sbuf) < 0) {
717 			if (statfailed++ > 5)
718 				return(-1);
719 			sleep(5);
720 			continue;
721 		}
722 		statfailed = 0;
723 		time(&curtime);
724 		if (curtime < sbuf.st_ctime + 30) {
725 			sleep(5);
726 			continue;
727 		}
728 		unlink(curlock);
729 	}
730 }
731 
732 /*
733  * Remove the mail lock, and note that we no longer
734  * have it locked.
735  */
736 
737 unlock()
738 {
739 
740 	unlink(curlock);
741 	locked = 0;
742 }
743 
744 /*
745  * Attempt to set the lock by creating the temporary file,
746  * then doing a link/unlink.  If it fails, return -1 else 0
747  */
748 
749 lock1(tempfile, name)
750 	char tempfile[], name[];
751 {
752 	register int fd;
753 
754 	fd = creat(tempfile, 0);
755 	if (fd < 0)
756 		return(-1);
757 	close(fd);
758 	if (link(tempfile, name) < 0) {
759 		unlink(tempfile);
760 		return(-1);
761 	}
762 	unlink(tempfile);
763 	return(0);
764 }
765 
766 done()
767 {
768 	if(locked)
769 		unlock();
770 	unlink(lettmp);
771 	unlink(locktmp);
772 	exit(error);
773 }
774 
775 cat(to, from1, from2)
776 char *to, *from1, *from2;
777 {
778 	int i, j;
779 
780 	j = 0;
781 	for (i=0; from1[i]; i++)
782 		to[j++] = from1[i];
783 	for (i=0; from2[i]; i++)
784 		to[j++] = from2[i];
785 	to[j] = 0;
786 }
787 
788 char *getarg(s, p)	/* copy p... into s, update p */
789 register char *s, *p;
790 {
791 	while (*p == ' ' || *p == '\t')
792 		p++;
793 	if (*p == '\n' || *p == '\0')
794 		return(NULL);
795 	while (*p != ' ' && *p != '\t' && *p != '\n' && *p != '\0')
796 		*s++ = *p++;
797 	*s = '\0';
798 	return(p);
799 }
800 
801 safefile(f)
802 	char *f;
803 {
804 	struct stat statb;
805 
806 	if (lstat(f, &statb) < 0)
807 		return (1);
808 	if (statb.st_nlink != 1 || (statb.st_mode & S_IFMT) == S_IFLNK) {
809 		fprintf(stderr, "mail: %s has more than one link or is a symbolic link\n", f);
810 		return (0);
811 	}
812 	return (1);
813 }
814