home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 October / usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso / unix / volume18 / elm2.2 / part14 < prev    next >
Encoding:
Internet Message Format  |  1989-04-12  |  49.3 KB

  1. Subject:  v18i093:  Elm mail system, release 2.2, Part14/24
  2. Newsgroups: comp.sources.unix
  3. Sender: sources
  4. Approved: rsalz@uunet.UU.NET
  5.  
  6. Submitted-by: dsinc!syd@uunet.UU.NET (Syd Weinstein)
  7. Posting-number: Volume 18, Issue 93
  8. Archive-name: elm2.2/part14
  9.  
  10. #!/bin/sh
  11. # this is part 14 of a multipart archive
  12. # do not concatenate these parts, unpack them in order with /bin/sh
  13. # file src/elm.c continued
  14. #
  15. CurArch=14
  16. if test ! -r s2_seq_.tmp
  17. then echo "Please unpack part 1 first!"
  18.      exit 1; fi
  19. ( read Scheck
  20.   if test "$Scheck" != $CurArch
  21.   then echo "Please unpack part $Scheck next!"
  22.        exit 1;
  23.   else exit 0; fi
  24. ) < s2_seq_.tmp || exit 1
  25. echo "x - Continuing file src/elm.c"
  26. sed 's/^X//' << 'SHAR_EOF' >> src/elm.c
  27. X        current);
  28. X
  29. X    Write_to_screen("Lines : %-5d\t\t\tStatus: A  C  D  E  F  N  O  P  T  U  V\n\r", 1,
  30. X        current_header->lines);
  31. X    Write_to_screen("            \t\t\t        c  o  e  x  o  e  l  r  a  r  i\n\r", 0);
  32. X    Write_to_screen("            \t\t\t        t  n  l  p  r  w  d  i  g  g  s\n\r", 0);
  33. X    Write_to_screen("            \t\t\t        n  f  d  d  m        v  d  n  i\n\r", 0);
  34. X
  35. X    sprintf(buffer, 
  36. X        "\n\rOffset: %ld\t\t\t        %d  %d  %d  %d  %d",
  37. X        current_header->offset,
  38. X        (current_header->status & ACTION) != 0,
  39. X        (current_header->status & CONFIDENTIAL) != 0,
  40. X        (current_header->status & DELETED) != 0,
  41. X        (current_header->status & EXPIRED) != 0,
  42. X        (current_header->status & FORM_LETTER) != 0);
  43. X    sprintf(buffer + strlen(buffer),
  44. X        "  %d  %d  %d  %d  %d  %d\n",
  45. X        (current_header->status & NEW) != 0,
  46. X        (current_header->status & UNREAD) != 0,
  47. X        (current_header->status & PRIVATE) != 0,
  48. X        (current_header->status & TAGGED) != 0,
  49. X        (current_header->status & URGENT) != 0,
  50. X        (current_header->status & VISIBLE) != 0);
  51. X
  52. X    Write_to_screen(buffer, 0);
  53. X
  54. X    sprintf(buffer, "\n\rReceived on: %d/%d/%d at %d:%02d\n\r",
  55. X            current_header->received.month+1,
  56. X            current_header->received.day,
  57. X            current_header->received.year,
  58. X            current_header->received.hour,
  59. X            current_header->received.minute);
  60. X    Write_to_screen(buffer, 0);
  61. X
  62. X    sprintf(buffer, "Message sent on: %s, %s %s, %s at %s\n\r",
  63. X            current_header->dayname,
  64. X            current_header->month,
  65. X            current_header->day,
  66. X            current_header->year,
  67. X            current_header->time);
  68. X    Write_to_screen(buffer, 0);
  69. X    
  70. X    Write_to_screen("From: %s\n\rSubject: %s", 2,
  71. X        current_header->from,
  72. X            current_header->subject);
  73. X
  74. X    Write_to_screen("\n\rPrimary Recipient: %s\nInternal Index Reference Number = %d\n\r", 2,
  75. X        current_header->to,
  76. X        current_header->index_number);
  77. X
  78. X    Write_to_screen("Message-ID: %s\n\r", 1,
  79. X        strlen(current_header->messageid) > 0 ? 
  80. X        current_header->messageid : "<none>");
  81. X
  82. X    Write_to_screen("Status: %s\n\r", 1, current_header->mailx_status);
  83. X    
  84. X    Raw(ON);
  85. X
  86. X    PutLine0(LINES,0,"Please Press any key to return.");
  87. X    (void) ReadCh();
  88. X}
  89. SHAR_EOF
  90. echo "File src/elm.c is complete"
  91. chmod 0444 src/elm.c || echo "restore of src/elm.c fails"
  92. echo "x - extracting src/encode.c (Text)"
  93. sed 's/^X//' << 'SHAR_EOF' > src/encode.c &&
  94. X
  95. Xstatic char rcsid[] = "@(#)$Id: encode.c,v 2.9 89/03/25 21:46:13 syd Exp $";
  96. X
  97. X/*******************************************************************************
  98. X *  The Elm Mail System  -  $Revision: 2.9 $   $State: Exp $
  99. X *
  100. X *             Copyright (c) 1986, 1987 Dave Taylor
  101. X *             Copyright (c) 1988, 1989 USENET Community Trust
  102. X *******************************************************************************
  103. X * Bug reports, patches, comments, suggestions should be sent to:
  104. X *
  105. X *    Syd Weinstein, Elm Coordinator
  106. X *    elm@dsinc.UUCP            dsinc!elm
  107. X *
  108. X *******************************************************************************
  109. X * $Log:    encode.c,v $
  110. X * Revision 2.9  89/03/25  21:46:13  syd
  111. X * Initial 2.2 Release checkin
  112. X * 
  113. X *
  114. X ******************************************************************************/
  115. X
  116. X/** This is a heavily mangled version of the 'cypher' program written by
  117. X    person or persons unknown.  
  118. X
  119. X**/
  120. X
  121. X#include "headers.h"
  122. X
  123. X#define RS    94
  124. X#define RN    4
  125. X#define RMASK    0x7fff    /* use only 15 bits */
  126. X
  127. Xstatic char r[RS][RN];        /* rotors */
  128. Xstatic char ir[RS][RN];        /* inverse rotors */
  129. Xstatic char h[RS];        /* half rotor */
  130. Xstatic char s[RS];        /* shuffle vector */
  131. Xstatic int  p[RN];        /* rotor indices */
  132. X
  133. Xstatic char the_key[SLEN];    /* unencrypted key */
  134. Xstatic char *encrypted_key;    /* encrypted key   */
  135. X
  136. Xchar *strncpy(), *strcpy();
  137. Xunsigned long sleep();
  138. X
  139. X#define DECRYPT_PROMPT        "Enter decryption key: "
  140. X#define FIRST_ENC_PROMPT    "Enter encryption key: "
  141. X#define SECOND_ENC_PROMPT    "Please enter it again: "
  142. X#define PROMPT_LINE        LINES-1
  143. X
  144. Xgetkey(send)
  145. Xint send;
  146. X{
  147. X    /** this routine prompts for and returns an encode/decode
  148. X        key for use in the rest of the program. **/
  149. X
  150. X    char buffer[2][NLEN];
  151. X
  152. X    while (1) {
  153. X      PutLine0(PROMPT_LINE, 0, (send ? FIRST_ENC_PROMPT : DECRYPT_PROMPT));
  154. X      CleartoEOLN();
  155. X      optionally_enter(buffer[0], PROMPT_LINE,
  156. X        strlen(send ? FIRST_ENC_PROMPT : DECRYPT_PROMPT), FALSE, TRUE);
  157. X      if (send) {
  158. X        PutLine0(PROMPT_LINE, 0, SECOND_ENC_PROMPT);
  159. X        CleartoEOLN();
  160. X        optionally_enter(buffer[1], PROMPT_LINE, strlen(SECOND_ENC_PROMPT),
  161. X          FALSE, TRUE);
  162. X        if(strcmp(buffer[0], buffer[1]) != 0) {
  163. X          error("Your keys were not the same!");
  164. X          sleep(1);
  165. X          clear_error();
  166. X          continue;
  167. X        }
  168. X      }
  169. X      break;
  170. X    }
  171. X        strcpy(the_key, buffer[0]);    /* save unencrypted key */
  172. X    makekey(buffer[0]);
  173. X
  174. X    setup();        /** initialize the rotors etc. **/
  175. X
  176. X    ClearLine(PROMPT_LINE);        
  177. X    clear_error();
  178. X}
  179. X
  180. Xget_key_no_prompt()
  181. X{
  182. X    /** This performs the same action as get_key, but assumes that
  183. X        the current value of 'the_key' is acceptable.  This is used
  184. X        when a message is encrypted twice... **/
  185. X
  186. X    char buffer[SLEN];
  187. X
  188. X    strcpy(buffer, the_key);
  189. X
  190. X    makekey( buffer );
  191. X
  192. X    setup();
  193. X}
  194. X
  195. Xencode(line)
  196. Xchar *line;
  197. X{
  198. X    /** encrypt or decrypt the specified line.  Uses the previously
  199. X        entered key... **/
  200. X
  201. X    register int i, iindex, j, ph = 0;
  202. X
  203. X    for (iindex=0; iindex < strlen(line); iindex++) {
  204. X      i = (int) line[iindex];
  205. X
  206. X      if ( (i >= ' ') && (i < '~') ) {
  207. X        i -= ' ';
  208. X
  209. X        for ( j = 0; j < RN; j++ )        /* rotor forwards */
  210. X          i = r[(i+p[j])%RS][j];
  211. X
  212. X        i = ((h[(i+ph)%RS])-ph+RS)%RS;    /* half rotor */
  213. X
  214. X        for ( j--  ; j >= 0; j-- )        /* rotor backwards */
  215. X          i = (ir[i][j]+RS-p[j])%RS;
  216. X
  217. X        j = 0;                /* rotate rotors */
  218. X        p[0]++;
  219. X        while ( p[j] == RS ) {
  220. X          p[j] = 0;
  221. X          j++;
  222. X          if ( j == RN ) break;
  223. X          p[j]++;
  224. X            }
  225. X  
  226. X        if ( ++ph == RS )
  227. X          ph = 0;
  228. X
  229. X        i += ' ';
  230. X      }
  231. X      
  232. X      line[iindex] = (char) i;    /* replace with altered one */
  233. X    }
  234. X}
  235. X
  236. X
  237. Xmakekey( rkey)
  238. Xchar *rkey;
  239. X{
  240. X    /** encrypt the key using the system routine 'crypt' **/
  241. X
  242. X    char key[9], salt[2], *crypt();
  243. X
  244. X    strncpy( key, rkey, 8);
  245. X    key[8] = '\0';
  246. X    salt[0] = key[0];
  247. X    salt[1] = key[1];
  248. X#ifdef CRYPT
  249. X    encrypted_key = crypt( key, salt);
  250. X#else
  251. X    encrypted_key = key;
  252. X#endif
  253. X}
  254. X
  255. X/*
  256. X * shuffle rotors.
  257. X * shuffle each of the rotors indiscriminately.  shuffle the half-rotor
  258. X * using a special obvious and not very tricky algorithm which is not as
  259. X * sophisticated as the one in crypt(1) and Oh God, I'm so depressed.
  260. X * After all this is done build the inverses of the rotors.
  261. X */
  262. X
  263. Xsetup()
  264. X{
  265. X    register long i, j, k, temp;
  266. X    long seed;
  267. X
  268. X    for ( j = 0; j < RN; j++ ) {
  269. X        p[j] = 0;
  270. X        for ( i = 0; i < RS; i++ )
  271. X            r[i][j] = i;
  272. X    }
  273. X
  274. X    seed = 123;
  275. X    for ( i = 0; i < 13; i++)        /* now personalize the seed */
  276. X      seed = (seed*encrypted_key[i] + i) & RMASK;
  277. X
  278. X    for ( i = 0; i < RS; i++ )        /* initialize shuffle vector */
  279. X      h[i] = s[i] = i;
  280. X
  281. X    for ( i = 0; i < RS; i++) {        /* shuffle the vector */
  282. X      seed = (5 * seed + encrypted_key[i%13]) & RMASK;;
  283. X      k = ((seed % 65521) & RMASK) % RS;
  284. X      temp = s[k];
  285. X      s[k] = s[i];
  286. X      s[i] = temp;
  287. X    }
  288. X
  289. X    for ( i = 0; i < RS; i += 2 ) {    /* scramble the half-rotor */
  290. X      temp = h[s[i]];            /* swap rotor elements ONCE */
  291. X      h[s[i]] = h[s[i+1]];
  292. X      h[s[i+1]] = temp;
  293. X    }
  294. X
  295. X    for ( j = 0; j < RN; j++) {            /* select a rotor */
  296. X
  297. X      for ( i = 0; i < RS; i++) {        /* shuffle the vector */
  298. X        seed = (5 * seed + encrypted_key[i%13]) & RMASK;;
  299. X        k = ((seed % 65521) & RMASK) % RS;
  300. X        temp = r[i][j];
  301. X        r[i][j] = r[k][j];
  302. X        r[k][j] = temp;
  303. X      }
  304. X
  305. X      for ( i = 0; i < RS; i++)         /* create inverse rotors */
  306. X        ir[r[i][j]][j] = i;
  307. X       }
  308. X}
  309. SHAR_EOF
  310. chmod 0444 src/encode.c || echo "restore of src/encode.c fails"
  311. echo "x - extracting src/errno.c (Text)"
  312. sed 's/^X//' << 'SHAR_EOF' > src/errno.c &&
  313. X
  314. Xstatic char rcsid[] = "@(#)$Id: errno.c,v 2.3 89/03/25 21:46:14 syd Exp $";
  315. X
  316. X/*******************************************************************************
  317. X *  The Elm Mail System  -  $Revision: 2.3 $   $State: Exp $
  318. X *
  319. X *             Copyright (c) 1986, 1987 Dave Taylor
  320. X *             Copyright (c) 1988, 1989 USENET Community Trust
  321. X *******************************************************************************
  322. X * Bug reports, patches, comments, suggestions should be sent to:
  323. X *
  324. X *    Syd Weinstein, Elm Coordinator
  325. X *    elm@dsinc.UUCP            dsinc!elm
  326. X *
  327. X *******************************************************************************
  328. X * $Log:    errno.c,v $
  329. X * Revision 2.3  89/03/25  21:46:14  syd
  330. X * Initial 2.2 Release checkin
  331. X * 
  332. X *
  333. X ******************************************************************************/
  334. X
  335. X/** This routine maps error numbers to error names and error messages.
  336. X    These are all directly ripped out of the include file errno.h, and
  337. X    are HOPEFULLY standardized across the different breeds of Unix!!
  338. X
  339. X    If (alas) yours are different, you should be able to use awk to
  340. X    mangle your errno.h file quite simply...
  341. X
  342. X**/
  343. X
  344. X#include "headers.h"
  345. X
  346. Xchar *err_name[] = { 
  347. X/* 0 */            "NOERROR", "No error status currently",
  348. X/* 1 */        "EPERM",   "Not super-user",
  349. X/* 2 */        "ENOENT",  "No such file or directory",
  350. X/* 3 */        "ESRCH",   "No such process",
  351. X/* 4 */        "EINTR",   "Interrupted system call",
  352. X/* 5 */        "EIO",     "I/O error",
  353. X/* 6 */        "ENXIO",   "No such device or address",
  354. X/* 7 */        "E2BIG",   "Arg list too long",
  355. X/* 8 */        "ENOEXEC", "Exec format error",
  356. X/* 9 */        "EBADF",   "Bad file number",
  357. X/* 10 */    "ECHILD",  "No children",
  358. X/* 11 */    "EAGAIN",  "No more processes",
  359. X/* 12 */    "ENOMEM",  "Not enough core",
  360. X/* 13 */    "EACCES",  "Permission denied",
  361. X/* 14 */    "EFAULT",  "Bad address",
  362. X/* 15 */    "ENOTBLK", "Block device required",
  363. X/* 16 */    "EBUSY",   "Mount device busy",
  364. X/* 17 */    "EEXIST",  "File exists",
  365. X/* 18 */    "EXDEV",   "Cross-device link",
  366. X/* 19 */    "ENODEV",  "No such device",
  367. X/* 20 */    "ENOTDIR", "Not a directory",
  368. X/* 21 */    "EISDIR",  "Is a directory",
  369. X/* 22 */    "EINVAL",  "Invalid argument",
  370. X/* 23 */    "ENFILE",  "File table overflow",
  371. X/* 24 */    "EMFILE",  "Too many open files",
  372. X/* 25 */    "ENOTTY",  "Not a typewriter",
  373. X/* 26 */    "ETXTBSY", "Text file busy",
  374. X/* 27 */    "EFBIG",   "File too large",
  375. X/* 28 */    "ENOSPC",  "No space left on device",
  376. X/* 29 */    "ESPIPE",  "Illegal seek",
  377. X/* 30 */    "EROFS",   "Read only file system",
  378. X/* 31 */    "EMLINK",  "Too many links",
  379. X/* 32 */    "EPIPE",   "Broken pipe",
  380. X/* 33 */    "EDOM",    "Math arg out of domain of func",
  381. X/* 34 */    "ERANGE",  "Math result not representable",
  382. X/* 35 */    "ENOMSG",  "No message of desired type",
  383. X/* 36 */    "EIDRM",   "Identifier removed"
  384. X    };
  385. X
  386. Xchar *strcpy();
  387. X
  388. Xchar *error_name(errnumber)
  389. Xint errnumber;
  390. X{
  391. X    static char buffer[50];
  392. X
  393. X    if (errnumber < 0 || errnumber > 36) 
  394. X      sprintf(buffer,"ERR-UNKNOWN (%d)", errnumber);
  395. X    else
  396. X      strcpy(buffer, err_name[2*errnumber]);
  397. X
  398. X    return( (char *) buffer);
  399. X}
  400. X
  401. Xchar *error_description(errnumber)
  402. Xint errnumber;
  403. X{
  404. X    static char buffer[50];
  405. X
  406. X    if (errnumber < 0 || errnumber > 36) 
  407. X      sprintf(buffer,"Unknown error - %d - No description", errnumber);
  408. X    else
  409. X      strcpy(buffer, err_name[2*errnumber + 1]);
  410. X
  411. X    return ( (char *) buffer);
  412. X}
  413. SHAR_EOF
  414. chmod 0444 src/errno.c || echo "restore of src/errno.c fails"
  415. echo "x - extracting src/expires.c (Text)"
  416. sed 's/^X//' << 'SHAR_EOF' > src/expires.c &&
  417. X
  418. Xstatic char rcsid[] = "@(#)$Id: expires.c,v 2.5 89/03/25 21:46:16 syd Exp $";
  419. X
  420. X/*******************************************************************************
  421. X *  The Elm Mail System  -  $Revision: 2.5 $   $State: Exp $
  422. X *
  423. X *             Copyright (c) 1986, 1987 Dave Taylor
  424. X *             Copyright (c) 1988, 1989 USENET Community Trust
  425. X *******************************************************************************
  426. X * Bug reports, patches, comments, suggestions should be sent to:
  427. X *
  428. X *    Syd Weinstein, Elm Coordinator
  429. X *    elm@dsinc.UUCP            dsinc!elm
  430. X *
  431. X *******************************************************************************
  432. X * $Log:    expires.c,v $
  433. X * Revision 2.5  89/03/25  21:46:16  syd
  434. X * Initial 2.2 Release checkin
  435. X * 
  436. X *
  437. X ******************************************************************************/
  438. X
  439. X/** This routine is written to deal with the Expires: header on the
  440. X    individual mail coming in.  What it does is to look at the date,
  441. X    compare it to todays date, then set the EXPIRED flag on the
  442. X    current message if it is true...
  443. X**/
  444. X
  445. X#include "headers.h"
  446. X
  447. X#ifdef BSD
  448. X#  ifdef TMINSYS
  449. X#    include <sys/time.h>
  450. X#  else
  451. X#    include <time.h>
  452. X#    include <sys/types.h>
  453. X#    include <sys/timeb.h>
  454. X#  endif
  455. X#else
  456. X#  include <time.h>
  457. X#endif
  458. X
  459. X#include <ctype.h>
  460. X
  461. X#ifdef BSD
  462. X#undef toupper
  463. X#undef tolower
  464. X#endif
  465. X
  466. Xprocess_expiration_date(date, message_status)
  467. Xchar *date;
  468. Xint  *message_status;
  469. X{
  470. X    struct tm *timestruct, *localtime();
  471. X    long thetime, time();
  472. X    char word1[WLEN], word2[WLEN], word3[WLEN], word4[WLEN], word5[WLEN];
  473. X    int  month = 0, day = 0, year = 0, hour = 0, minute = 0;
  474. X
  475. X    /** first step is to break down the date given into MM DD YY HH MM
  476. X        format:  The possible formats for this field are, by example:
  477. X    
  478. X        (1) Mon, Jun 11, 87
  479. X        (2) Mon, 11 Jun 87
  480. X        (3) Jun 11, 87
  481. X        (4) 11 Jun 87
  482. X        (5) 11/06/87    <- ambiguous - will be ignored!!
  483. X        (6) 8711061248GMT
  484. X        (7) Mon, Jun 11, 87 12:48:35 GMT
  485. X
  486. X        The reason #5 is considered ambiguous will be made clear
  487. X        if we consider a message to be expired on Jan 4, 88:
  488. X            01/04/88    in the United States
  489. X            04/01/88    in Europe
  490. X        so is the first field the month or the day?  Standard prob.
  491. X    **/
  492. X
  493. X    sscanf(date, "%s %s %s %s %s",
  494. X        word1, word2, word3, word4, word5);
  495. X
  496. X    if (strlen(word5) != 0) {    /* we have form #7 */
  497. X      day   = atoi(word1);
  498. X      month = month_number(word2);
  499. X      year  = atoi(word3);
  500. X      sscanf(word4, "%02d%*c%02d",
  501. X           &hour, &minute);
  502. X    }
  503. X    else if (strlen(word2) == 0) {    /* we have form #6 or form #5 */
  504. X      if (isdigit(word1[1]) && isdigit(word1[2]))      /* form #6 */
  505. X        sscanf(word1, "%02d%02d%02d%02d%02d%*c",
  506. X         &year, &month, &day, &hour, &minute);
  507. X    }
  508. X    else if (strlen(word4) != 0) {           /* form #1 or form #2 */
  509. X      if(isdigit(word2[0])) {           /* form #2 */
  510. X          month = month_number(word3);
  511. X          day   = atoi(word2);
  512. X          year  = atoi(word4);
  513. X      } else {                   /* form #1 */
  514. X          month = month_number(word2);
  515. X          day   = atoi(word3);
  516. X          year  = atoi(word4);
  517. X      }
  518. X    }
  519. X    else if (! isdigit(word1[0])) {           /* form #3 */
  520. X      month = month_number(word1);
  521. X      day   = atoi(word2);
  522. X      year  = atoi(word3);
  523. X    }
  524. X    else {                       /* form #4 */
  525. X      day   = atoi(word1);
  526. X      month = month_number(word2);
  527. X      year  = atoi(word3);
  528. X    }
  529. X
  530. X    if (day == 0 || year == 0)
  531. X      return;            /* we didn't get a valid date */
  532. X
  533. X    /** next let's get the current time and date, please **/
  534. X
  535. X    thetime = time((long *) 0);
  536. X
  537. X    timestruct = localtime(&thetime);
  538. X
  539. X    /** and compare 'em **/
  540. X
  541. X    if (year > timestruct->tm_year)
  542. X      return;
  543. X    else if (year < timestruct->tm_year)
  544. X      goto expire_message;
  545. X
  546. X    if (month > timestruct->tm_mon)
  547. X      return;
  548. X    else if (month < timestruct->tm_mon)
  549. X      goto expire_message;
  550. X
  551. X    if (day > timestruct->tm_mday)
  552. X      return;
  553. X    else if (day < timestruct->tm_mday)
  554. X      goto expire_message;
  555. X
  556. X    if (hour > timestruct->tm_hour)
  557. X      return;
  558. X    else if (hour < timestruct->tm_hour)
  559. X      goto expire_message;
  560. X
  561. X    if (minute > timestruct->tm_min)
  562. X      return;
  563. X
  564. Xexpire_message:
  565. X
  566. X    /** it's EXPIRED!  Yow!! **/
  567. X
  568. X    (*message_status) |= EXPIRED;
  569. X}
  570. SHAR_EOF
  571. chmod 0444 src/expires.c || echo "restore of src/expires.c fails"
  572. echo "x - extracting src/file.c (Text)"
  573. sed 's/^X//' << 'SHAR_EOF' > src/file.c &&
  574. X
  575. Xstatic char rcsid[] = "@(#)$Id: file.c,v 2.19 89/03/25 21:46:17 syd Exp $";
  576. X
  577. X/*******************************************************************************
  578. X *  The Elm Mail System  -  $Revision: 2.19 $   $State: Exp $
  579. X *
  580. X *             Copyright (c) 1986, 1987 Dave Taylor
  581. X *             Copyright (c) 1988, 1989 USENET Community Trust
  582. X *******************************************************************************
  583. X * Bug reports, patches, comments, suggestions should be sent to:
  584. X *
  585. X *    Syd Weinstein, Elm Coordinator
  586. X *    elm@dsinc.UUCP            dsinc!elm
  587. X *
  588. X *******************************************************************************
  589. X * $Log:    file.c,v $
  590. X * Revision 2.19  89/03/25  21:46:17  syd
  591. X * Initial 2.2 Release checkin
  592. X * 
  593. X *
  594. X ******************************************************************************/
  595. X
  596. X/** File I/O routines, mostly the save to file command...
  597. X
  598. X**/
  599. X
  600. X#include "headers.h"
  601. X#include <ctype.h>
  602. X#include <errno.h>
  603. X
  604. X#ifdef BSD
  605. X#undef tolower
  606. X#endif
  607. X
  608. Xextern int errno;
  609. X
  610. Xchar *error_name(), *error_description(), *strcpy(), *getenv(), *nameof();
  611. Xunsigned long sleep();
  612. X
  613. Xint
  614. Xsave(redraw, silently, delete)
  615. Xint *redraw, silently, delete;
  616. X{
  617. X    /** Save all tagged messages + current in a folder.  If no messages
  618. X        are tagged, save the current message instead!  This routine
  619. X        will return ZERO if the operation failed.
  620. X        'redraw' is set to TRUE iff we use the '?' and mess up
  621. X        the screen.  Pretty reasonable, eh?  If "silently" is set,
  622. X        then don't output the "D" character upon marking for
  623. X        deletion...
  624. X        If delete is set, then delete the saved messages, else
  625. X        we are just copying the messages without deletion.
  626. X    **/
  627. X
  628. X    register int tagged = 0, i, oldstat, appending = 0;
  629. X    int mesgnum;    /* message whose address is used for save-by-name fn */
  630. X    char filename[SLEN], address[SLEN], buffer[SLEN];
  631. X    static char helpmsg[LONG_STRING];
  632. X    FILE *save_file;
  633. X
  634. X    oldstat = headers[current-1]->status;    /* remember */
  635. X    *redraw = FALSE;
  636. X
  637. X    for (i=0; i < message_count; i++) {
  638. X      if (ison(headers[i]->status, TAGGED)) {
  639. X        if(!tagged)
  640. X          mesgnum = i;    /* first tagged msg -  use this one for
  641. X                 * save-by-name folder name */
  642. X        tagged++;
  643. X      }
  644. X    }
  645. X
  646. X    if (tagged == 0) {
  647. X      mesgnum = current-1;    /* use this one for save-by-name folder name */
  648. X      tagged = 1;
  649. X      setit(headers[current-1]->status, TAGGED);
  650. X    }
  651. X
  652. X    dprint(4, (debugfile, "%d message%s tagged for saving (save)\n", tagged,
  653. X        plural(tagged)));
  654. X
  655. X    while (1) {
  656. X
  657. X      PutLine2(LINES-2, 0, "%s message%s to: ",
  658. X          (delete ? "Save" : "Copy"), plural(tagged));
  659. X
  660. X      if (save_by_name) {
  661. X        /** build default filename to save to **/
  662. X        get_return(address, mesgnum);
  663. X        get_return_name(address, buffer, TRUE);
  664. X        if(strcmp(buffer, username) == 0) {
  665. X          get_existing_address(address, mesgnum);
  666. X          get_return_name(address, buffer, TRUE);
  667. X        }
  668. X        sprintf(filename, "=%s", buffer);
  669. X      }
  670. X      else
  671. X        filename[0] = '\0';
  672. X
  673. X      if (tagged > 1)
  674. X        optionally_enter(filename, LINES-2, 19, FALSE, FALSE);
  675. X      else    
  676. X        optionally_enter(filename, LINES-2, 18, FALSE, FALSE);
  677. X  
  678. X
  679. X      if (strlen(filename) == 0) {  /** <return> means 'cancel', right? **/
  680. X        headers[current-1]->status = oldstat;    /* BACK! */
  681. X        return(0);
  682. X      }
  683. X     
  684. X      if (strcmp(filename,"?") == 0) {    /* user asked for listing */
  685. X        *redraw = TRUE;    /* set the flag so we know what to do later */
  686. X        if(!*helpmsg) {    /* format helpmsg if not yet done */
  687. X
  688. X          strcpy(helpmsg, "\n\r\n\rEnter: <nothing> to not ");
  689. X          strcat(helpmsg, (delete ? "save" : "copy"));
  690. X          strcat(helpmsg, " your message");
  691. X          strcat(helpmsg, (plural(tagged) ? "s" : ""));
  692. X          strcat(helpmsg, "\n\r       '>' to ");
  693. X          strcat(helpmsg, (delete ? "save" : "copy"));
  694. X          strcat(helpmsg, " your message");
  695. X          strcat(helpmsg, (plural(tagged) ? "s" : ""));
  696. X          strcat(helpmsg, " to your \"received\" folder (");
  697. X          strcat(helpmsg, nameof(recvd_mail));
  698. X          strcat(helpmsg, ")\n\r       '<' to ");
  699. X          strcat(helpmsg, (delete ? "save" : "copy"));
  700. X          strcat(helpmsg, " your message");
  701. X          strcat(helpmsg, (plural(tagged) ? "s" : ""));
  702. X          strcat(helpmsg, " to your \"sent\" folder (");
  703. X          strcat(helpmsg, nameof(sent_mail));
  704. X          strcat(helpmsg, ") \n\r       a filename");
  705. X          strcat(helpmsg, " (leading '=' denotes your folder directory ");
  706. X          strcat(helpmsg, folders);
  707. X          strcat(helpmsg, ").\n\r");
  708. X        }
  709. X
  710. X        list_folders(4, helpmsg);
  711. X        continue;
  712. X      }
  713. X
  714. X      /* else - got a folder name - check it out */
  715. X      if (! expand_filename(filename, TRUE)) {
  716. X        dprint(2, (debugfile,
  717. X          "Error: Failed on expansion of filename %s (save)\n", 
  718. X          filename));
  719. X        continue;
  720. X      }
  721. X      if ((errno = can_open(filename, "a"))) {
  722. X        error2("Cannot %s message to folder %s!",
  723. X          delete ? "save":"copy", filename);
  724. X        continue;
  725. X      }
  726. X      break;    /* got a valid filename */
  727. X    }
  728. X
  729. X    save_file_stats(filename);
  730. X
  731. X    if (access(filename,ACCESS_EXISTS)== 0)     /* already there!! */
  732. X      appending = 1;
  733. X      
  734. X    dprint(4,(debugfile, "Saving mail to folder '%s'...\n", filename));
  735. X
  736. X    if ((save_file = fopen(filename,"a")) == NULL) {
  737. X      dprint(2, (debugfile,
  738. X        "Error: couldn't append to specified folder %s (save)\n", 
  739. X        filename));
  740. X      error1("Couldn't append to folder %s!", filename);
  741. X      headers[current-1]->status = oldstat;    /* BACK! */
  742. X      return(0); 
  743. X    }
  744. X
  745. X    /* if we need a redraw that means index screen no longer present
  746. X     * so whatever silently was, now it's true - we can't show those
  747. X     * delete markings.
  748. X     */
  749. X    if(*redraw) silently = TRUE;
  750. X
  751. X    for (i=0; i < message_count; i++)     /* save each tagged msg */
  752. X      if (headers[i]->status & TAGGED)
  753. X        save_message(i, filename, save_file, (tagged > 1), appending++, 
  754. X             silently, delete);
  755. X
  756. X    fclose(save_file);
  757. X
  758. X    restore_file_stats(filename);
  759. X
  760. X    if (tagged > 1)
  761. X      error2("Message%s %s.", plural(tagged), delete ? "saved": "copied");
  762. X    return(1);
  763. X}
  764. X
  765. Xint
  766. Xsave_message(number, filename, fd, pause, appending, silently, delete)
  767. Xint number, pause, appending, silently, delete;
  768. Xchar *filename;
  769. XFILE *fd;
  770. X{
  771. X    /** Save an actual message to a folder.  This is called by 
  772. X        "save()" only!  The parameters are the message number,
  773. X        and the name and file descriptor of the folder to save to.
  774. X        If 'pause' is true, a sleep(2) will be done after the
  775. X        saved message appears on the screen...
  776. X        'appending' is only true if the folder already exists 
  777. X        If 'delete' is true, mark the message for deletion.
  778. X    **/
  779. X
  780. X    register int save_current, is_new;
  781. X    
  782. X    dprint(4, (debugfile, "\tSaving message %d to folder...\n", number));
  783. X
  784. X    save_current = current;
  785. X    current = number+1;
  786. X
  787. X    /* change status from NEW before copy and reset to what it was
  788. X     * so that copy doesn't look new, but we can preserve new status
  789. X     * of message in this mailfile. This is important because if
  790. X     * user does a resync, we don't want NEW status to be lost.
  791. X     * I.e. NEW becomes UNREAD when we "really" leave a mailfile.
  792. X     */
  793. X    if(is_new = ison(headers[number]->status, NEW))
  794. X      clearit(headers[number]->status, NEW);
  795. X    copy_message("", fd, FALSE, FALSE, TRUE);
  796. X    if(is_new)
  797. X      setit(headers[number]->status, NEW);
  798. X    current = save_current;
  799. X
  800. X    if (delete)
  801. X      setit(headers[number]->status, DELETED); /* deleted, but ...   */
  802. X    clearit(headers[number]->status, TAGGED);  /* not tagged anymore */
  803. X
  804. X    if (appending)
  805. X      error2("Message %d appended to folder %s.", number+1, filename);
  806. X    else
  807. X      error3("Message %d %s to folder %s.", number+1,
  808. X         delete ? "saved" : "copied", filename);
  809. X
  810. X    if (! silently)
  811. X      show_new_status(number);    /* update screen, if needed */
  812. X
  813. X    if (pause && ! silently) sleep(2);
  814. X}
  815. X
  816. Xint
  817. Xexpand_filename(filename, use_cursor_control)
  818. Xchar *filename;
  819. Xint use_cursor_control;
  820. X{
  821. X    /** Expands    ~    to the user's home directory
  822. X            =,+,%    to the user's folder's directory
  823. X            !    to the user's incoming mailbox
  824. X            >    to the user's received folder
  825. X            <    to the user's sent folder
  826. X            shell variables (begun with $)
  827. X
  828. X        Returns     1    upon proper expansions
  829. X            0    upon failed expansions
  830. X     **/
  831. X
  832. X    char temp_filename[SLEN], varname[SLEN], env_value[SLEN], *ptr;
  833. X    register int i = 1, iindex = 0;
  834. X    char *getenv();
  835. X
  836. X    ptr = filename;
  837. X    while (*ptr == ' ') ptr++;    /* leading spaces GONE! */
  838. X    strcpy(temp_filename, ptr);
  839. X
  840. X    /** New stuff - make sure no illegal char as last **/
  841. X    if (lastch(temp_filename) == '\n' || lastch(temp_filename) == '\r')
  842. X      lastch(temp_filename) = '\0';
  843. X      
  844. X    if (temp_filename[0] == '~') {
  845. X      sprintf(filename, "%s%s%s", home, 
  846. X        (temp_filename[1] != '/' && lastch(folders) != '/')? "/" : "",
  847. X          &temp_filename[1]);
  848. X    }
  849. X    else if (temp_filename[0] == '=' || temp_filename[0] == '+' || 
  850. X          temp_filename[0] == '%') {
  851. X      sprintf(filename, "%s%s%s", folders, 
  852. X        (temp_filename[1] != '/' && lastch(folders) != '/')? "/" : "",
  853. X          &temp_filename[1]);
  854. X    }
  855. X    else if (temp_filename[0] == '$') {    /* env variable! */
  856. X      while (isalnum(temp_filename[i]))
  857. X        varname[iindex++] = temp_filename[i++];
  858. X      varname[iindex] = '\0';
  859. X
  860. X      env_value[0] = '\0';            /* null string for strlen! */
  861. X      if (getenv(varname) != NULL)
  862. X        strcpy(env_value, getenv(varname));
  863. X
  864. X      if (strlen(env_value) == 0) {
  865. X        dprint(3,(debugfile, 
  866. X            "Error: Can't expand environment variable $%s (%s)\n",
  867. X            varname, "expand_filename"));
  868. X        if(use_cursor_control)
  869. X          error1("Don't know what the value of $%s is!", varname);
  870. X        else
  871. X          printf("\n\rDon't know what the value of $%s is!\n\r", varname);
  872. X        return(0);
  873. X      }
  874. X
  875. X      sprintf(filename, "%s%s%s", env_value, 
  876. X        (temp_filename[i] != '/' && lastch(env_value) != '/')? "/" : "",
  877. X        &temp_filename[1]);
  878. X
  879. X    } else if (strcmp(temp_filename, "!") == 0) {
  880. X      strcpy(filename, defaultfile);
  881. X    } else if (strcmp(temp_filename, ">") == 0) {
  882. X      strcpy(filename, recvd_mail);
  883. X    } else if (strcmp(temp_filename, "<") == 0) {
  884. X      strcpy(filename, sent_mail);
  885. X    } else
  886. X      strcpy(filename, temp_filename);
  887. X      
  888. X    return(1);
  889. X}
  890. SHAR_EOF
  891. chmod 0444 src/file.c || echo "restore of src/file.c fails"
  892. echo "x - extracting src/file_util.c (Text)"
  893. sed 's/^X//' << 'SHAR_EOF' > src/file_util.c &&
  894. X
  895. Xstatic char rcsid[] = "@(#)$Id: file_util.c,v 2.8 89/03/25 21:46:19 syd Exp $";
  896. X
  897. X/*******************************************************************************
  898. X *  The Elm Mail System  -  $Revision: 2.8 $   $State: Exp $
  899. X *
  900. X *             Copyright (c) 1986, 1987 Dave Taylor
  901. X *             Copyright (c) 1988, 1989 USENET Community Trust
  902. X *******************************************************************************
  903. X * Bug reports, patches, comments, suggestions should be sent to:
  904. X *
  905. X *    Syd Weinstein, Elm Coordinator
  906. X *    elm@dsinc.UUCP            dsinc!elm
  907. X *
  908. X *******************************************************************************
  909. X * $Log:    file_util.c,v $
  910. X * Revision 2.8  89/03/25  21:46:19  syd
  911. X * Initial 2.2 Release checkin
  912. X * 
  913. X *
  914. X ******************************************************************************/
  915. X
  916. X/** File oriented utility routines for ELM 
  917. X
  918. X**/
  919. X
  920. X#include "headers.h"
  921. X#include <sys/types.h>
  922. X#include <sys/stat.h>
  923. X#include <ctype.h>
  924. X#include <errno.h>
  925. X
  926. X#ifdef BSD
  927. X# undef tolower
  928. X#endif
  929. X
  930. X#include <signal.h>
  931. X#include <errno.h>
  932. X
  933. X#ifdef BSD
  934. X# include <sys/wait.h>
  935. X#endif
  936. X
  937. Xextern int errno;        /* system error number */
  938. X
  939. Xchar *error_name(), *error_description(), *strcpy(), *getlogin();
  940. Xlong  fsize();
  941. X
  942. Xlong
  943. Xbytes(name)
  944. Xchar *name;
  945. X{
  946. X    /** return the number of bytes in the specified file.  This
  947. X        is to check to see if new mail has arrived....  (also
  948. X        see "fsize()" to see how we can get the same information
  949. X        on an opened file descriptor slightly more quickly)
  950. X    **/
  951. X
  952. X    int ok = 1;
  953. X    extern int errno;    /* system error number! */
  954. X    struct stat buffer;
  955. X
  956. X    if (stat(name, &buffer) != 0)
  957. X      if (errno != 2) {
  958. X        dprint(1,(debugfile,
  959. X             "Error: errno %s on fstat of file %s (bytes)\n", 
  960. X             error_name(errno), name));
  961. X        Write_to_screen("\n\rError attempting fstat on file %s!\n\r",
  962. X             1, name);
  963. X        Write_to_screen("** %s - %s. **\n\r", 2, error_name(errno),
  964. X          error_description(errno));
  965. X        emergency_exit();
  966. X      }
  967. X      else
  968. X        ok = 0;
  969. X    
  970. X    return(ok ? (long) buffer.st_size : 0L);
  971. X}
  972. X
  973. Xint
  974. Xcan_access(file, mode)
  975. Xchar *file; 
  976. Xint   mode;
  977. X{
  978. X    /** returns ZERO iff user can access file or "errno" otherwise **/
  979. X
  980. X    int the_stat = 0, pid, w; 
  981. X    void _exit(), exit();
  982. X#ifdef BSD
  983. X    union wait status;
  984. X#else
  985. X    int status;
  986. X#endif
  987. X#ifdef VOIDSIG
  988. X    register void (*istat)(), (*qstat)();
  989. X#else
  990. X    register int (*istat)(), (*qstat)();
  991. X#endif
  992. X    
  993. X    if ((pid = vfork()) == 0) {
  994. X      setgid(groupid);
  995. X      setuid(userid);        /** back to normal userid **/
  996. X
  997. X      errno = 0;
  998. X
  999. X      if (access(file, mode) == 0) 
  1000. X        _exit(0);
  1001. X      else 
  1002. X        _exit(errno != 0? errno : 1);    /* never return zero! */
  1003. X      _exit(127);
  1004. X    }
  1005. X
  1006. X    istat = signal(SIGINT, SIG_IGN);
  1007. X    qstat = signal(SIGQUIT, SIG_IGN);
  1008. X
  1009. X    while ((w = wait(&status)) != pid && w != -1)
  1010. X        ;
  1011. X
  1012. X#ifdef BSD
  1013. X    the_stat = status.w_retcode;
  1014. X#else
  1015. X    the_stat = status >> 8;
  1016. X#endif
  1017. X
  1018. X    signal(SIGINT, istat);
  1019. X    signal(SIGQUIT, qstat);
  1020. X
  1021. X    return(the_stat);
  1022. X}
  1023. X
  1024. Xint
  1025. Xcan_open(file, mode)
  1026. Xchar *file, *mode;
  1027. X{
  1028. X    /** Returns 0 iff user can open the file.  This is not
  1029. X        the same as can_access - it's used for when the file might
  1030. X        not exist... **/
  1031. X
  1032. X    FILE *fd;
  1033. X    int the_stat = 0, pid, w, preexisted = 0; 
  1034. X    void _exit(), exit();
  1035. X#ifdef BSD
  1036. X    union wait status;
  1037. X#else
  1038. X    int status;
  1039. X#endif
  1040. X#ifdef VOIDSIG
  1041. X    register void (*istat)(), (*qstat)();
  1042. X#else
  1043. X    register int (*istat)(), (*qstat)();
  1044. X#endif
  1045. X    
  1046. X    if ((pid = vfork()) == 0) {
  1047. X      setgid(groupid);
  1048. X      setuid(userid);        /** back to normal userid **/
  1049. X      errno = 0;
  1050. X      if (access(file, ACCESS_EXISTS) == 0)
  1051. X        preexisted = 1;
  1052. X      if ((fd = fopen(file, mode)) == NULL)
  1053. X        _exit(errno);
  1054. X      else {
  1055. X        fclose(fd);        /* don't just leave it open! */
  1056. X        if(!preexisted)    /* don't leave it if this test created it! */
  1057. X          unlink(file);
  1058. X        _exit(0);
  1059. X      }
  1060. X      _exit(127);
  1061. X    }
  1062. X
  1063. X    istat = signal(SIGINT, SIG_IGN);
  1064. X    qstat = signal(SIGQUIT, SIG_IGN);
  1065. X
  1066. X    while ((w = wait(&status)) != pid && w != -1)
  1067. X        ;
  1068. X
  1069. X#ifdef BSD
  1070. X    the_stat = status.w_retcode;
  1071. X#else
  1072. X    the_stat = status >> 8;
  1073. X#endif
  1074. X    
  1075. X    signal(SIGINT, istat);
  1076. X    signal(SIGQUIT, qstat);
  1077. X
  1078. X    return(the_stat);
  1079. X}
  1080. X
  1081. Xint
  1082. Xcopy(from, to)
  1083. Xchar *from, *to;
  1084. X{
  1085. X    /** this routine copies a specified file to the destination
  1086. X        specified.  Non-zero return code indicates that something
  1087. X        dreadful happened! **/
  1088. X
  1089. X    FILE *from_file, *to_file;
  1090. X    char buffer[VERY_LONG_STRING];
  1091. X    
  1092. X    if ((from_file = fopen(from, "r")) == NULL) {
  1093. X      dprint(1, (debugfile, "Error: could not open %s for reading (copy)\n",
  1094. X         from));
  1095. X      error1("Could not open file %s.", from);
  1096. X      return(1);
  1097. X    }
  1098. X
  1099. X    if ((to_file = fopen(to, "w")) == NULL) {
  1100. X      dprint(1, (debugfile, "Error: could not open %s for writing (copy)\n",
  1101. X         to));
  1102. X      error1("Could not open file %s.", to);
  1103. X      return(1);
  1104. X    }
  1105. X
  1106. X    while (fgets(buffer, VERY_LONG_STRING, from_file) != NULL)
  1107. X      fputs(buffer, to_file);
  1108. X
  1109. X    fclose(from_file);
  1110. X    fclose(to_file);
  1111. X    chown( to, userid, groupid);
  1112. X
  1113. X    return(0);
  1114. X}
  1115. X
  1116. Xmove(from, to)
  1117. Xchar *from, *to;
  1118. X{
  1119. X    /** This routine moves a specified file to the destination
  1120. X        specified.  It starts by trying to it all by link'ing.
  1121. X    **/
  1122. X
  1123. X    FILE *from_file, *to_file;
  1124. X    char buffer[VERY_LONG_STRING];
  1125. X    
  1126. X    if (access(from, ACCESS_EXISTS) == -1) /* doesn't exist? */
  1127. X      return;
  1128. X
  1129. X    /** does the dest file exist?? **/
  1130. X
  1131. X    if (access(to, ACCESS_EXISTS) != -1) { /* dest DOES exist! */
  1132. X      printf("File %s already exists!  Overwriting...\n", to);
  1133. X      (void) unlink(to);
  1134. X    }
  1135. X
  1136. X    /** first off, let's try to link() it **/
  1137. X
  1138. X    if (link(from, to) != -1) {    /* yeah! */
  1139. X      return;
  1140. X    }
  1141. X
  1142. X    /** nope.  Let's open 'em both up and move the data... **/
  1143. X
  1144. X    if ((from_file = fopen(from, "r")) == NULL) {
  1145. X      dprint(1, (debugfile, "Error: could not open %s for reading (copy)\n",
  1146. X         from));
  1147. X      printf("Could not open file %s for reading\n", from);
  1148. X      return;
  1149. X    }
  1150. X
  1151. X    if ((to_file = fopen(to, "w")) == NULL) {
  1152. X      dprint(1, (debugfile, "Error: could not open %s for writing (copy)\n",
  1153. X         to));
  1154. X      printf("Could not open file %s for writing\n", to);
  1155. X      return;
  1156. X    }
  1157. X
  1158. X    while (fgets(buffer, VERY_LONG_STRING, from_file) != NULL)
  1159. X      fputs(buffer, to_file);
  1160. X
  1161. X    fclose(from_file);
  1162. X    fclose(to_file);
  1163. X    chown( to, userid, groupid);
  1164. X
  1165. X    return;
  1166. X}
  1167. X
  1168. X
  1169. Xint
  1170. Xappend(fd, filename)
  1171. XFILE *fd;
  1172. Xchar *filename;
  1173. X{
  1174. X    /** This routine appends the specified file to the already
  1175. X        open file descriptor.. Returns non-zero if fails.  **/
  1176. X
  1177. X    FILE *my_fd;
  1178. X    char buffer[VERY_LONG_STRING];
  1179. X    
  1180. X    if ((my_fd = fopen(filename, "r")) == NULL) {
  1181. X      dprint(1, (debugfile,
  1182. X        "Error: could not open %s for reading (append)\n", filename));
  1183. X      return(1);
  1184. X    }
  1185. X
  1186. X    while (fgets(buffer, VERY_LONG_STRING, my_fd) != NULL)
  1187. X      fputs(buffer, fd);
  1188. X
  1189. X    fclose(my_fd);
  1190. X
  1191. X    return(0);
  1192. X}
  1193. X
  1194. X#define FORWARDSIGN    "Forward to "
  1195. Xint
  1196. Xcheck_mailfile_size(mfile)
  1197. Xchar *mfile;
  1198. X{
  1199. X    /** Check to ensure we have mail.  Only used with the '-z'
  1200. X        starting option. So we output a diagnostic if there is
  1201. X        no mail to read (including  forwarding).
  1202. X        Return 0 if there is mail,
  1203. X           <0 if no permission to check,
  1204. X           1 if no mail,
  1205. X           2 if no mail because mail is being forwarded.
  1206. X     **/
  1207. X
  1208. X    char firstline[SLEN];
  1209. X    int retcode;
  1210. X    struct stat statbuf;
  1211. X    FILE *fp;
  1212. X
  1213. X    /* see if file exists first */
  1214. X    if (access(mfile, ACCESS_EXISTS) != 0)
  1215. X      retcode = 1;                    /* no file */
  1216. X
  1217. X    /* exists - now see if user has read access */
  1218. X    else if (can_access(mfile, READ_ACCESS) != 0)
  1219. X      retcode = -1;                    /* no perm */
  1220. X
  1221. X    /* read access - now see if file has a reasonable size */
  1222. X    else if ((fp = fopen(mfile, "r")) == NULL)
  1223. X      retcode = -1;        /* no perm? should have detected this above! */
  1224. X    else if (fstat(fileno(fp), &statbuf) == -1) 
  1225. X      retcode = -1;                    /* arg error! */
  1226. X    else if (statbuf.st_size < 2)        
  1227. X      retcode = 1;    /* empty or virtually empty, e.g. just a newline */
  1228. X
  1229. X    /* file has reasonable size - see if forwarding */
  1230. X    else if (fgets (firstline, SLEN, fp) == NULL)
  1231. X      retcode = 1;         /* empty? should have detected this above! */
  1232. X    else if (first_word(firstline, FORWARDSIGN))
  1233. X      retcode = 2;                    /* forwarding */
  1234. X
  1235. X    /* not forwarding - so file must have some mail in it */
  1236. X    else
  1237. X      retcode = 0;
  1238. X
  1239. X    /* now display the appropriate message if there isn't mail in it */
  1240. X    switch(retcode) {
  1241. X
  1242. X    case -1:    printf("You have no permission to read %s!\n\r", mfile);
  1243. X            break;
  1244. X    case 1:        printf("You have no mail.\n\r");
  1245. X            break;
  1246. X    case 2:        no_ret(firstline) /* remove newline before using */
  1247. X            printf("Your mail is being forwarded to %s.\n\r",
  1248. X              firstline + strlen(FORWARDSIGN));
  1249. X            break;
  1250. X    }
  1251. X    return(retcode);
  1252. X}
  1253. X
  1254. Xcreate_readmsg_file()
  1255. X{
  1256. X    /** Creates the file ".current" in the users home directory
  1257. X        for use with the "readmsg" program.
  1258. X    **/
  1259. X
  1260. X    FILE *fd;
  1261. X    char buffer[SLEN];
  1262. X
  1263. X    sprintf(buffer,"%s/%s", home, readmsg_file);
  1264. X
  1265. X    if ((fd = fopen (buffer, "w")) == NULL) {
  1266. X      dprint(1, (debugfile, 
  1267. X         "Error: couldn't create file %s - error %s (%s)\n",
  1268. X         buffer, error_name(errno), "create_readmsg_file"));
  1269. X      return;    /* no error to user */
  1270. X    }
  1271. X
  1272. X    if (current)
  1273. X      fprintf(fd, "%d\n", headers[current-1]->index_number);
  1274. X    else
  1275. X      fprintf(fd, "\n");
  1276. X
  1277. X    fclose(fd);
  1278. X    chown( buffer, userid, groupid);
  1279. X}
  1280. X
  1281. Xlong fsize(fd)
  1282. XFILE *fd;
  1283. X{
  1284. X    /** return the size of the current file pointed to by the given
  1285. X        file descriptor - see "bytes()" for the same function with
  1286. X        filenames instead of open files...
  1287. X    **/
  1288. X
  1289. X    struct stat buffer;
  1290. X
  1291. X    (void) fstat(fd, &buffer);
  1292. X
  1293. X    return( (long) buffer.st_size );
  1294. X}
  1295. SHAR_EOF
  1296. chmod 0444 src/file_util.c || echo "restore of src/file_util.c fails"
  1297. echo "x - extracting src/fileio.c (Text)"
  1298. sed 's/^X//' << 'SHAR_EOF' > src/fileio.c &&
  1299. X
  1300. Xstatic char rcsid[] = "@(#)$Id: fileio.c,v 2.16 89/03/25 21:46:21 syd Exp $";
  1301. X
  1302. X/*******************************************************************************
  1303. X *  The Elm Mail System  -  $Revision: 2.16 $   $State: Exp $
  1304. X *
  1305. X *             Copyright (c) 1986, 1987 Dave Taylor
  1306. X *             Copyright (c) 1988, 1989 USENET Community Trust
  1307. X *******************************************************************************
  1308. X * Bug reports, patches, comments, suggestions should be sent to:
  1309. X *
  1310. X *    Syd Weinstein, Elm Coordinator
  1311. X *    elm@dsinc.UUCP            dsinc!elm
  1312. X *
  1313. X *******************************************************************************
  1314. X * $Log:    fileio.c,v $
  1315. X * Revision 2.16  89/03/25  21:46:21  syd
  1316. X * Initial 2.2 Release checkin
  1317. X * 
  1318. X *
  1319. X ******************************************************************************/
  1320. X
  1321. X/** File I/O routines, including deletion from the folder! 
  1322. X
  1323. X**/
  1324. X
  1325. X#include "headers.h"
  1326. X#include <sys/types.h>
  1327. X#include <sys/stat.h>
  1328. X#include <ctype.h>
  1329. X#include <errno.h>
  1330. X
  1331. X#ifdef BSD
  1332. X#undef tolower
  1333. X#endif
  1334. X
  1335. Xextern int errno;
  1336. X
  1337. Xchar *error_name();
  1338. X
  1339. Xcopy_message(prefix, dest_file, remove_header, remote, update_status)
  1340. Xchar *prefix;
  1341. XFILE *dest_file;
  1342. Xint  remove_header, remote, update_status;
  1343. X{
  1344. X    /** Copy current message to destination file, with optional 'prefix' 
  1345. X        as the prefix for each line.  If remove_header is true, it will 
  1346. X        skip lines in the message until it finds the end of header line...
  1347. X        then it will start copying into the file... If remote is true
  1348. X        then it will append "remote from <hostname>" at the end of the
  1349. X        very first line of the file (for remailing) 
  1350. X
  1351. X        If "forwarding" is true then it'll do some nice things to
  1352. X        ensure that the forwarded message looks pleasant (e.g. remove
  1353. X        stuff like ">From " lines and "Received:" lines, and prefix
  1354. X        the entire message with "Forwarded message:\n" etc etc)
  1355. X
  1356. X        If "update_status" is true then it will write a new Status:
  1357. X        line at the end of the headers and not copy any existing one.
  1358. X    **/
  1359. X
  1360. X    char buffer[SLEN];
  1361. X    register struct header_rec *current_header = headers[current-1];
  1362. X    register int  lines, front_line, next_front,
  1363. X          in_header = 1, first_line = TRUE, ignoring = FALSE;
  1364. X    int    end_header = 0;
  1365. X
  1366. X      /** get to the first line of the message desired **/
  1367. X
  1368. X    if (fseek(mailfile, current_header->offset, 0) == -1) {
  1369. X       dprint(1, (debugfile, 
  1370. X        "ERROR: Attempt to seek %d bytes into file failed (%s)",
  1371. X        current_header->offset, "copy_message"));
  1372. X       error1("ELM [seek] failed trying to read %d bytes into file.",
  1373. X         current_header->offset);
  1374. X       return;
  1375. X    }
  1376. X
  1377. X    /* how many lines in message? */
  1378. X
  1379. X    lines = current_header->lines;
  1380. X
  1381. X    /* set up for forwarding just in case... */
  1382. X
  1383. X    if (forwarding)
  1384. X      remove_header = FALSE;
  1385. X
  1386. X    /* now while not EOF & still in message... copy it! */
  1387. X
  1388. X    next_front = TRUE;
  1389. X
  1390. X    while (lines) {
  1391. X      if (fgets(buffer, SLEN, mailfile) == NULL)
  1392. X        break;
  1393. X
  1394. X      front_line = next_front;
  1395. X
  1396. X      if(buffer[strlen(buffer)-1] == '\n') {
  1397. X    lines--;    /* got a full line */
  1398. X    next_front = TRUE;
  1399. X      }
  1400. X      else
  1401. X    next_front = FALSE;
  1402. X      
  1403. X      if (front_line && ignoring)
  1404. X    ignoring = whitespace(buffer[0]);
  1405. X
  1406. X      if (ignoring)
  1407. X    continue;
  1408. X
  1409. X      if (first_line && forwarding) {
  1410. X    first_line = FALSE;
  1411. X    fprintf(dest_file, "Forwarded message:\n\n");
  1412. X      }
  1413. X
  1414. X      /* are we still in the header? */
  1415. X
  1416. X      if (in_header && front_line) {
  1417. X    if (strlen(buffer) < 2) {
  1418. X      in_header = 0;
  1419. X      end_header = -1;
  1420. X    } else if ((!isspace(*buffer)) && strchr(buffer, ':') == NULL) {
  1421. X      in_header = 0;
  1422. X      end_header = 1;
  1423. X    }
  1424. X    if (end_header) {
  1425. X      if (update_status) {
  1426. X          if (isoff(current_header->status, NEW)) {
  1427. X        if (ison(current_header->status, UNREAD))
  1428. X          fprintf(dest_file, "%sStatus: O\n", prefix);
  1429. X        else    /* read */
  1430. X#ifdef BSD
  1431. X          fprintf(dest_file, "%sStatus: OR\n", prefix);
  1432. X#else
  1433. X          fprintf(dest_file, "%sStatus: RO\n", prefix);
  1434. X#endif
  1435. X        update_status = FALSE; /* do it only once */
  1436. X          }    /* else if NEW - indicate NEW with no Status: line. This is
  1437. X         * important if we resync a mailfile - we don't want
  1438. X         * NEW status lost when we copy each message out.
  1439. X         * It is the responsibility of the function that calls
  1440. X         * this function to unset NEW as appropriate to its
  1441. X         * reason for using this function to copy a message
  1442. X         */
  1443. X
  1444. X          if (end_header > 0)
  1445. X        fprintf(dest_file, "\n"); /* add the missing newline for RFC 822 */
  1446. X      }
  1447. X    }
  1448. X      }
  1449. X
  1450. X      if (in_header) {
  1451. X    /* Process checks while in header area */
  1452. X
  1453. X    if (remove_header) {
  1454. X      ignoring = TRUE;
  1455. X      continue;
  1456. X    }
  1457. X
  1458. X    /* add remote on to front? */
  1459. X    if (first_line && remote) {
  1460. X      no_ret(buffer);
  1461. X      fprintf(dest_file, "%s%s remote from %s\n",
  1462. X          prefix, buffer, hostname);
  1463. X      first_line = FALSE;
  1464. X      continue;
  1465. X    }
  1466. X
  1467. X    if (!forwarding) {
  1468. X      if(! first_word(buffer, "Status:")) {
  1469. X        fprintf(dest_file, "%s%s", prefix, buffer);
  1470. X        continue;
  1471. X      }
  1472. X      else if (update_status) {
  1473. X        ignoring = TRUE;
  1474. X        continue;    /* we will output a new Status: line later. */
  1475. X      } else {
  1476. X        fprintf(dest_file, "%sStatus: %s\n", prefix, buffer);
  1477. X        continue;
  1478. X      }
  1479. X    }
  1480. X    else { /* forwarding */
  1481. X
  1482. X      if (first_word(buffer, "Received:"   ) ||
  1483. X          first_word(buffer, ">From"       ) ||
  1484. X          first_word(buffer, "Status:"     ) ||
  1485. X          first_word(buffer, "Return-Path:"))
  1486. X          ignoring = TRUE;
  1487. X      else
  1488. X        fprintf(dest_file, "%s%s", prefix, buffer);
  1489. X    }
  1490. X      }
  1491. X      else { /* not in header */
  1492. X        /* Process checks that occur after the header area */
  1493. X
  1494. X    if(first_word(buffer, "From ") && (real_from(buffer, NULL))) {
  1495. X      dprint(1, (debugfile,
  1496. X         "\n*** Internal Problem...Tried to add the following;\n"));
  1497. X      dprint(1, (debugfile,
  1498. X         "  '%s'\nto output file (copy_message) ***\n", buffer));
  1499. X      break;    /* STOP NOW! */
  1500. X    }
  1501. X
  1502. X    fprintf(dest_file, "%s%s", prefix, buffer);
  1503. X      }
  1504. X    }
  1505. X    if (strlen(buffer) + strlen(prefix) > 1)
  1506. X      fprintf(dest_file, "\n");    /* blank line to keep mailx happy *sigh* */
  1507. X}
  1508. X
  1509. X
  1510. Xstatic struct stat saved_buf;
  1511. Xstatic char saved_fname[SLEN];
  1512. X
  1513. Xint
  1514. Xsave_file_stats(fname)
  1515. Xchar *fname;
  1516. X{
  1517. X    /* if fname exists, save the owner, group, mode and filename.
  1518. X     * otherwise flag nothing saved. Return 0 if saved, else -1.
  1519. X     */
  1520. X
  1521. X    if(stat(fname, &saved_buf) != -1) {
  1522. X      (void)strcpy(saved_fname, fname);
  1523. X      dprint(2, (debugfile,
  1524. X        "** saved stats for file owner = %d group = %d mode = %o %s **\n",
  1525. X        saved_buf.st_uid, saved_buf.st_gid, saved_buf.st_mode, fname));
  1526. X      return(0);
  1527. X    }
  1528. X    dprint(2, (debugfile,
  1529. X      "** couldn't save stats for file %s [errno=%d] **\n",
  1530. X      fname, errno));
  1531. X    return(-1);
  1532. X
  1533. X}
  1534. X
  1535. Xrestore_file_stats(fname)
  1536. Xchar *fname;
  1537. X{
  1538. X    /* if fname matches the saved file name, set the owner and group
  1539. X     * of fname to the saved owner, group and mode,
  1540. X     * else to the userid and groupid of the user and to 700.
  1541. X     * Return    -1 if the  either mode or owner/group not set
  1542. X     *        0 if the default values were used
  1543. X     *        1 if the saved values were used
  1544. X     */
  1545. X
  1546. X    int old_umask, i, new_mode, new_owner, new_group, ret_code;
  1547. X
  1548. X
  1549. X    new_mode = 0600;
  1550. X    new_owner = userid;
  1551. X    new_group = groupid;
  1552. X    ret_code = 0;
  1553. X
  1554. X    if(strcmp(fname, saved_fname) == 0) {
  1555. X      new_mode = saved_buf.st_mode;
  1556. X      new_owner = saved_buf.st_uid;
  1557. X      new_group = saved_buf.st_gid;
  1558. X      ret_code = 1;
  1559. X    }
  1560. X    dprint(2, (debugfile, "** %s file stats for %s **\n",
  1561. X      (ret_code ? "restoring" : "setting"), fname));
  1562. X
  1563. X    old_umask = umask(0);
  1564. X    if((i = chmod(fname, new_mode & 0777)) == -1)
  1565. X      ret_code = -1;
  1566. X
  1567. X    dprint(2, (debugfile, "** chmod(%s, %.3o) returns %d [errno=%d] **\n",
  1568. X        fname, new_mode & 0777, i, errno));
  1569. X
  1570. X    (void) umask(old_umask);
  1571. X
  1572. X#ifdef    BSD
  1573. X    /*
  1574. X     * Chown is restricted to root on BSD unix
  1575. X     */
  1576. X    (void) chown(fname, new_owner, new_group);
  1577. X#else
  1578. X    if((i = chown(fname, new_owner, new_group)) == -1)
  1579. X      ret_code = -1;
  1580. X
  1581. X    dprint(2, (debugfile, "** chown(%s, %d, %d) returns %d [errno=%d] **\n",
  1582. X           fname, new_owner, new_group, i, errno));
  1583. X#endif
  1584. X
  1585. X    return(ret_code);
  1586. X
  1587. X}
  1588. X
  1589. X/** and finally, here's something for that evil trick: site hiding **/
  1590. X
  1591. X#ifdef SITE_HIDING
  1592. X
  1593. Xint
  1594. Xis_a_hidden_user(specific_username)
  1595. Xchar *specific_username;
  1596. X{
  1597. X    /** Returns true iff the username is present in the list of
  1598. X       'hidden users' on the system.
  1599. X    **/
  1600. X    
  1601. X    FILE *hidden_users;
  1602. X    char  buffer[SLEN];
  1603. X
  1604. X        /* 
  1605. X    this line is deliberately inserted to ensure that you THINK
  1606. X    about what you're doing, and perhaps even contact the author
  1607. X    of Elm before you USE this option...
  1608. X        */
  1609. X
  1610. X    if ((hidden_users = fopen (HIDDEN_SITE_USERS,"r")) == NULL) {
  1611. X      dprint(1, (debugfile,
  1612. X          "Couldn't open hidden site file %s [%s]\n",
  1613. X          HIDDEN_SITE_USERS, error_name(errno)));
  1614. X      return(FALSE);
  1615. X    }
  1616. X
  1617. X    while (fscanf(hidden_users, "%s", buffer) != EOF)
  1618. X      if (strcmp(buffer, specific_username) == 0) {
  1619. X        dprint(3, (debugfile, "** Found user '%s' in hidden site file!\n",
  1620. X            specific_username));
  1621. X        fclose(hidden_users);
  1622. X        return(TRUE);
  1623. X      }
  1624. X
  1625. X    fclose(hidden_users);
  1626. X    dprint(3, (debugfile, 
  1627. X        "** Couldn't find user '%s' in hidden site file!\n",
  1628. X        specific_username));
  1629. X
  1630. X    return(FALSE);
  1631. X}
  1632. X
  1633. X#endif
  1634. SHAR_EOF
  1635. chmod 0444 src/fileio.c || echo "restore of src/fileio.c fails"
  1636. echo "x - extracting src/forms.c (Text)"
  1637. sed 's/^X//' << 'SHAR_EOF' > src/forms.c &&
  1638. X
  1639. Xstatic char rcsid[] = "@(#)$Id: forms.c,v 2.7 89/03/25 21:46:23 syd Exp $";
  1640. X
  1641. X/*******************************************************************************
  1642. X *  The Elm Mail System  -  $Revision: 2.7 $   $State: Exp $
  1643. X *
  1644. X *             Copyright (c) 1986, 1987 Dave Taylor
  1645. X *             Copyright (c) 1988, 1989 USENET Community Trust
  1646. X *******************************************************************************
  1647. X * Bug reports, patches, comments, suggestions should be sent to:
  1648. X *
  1649. X *    Syd Weinstein, Elm Coordinator
  1650. X *    elm@dsinc.UUCP            dsinc!elm
  1651. X *
  1652. X *******************************************************************************
  1653. X * $Log:    forms.c,v $
  1654. X * Revision 2.7  89/03/25  21:46:23  syd
  1655. X * Initial 2.2 Release checkin
  1656. X * 
  1657. X *
  1658. X ******************************************************************************/
  1659. X
  1660. X/** This set of files supports the 'forms' options (AT&T Mail Forms) to
  1661. X    the mail system.  The specs are drawn from a document from AT&T entitled
  1662. X    "Standard for Exchanging Forms on AT&T Mail", version 1.9.
  1663. X
  1664. X**/
  1665. X
  1666. X/** Some notes on the format of a FORM;
  1667. X
  1668. X    First off, in AT&T Mail parlance, this program only supports SIMPLE
  1669. X    forms, currently.  This means that while each form must have three
  1670. X     sections;
  1671. X
  1672. X        [options-section]
  1673. X        ***
  1674. X        [form-image]
  1675. X        ***
  1676. X        [rules-section]
  1677. X
  1678. X    this program will ignore the first and third sections completely.  The
  1679. X    program will assume that the user merely enteres the form-image section,
  1680. X    and will append and prepend the triple asterisk sequences that *MUST*
  1681. X    be part of the message.  The messages are also expected to have a 
  1682. X    specific header - "Content-Type: mailform" - which will be added on all
  1683. X    outbound mail and checked on inbound...
  1684. X**/
  1685. X
  1686. X#include "headers.h"
  1687. X#include <errno.h>
  1688. X
  1689. Xextern int errno;
  1690. X
  1691. Xchar *error_name(), *strcat(), *strcpy();
  1692. X
  1693. Xcheck_form_file(filename)
  1694. Xchar *filename;
  1695. X{
  1696. X    /** This routine returns the number of fields in the specified file,
  1697. X        or -1 if an error is encountered. **/
  1698. X
  1699. X    FILE *form;
  1700. X    char buffer[SLEN];
  1701. X    register int field_count = 0;
  1702. X
  1703. X    if ((form = fopen(filename, "r")) == NULL) {
  1704. X      error2("Error %s trying to open %s to check fields!",
  1705. X          error_name(errno), filename);
  1706. X      return(-1);
  1707. X    }
  1708. X    
  1709. X    while (fgets(buffer, SLEN, form) != NULL) {
  1710. X      field_count += occurances_of(COLON, buffer);
  1711. X    }
  1712. X
  1713. X    fclose(form);
  1714. X
  1715. X    return(field_count);
  1716. X}
  1717. X
  1718. Xformat_form(filename)
  1719. Xchar *filename;
  1720. X{
  1721. X    /** This routine accepts a validated file that is the middle 
  1722. X        section of a form message and prepends and appends the appropriate 
  1723. X        instructions.  It's pretty simple. 
  1724. X        This returns the number of forms in the file, or -1 on errors
  1725. X    **/
  1726. X    
  1727. X    FILE *form, *newform;
  1728. X    char  newfname[SLEN], buffer[SLEN];
  1729. X    register form_count = 0;
  1730. X
  1731. X    dprint(4, (debugfile, "Formatting form file '%s'\n", filename));
  1732. X
  1733. X    /** first off, let's open the files... **/
  1734. X
  1735. X    if ((form = fopen(filename, "r")) == NULL) {
  1736. X      error("Can't read the message to validate the form!");
  1737. X      dprint(1, (debugfile,
  1738. X              "** Error encountered opening file \"%s\" - %s (check_form) **\n",
  1739. X          filename, error_name(errno)));
  1740. X      return(-1);
  1741. X    }
  1742. X
  1743. X    sprintf(newfname, "%s%d", temp_form_file, getpid());
  1744. X
  1745. X    if ((newform = fopen(newfname, "w")) == NULL) {
  1746. X      error("Couldn't open newform file for form output!");
  1747. X      dprint(1, (debugfile, 
  1748. X              "** Error encountered opening file \"%s\" - %s (check_form) **\n",
  1749. X          newfname, error_name(errno)));
  1750. X      return(-1);
  1751. X    }
  1752. X
  1753. X    /** the required header... **/
  1754. X
  1755. X    /* these are actually the defaults, but let's be sure, okay? */
  1756. X
  1757. X    fprintf(newform, "WIDTH=78\nTYPE=SIMPLE\nOUTPUT=TEXT\n***\n");
  1758. X
  1759. X    /** and let's have some fun transfering the stuff across... **/
  1760. X
  1761. X    while (fgets(buffer, SLEN, form) != NULL) {
  1762. X      fputs(buffer, newform);
  1763. X      form_count += occurances_of(COLON, buffer);
  1764. X    }
  1765. X
  1766. X    fprintf(newform, "***\n");    /* that closing bit! */
  1767. X
  1768. X    fclose(form);
  1769. X    fclose(newform);
  1770. X
  1771. X    if (form_count > 0) {
  1772. X      if (unlink(filename) != 0) {
  1773. X        error2("Error %s unlinking file %s.", error_name(errno), filename);
  1774. X        return(-1);
  1775. X      }
  1776. X      if (link(newfname, filename)) {
  1777. X        error3("Error %s linking %s to %s.", error_name(errno), 
  1778. X            newfname, filename);
  1779. X        return(-1);
  1780. X      }
  1781. X    }
  1782. X
  1783. X    if (unlink(newfname)) {
  1784. X      error2("Error %s unlinking file %s.", error_name(errno), newfname);
  1785. X      return(-1);    
  1786. X    }
  1787. X
  1788. X    return(form_count);
  1789. SHAR_EOF
  1790. echo "End of part 14"
  1791. echo "File src/forms.c is continued in part 15"
  1792. echo "15" > s2_seq_.tmp
  1793. exit 0
  1794.  
  1795.