/usr.bin/mail/names.c

https://bitbucket.org/freebsd/freebsd-head/ · C · 762 lines · 511 code · 62 blank · 189 comment · 208 complexity · 40d5b23ae136d421fdb66051b19b1af4 MD5 · raw file

  1. /*
  2. * Copyright (c) 1980, 1993
  3. * The Regents of the University of California. All rights reserved.
  4. *
  5. * Redistribution and use in source and binary forms, with or without
  6. * modification, are permitted provided that the following conditions
  7. * are met:
  8. * 1. Redistributions of source code must retain the above copyright
  9. * notice, this list of conditions and the following disclaimer.
  10. * 2. Redistributions in binary form must reproduce the above copyright
  11. * notice, this list of conditions and the following disclaimer in the
  12. * documentation and/or other materials provided with the distribution.
  13. * 4. Neither the name of the University nor the names of its contributors
  14. * may be used to endorse or promote products derived from this software
  15. * without specific prior written permission.
  16. *
  17. * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
  18. * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  19. * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  20. * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
  21. * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  22. * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  23. * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  24. * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  25. * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  26. * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  27. * SUCH DAMAGE.
  28. */
  29. #ifndef lint
  30. #if 0
  31. static char sccsid[] = "@(#)names.c 8.1 (Berkeley) 6/6/93";
  32. #endif
  33. #endif /* not lint */
  34. #include <sys/cdefs.h>
  35. __FBSDID("$FreeBSD$");
  36. /*
  37. * Mail -- a mail program
  38. *
  39. * Handle name lists.
  40. */
  41. #include "rcv.h"
  42. #include <fcntl.h>
  43. #include "extern.h"
  44. /*
  45. * Allocate a single element of a name list,
  46. * initialize its name field to the passed
  47. * name and return it.
  48. */
  49. struct name *
  50. nalloc(char str[], int ntype)
  51. {
  52. struct name *np;
  53. np = (struct name *)salloc(sizeof(*np));
  54. np->n_flink = NULL;
  55. np->n_blink = NULL;
  56. np->n_type = ntype;
  57. np->n_name = savestr(str);
  58. return (np);
  59. }
  60. /*
  61. * Find the tail of a list and return it.
  62. */
  63. struct name *
  64. tailof(struct name *name)
  65. {
  66. struct name *np;
  67. np = name;
  68. if (np == NULL)
  69. return (NULL);
  70. while (np->n_flink != NULL)
  71. np = np->n_flink;
  72. return (np);
  73. }
  74. /*
  75. * Extract a list of names from a line,
  76. * and make a list of names from it.
  77. * Return the list or NULL if none found.
  78. */
  79. struct name *
  80. extract(char line[], int ntype)
  81. {
  82. char *cp, *nbuf;
  83. struct name *top, *np, *t;
  84. if (line == NULL || *line == '\0')
  85. return (NULL);
  86. if ((nbuf = malloc(strlen(line) + 1)) == NULL)
  87. err(1, "Out of memory");
  88. top = NULL;
  89. np = NULL;
  90. cp = line;
  91. while ((cp = yankword(cp, nbuf)) != NULL) {
  92. t = nalloc(nbuf, ntype);
  93. if (top == NULL)
  94. top = t;
  95. else
  96. np->n_flink = t;
  97. t->n_blink = np;
  98. np = t;
  99. }
  100. (void)free(nbuf);
  101. return (top);
  102. }
  103. /*
  104. * Turn a list of names into a string of the same names.
  105. */
  106. char *
  107. detract(struct name *np, int ntype)
  108. {
  109. int s, comma;
  110. char *cp, *top;
  111. struct name *p;
  112. comma = ntype & GCOMMA;
  113. if (np == NULL)
  114. return (NULL);
  115. ntype &= ~GCOMMA;
  116. s = 0;
  117. if (debug && comma)
  118. fprintf(stderr, "detract asked to insert commas\n");
  119. for (p = np; p != NULL; p = p->n_flink) {
  120. if (ntype && (p->n_type & GMASK) != ntype)
  121. continue;
  122. s += strlen(p->n_name) + 1;
  123. if (comma)
  124. s++;
  125. }
  126. if (s == 0)
  127. return (NULL);
  128. s += 2;
  129. top = salloc(s);
  130. cp = top;
  131. for (p = np; p != NULL; p = p->n_flink) {
  132. if (ntype && (p->n_type & GMASK) != ntype)
  133. continue;
  134. cp += strlcpy(cp, p->n_name, strlen(p->n_name) + 1);
  135. if (comma && p->n_flink != NULL)
  136. *cp++ = ',';
  137. *cp++ = ' ';
  138. }
  139. *--cp = '\0';
  140. if (comma && *--cp == ',')
  141. *cp = '\0';
  142. return (top);
  143. }
  144. /*
  145. * Grab a single word (liberal word)
  146. * Throw away things between ()'s, and take anything between <>.
  147. */
  148. char *
  149. yankword(char *ap, char wbuf[])
  150. {
  151. char *cp, *cp2;
  152. cp = ap;
  153. for (;;) {
  154. if (*cp == '\0')
  155. return (NULL);
  156. if (*cp == '(') {
  157. int nesting = 0;
  158. while (*cp != '\0') {
  159. switch (*cp++) {
  160. case '(':
  161. nesting++;
  162. break;
  163. case ')':
  164. --nesting;
  165. break;
  166. }
  167. if (nesting <= 0)
  168. break;
  169. }
  170. } else if (*cp == ' ' || *cp == '\t' || *cp == ',')
  171. cp++;
  172. else
  173. break;
  174. }
  175. if (*cp == '<')
  176. for (cp2 = wbuf; *cp && (*cp2++ = *cp++) != '>';)
  177. ;
  178. else
  179. for (cp2 = wbuf; *cp != '\0' && strchr(" \t,(", *cp) == NULL;
  180. *cp2++ = *cp++)
  181. ;
  182. *cp2 = '\0';
  183. return (cp);
  184. }
  185. /*
  186. * Grab a single login name (liberal word)
  187. * Throw away things between ()'s, take anything between <>,
  188. * and look for words before metacharacters %, @, !.
  189. */
  190. char *
  191. yanklogin(char *ap, char wbuf[])
  192. {
  193. char *cp, *cp2, *cp_temp;
  194. int n;
  195. cp = ap;
  196. for (;;) {
  197. if (*cp == '\0')
  198. return (NULL);
  199. if (*cp == '(') {
  200. int nesting = 0;
  201. while (*cp != '\0') {
  202. switch (*cp++) {
  203. case '(':
  204. nesting++;
  205. break;
  206. case ')':
  207. --nesting;
  208. break;
  209. }
  210. if (nesting <= 0)
  211. break;
  212. }
  213. } else if (*cp == ' ' || *cp == '\t' || *cp == ',')
  214. cp++;
  215. else
  216. break;
  217. }
  218. /*
  219. * Now, let's go forward till we meet the needed character,
  220. * and step one word back.
  221. */
  222. /* First, remember current point. */
  223. cp_temp = cp;
  224. n = 0;
  225. /*
  226. * Note that we look ahead in a cycle. This is safe, since
  227. * non-end of string is checked first.
  228. */
  229. while(*cp != '\0' && strchr("@%!", *(cp + 1)) == NULL)
  230. cp++;
  231. /*
  232. * Now, start stepping back to the first non-word character,
  233. * while counting the number of symbols in a word.
  234. */
  235. while(cp != cp_temp && strchr(" \t,<>", *(cp - 1)) == NULL) {
  236. n++;
  237. cp--;
  238. }
  239. /* Finally, grab the word forward. */
  240. cp2 = wbuf;
  241. while(n >= 0) {
  242. *cp2++=*cp++;
  243. n--;
  244. }
  245. *cp2 = '\0';
  246. return (cp);
  247. }
  248. /*
  249. * For each recipient in the passed name list with a /
  250. * in the name, append the message to the end of the named file
  251. * and remove him from the recipient list.
  252. *
  253. * Recipients whose name begins with | are piped through the given
  254. * program and removed.
  255. */
  256. struct name *
  257. outof(struct name *names, FILE *fo, struct header *hp)
  258. {
  259. int c, ispipe;
  260. struct name *np, *top;
  261. time_t now;
  262. char *date, *fname;
  263. FILE *fout, *fin;
  264. top = names;
  265. np = names;
  266. (void)time(&now);
  267. date = ctime(&now);
  268. while (np != NULL) {
  269. if (!isfileaddr(np->n_name) && np->n_name[0] != '|') {
  270. np = np->n_flink;
  271. continue;
  272. }
  273. ispipe = np->n_name[0] == '|';
  274. if (ispipe)
  275. fname = np->n_name+1;
  276. else
  277. fname = expand(np->n_name);
  278. /*
  279. * See if we have copied the complete message out yet.
  280. * If not, do so.
  281. */
  282. if (image < 0) {
  283. int fd;
  284. char tempname[PATHSIZE];
  285. (void)snprintf(tempname, sizeof(tempname),
  286. "%s/mail.ReXXXXXXXXXX", tmpdir);
  287. if ((fd = mkstemp(tempname)) == -1 ||
  288. (fout = Fdopen(fd, "a")) == NULL) {
  289. warn("%s", tempname);
  290. senderr++;
  291. goto cant;
  292. }
  293. image = open(tempname, O_RDWR);
  294. (void)rm(tempname);
  295. if (image < 0) {
  296. warn("%s", tempname);
  297. senderr++;
  298. (void)Fclose(fout);
  299. goto cant;
  300. }
  301. (void)fcntl(image, F_SETFD, 1);
  302. fprintf(fout, "From %s %s", myname, date);
  303. puthead(hp, fout,
  304. GTO|GSUBJECT|GCC|GREPLYTO|GINREPLYTO|GNL);
  305. while ((c = getc(fo)) != EOF)
  306. (void)putc(c, fout);
  307. rewind(fo);
  308. fprintf(fout, "\n");
  309. (void)fflush(fout);
  310. if (ferror(fout)) {
  311. warn("%s", tempname);
  312. senderr++;
  313. (void)Fclose(fout);
  314. goto cant;
  315. }
  316. (void)Fclose(fout);
  317. }
  318. /*
  319. * Now either copy "image" to the desired file
  320. * or give it as the standard input to the desired
  321. * program as appropriate.
  322. */
  323. if (ispipe) {
  324. int pid;
  325. char *sh;
  326. sigset_t nset;
  327. /*
  328. * XXX
  329. * We can't really reuse the same image file,
  330. * because multiple piped recipients will
  331. * share the same lseek location and trample
  332. * on one another.
  333. */
  334. if ((sh = value("SHELL")) == NULL)
  335. sh = _PATH_CSHELL;
  336. (void)sigemptyset(&nset);
  337. (void)sigaddset(&nset, SIGHUP);
  338. (void)sigaddset(&nset, SIGINT);
  339. (void)sigaddset(&nset, SIGQUIT);
  340. pid = start_command(sh, &nset, image, -1, "-c", fname,
  341. NULL);
  342. if (pid < 0) {
  343. senderr++;
  344. goto cant;
  345. }
  346. free_child(pid);
  347. } else {
  348. int f;
  349. if ((fout = Fopen(fname, "a")) == NULL) {
  350. warn("%s", fname);
  351. senderr++;
  352. goto cant;
  353. }
  354. if ((f = dup(image)) < 0) {
  355. warn("dup");
  356. fin = NULL;
  357. } else
  358. fin = Fdopen(f, "r");
  359. if (fin == NULL) {
  360. fprintf(stderr, "Can't reopen image\n");
  361. (void)Fclose(fout);
  362. senderr++;
  363. goto cant;
  364. }
  365. rewind(fin);
  366. while ((c = getc(fin)) != EOF)
  367. (void)putc(c, fout);
  368. if (ferror(fout)) {
  369. warnx("%s", fname);
  370. senderr++;
  371. (void)Fclose(fout);
  372. (void)Fclose(fin);
  373. goto cant;
  374. }
  375. (void)Fclose(fout);
  376. (void)Fclose(fin);
  377. }
  378. cant:
  379. /*
  380. * In days of old we removed the entry from the
  381. * the list; now for sake of header expansion
  382. * we leave it in and mark it as deleted.
  383. */
  384. np->n_type |= GDEL;
  385. np = np->n_flink;
  386. }
  387. if (image >= 0) {
  388. (void)close(image);
  389. image = -1;
  390. }
  391. return (top);
  392. }
  393. /*
  394. * Determine if the passed address is a local "send to file" address.
  395. * If any of the network metacharacters precedes any slashes, it can't
  396. * be a filename. We cheat with .'s to allow path names like ./...
  397. */
  398. int
  399. isfileaddr(char *name)
  400. {
  401. char *cp;
  402. if (*name == '+')
  403. return (1);
  404. for (cp = name; *cp != '\0'; cp++) {
  405. if (*cp == '!' || *cp == '%' || *cp == '@')
  406. return (0);
  407. if (*cp == '/')
  408. return (1);
  409. }
  410. return (0);
  411. }
  412. /*
  413. * Map all of the aliased users in the invoker's mailrc
  414. * file and insert them into the list.
  415. * Changed after all these months of service to recursively
  416. * expand names (2/14/80).
  417. */
  418. struct name *
  419. usermap(struct name *names)
  420. {
  421. struct name *new, *np, *cp;
  422. struct grouphead *gh;
  423. int metoo;
  424. new = NULL;
  425. np = names;
  426. metoo = (value("metoo") != NULL);
  427. while (np != NULL) {
  428. if (np->n_name[0] == '\\') {
  429. cp = np->n_flink;
  430. new = put(new, np);
  431. np = cp;
  432. continue;
  433. }
  434. gh = findgroup(np->n_name);
  435. cp = np->n_flink;
  436. if (gh != NULL)
  437. new = gexpand(new, gh, metoo, np->n_type);
  438. else
  439. new = put(new, np);
  440. np = cp;
  441. }
  442. return (new);
  443. }
  444. /*
  445. * Recursively expand a group name. We limit the expansion to some
  446. * fixed level to keep things from going haywire.
  447. * Direct recursion is not expanded for convenience.
  448. */
  449. struct name *
  450. gexpand(struct name *nlist, struct grouphead *gh, int metoo, int ntype)
  451. {
  452. struct group *gp;
  453. struct grouphead *ngh;
  454. struct name *np;
  455. static int depth;
  456. char *cp;
  457. if (depth > MAXEXP) {
  458. printf("Expanding alias to depth larger than %d\n", MAXEXP);
  459. return (nlist);
  460. }
  461. depth++;
  462. for (gp = gh->g_list; gp != NULL; gp = gp->ge_link) {
  463. cp = gp->ge_name;
  464. if (*cp == '\\')
  465. goto quote;
  466. if (strcmp(cp, gh->g_name) == 0)
  467. goto quote;
  468. if ((ngh = findgroup(cp)) != NULL) {
  469. nlist = gexpand(nlist, ngh, metoo, ntype);
  470. continue;
  471. }
  472. quote:
  473. np = nalloc(cp, ntype);
  474. /*
  475. * At this point should allow to expand
  476. * to self if only person in group
  477. */
  478. if (gp == gh->g_list && gp->ge_link == NULL)
  479. goto skip;
  480. if (!metoo && strcmp(cp, myname) == 0)
  481. np->n_type |= GDEL;
  482. skip:
  483. nlist = put(nlist, np);
  484. }
  485. depth--;
  486. return (nlist);
  487. }
  488. /*
  489. * Concatenate the two passed name lists, return the result.
  490. */
  491. struct name *
  492. cat(struct name *n1, struct name *n2)
  493. {
  494. struct name *tail;
  495. if (n1 == NULL)
  496. return (n2);
  497. if (n2 == NULL)
  498. return (n1);
  499. tail = tailof(n1);
  500. tail->n_flink = n2;
  501. n2->n_blink = tail;
  502. return (n1);
  503. }
  504. /*
  505. * Unpack the name list onto a vector of strings.
  506. * Return an error if the name list won't fit.
  507. */
  508. char **
  509. unpack(struct name *np)
  510. {
  511. char **ap, **top;
  512. struct name *n;
  513. int t, extra, metoo, verbose;
  514. n = np;
  515. if ((t = count(n)) == 0)
  516. errx(1, "No names to unpack");
  517. /*
  518. * Compute the number of extra arguments we will need.
  519. * We need at least two extra -- one for "mail" and one for
  520. * the terminating 0 pointer. Additional spots may be needed
  521. * to pass along -f to the host mailer.
  522. */
  523. extra = 2;
  524. extra++;
  525. metoo = value("metoo") != NULL;
  526. if (metoo)
  527. extra++;
  528. verbose = value("verbose") != NULL;
  529. if (verbose)
  530. extra++;
  531. top = (char **)salloc((t + extra) * sizeof(*top));
  532. ap = top;
  533. *ap++ = "send-mail";
  534. *ap++ = "-i";
  535. if (metoo)
  536. *ap++ = "-m";
  537. if (verbose)
  538. *ap++ = "-v";
  539. for (; n != NULL; n = n->n_flink)
  540. if ((n->n_type & GDEL) == 0)
  541. *ap++ = n->n_name;
  542. *ap = NULL;
  543. return (top);
  544. }
  545. /*
  546. * Remove all of the duplicates from the passed name list by
  547. * insertion sorting them, then checking for dups.
  548. * Return the head of the new list.
  549. */
  550. struct name *
  551. elide(struct name *names)
  552. {
  553. struct name *np, *t, *new;
  554. struct name *x;
  555. if (names == NULL)
  556. return (NULL);
  557. new = names;
  558. np = names;
  559. np = np->n_flink;
  560. if (np != NULL)
  561. np->n_blink = NULL;
  562. new->n_flink = NULL;
  563. while (np != NULL) {
  564. t = new;
  565. while (strcasecmp(t->n_name, np->n_name) < 0) {
  566. if (t->n_flink == NULL)
  567. break;
  568. t = t->n_flink;
  569. }
  570. /*
  571. * If we ran out of t's, put the new entry after
  572. * the current value of t.
  573. */
  574. if (strcasecmp(t->n_name, np->n_name) < 0) {
  575. t->n_flink = np;
  576. np->n_blink = t;
  577. t = np;
  578. np = np->n_flink;
  579. t->n_flink = NULL;
  580. continue;
  581. }
  582. /*
  583. * Otherwise, put the new entry in front of the
  584. * current t. If at the front of the list,
  585. * the new guy becomes the new head of the list.
  586. */
  587. if (t == new) {
  588. t = np;
  589. np = np->n_flink;
  590. t->n_flink = new;
  591. new->n_blink = t;
  592. t->n_blink = NULL;
  593. new = t;
  594. continue;
  595. }
  596. /*
  597. * The normal case -- we are inserting into the
  598. * middle of the list.
  599. */
  600. x = np;
  601. np = np->n_flink;
  602. x->n_flink = t;
  603. x->n_blink = t->n_blink;
  604. t->n_blink->n_flink = x;
  605. t->n_blink = x;
  606. }
  607. /*
  608. * Now the list headed up by new is sorted.
  609. * Go through it and remove duplicates.
  610. */
  611. np = new;
  612. while (np != NULL) {
  613. t = np;
  614. while (t->n_flink != NULL &&
  615. strcasecmp(np->n_name, t->n_flink->n_name) == 0)
  616. t = t->n_flink;
  617. if (t == np || t == NULL) {
  618. np = np->n_flink;
  619. continue;
  620. }
  621. /*
  622. * Now t points to the last entry with the same name
  623. * as np. Make np point beyond t.
  624. */
  625. np->n_flink = t->n_flink;
  626. if (t->n_flink != NULL)
  627. t->n_flink->n_blink = np;
  628. np = np->n_flink;
  629. }
  630. return (new);
  631. }
  632. /*
  633. * Put another node onto a list of names and return
  634. * the list.
  635. */
  636. struct name *
  637. put(struct name *list, struct name *node)
  638. {
  639. node->n_flink = list;
  640. node->n_blink = NULL;
  641. if (list != NULL)
  642. list->n_blink = node;
  643. return (node);
  644. }
  645. /*
  646. * Determine the number of undeleted elements in
  647. * a name list and return it.
  648. */
  649. int
  650. count(struct name *np)
  651. {
  652. int c;
  653. for (c = 0; np != NULL; np = np->n_flink)
  654. if ((np->n_type & GDEL) == 0)
  655. c++;
  656. return (c);
  657. }
  658. /*
  659. * Delete the given name from a namelist.
  660. */
  661. struct name *
  662. delname(struct name *np, char name[])
  663. {
  664. struct name *p;
  665. for (p = np; p != NULL; p = p->n_flink)
  666. if (strcasecmp(p->n_name, name) == 0) {
  667. if (p->n_blink == NULL) {
  668. if (p->n_flink != NULL)
  669. p->n_flink->n_blink = NULL;
  670. np = p->n_flink;
  671. continue;
  672. }
  673. if (p->n_flink == NULL) {
  674. if (p->n_blink != NULL)
  675. p->n_blink->n_flink = NULL;
  676. continue;
  677. }
  678. p->n_blink->n_flink = p->n_flink;
  679. p->n_flink->n_blink = p->n_blink;
  680. }
  681. return (np);
  682. }
  683. /*
  684. * Pretty print a name list
  685. * Uncomment it if you need it.
  686. */
  687. /*
  688. void
  689. prettyprint(struct name *name)
  690. {
  691. struct name *np;
  692. np = name;
  693. while (np != NULL) {
  694. fprintf(stderr, "%s(%d) ", np->n_name, np->n_type);
  695. np = np->n_flink;
  696. }
  697. fprintf(stderr, "\n");
  698. }
  699. */