home *** CD-ROM | disk | FTP | other *** search
/ ftp.wwiv.com / ftp.wwiv.com.zip / ftp.wwiv.com / pub / OFFLINE / UQWK18.ZIP / UQWK18.TAR / nntplib.c < prev    next >
C/C++ Source or Header  |  1993-07-29  |  14KB  |  639 lines

  1. #ifndef lint
  2. static char    *rcsid = "@(#)$Header: clientlib.c,v 1.16 91/01/12 15:12:37 sob Exp $";
  3. #endif
  4.  
  5. /*
  6.  * This software is Copyright 1991 by Stan Barber. 
  7.  *
  8.  * Permission is hereby granted to copy, reproduce, redistribute or otherwise
  9.  * use this software as long as: there is no monetary profit gained
  10.  * specifically from the use or reproduction or this software, it is not
  11.  * sold, rented, traded or otherwise marketed, and this copyright notice is
  12.  * included prominently in any copy made. 
  13.  *
  14.  * The author make no claims as to the fitness or correctness of this software
  15.  * for any use whatsoever, and it is provided as is. Any use of this software
  16.  * is at the user's own risk. 
  17.  *
  18.  */
  19. /*
  20.  * NNTP client routines.
  21.  */
  22.  
  23. /*
  24.  * The functions in this file come from NNTP 1.5.10
  25.  * They have been modified slightly to work with uqwk.
  26.  * They are used to read news articles from a remote
  27.  * news server.
  28.  *
  29.  *  -- Ken Whedbee   3/31/93
  30.  *
  31.  */
  32.  
  33.  
  34. #include <stdio.h>
  35. #include <sys/types.h>
  36. #include <ctype.h>
  37. #ifdef TLI
  38. #include    <fcntl.h>
  39. #include    <tiuser.h>
  40. #include    <stropts.h>
  41. #include    <sys/socket.h>
  42. #ifdef WIN_TCP
  43. #include    <sys/in.h>
  44. #else
  45. #include    <netinet/in.h>
  46. #endif
  47. # define    IPPORT_NNTP    ((unsigned short) 119)
  48. # include     <netdb.h>    /* All TLI implementations may not have this */
  49. #else /* !TLI */
  50. #include <sys/socket.h>
  51. #include <netinet/in.h>
  52. #ifndef EXCELAN
  53. # include <netdb.h>
  54. #endif /* !EXCELAN */
  55. #endif /* !TLI */
  56.  
  57. #ifdef USG
  58. # define    index    strchr
  59. # define        bcopy(a,b,c)   memcpy(b,a,c)
  60. # define        bzero(a,b)     memset(a,'\0',b)
  61. #endif /* USG */
  62.  
  63. #ifdef EXCELAN
  64. # define    IPPORT_NNTP    ((unsigned short) 119)
  65. #if __STDC__
  66. int connect();
  67. unsigned short htons();
  68. unsigned long rhost();
  69. int rresvport();
  70. int socket();
  71. #endif
  72. #endif
  73.  
  74. #ifdef DECNET
  75. #include <netdnet/dn.h>
  76. #include <netdnet/dnetdb.h>
  77. #endif /* DECNET */
  78.  
  79. #include "nntp.h"
  80.  
  81. FILE    *ser_rd_fp = NULL;
  82. FILE    *ser_wr_fp = NULL;
  83.  
  84. /*
  85.  * getserverbyfile    Get the name of a server from a named file.
  86.  *            Handle white space and comments.
  87.  *            Use NNTPSERVER environment variable if set.
  88.  *
  89.  *    Parameters:    "file" is the name of the file to read.
  90.  *
  91.  *    Returns:    Pointer to static data area containing the
  92.  *            first non-ws/comment line in the file.
  93.  *            NULL on error (or lack of entry in file).
  94.  *
  95.  *    Side effects:    None.
  96.  */
  97.  
  98. char *
  99. getserverbyfile(file)
  100. char    *file;
  101. {
  102.     register FILE    *fp;
  103.     register char    *cp;
  104.     static char    buf[256];
  105.     char        *index();
  106.     char        *getenv();
  107.     char        *strcpy();
  108.  
  109.     if (cp = getenv("NNTPSERVER")) {
  110.         (void) strcpy(buf, cp);
  111.         return (buf);
  112.     }
  113.  
  114.     if (file == NULL)
  115.         return (NULL);
  116.  
  117.     fp = fopen(file, "r");
  118.     if (fp == NULL)
  119.         return (NULL);
  120.  
  121.     while (fgets(buf, sizeof (buf), fp) != NULL) {
  122.         if (*buf == '\n' || *buf == '#')
  123.             continue;
  124.         cp = index(buf, '\n');
  125.         if (cp)
  126.             *cp = '\0';
  127.         (void) fclose(fp);
  128.         return (buf);
  129.     }
  130.  
  131.     (void) fclose(fp);
  132.     return (NULL);             /* No entry */
  133. }
  134.  
  135.  
  136. /*
  137.  * server_init  Get a connection to the remote news server.
  138.  *
  139.  *    Parameters:    "machine" is the machine to connect to.
  140.  *
  141.  *    Returns:    -1 on error
  142.  *            server's initial response code on success.
  143.  *
  144.  *    Side effects:    Connects to server.
  145.  *            "ser_rd_fp" and "ser_wr_fp" are fp's
  146.  *            for reading and writing to server.
  147.  */
  148.  
  149. server_init(machine)
  150. char    *machine;
  151. {
  152.     int    sockt_rd, sockt_wr;
  153.     char    line[256];
  154.     char    *index();
  155. #ifdef DECNET
  156.     char    *cp;
  157.  
  158.     cp = index(machine, ':');
  159.  
  160.     if (cp && cp[1] == ':') {
  161.         *cp = '\0';
  162.         sockt_rd = get_dnet_socket(machine);
  163.     } else
  164.         sockt_rd = get_tcp_socket(machine);
  165. #else
  166.     sockt_rd = get_tcp_socket(machine);
  167. #endif
  168.  
  169.     if (sockt_rd < 0)
  170.         return (-1);
  171.  
  172.     /*
  173.      * Now we'll make file pointers (i.e., buffered I/O) out of
  174.      * the socket file descriptor.  Note that we can't just
  175.      * open a fp for reading and writing -- we have to open
  176.      * up two separate fp's, one for reading, one for writing.
  177.      */
  178.  
  179.     if ((ser_rd_fp = fdopen(sockt_rd, "r")) == NULL) {
  180.         perror("server_init: fdopen #1");
  181.         return (-1);
  182.     }
  183.  
  184.     sockt_wr = dup(sockt_rd);
  185. #ifdef TLI
  186.     if (t_sync(sockt_rd) < 0){    /* Sync up new fd with TLI */
  187.             t_error("server_init: t_sync");
  188.         ser_rd_fp = NULL;        /* from above */
  189.         return (-1);
  190.     }
  191. #endif
  192.     if ((ser_wr_fp = fdopen(sockt_wr, "w")) == NULL) {
  193.         perror("server_init: fdopen #2");
  194.         ser_rd_fp = NULL;        /* from above */
  195.         return (-1);
  196.     }
  197.  
  198.     /* Now get the server's signon message */
  199.  
  200.     (void) get_server(line, sizeof(line));
  201.     return (atoi(line));
  202. }
  203.  
  204.  
  205. /*
  206.  * get_tcp_socket -- get us a socket connected to the news server.
  207.  *
  208.  *    Parameters:    "machine" is the machine the server is running on.
  209.  *
  210.  *    Returns:    Socket connected to the news server if
  211.  *            all is ok, else -1 on error.
  212.  *
  213.  *    Side effects:    Connects to server.
  214.  *
  215.  *    Errors:        Printed via perror.
  216.  */
  217.  
  218. get_tcp_socket(machine)
  219. char    *machine;    /* remote host */
  220. {
  221.     int    s;
  222.     struct    sockaddr_in sin;
  223. #ifdef TLI 
  224.     char     *t_alloc();
  225.     struct    t_call    *callptr;
  226.     /*
  227.      * Create a TCP transport endpoint.
  228.      */
  229.     if ((s = t_open("/dev/tcp", O_RDWR, (struct t_info*) 0)) < 0){
  230.         t_error("t_open: can't t_open /dev/tcp");
  231.         return(-1);
  232.     }
  233.     if(t_bind(s, (struct t_bind *)0, (struct t_bind *)0) < 0){
  234.             t_error("t_bind");
  235.         t_close(s);
  236.         return(-1);
  237.     }
  238.     bzero((char *) &sin, sizeof(sin));    
  239.     sin.sin_family = AF_INET;
  240.     sin.sin_port = htons(IPPORT_NNTP);
  241.     if (!isdigit(*machine) ||
  242.         (long)(sin.sin_addr.s_addr = inet_addr(machine)) == -1){
  243.         struct    hostent *gethostbyname(), *hp;
  244.         if((hp = gethostbyname(machine)) == NULL){
  245.                 fprintf(stderr,"gethostbyname: %s: host unknown\n",
  246.                 machine);
  247.             t_close(s);
  248.             return(-1);
  249.         }
  250.         bcopy(hp->h_addr, (char *) &sin.sin_addr, hp->h_length);
  251.     }
  252.     /*
  253.      * Allocate a t_call structure and initialize it.
  254.      * Let t_alloc() initialize the addr structure of the t_call structure.
  255.      */
  256.     if ((callptr = (struct t_call *) t_alloc(s,T_CALL,T_ADDR)) == NULL){
  257.         t_error("t_alloc");
  258.         t_close(s);
  259.         return(-1);
  260.     }
  261.  
  262.     callptr->addr.maxlen = sizeof(sin);
  263.     callptr->addr.len = sizeof(sin);
  264.     callptr->addr.buf = (char *) &sin;
  265.     callptr->opt.len = 0;            /* no options */
  266.     callptr->udata.len = 0;            /* no user data with connect */
  267.  
  268.     /*
  269.      * Connect to the server.
  270.      */
  271.     if (t_connect(s, callptr, (struct t_call *) 0) < 0) {
  272.         t_error("t_connect");
  273.         t_close(s);
  274.         return(-1);
  275.     }
  276.  
  277.     /*
  278.      * Now replace the timod module with the tirdwr module so that
  279.      * standard read() and write() system calls can be used on the
  280.      * descriptor.
  281.      */
  282.  
  283.     if (ioctl(s,  I_POP,  (char *) 0) < 0){
  284.         perror("I_POP(timod)");
  285.         t_close(s);
  286.         return(-1);
  287.     }
  288.  
  289.     if (ioctl(s,  I_PUSH, "tirdwr") < 0){
  290.         perror("I_PUSH(tirdwr)");
  291.         t_close(s);
  292.         return(-1);
  293.     }
  294.     
  295. #else /* !TLI */
  296. #ifndef EXCELAN
  297.     struct    servent *getservbyname(), *sp;
  298.     struct    hostent *gethostbyname(), *hp;
  299. #ifdef h_addr
  300.     int    x = 0;
  301.     register char **cp;
  302.     static char *alist[1];
  303. #endif /* h_addr */
  304.     unsigned long inet_addr();
  305.     static struct hostent def;
  306.     static struct in_addr defaddr;
  307.     static char namebuf[ 256 ];
  308.  
  309.     if ((sp = getservbyname("nntp", "tcp")) ==  NULL) {
  310.         fprintf(stderr, "nntp/tcp: Unknown service.\n");
  311.         return (-1);
  312.     }
  313.     /* If not a raw ip address, try nameserver */
  314.     if (!isdigit(*machine) ||
  315.         (long)(defaddr.s_addr = inet_addr(machine)) == -1)
  316.         hp = gethostbyname(machine);
  317.     else {
  318.         /* Raw ip address, fake  */
  319.         (void) strcpy(namebuf, machine);
  320.         def.h_name = namebuf;
  321. #ifdef h_addr
  322.         def.h_addr_list = alist;
  323. #endif
  324.         def.h_addr = (char *)&defaddr;
  325.         def.h_length = sizeof(struct in_addr);
  326.         def.h_addrtype = AF_INET;
  327.         def.h_aliases = 0;
  328.         hp = &def;
  329.     }
  330.     if (hp == NULL) {
  331.         fprintf(stderr, "%s: Unknown host.\n", machine);
  332.         return (-1);
  333.     }
  334.  
  335.     bzero((char *) &sin, sizeof(sin));
  336.     sin.sin_family = hp->h_addrtype;
  337.     sin.sin_port = sp->s_port;
  338. #else /* EXCELAN */
  339.     bzero((char *) &sin, sizeof(sin));
  340.     sin.sin_family = AF_INET;
  341. #endif /* EXCELAN */
  342.  
  343.     /*
  344.      * The following is kinda gross.  The name server under 4.3
  345.      * returns a list of addresses, each of which should be tried
  346.      * in turn if the previous one fails.  However, 4.2 hostent
  347.      * structure doesn't have this list of addresses.
  348.      * Under 4.3, h_addr is a #define to h_addr_list[0].
  349.      * We use this to figure out whether to include the NS specific
  350.      * code...
  351.      */
  352.  
  353. #ifdef    h_addr
  354.  
  355.     /* get a socket and initiate connection -- use multiple addresses */
  356.  
  357.     for (cp = hp->h_addr_list; cp && *cp; cp++) {
  358.         s = socket(hp->h_addrtype, SOCK_STREAM, 0);
  359.         if (s < 0) {
  360.             perror("socket");
  361.             return (-1);
  362.         }
  363.             bcopy(*cp, (char *)&sin.sin_addr, hp->h_length);
  364.         
  365.         if (x < 0)
  366.             fprintf(stderr, "trying %s\n", inet_ntoa(sin.sin_addr));
  367.         x = connect(s, (struct sockaddr *)&sin, sizeof (sin));
  368.         if (x == 0)
  369.             break;
  370.                 fprintf(stderr, "connection to %s: ", inet_ntoa(sin.sin_addr));
  371.         perror("");
  372.         (void) close(s);
  373.     }
  374.     if (x < 0) {
  375.         fprintf(stderr, "giving up...\n");
  376.         return (-1);
  377.     }
  378. #else    /* no name server */
  379. #ifdef EXCELAN
  380.     if ((s = socket(SOCK_STREAM,(struct sockproto *)NULL,&sin,SO_KEEPALIVE)) < 0)
  381.     {
  382.         /* Get the socket */
  383.         perror("socket");
  384.         return (-1);
  385.     }
  386.     bzero((char *) &sin, sizeof(sin));
  387.     sin.sin_family = AF_INET;
  388.     sin.sin_port = htons(IPPORT_NNTP);
  389.     /* set up addr for the connect */
  390.  
  391.     if ((sin.sin_addr.s_addr = rhost(&machine)) == -1) {
  392.         fprintf(stderr, "%s: Unknown host.\n", machine);
  393.         return (-1);
  394.     }
  395.     /* And then connect */
  396.  
  397.     if (connect(s, (struct sockaddr *)&sin) < 0) {
  398.         perror("connect");
  399.         (void) close(s);
  400.         return (-1);
  401.     }
  402. #else /* not EXCELAN */
  403.     if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
  404.         perror("socket");
  405.         return (-1);
  406.     }
  407.  
  408.     /* And then connect */
  409.  
  410.     bcopy(hp->h_addr, (char *) &sin.sin_addr, hp->h_length);
  411.     if (connect(s, (struct sockaddr *) &sin, sizeof(sin)) < 0) {
  412.         perror("connect");
  413.         (void) close(s);
  414.         return (-1);
  415.     }
  416.  
  417. #endif /* !EXCELAN */
  418. #endif /* !h_addr */
  419. #endif /* !TLI */
  420.     return (s);
  421. }
  422.  
  423. #ifdef DECNET
  424. /*
  425.  * get_dnet_socket -- get us a socket connected to the news server.
  426.  *
  427.  *    Parameters:    "machine" is the machine the server is running on.
  428.  *
  429.  *    Returns:    Socket connected to the news server if
  430.  *            all is ok, else -1 on error.
  431.  *
  432.  *    Side effects:    Connects to server.
  433.  *
  434.  *    Errors:        Printed via nerror.
  435.  */
  436.  
  437. get_dnet_socket(machine)
  438. char    *machine;
  439. {
  440.     int    s, area, node;
  441.     struct    sockaddr_dn sdn;
  442.     struct    nodeent *getnodebyname(), *np;
  443.  
  444.     bzero((char *) &sdn, sizeof(sdn));
  445.  
  446.     switch (s = sscanf( machine, "%d%*[.]%d", &area, &node )) {
  447.         case 1: 
  448.             node = area;
  449.             area = 0;
  450.         case 2: 
  451.             node += area*1024;
  452.             sdn.sdn_add.a_len = 2;
  453.             sdn.sdn_family = AF_DECnet;
  454.             sdn.sdn_add.a_addr[0] = node % 256;
  455.             sdn.sdn_add.a_addr[1] = node / 256;
  456.             break;
  457.         default:
  458.             if ((np = getnodebyname(machine)) == NULL) {
  459.                 fprintf(stderr, 
  460.                     "%s: Unknown host.\n", machine);
  461.                 return (-1);
  462.             } else {
  463.                 bcopy(np->n_addr, 
  464.                     (char *) sdn.sdn_add.a_addr, 
  465.                     np->n_length);
  466.                 sdn.sdn_add.a_len = np->n_length;
  467.                 sdn.sdn_family = np->n_addrtype;
  468.             }
  469.             break;
  470.     }
  471.     sdn.sdn_objnum = 0;
  472.     sdn.sdn_flags = 0;
  473.     sdn.sdn_objnamel = strlen("NNTP");
  474.     bcopy("NNTP", &sdn.sdn_objname[0], sdn.sdn_objnamel);
  475.  
  476.     if ((s = socket(AF_DECnet, SOCK_STREAM, 0)) < 0) {
  477.         nerror("socket");
  478.         return (-1);
  479.     }
  480.  
  481.     /* And then connect */
  482.  
  483.     if (connect(s, (struct sockaddr *) &sdn, sizeof(sdn)) < 0) {
  484.         nerror("connect");
  485.         close(s);
  486.         return (-1);
  487.     }
  488.  
  489.     return (s);
  490. }
  491. #endif
  492.  
  493.  
  494.  
  495. /*
  496.  * handle_server_response
  497.  *
  498.  *    Print some informative messages based on the server's initial
  499.  *    response code.  This is here so inews, rn, etc. can share
  500.  *    the code.
  501.  *
  502.  *    Parameters:    "response" is the response code which the
  503.  *            server sent us, presumably from "server_init",
  504.  *            above.
  505.  *            "nntpserver" is the news server we got the
  506.  *            response code from.
  507.  *
  508.  *    Returns:    -1 if the error is fatal (and we should exit).
  509.  *            0 otherwise.
  510.  *
  511.  *    Side effects:    None.
  512.  */
  513.  
  514. handle_server_response(response, nntpserver)
  515. int    response;
  516. char    *nntpserver;
  517. {
  518.     switch (response) {
  519.     case OK_NOPOST:        /* fall through */
  520.             printf(
  521.     "NOTE: This machine does not have permission to post articles.\n");
  522.         printf(
  523.     "      Please don't waste your time trying.\n\n");
  524.  
  525.     case OK_CANPOST:
  526.         return (0);
  527.         break;
  528.  
  529.     case ERR_ACCESS:
  530.         printf(
  531.    "This machine does not have permission to use the %s news server.\n",
  532.         nntpserver);
  533.         return (-1);
  534.         break;
  535.  
  536.     default:
  537.         printf("Unexpected response code from %s news server: %d\n",
  538.             nntpserver, response);
  539.         return (-1);
  540.         break;
  541.     }
  542.     /*NOTREACHED*/
  543. }
  544.  
  545.  
  546. /*
  547.  * put_server -- send a line of text to the server, terminating it
  548.  * with CR and LF, as per ARPA standard.
  549.  *
  550.  *    Parameters:    "string" is the string to be sent to the
  551.  *            server.
  552.  *
  553.  *    Returns:    Nothing.
  554.  *
  555.  *    Side effects:    Talks to the server.
  556.  *
  557.  *    Note:        This routine flushes the buffer each time
  558.  *            it is called.  For large transmissions
  559.  *            (i.e., posting news) don't use it.  Instead,
  560.  *            do the fprintf's yourself, and then a final
  561.  *            fflush.
  562.  */
  563.  
  564. void
  565. put_server(string)
  566. char *string;
  567. {
  568. #ifdef DEBUG
  569.     fprintf(stderr, ">>> %s\n", string);
  570. #endif
  571.     fprintf(ser_wr_fp, "%s\r\n", string);
  572.     (void) fflush(ser_wr_fp);
  573. }
  574.  
  575.  
  576. /*
  577.  * get_server -- get a line of text from the server.  Strips
  578.  * CR's and LF's.
  579.  *
  580.  *    Parameters:    "string" has the buffer space for the
  581.  *            line received.
  582.  *            "size" is the size of the buffer.
  583.  *
  584.  *    Returns:    -1 on error, 0 otherwise.
  585.  *
  586.  *    Side effects:    Talks to server, changes contents of "string".
  587.  */
  588.  
  589. get_server(string, size)
  590. char    *string;
  591. int    size;
  592. {
  593.     register char *cp;
  594.     char    *index();
  595.  
  596.     if (fgets(string, size, ser_rd_fp) == NULL)
  597.         return (-1);
  598.  
  599.     if ((cp = index(string, '\r')) != NULL)
  600.         *cp = '\0';
  601.     else if ((cp = index(string, '\n')) != NULL)
  602.         *cp = '\0';
  603. #ifdef DEBUG
  604.     fprintf(stderr, "<<< %s\n", string);
  605. #endif
  606.  
  607.     return (0);
  608. }
  609.  
  610.  
  611. /*
  612.  * close_server -- close the connection to the server, after sending
  613.  *        the "quit" command.
  614.  *
  615.  *    Parameters:    None.
  616.  *
  617.  *    Returns:    Nothing.
  618.  *
  619.  *    Side effects:    Closes the connection with the server.
  620.  *            You can't use "put_server" or "get_server"
  621.  *            after this routine is called.
  622.  */
  623.  
  624. void
  625. close_server()
  626. {
  627.     char    ser_line[256];
  628.  
  629.     if (ser_wr_fp == NULL || ser_rd_fp == NULL)
  630.         return;
  631.  
  632.     put_server("QUIT");
  633.     (void) get_server(ser_line, sizeof(ser_line));
  634.  
  635.     (void) fclose(ser_wr_fp);
  636.     (void) fclose(ser_rd_fp);
  637. }
  638.  
  639.