home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / misc / volume30 / tin / part10 / nntplib.c < prev    next >
Encoding:
C/C++ Source or Header  |  1992-05-20  |  14.5 KB  |  657 lines

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