home *** CD-ROM | disk | FTP | other *** search
/ ftp.freefriends.org / ftp.freefriends.org.tar / ftp.freefriends.org / arnold / Source / mush.rstevens.tar.gz / mush.tar / pop.c < prev    next >
C/C++ Source or Header  |  1992-10-30  |  22KB  |  986 lines

  1. /*
  2.  * pop.c: client routines for talking to a POP3-protocol post-office
  3.  * server
  4.  *
  5.  * Jonathan Kamens
  6.  * August 13, 1991
  7.  */
  8.  
  9. #ifdef POP3_SUPPORT
  10.  
  11. #include <sys/types.h>
  12. #include <netinet/in.h>
  13. #include <sys/socket.h>
  14. #include "pop.h"
  15.  
  16. extern int errno;
  17.  
  18. #ifdef KERBEROS
  19. #include <krb.h>
  20. #include <des.h>
  21. #ifdef sun
  22. #include <malloc.h>
  23. #else /* !sun */
  24. extern char *
  25. malloc( /* unsigned */ );
  26. extern void 
  27. free( /* char * */ );
  28. #endif /* sun */
  29. #endif /* KERBEROS */
  30.  
  31. #ifdef HESIOD
  32. #include <hesiod.h>
  33. /*
  34.  * It really shouldn't be necessary to put this declaration here, but
  35.  * the version of hesiod.h that Athena has installed in release 7.2
  36.  * doesn't declare this function; I don't know if the 7.3 version of
  37.  * hesiod.h does.
  38.  */
  39. extern struct servent *
  40. hes_getservbyname( /* char *, char * */ );
  41. #endif /* HESIOD */
  42.  
  43. #include <pwd.h>
  44. #include <string.h>
  45. #include <strings.h>
  46.  
  47. extern char *
  48. strstr( /* char *, char * */ );
  49.  
  50. #include <netdb.h>
  51. #include <errno.h>
  52. #include <stdio.h>
  53.  
  54. extern char *sys_errlist[];
  55. #define strerror(eno)    sys_errlist[eno]
  56.  
  57. extern char *
  58. getenv( /* char * */ );
  59. extern char *
  60. getlogin( /* void */ );
  61. extern char *
  62. getpass( /* char * */ );
  63. extern int 
  64. getuid( /* void */ );
  65. extern void 
  66. bzero( /* char *, int */ ), bcopy( /* char *, char *, int */ );
  67. extern int 
  68. socket( /* int, int, int */ );
  69. extern int 
  70. connect( /* int, struct sockaddr *, int */ );
  71. extern int 
  72. close( /* int */ );
  73. extern int 
  74. read( /* int, char *, int */ ), write( /* int, char *, int */ );
  75. extern int 
  76. atoi( /* char * */ );
  77.  
  78. #if !(defined(vax) && defined(__GNUC__))
  79. extern int 
  80. fprintf( /* FILE *, char *, ... */ );
  81. #endif /* !(vax && __GNUC__) */
  82.  
  83. #ifdef KERBEROS
  84. extern int 
  85. krb_sendauth( /* long, int, KTEXT, char *, char *, char *, u_long, MSG_DAT **,
  86.         CREDENTIALS *, Key_schedule, struct sockaddr_in *,
  87.         struct sockaddr_in *, char * */ );
  88. extern char *
  89. krb_realmofhost( /* char * */ );
  90. #endif /* KERBEROS */
  91.  
  92. extern int h_errno;
  93.  
  94. static int socket_connection();
  95. static char *getline();
  96. static int sendline();
  97. static int fullwrite();
  98. static int getok();
  99. static int gettermination();
  100.  
  101. #define ERROR_MAX 80    /* a pretty arbitrary size */
  102. #define POP_PORT 110
  103. #define KPOP_PORT 1109
  104. #define POP_SERVICE "pop"
  105. #define KPOP_SERVICE "kpop"
  106.  
  107. char pop_error[ERROR_MAX];
  108. int pop_debug = 0;
  109.  
  110. /*
  111.  * Function: pop_open(char *host, char *username, char *password,
  112.  *               int flags)
  113.  *
  114.  * Purpose: Establishes a connection with a post-office server, and
  115.  *     completes the authorization portion of the session.
  116.  *
  117.  * Arguments:
  118.  *     host    The server host with which the connection should be
  119.  *         established.  Optional.  If omitted, internal
  120.  *         heuristics will be used to determine the server host,
  121.  *         if possible.
  122.  *     username
  123.  *         The username of the mail-drop to access.  Optional.
  124.  *         If omitted, internal heuristics will be used to
  125.  *         determine the username, if possible.
  126.  *     password
  127.  *         The password to use for authorization.  If omitted,
  128.  *         internal heuristics will be used to determine the
  129.  *         password, if possible.
  130.  *     flags    A bit mask containing flags controlling certain
  131.  *         functions of the routine.  Valid flags are defined in
  132.  *         the file pop.h
  133.  *
  134.  * Return value: Upon successful establishment of a connection, a
  135.  *     non-null PopServer will be returned.  Otherwise, null will be
  136.  *     returned, and the string variable pop_error will contain an
  137.  *     explanation of the error.
  138.  */
  139. PopServer 
  140. pop_open(host, username, password, flags)
  141. char *host, *username, *password;
  142. int flags;
  143. {
  144.     int sock;
  145.     PopServer server;
  146.  
  147.     /* Determine the user name */
  148.     if (!username) {
  149.     username = getenv("USER");
  150.     if (!(username && *username)) {
  151.         username = getlogin();
  152.         if (!(username && *username)) {
  153.         struct passwd *passwd;
  154.  
  155.         passwd = getpwuid(getuid());
  156.         if (passwd && passwd->pw_name && *passwd->pw_name) {
  157.             username = passwd->pw_name;
  158.         } else {
  159.             strcpy(pop_error, "Could not determine username");
  160.             return (0);
  161.         }
  162.         }
  163.     }
  164.     }
  165.     /* Determine the mail host */
  166. #ifdef HESIOD
  167.     if ((!host) && (!(flags & POP_NO_HESIOD))) {
  168.     struct hes_postoffice *office;
  169.  
  170.     office = hes_getmailhost(username);
  171.     if (office && office->po_type && (!strcmp(office->po_type, "POP"))
  172.         && office->po_name && *office->po_name && office->po_host
  173.         && *office->po_host) {
  174.         host = office->po_host;
  175.         username = office->po_name;
  176.     }
  177.     }
  178. #endif
  179.     if (!host) {
  180.     host = getenv("MAILHOST");
  181.     if (!host) {
  182.         strcpy(pop_error, "Could not determine POP server");
  183.         return (0);
  184.     }
  185.     }
  186.     /* Determine the password */
  187. #ifdef KERBEROS
  188. #define DONT_NEED_PASSWORD (! (flags & POP_NO_KERBEROS))
  189. #else
  190. #define DONT_NEED_PASSWORD 0
  191. #endif
  192.  
  193.     /* Modified to return password if possible -- Bart */
  194.     if ((!password || !*password) && (!DONT_NEED_PASSWORD)) {
  195.     if (!(flags & POP_NO_GETPASS)) {
  196.         char *p = getpass("Enter POP password:");
  197.         if (p && password)
  198.         (void) strcpy(password, p);
  199.         password = p;
  200.     }
  201.     if (!password) {
  202.         strcpy(pop_error, "Could not determine POP password");
  203.         return (0);
  204.     }
  205.     }
  206.     if (password)
  207.     flags |= POP_NO_KERBEROS;
  208.     else
  209.     password = username;
  210.  
  211.     sock = socket_connection(host, flags);
  212.     if (sock == -1)
  213.     return (0);
  214.  
  215.     server = (PopServer) malloc(sizeof(struct _PopServer));
  216.     if (!server) {
  217.     strcpy(pop_error, "Out of memory in pop_open");
  218.     return (0);
  219.     }
  220.     server->file = sock;
  221.     server->data = 0;
  222.     server->dp = server->buffer;
  223.  
  224.     if (getok(server))
  225.     return (0);
  226.  
  227.     /*
  228.      * I really shouldn't use the pop_error variable like this, but.... 
  229.      */
  230.     if (strlen(username) > ERROR_MAX - 6) {
  231.     pop_close(server);
  232.     strcpy(pop_error,
  233.         "Username too long; recompile pop.c with larger ERROR_MAX");
  234.     return (0);
  235.     }
  236.     sprintf(pop_error, "USER %s", username);
  237.  
  238.     if (sendline(server, pop_error) || getok(server)) {
  239.     return (0);
  240.     }
  241.     if (strlen(password) > ERROR_MAX - 6) {
  242.     pop_close(server);
  243.     strcpy(pop_error,
  244.         "Password too long; recompile pop.c with larger ERROR_MAX");
  245.     return (0);
  246.     }
  247.     sprintf(pop_error, "PASS %s", password);
  248.  
  249.     if (sendline(server, pop_error) || getok(server)) {
  250.     return (0);
  251.     }
  252.     return (server);
  253. }
  254.  
  255. /*
  256.  * Function: pop_stat
  257.  *
  258.  * Purpose: Issue the STAT command to the server and return (in the
  259.  *     value parameters) the number of messages in the maildrop and
  260.  *     the total size of the maildrop.
  261.  *
  262.  * Return value: 0 on success, or non-zero with an error in pop_error
  263.  *     in failure.
  264.  *
  265.  * Side effects: Closes the connection on failure.
  266.  */
  267. int 
  268. pop_stat(server, count, size)
  269. PopServer server;
  270. int *count, *size;
  271. {
  272.     char *fromserver;
  273.  
  274.     if (sendline(server, "STAT") || (!(fromserver = getline(server))))
  275.     return (-1);
  276.  
  277.     if (strncmp(fromserver, "+OK ", 4)) {
  278.     strcpy(pop_error, "Unexpected response from POP server in pop_stat");
  279.     pop_close();
  280.     return (-1);
  281.     }
  282.     *count = atoi(&fromserver[4]);
  283.  
  284.     fromserver = index(&fromserver[4], ' ');
  285.     if (!fromserver) {
  286.     strcpy(pop_error,
  287.         "Badly formatted response from server in pop_stat");
  288.     pop_close(server);
  289.     return (-1);
  290.     }
  291.     *size = atoi(fromserver + 1);
  292.  
  293.     return (0);
  294. }
  295.  
  296. /*
  297.  * Function: pop_list
  298.  *
  299.  * Purpose: Performs the POP "list" command and returns (in value
  300.  *     parameters) two malloc'd zero-terminated arrays -- one of
  301.  *     message IDs, and a parallel one of sizes.
  302.  *
  303.  * Arguments:
  304.  *     server    The pop connection to talk to.
  305.  *     message    The number of the one message about which to get
  306.  *         information, or 0 to get information about all
  307.  *         messages.
  308.  *
  309.  * Return value: 0 on success, non-zero with error in pop_error on
  310.  *     failure.
  311.  *
  312.  * Side effects: Closes the connection on error.
  313.  */
  314. int 
  315. pop_list(server, message, IDs, sizes)
  316. PopServer server;
  317. int message, **IDs, **sizes;
  318. {
  319.     int how_many, i;
  320.     char *fromserver;
  321.  
  322.     if (message)
  323.     how_many = 1;
  324.     else {
  325.     int count, size;
  326.  
  327.     if (pop_stat(server, &count, &size))
  328.         return (-1);
  329.     how_many = count;
  330.     }
  331.  
  332.     *IDs = (int *) malloc((how_many + 1) * sizeof(int));
  333.     *sizes = (int *) malloc((how_many + 1) * sizeof(int));
  334.     if (!(*IDs && *sizes)) {
  335.     strcpy(pop_error, "Out of memory in pop_list");
  336.     pop_close(server);
  337.     return (-1);
  338.     }
  339.     if (message) {
  340.     sprintf(pop_error, "LIST %d", message);
  341.     if (sendline(server, pop_error)) {
  342.         free((char *) *IDs);
  343.         free((char *) *sizes);
  344.         return (-1);
  345.     }
  346.     if (!(fromserver = getline(server))) {
  347.         free((char *) *IDs);
  348.         free((char *) *sizes);
  349.         return (-1);
  350.     }
  351.     if (strncmp(fromserver, "+OK ", 4)) {
  352.         if (!strncmp(fromserver, "-ERR", 4))
  353.         strncpy(pop_error, fromserver, ERROR_MAX);
  354.         else
  355.         strcpy(pop_error,
  356.             "Unexpected response from server in pop_list");
  357.         free((char *) *IDs);
  358.         free((char *) *sizes);
  359.         pop_close(server);
  360.         return (-1);
  361.     }
  362.     (*IDs)[0] = atoi(&fromserver[4]);
  363.     fromserver = index(&fromserver[4], ' ');
  364.     if (!fromserver) {
  365.         strcpy(pop_error,
  366.             "Badly formatted response from server in pop_list");
  367.         free((char *) *IDs);
  368.         free((char *) *sizes);
  369.         pop_close(server);
  370.         return (-1);
  371.     }
  372.     (*sizes)[0] = atoi(fromserver);
  373.     (*IDs)[1] = (*sizes)[1] = 0;
  374.     return (0);
  375.     } else {
  376.     if (sendline(server, "LIST") || getok(server)) {
  377.         free((char *) *IDs);
  378.         free((char *) *sizes);
  379.         return (-1);
  380.     }
  381.     for (i = 0; i < how_many; i++) {
  382.         if (!(fromserver = getline(server))) {
  383.         free((char *) *IDs);
  384.         free((char *) *sizes);
  385.         return (-1);
  386.         }
  387.         (*IDs)[i] = atoi(fromserver);
  388.         fromserver = index(fromserver, ' ');
  389.         if (!fromserver) {
  390.         strcpy(pop_error,
  391.             "Badly formatted response from server in pop_list");
  392.         free((char *) *IDs);
  393.         free((char *) *sizes);
  394.         pop_close(server);
  395.         return (-1);
  396.         }
  397.         (*sizes)[i] = atoi(fromserver);
  398.     }
  399.     if (gettermination(server)) {
  400.         free((char *) *IDs);
  401.         free((char *) *sizes);
  402.         return (-1);
  403.     }
  404.     (*IDs)[i] = (*sizes)[i] = 0;
  405.     return (0);
  406.     }
  407. }
  408.  
  409. /*
  410.  * Function: pop_retrieve
  411.  *
  412.  * Purpose: Retrieve a specified message from the maildrop.
  413.  *
  414.  * Arguments:
  415.  *     server    The server to retrieve from.
  416.  *     message    The message number to retrieve.
  417.  *
  418.  * Return value: A string pointing to the message, if successful, or
  419.  *     null with pop_error set if not.
  420.  *
  421.  * Side effects: Closes the connection on error.
  422.  */
  423. char *
  424. pop_retrieve(server, message)
  425. PopServer server;
  426. int message;
  427. {
  428.     int *IDs, *sizes;
  429.     char *ptr, *cp;
  430.  
  431.     if (pop_list(server, message, &IDs, &sizes))
  432.     return (0);
  433.  
  434.     sprintf(pop_error, "RETR %d", message);
  435.     if (sendline(server, pop_error) || getok(server)) {
  436.     return (0);
  437.     }
  438.     cp = ptr = (char *) malloc(sizes[0]+1);
  439.     free((char *) IDs);
  440.     free((char *) sizes);
  441.  
  442.     if (!ptr) {
  443.     strcpy(pop_error, "Out of memory in pop_retrieve");
  444.     pop_close(server);
  445.     return (0);
  446.     }
  447.     *cp = '\0';
  448.  
  449.     while (1) {
  450.     char *fromserver = getline(server);
  451.     int size;
  452.  
  453.     if (!fromserver) {
  454.         free(ptr);
  455.         pop_close(server);
  456.         return (0);
  457.     }
  458.     if (fromserver[0] == '.') {
  459.         if (!fromserver[1]) {
  460.         return (ptr);
  461.         }
  462.         fromserver++;
  463.     }
  464.     size = strlen(fromserver);
  465.     bcopy(fromserver, cp, size + 1);
  466.     cp += size;
  467.     *cp++ = '\n';
  468.     *cp = '\0';
  469.     }
  470. }
  471.  
  472. /* Function: pop_delete
  473.  *
  474.  * Purpose: Delete a specified message.
  475.  *
  476.  * Arguments:
  477.  *     server    Server from which to delete the message.
  478.  *     message    Message to delete.
  479.  *
  480.  * Return value: 0 on success, non-zero with error in pop_error
  481.  *     otherwise.
  482.  */
  483. int 
  484. pop_delete(server, message)
  485. PopServer server;
  486. int message;
  487. {
  488.     sprintf(pop_error, "DELE %d", message);
  489.  
  490.     if (sendline(server, pop_error) || getok(server)) {
  491.     pop_close(server);
  492.     return (-1);
  493.     }
  494.     return (0);
  495. }
  496.  
  497. /*
  498.  * Function: pop_noop
  499.  *
  500.  * Purpose: Send a noop command to the server.
  501.  *
  502.  * Argument:
  503.  *     server    The server to send to.
  504.  *
  505.  * Return value: 0 on success, non-zero with error in pop_error
  506.  *     otherwise.
  507.  *
  508.  * Side effects: Closes connection on error.
  509.  */
  510. int 
  511. pop_noop(server)
  512. PopServer server;
  513. {
  514.     if (sendline(server, "NOOP") || getok(server))
  515.     return (-1);
  516.  
  517.     return (0);
  518. }
  519.  
  520. /*
  521.  * Function: pop_last
  522.  *
  523.  * Purpose: Find out the highest seen message from the server.
  524.  *
  525.  * Arguments:
  526.  *     server    The server.
  527.  *
  528.  * Return value: If successful, the highest seen message, which is
  529.  *     greater than or equal to 0.  Otherwise, a negative number with
  530.  *     the error explained in pop_error.
  531.  *
  532.  * Side effects: Closes the connection on error.
  533.  */
  534. int 
  535. pop_last(server)
  536. PopServer server;
  537. {
  538.     char *fromserver;
  539.  
  540.     if (sendline(server, "LAST"))
  541.     return (-1);
  542.  
  543.     if (!(fromserver = getline(server)))
  544.     return (-1);
  545.  
  546.     if (!strncmp(fromserver, "-ERR", 4)) {
  547.     strncpy(pop_error, fromserver, ERROR_MAX);
  548.     pop_close(server);
  549.     return (-1);
  550.     } else if (strncmp(fromserver, "+OK", 3)) {
  551.     strcpy(pop_error, "Unexpected response from server in pop_last");
  552.     pop_close(server);
  553.     return (-1);
  554.     } else {
  555.     return (atoi(&fromserver[4]));
  556.     }
  557. }
  558.  
  559. /*
  560.  * Function: pop_reset
  561.  *
  562.  * Purpose: Reset the server to its initial connect state
  563.  *
  564.  * Arguments:
  565.  *     server    The server.
  566.  *
  567.  * Return value: 0 for success, non-0 with error in pop_error
  568.  *     otherwise.
  569.  *
  570.  * Side effects: Closes the connection on error.
  571.  */
  572. int 
  573. pop_reset(server)
  574. PopServer server;
  575. {
  576.     if (sendline(server, "RSET") || getok(server)) {
  577.     pop_close(server);
  578.     return (-1);
  579.     }
  580.     return (0);
  581. }
  582.  
  583. /*
  584.  * Function: pop_quit
  585.  *
  586.  * Purpose: Quit the connection to the server,
  587.  *
  588.  * Arguments:
  589.  *     server    The server to quit.
  590.  *
  591.  * Return value: 0 for success, non-zero otherwise with error in
  592.  *     pop_error.
  593.  *
  594.  * Side Effects: The PopServer passed in is unuseable after this
  595.  *     function is called, even if an error occurs.
  596.  */
  597. int 
  598. pop_quit(server)
  599. PopServer server;
  600. {
  601.     int ret = 0;
  602.  
  603.     if (sendline(server, "QUIT") || getok(server))
  604.     ret = -1;
  605.  
  606.     close(server->file);
  607.     free((char *) server);
  608.  
  609.     return (ret);
  610. }
  611.  
  612. /*
  613.  * Function: socket_connection
  614.  *
  615.  * Purpose: Opens the network connection with the mail host, without
  616.  *     doing any sort of I/O with it or anything.
  617.  *
  618.  * Arguments:
  619.  *     host    The host to which to connect.
  620.  *    flags    Option flags.
  621.  *     
  622.  * Return value: A file descriptor indicating the connection, or -1
  623.  *     indicating failure, in which case an error has been copied
  624.  *     into pop_error.
  625.  */
  626. static int 
  627. socket_connection(host, flags)
  628. char *host;
  629. int flags;
  630. {
  631.     struct hostent *hostent;
  632.     struct servent *servent;
  633.     struct sockaddr_in addr;
  634.     char found_port = 0;
  635.     char *service;
  636.     int sock;
  637.  
  638. #ifdef KERBEROS
  639.     KTEXT ticket;
  640.     MSG_DAT msg_data;
  641.     CREDENTIALS cred;
  642.     Key_schedule schedule;
  643.     int rem;
  644.  
  645. #endif
  646.  
  647.     int try_count = 0;
  648.  
  649.     do {
  650.     hostent = gethostbyname(host);
  651.     try_count++;
  652.     if ((!hostent) && ((h_errno != TRY_AGAIN) || (try_count == 5))) {
  653.         strcpy(pop_error, "Could not determine POP server's address");
  654.         return (-1);
  655.     }
  656.     } while (!hostent);
  657.  
  658.     bzero((char *) &addr, sizeof(addr));
  659.     addr.sin_family = AF_INET;
  660.  
  661. #ifdef KERBEROS
  662.     service = (flags & POP_NO_KERBEROS) ? POP_SERVICE : KPOP_SERVICE;
  663. #else
  664.     service = POP_SERVICE;
  665. #endif
  666.  
  667. #ifdef HESIOD
  668.     if (!(flags & POP_NO_HESIOD)) {
  669.     servent = hes_getservbyname(service, "tcp");
  670.     if (servent) {
  671.         addr.sin_port = servent->s_port;
  672.         found_port = 1;
  673.     }
  674.     }
  675. #endif
  676.     if (!found_port) {
  677.     servent = getservbyname(service, "tcp");
  678.     if (servent) {
  679.         addr.sin_port = servent->s_port;
  680.     } else {
  681. #ifdef KERBEROS
  682.         addr.sin_port = htons((flags & POP_NO_KERBEROS) ?
  683.             POP_PORT : KPOP_PORT);
  684. #else
  685.         addr.sin_port = htons(POP_PORT);
  686. #endif
  687.     }
  688.     }
  689. #define SOCKET_ERROR "Could not create socket for POP connection: "
  690.  
  691.     sock = socket(PF_INET, SOCK_STREAM, 0);
  692.     if (sock < 0) {
  693.     strcpy(pop_error, SOCKET_ERROR);
  694.     strncat(pop_error, strerror(errno),
  695.         ERROR_MAX - sizeof(SOCKET_ERROR));
  696.     return (-1);
  697.  
  698.     }
  699.     while (*hostent->h_addr_list) {
  700.     bcopy(*hostent->h_addr_list, (char *) &addr.sin_addr,
  701.         hostent->h_length);
  702.     if (!connect(sock, (struct sockaddr *) & addr, sizeof(addr)))
  703.         break;
  704.     hostent->h_addr_list++;
  705.     }
  706.  
  707. #define CONNECT_ERROR "Could not connect to POP server: "
  708.  
  709.     if (!*hostent->h_addr_list) {
  710.     (void) close(sock);
  711.     strcpy(pop_error, CONNECT_ERROR);
  712.     strncat(pop_error, strerror(errno),
  713.         ERROR_MAX - sizeof(CONNECT_ERROR));
  714.     return (-1);
  715.  
  716.     }
  717. #ifdef KERBEROS
  718.     if (!(flags & POP_NO_KERBEROS)) {
  719.     ticket = (KTEXT) malloc(sizeof(KTEXT_ST));
  720.     rem = krb_sendauth(0L, sock, ticket, "pop", hostent->h_name,
  721.         (char *) krb_realmofhost(hostent->h_name),
  722.         (unsigned long) 0, &msg_data, &cred, schedule,
  723.         (struct sockaddr_in *) 0,
  724.         (struct sockaddr_in *) 0,
  725.         "KPOPV0.1");
  726.     free((char *) ticket);
  727.     if (rem != KSUCCESS) {
  728. #define KRB_ERROR "Kerberos error connecting to POP server: "
  729.         strcpy(pop_error, KRB_ERROR);
  730.         strncat(pop_error, krb_err_txt[rem],
  731.             ERROR_MAX - sizeof(KRB_ERROR));
  732.         (void) close(sock);
  733.         return (-1);
  734.     }
  735.     }
  736. #endif    /* KERBEROS */
  737.  
  738.     return (sock);
  739. }/* socket_connection */
  740.  
  741. /*
  742.  * Function: getline
  743.  *
  744.  * Purpose: Get a line of text from the connection and return a
  745.  *     pointer to it.  The carriage return and linefeed at the end of
  746.  *     the line are stripped, but periods at the beginnings of lines
  747.  *     are NOT dealt with in any special way.
  748.  *
  749.  * Arguments:
  750.  *     server    The server from which to get the line of text.
  751.  *
  752.  * Returns: A non-null pointer if successful, or a null pointer on any
  753.  *     error, with an error message copied into pop_error.
  754.  *
  755.  * Notes: The line returned is overwritten with each call to getline.
  756.  *
  757.  * Side effects: Closes the connection on error.
  758.  */
  759. static char *
  760. getline(server)
  761. PopServer server;
  762. {
  763. #define GETLINE_ERROR "Error reading from server: "
  764.  
  765.     int ret;
  766.  
  767.     if (server->data) {
  768.     char *cp = strstr(server->dp, "\r\n");
  769.  
  770.     if (cp) {
  771.         char *found;
  772.  
  773.         *cp = '\0';
  774.         server->data -= (&cp[2] - server->dp);
  775.         found = server->dp;
  776.         server->dp = &cp[2];
  777.  
  778.         if (pop_debug)
  779.         fprintf(stderr, "<<< %s\n", found);
  780.         return (found);
  781.     } else {
  782.         bcopy(server->dp, server->buffer, server->data);
  783.         server->dp = server->buffer;
  784.     }
  785.     } else {
  786.     server->dp = server->buffer;
  787.     }
  788.  
  789.     while (server->data < GETLINE_MAX) {
  790.     ret = read(server->file, &server->buffer[server->data],
  791.         GETLINE_MAX - server->data);
  792.     if (ret < 0) {
  793.         strcpy(pop_error, GETLINE_ERROR);
  794.         strncat(pop_error, strerror(errno),
  795.             GETLINE_MAX - sizeof(GETLINE_ERROR));
  796.         pop_close(server);
  797.         return (0);
  798.     } else if (ret == 0) {
  799.         strcpy(pop_error, "Unexpected EOF from server in getline");
  800.         pop_close(server);
  801.         return (0);
  802.     } else {
  803.         char *cp = strstr(server->buffer, "\r\n");
  804.  
  805.         server->data += ret;
  806.  
  807.         if (cp) {
  808.         *cp = '\0';
  809.         server->data -= (&cp[2] - server->dp);
  810.         server->dp = &cp[2];
  811.  
  812.         if (pop_debug)
  813.             fprintf(stderr, "<<< %s\n", server->buffer);
  814.         return (server->buffer);
  815.         }
  816.     }
  817.     }
  818.  
  819.     strcpy(pop_error, "Line too long reading from server; compile pop.c with larger GETLINE_MAX");
  820.     pop_close(server);
  821.     return (0);
  822. }
  823.  
  824. /*
  825.  * Function: sendline
  826.  *
  827.  * Purpose: Sends a line of text to the POP server.  The line of text
  828.  *     passed into this function should NOT have the carriage return
  829.  *     and linefeed on the end of it.  Periods at beginnings of lines
  830.  *     will NOT be treated specially by this function.
  831.  *
  832.  * Arguments:
  833.  *     server    The server to which to send the text.
  834.  *     line    The line of text to send.
  835.  *
  836.  * Return value: Upon successful completion, a value of 0 will be
  837.  *     returned.  Otherwise, a non-zero value will be returned, and
  838.  *     an error will be copied into pop_error.
  839.  *
  840.  * Side effects: Closes the connection on error.
  841.  */
  842. static int 
  843. sendline(server, line)
  844. PopServer server;
  845. char *line;
  846. {
  847. #define SENDLINE_ERROR "Error writing to POP server: "
  848.     int ret;
  849.  
  850.     ret = fullwrite(server->file, line, strlen(line));
  851.     if (ret >= 0) {    /* 0 indicates that a blank line was written */
  852.     ret = fullwrite(server->file, "\r\n", 2);
  853.     }
  854.     if (ret < 0) {
  855.     pop_close(server);
  856.     strcpy(pop_error, SENDLINE_ERROR);
  857.     strncat(pop_error, strerror(errno),
  858.         ERROR_MAX - sizeof(SENDLINE_ERROR));
  859.     return (ret);
  860.     }
  861.     if (pop_debug)
  862.     fprintf(stderr, ">>> %s\n", line);
  863.  
  864.     return (0);
  865. }
  866.  
  867. /*
  868.  * Procedure: fullwrite
  869.  *
  870.  * Purpose: Just like write, but keeps trying until the entire string
  871.  *     has been written.
  872.  *
  873.  * Return value: Same as write.  Pop_error is not set.
  874.  */
  875. static int 
  876. fullwrite(fd, buf, nbytes)
  877. int fd;
  878. char *buf;
  879. int nbytes;
  880. {
  881.     char *cp;
  882.     int ret;
  883.  
  884.     cp = buf;
  885.     while ((ret = write(fd, cp, nbytes)) > 0) {
  886.     cp += ret;
  887.     nbytes -= ret;
  888.     }
  889.  
  890.     return (ret);
  891. }
  892.  
  893. /*
  894.  * Procedure getok
  895.  *
  896.  * Purpose: Reads a line from the server.  If the return indicator is
  897.  *     positive, return with a zero exit status.  If not, return with
  898.  *     a negative exit status.
  899.  *
  900.  * Arguments:
  901.  *     server    The server to read from.
  902.  * 
  903.  * Returns: 0 for success, else for failure and puts error in pop_error.
  904.  *
  905.  * Side effects: Closes the connection on error.
  906.  */
  907. static int 
  908. getok(server)
  909. PopServer server;
  910. {
  911.     char *fromline;
  912.  
  913.     if (!(fromline = getline(server))) {
  914.     return (-1);
  915.     }
  916.     if (!strncmp(fromline, "+OK", 3))
  917.     return (0);
  918.     else if (!strncmp(fromline, "-ERR", 4)) {
  919.     strncpy(pop_error, fromline, ERROR_MAX);
  920.     pop_error[ERROR_MAX - 1] = '\0';
  921.     pop_close(server);
  922.     return (-1);
  923.     } else {
  924.     strcpy(pop_error,
  925.         "Unknown response from server; expecting +OK or -ERR");
  926.     pop_close(server);
  927.     return (-1);
  928.     }
  929. }
  930.  
  931. /*
  932.  * Function: gettermination
  933.  *
  934.  * Purpose: Gets the next line and verifies that it is a termination
  935.  *     line (nothing but a dot).
  936.  *
  937.  * Return value: 0 on success, non-zero with pop_error set on error.
  938.  *
  939.  * Side effects: Closes the connection on error.
  940.  */
  941. static int 
  942. gettermination(server)
  943. PopServer server;
  944. {
  945.     char *fromserver;
  946.  
  947.     fromserver = getline(server);
  948.     if (!fromserver)
  949.     return (-1);
  950.  
  951.     if (strcmp(fromserver, ".")) {
  952.     strcpy(pop_error,
  953.         "Unexpected response from server in gettermination");
  954.     pop_close(server);
  955.     return (-1);
  956.     }
  957.     return (0);
  958. }
  959.  
  960. /*
  961.  * Function pop_close
  962.  *
  963.  * Purpose: Close a pop connection, sending a "RSET" command to try to
  964.  *     preserve any changes that were made and a "QUIT" command to
  965.  *     try to get the server to quit, but ignoring any responses that
  966.  *     are received.
  967.  *
  968.  * Side effects: The server is unuseable after this function returns.
  969.  *     Changes made to the maildrop since the session was started (or
  970.  *     since the last pop_reset) may be lost.
  971.  */
  972. void 
  973. pop_close(server)
  974. PopServer server;
  975. {
  976.     sendline(server, "RSET");
  977.     sendline(server, "QUIT");
  978.  
  979.     close(server->file);
  980.     free((char *) server);
  981.  
  982.     return;
  983. }
  984.  
  985. #endif /* POP3_SUPPORT */
  986.