PageRenderTime 47ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

/usr.bin/mail/util.c

https://gitlab.com/tlevine/DragonFlyBSD
C | 590 lines | 414 code | 49 blank | 127 comment | 175 complexity | 17ceb73e7bf1523472f0b4b26d9fe0ad 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. * 3. 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. * @(#)aux.c 8.1 (Berkeley) 6/6/93
  30. * $FreeBSD: src/usr.bin/mail/aux.c,v 1.4.6.4 2003/01/06 05:46:03 mikeh Exp $
  31. */
  32. #include <sys/time.h>
  33. #include "rcv.h"
  34. #include "extern.h"
  35. /*
  36. * Mail -- a mail program
  37. *
  38. * Auxiliary functions.
  39. */
  40. static char *save2str(char *, char *);
  41. /*
  42. * Return a pointer to a dynamic copy of the argument.
  43. */
  44. char *
  45. savestr(char *str)
  46. {
  47. char *new;
  48. int size = strlen(str) + 1;
  49. if ((new = salloc(size)) != NULL)
  50. bcopy(str, new, size);
  51. return (new);
  52. }
  53. /*
  54. * Make a copy of new argument incorporating old one.
  55. */
  56. char *
  57. save2str(char *str, char *old)
  58. {
  59. char *new;
  60. int newsize = strlen(str) + 1;
  61. int oldsize = old ? strlen(old) + 1 : 0;
  62. if ((new = salloc(newsize + oldsize)) != NULL) {
  63. if (oldsize) {
  64. bcopy(old, new, oldsize);
  65. new[oldsize - 1] = ' ';
  66. }
  67. bcopy(str, new + oldsize, newsize);
  68. }
  69. return (new);
  70. }
  71. /*
  72. * Touch the named message by setting its MTOUCH flag.
  73. * Touched messages have the effect of not being sent
  74. * back to the system mailbox on exit.
  75. */
  76. void
  77. touch(struct message *mp)
  78. {
  79. mp->m_flag |= MTOUCH;
  80. if ((mp->m_flag & MREAD) == 0)
  81. mp->m_flag |= MREAD|MSTATUS;
  82. }
  83. /*
  84. * Test to see if the passed file name is a directory.
  85. * Return true if it is.
  86. */
  87. int
  88. isdir(char *name)
  89. {
  90. struct stat sbuf;
  91. if (stat(name, &sbuf) < 0)
  92. return (0);
  93. return (S_ISDIR(sbuf.st_mode));
  94. }
  95. /*
  96. * Count the number of arguments in the given string raw list.
  97. */
  98. int
  99. argcount(char **argv)
  100. {
  101. char **ap;
  102. for (ap = argv; *ap++ != NULL;)
  103. ;
  104. return (ap - argv - 1);
  105. }
  106. /*
  107. * Return the desired header line from the passed message
  108. * pointer (or NULL if the desired header field is not available).
  109. */
  110. char *
  111. hfield(const char *field, struct message *mp)
  112. {
  113. FILE *ibuf;
  114. char linebuf[LINESIZE];
  115. int lc;
  116. char *hfield;
  117. char *colon, *oldhfield = NULL;
  118. ibuf = setinput(mp);
  119. if ((lc = mp->m_lines - 1) < 0)
  120. return (NULL);
  121. if (readline(ibuf, linebuf, LINESIZE) < 0)
  122. return (NULL);
  123. while (lc > 0) {
  124. if ((lc = gethfield(ibuf, linebuf, lc, &colon)) < 0)
  125. return (oldhfield);
  126. if ((hfield = ishfield(linebuf, colon, field)) != NULL)
  127. oldhfield = save2str(hfield, oldhfield);
  128. }
  129. return (oldhfield);
  130. }
  131. /*
  132. * Return the next header field found in the given message.
  133. * Return >= 0 if something found, < 0 elsewise.
  134. * "colon" is set to point to the colon in the header.
  135. * Must deal with \ continuations & other such fraud.
  136. */
  137. int
  138. gethfield(FILE *f, char *linebuf, int rem, char **colon)
  139. {
  140. char line2[LINESIZE];
  141. char *cp, *cp2;
  142. int c;
  143. for (;;) {
  144. if (--rem < 0)
  145. return (-1);
  146. if ((c = readline(f, linebuf, LINESIZE)) <= 0)
  147. return (-1);
  148. for (cp = linebuf; isprint((unsigned char)*cp) && *cp != ' ' && *cp != ':';
  149. cp++)
  150. ;
  151. if (*cp != ':' || cp == linebuf)
  152. continue;
  153. /*
  154. * I guess we got a headline.
  155. * Handle wraparounding
  156. */
  157. *colon = cp;
  158. cp = linebuf + c;
  159. for (;;) {
  160. while (--cp >= linebuf && (*cp == ' ' || *cp == '\t'))
  161. ;
  162. cp++;
  163. if (rem <= 0)
  164. break;
  165. ungetc(c = getc(f), f);
  166. if (c != ' ' && c != '\t')
  167. break;
  168. if ((c = readline(f, line2, LINESIZE)) < 0)
  169. break;
  170. rem--;
  171. for (cp2 = line2; *cp2 == ' ' || *cp2 == '\t'; cp2++)
  172. ;
  173. c -= cp2 - line2;
  174. if (cp + c >= linebuf + LINESIZE - 2)
  175. break;
  176. *cp++ = ' ';
  177. bcopy(cp2, cp, c);
  178. cp += c;
  179. }
  180. *cp = 0;
  181. return (rem);
  182. }
  183. /* NOTREACHED */
  184. }
  185. /*
  186. * Check whether the passed line is a header line of
  187. * the desired breed. Return the field body, or 0.
  188. */
  189. char*
  190. ishfield(char *linebuf, char *colon, const char *field)
  191. {
  192. char *cp = colon;
  193. *cp = 0;
  194. if (strcasecmp(linebuf, field) != 0) {
  195. *cp = ':';
  196. return (0);
  197. }
  198. *cp = ':';
  199. for (cp++; *cp == ' ' || *cp == '\t'; cp++)
  200. ;
  201. return (cp);
  202. }
  203. /*
  204. * Copy a string and lowercase the result.
  205. * dsize: space left in buffer (including space for NULL)
  206. */
  207. void
  208. istrncpy(char *dest, const char *src, size_t dsize)
  209. {
  210. strlcpy(dest, src, dsize);
  211. for (; *dest; dest++)
  212. *dest = tolower((unsigned char)*dest);
  213. }
  214. /*
  215. * The following code deals with input stacking to do source
  216. * commands. All but the current file pointer are saved on
  217. * the stack.
  218. */
  219. static int ssp; /* Top of file stack */
  220. struct sstack {
  221. FILE *s_file; /* File we were in. */
  222. int s_cond; /* Saved state of conditionals */
  223. int s_loading; /* Loading .mailrc, etc. */
  224. };
  225. #define SSTACK_SIZE 64 /* XXX was NOFILE. */
  226. static struct sstack sstack[SSTACK_SIZE];
  227. /*
  228. * Pushdown current input file and switch to a new one.
  229. * Set the global flag "sourcing" so that others will realize
  230. * that they are no longer reading from a tty (in all probability).
  231. */
  232. int
  233. source(char **arglist)
  234. {
  235. FILE *fi;
  236. char *cp;
  237. if ((cp = expand(*arglist)) == NULL)
  238. return (1);
  239. if ((fi = Fopen(cp, "r")) == NULL) {
  240. warn("%s", cp);
  241. return (1);
  242. }
  243. if (ssp >= SSTACK_SIZE - 1) {
  244. printf("Too much \"sourcing\" going on.\n");
  245. Fclose(fi);
  246. return (1);
  247. }
  248. sstack[ssp].s_file = input;
  249. sstack[ssp].s_cond = cond;
  250. sstack[ssp].s_loading = loading;
  251. ssp++;
  252. loading = 0;
  253. cond = CANY;
  254. input = fi;
  255. sourcing++;
  256. return (0);
  257. }
  258. /*
  259. * Pop the current input back to the previous level.
  260. * Update the "sourcing" flag as appropriate.
  261. */
  262. int
  263. unstack(void)
  264. {
  265. if (ssp <= 0) {
  266. printf("\"Source\" stack over-pop.\n");
  267. sourcing = 0;
  268. return (1);
  269. }
  270. Fclose(input);
  271. if (cond != CANY)
  272. printf("Unmatched \"if\"\n");
  273. ssp--;
  274. cond = sstack[ssp].s_cond;
  275. loading = sstack[ssp].s_loading;
  276. input = sstack[ssp].s_file;
  277. if (ssp == 0)
  278. sourcing = loading;
  279. return (0);
  280. }
  281. /*
  282. * Touch the indicated file.
  283. * This is nifty for the shell.
  284. */
  285. void
  286. alter(char *name)
  287. {
  288. struct stat sb;
  289. struct timeval tv[2];
  290. if (stat(name, &sb))
  291. return;
  292. gettimeofday(&tv[0], NULL);
  293. tv[0].tv_sec++;
  294. TIMESPEC_TO_TIMEVAL(&tv[1], &sb.st_mtimespec);
  295. utimes(name, tv);
  296. }
  297. /*
  298. * Get sender's name from this message. If the message has
  299. * a bunch of arpanet stuff in it, we may have to skin the name
  300. * before returning it.
  301. */
  302. char *
  303. nameof(struct message *mp, int reptype)
  304. {
  305. char *cp, *cp2;
  306. cp = skin(name1(mp, reptype));
  307. if (reptype != 0 || charcount(cp, '!') < 2)
  308. return (cp);
  309. cp2 = strrchr(cp, '!');
  310. cp2--;
  311. while (cp2 > cp && *cp2 != '!')
  312. cp2--;
  313. if (*cp2 == '!')
  314. return (cp2 + 1);
  315. return (cp);
  316. }
  317. /*
  318. * Start of a "comment".
  319. * Ignore it.
  320. */
  321. char *
  322. skip_comment(char *cp)
  323. {
  324. int nesting = 1;
  325. for (; nesting > 0 && *cp; cp++) {
  326. switch (*cp) {
  327. case '\\':
  328. if (cp[1])
  329. cp++;
  330. break;
  331. case '(':
  332. nesting++;
  333. break;
  334. case ')':
  335. nesting--;
  336. break;
  337. }
  338. }
  339. return (cp);
  340. }
  341. /*
  342. * Skin an arpa net address according to the RFC 822 interpretation
  343. * of "host-phrase."
  344. */
  345. char *
  346. skin(char *name)
  347. {
  348. char *nbuf, *bufend, *cp, *cp2;
  349. int c, gotlt, lastsp;
  350. if (name == NULL)
  351. return (NULL);
  352. if (strchr(name, '(') == NULL && strchr(name, '<') == NULL
  353. && strchr(name, ' ') == NULL)
  354. return (name);
  355. /* We assume that length(input) <= length(output) */
  356. if ((nbuf = malloc(strlen(name) + 1)) == NULL)
  357. err(1, "Out of memory");
  358. gotlt = 0;
  359. lastsp = 0;
  360. bufend = nbuf;
  361. for (cp = name, cp2 = bufend; (c = *cp++) != '\0'; ) {
  362. switch (c) {
  363. case '(':
  364. cp = skip_comment(cp);
  365. lastsp = 0;
  366. break;
  367. case '"':
  368. /*
  369. * Start of a "quoted-string".
  370. * Copy it in its entirety.
  371. */
  372. while ((c = *cp) != '\0') {
  373. cp++;
  374. if (c == '"')
  375. break;
  376. if (c != '\\')
  377. *cp2++ = c;
  378. else if ((c = *cp) != '\0') {
  379. *cp2++ = c;
  380. cp++;
  381. }
  382. }
  383. lastsp = 0;
  384. break;
  385. case ' ':
  386. if (cp[0] == 'a' && cp[1] == 't' && cp[2] == ' ')
  387. cp += 3, *cp2++ = '@';
  388. else
  389. if (cp[0] == '@' && cp[1] == ' ')
  390. cp += 2, *cp2++ = '@';
  391. else
  392. lastsp = 1;
  393. break;
  394. case '<':
  395. cp2 = bufend;
  396. gotlt++;
  397. lastsp = 0;
  398. break;
  399. case '>':
  400. if (gotlt) {
  401. gotlt = 0;
  402. while ((c = *cp) != '\0' && c != ',') {
  403. cp++;
  404. if (c == '(')
  405. cp = skip_comment(cp);
  406. else if (c == '"')
  407. while ((c = *cp) != '\0') {
  408. cp++;
  409. if (c == '"')
  410. break;
  411. if (c == '\\' && *cp != '\0')
  412. cp++;
  413. }
  414. }
  415. lastsp = 0;
  416. break;
  417. }
  418. /* FALLTHROUGH */
  419. default:
  420. if (lastsp) {
  421. lastsp = 0;
  422. *cp2++ = ' ';
  423. }
  424. *cp2++ = c;
  425. if (c == ',' && *cp == ' ' && !gotlt) {
  426. *cp2++ = ' ';
  427. while (*++cp == ' ')
  428. ;
  429. lastsp = 0;
  430. bufend = cp2;
  431. }
  432. }
  433. }
  434. *cp2 = '\0';
  435. if ((cp = realloc(nbuf, strlen(nbuf) + 1)) != NULL)
  436. nbuf = cp;
  437. return (nbuf);
  438. }
  439. /*
  440. * Fetch the sender's name from the passed message.
  441. * Reptype can be
  442. * 0 -- get sender's name for display purposes
  443. * 1 -- get sender's name for reply
  444. * 2 -- get sender's name for Reply
  445. */
  446. char *
  447. name1(struct message *mp, int reptype)
  448. {
  449. char namebuf[LINESIZE];
  450. char linebuf[LINESIZE];
  451. char *cp, *cp2;
  452. FILE *ibuf;
  453. int first = 1;
  454. if ((cp = hfield("from", mp)) != NULL)
  455. return (cp);
  456. if (reptype == 0 && (cp = hfield("sender", mp)) != NULL)
  457. return (cp);
  458. ibuf = setinput(mp);
  459. namebuf[0] = '\0';
  460. if (readline(ibuf, linebuf, LINESIZE) < 0)
  461. return (savestr(namebuf));
  462. newname:
  463. for (cp = linebuf; *cp != '\0' && *cp != ' '; cp++)
  464. ;
  465. for (; *cp == ' ' || *cp == '\t'; cp++)
  466. ;
  467. for (cp2 = &namebuf[strlen(namebuf)];
  468. *cp != '\0' && *cp != ' ' && *cp != '\t' &&
  469. cp2 < namebuf + LINESIZE - 1;)
  470. *cp2++ = *cp++;
  471. *cp2 = '\0';
  472. if (readline(ibuf, linebuf, LINESIZE) < 0)
  473. return (savestr(namebuf));
  474. if ((cp = strchr(linebuf, 'F')) == NULL)
  475. return (savestr(namebuf));
  476. if (strncmp(cp, "From", 4) != 0)
  477. return (savestr(namebuf));
  478. while ((cp = strchr(cp, 'r')) != NULL) {
  479. if (strncmp(cp, "remote", 6) == 0) {
  480. if ((cp = strchr(cp, 'f')) == NULL)
  481. break;
  482. if (strncmp(cp, "from", 4) != 0)
  483. break;
  484. if ((cp = strchr(cp, ' ')) == NULL)
  485. break;
  486. cp++;
  487. if (first) {
  488. cp2 = namebuf;
  489. first = 0;
  490. } else
  491. cp2 = strrchr(namebuf, '!') + 1;
  492. strlcpy(cp2, cp, sizeof(namebuf) - (cp2 - namebuf) - 1);
  493. strcat(namebuf, "!");
  494. goto newname;
  495. }
  496. cp++;
  497. }
  498. return (savestr(namebuf));
  499. }
  500. /*
  501. * Count the occurances of c in str
  502. */
  503. int
  504. charcount(char *str, int c)
  505. {
  506. char *cp;
  507. int i;
  508. for (i = 0, cp = str; *cp != '\0'; cp++)
  509. if (*cp == c)
  510. i++;
  511. return (i);
  512. }
  513. /*
  514. * See if the given header field is supposed to be ignored.
  515. */
  516. int
  517. isign(const char *field, struct ignoretab ignore[2])
  518. {
  519. char realfld[LINESIZE];
  520. if (ignore == ignoreall)
  521. return (1);
  522. /*
  523. * Lower-case the string, so that "Status" and "status"
  524. * will hash to the same place.
  525. */
  526. istrncpy(realfld, field, sizeof(realfld));
  527. if (ignore[1].i_count > 0)
  528. return (!member(realfld, ignore + 1));
  529. else
  530. return (member(realfld, ignore));
  531. }
  532. int
  533. member(char *realfield, struct ignoretab *table)
  534. {
  535. struct ignore *igp;
  536. for (igp = table->i_head[hash(realfield)]; igp != NULL; igp = igp->i_link)
  537. if (*igp->i_field == *realfield &&
  538. equal(igp->i_field, realfield))
  539. return (1);
  540. return (0);
  541. }