home *** CD-ROM | disk | FTP | other *** search
/ PC Welt 2000 January / PCW0001.ISO / software / hw / pc2000 / junkbust.exe / filters.c < prev    next >
Encoding:
C/C++ Source or Header  |  1999-10-07  |  16.0 KB  |  737 lines

  1. char *filters_rcs = "$Id: filters.c,v 1.1.1.1 1999/02/12 19:24:01 swa Exp $";
  2. /* Written and copyright 1997 Anonymous Coders and Junkbusters Corporation.
  3.  * Distributed under the GNU General Public License; see the README file.
  4.  * This code comes with NO WARRANTY. http://www.junkbusters.com/ht/en/gpl.html
  5.  */
  6.  
  7. #include <stdio.h>
  8. #include <sys/types.h>
  9. #include <stdlib.h>
  10. #include <ctype.h>
  11. #include <string.h>
  12.  
  13. #ifndef _WIN32
  14. #include <unistd.h>
  15. #endif
  16.  
  17. #ifdef REGEX
  18. #include <gnu_regex.h>
  19. #endif
  20.  
  21. #include "jcc.h"
  22.  
  23. #define URL(X) url_encode(url_code_map, (X))
  24.  
  25. char CBLOCK[] = "HTTP/1.0 202 Request for blocked URL\n"
  26.          "Pragma: no-cache\n"
  27.          "Last-Modified: Thu Jul 31, 1997 07:42:22 pm GMT\n"
  28.          "Expires:       Thu Jul 31, 1997 07:42:22 pm GMT\n"
  29.          "Content-Type: text/html\n\n"
  30.          "<html>\n"
  31.          "<head>\n"
  32.          "<title>Internet Junkbuster: Request for blocked URL</title>\n"
  33.          "</head>\n"
  34.          WHITEBG
  35.          "<center>"
  36.          "<a href=http://internet.junkbuster.com/ij-blocked-url?%s+%s+%s>"
  37.          BANNER
  38.          "</a>"
  39.          "</center>"
  40.          "</body>\n"
  41.          "</html>\n"
  42.          ;
  43.  
  44. char CTRUST[] = "HTTP/1.0 202 Request for untrusted URL\n"
  45.          "Pragma: no-cache\n"
  46.          "Last-Modified: Thu Jul 31, 1997 07:42:22 pm GMT\n"
  47.          "Expires:       Thu Jul 31, 1997 07:42:22 pm GMT\n"
  48.          "Content-Type: text/html\n\n"
  49.          "<html>\n"
  50.          "<head>\n"
  51.          "<title>Internet Junkbuster: Request for untrusted URL</title>\n"
  52.          "</head>\n"
  53.          WHITEBG
  54.          "<center>"
  55.          "<a href=http://internet.junkbuster.com/ij-untrusted-url?%s+%s+%s>"
  56.          BANNER
  57.          "</a>"
  58.          "</center>"
  59.          "</body>\n"
  60.          "</html>\n"
  61.          ;
  62.  
  63. char *
  64. block_url(struct http_request *http, struct client_state *csp)
  65. {
  66.     struct file_list *fl;
  67.     struct block_spec *b;
  68.     struct url_spec url[1];
  69.     char *p;
  70.     char *hostport, *path, *spec;
  71.     int n;
  72.  
  73.     if(((fl = csp->blist) == NULL) || ((b  = fl->f) == NULL)) {
  74.         return(NULL);
  75.     }
  76.  
  77.     *url = dsplit(http->host);
  78.  
  79.     /* if splitting the domain fails, punt */
  80.     if(url->dbuf == NULL) return(NULL);
  81.  
  82.     for(b = b->next; b ; b = b->next) {
  83.         if((b->url->port == 0) || (b->url->port == http->port)) {
  84.             if((b->url->domain[0] == '\0') || (domaincmp(b->url, url) == 0)) {
  85.                 if((b->url->path == NULL) ||
  86. #ifdef REGEX
  87.                    (regexec(b->url->preg, http->path, 0, NULL, 0) == 0)
  88. #else
  89.                    (strncmp(b->url->path, http->path, b->url->pathlen) == 0)
  90. #endif
  91.                 ) {
  92.                     freez(url->dbuf);
  93.                     freez(url->dvec);
  94.  
  95.                     if(b->reject == 0) return(NULL);
  96.  
  97.                     hostport = URL(http->hostport);
  98.                     path     = URL(http->path);
  99.                     spec     = URL(b->url->spec);
  100.  
  101.                     n  = strlen(CBLOCK);
  102.                     n += strlen(hostport);
  103.                     n += strlen(path);
  104.                     n += strlen(spec);
  105.  
  106.                     p = malloc(n);
  107.  
  108.                     sprintf(p, CBLOCK,
  109.                         hostport, path, spec);
  110.  
  111.                     freez(hostport);
  112.                     freez(path);
  113.                     freez(spec);
  114.  
  115.                     return(p);
  116.                 }
  117.             }
  118.         }
  119.     }
  120.     freez(url->dbuf);
  121.     freez(url->dvec);
  122.     return(NULL);
  123. }
  124.  
  125. char *
  126. block_imageurl(struct http_request *http, struct client_state *csp) /* swa */
  127. {
  128.     struct file_list *fl;
  129.     struct block_spec *b;
  130.     struct url_spec url[1];
  131.     char *p;
  132.     char *hostport, *path, *spec;
  133.     int n;
  134.  
  135.     if(((fl = csp->ilist) == NULL) || ((b  = fl->f) == NULL)) {
  136.         return(NULL);
  137.     }
  138.  
  139.     *url = dsplit(http->host);
  140.  
  141.     /* if splitting the domain fails, punt */
  142.     if(url->dbuf == NULL) return(NULL);
  143.  
  144.     for(b = b->next; b ; b = b->next) {
  145.  
  146.         if((b->url->port == 0) || (b->url->port == http->port))
  147.           {
  148.             if((b->url->domain[0] == '\0') || (domaincmp(b->url,
  149.                                      url) ==
  150.                                0)) {
  151.                 if((b->url->path == NULL) ||
  152. #ifdef REGEX
  153.                    (regexec(b->url->preg, http->path, 0, NULL, 0) == 0)
  154. #else
  155.                    (strncmp(b->url->path, http->path, b->url->pathlen) == 0)
  156. #endif
  157.                 ) {
  158.                     freez(url->dbuf);
  159.                     freez(url->dvec);
  160.  
  161.                     if(b->reject == 0) return(NULL);
  162.  
  163.                     hostport = URL(http->hostport);
  164.                     path     = URL(http->path);
  165.                     spec     = URL(b->url->spec);
  166.  
  167.                     n  = strlen(CBLOCK);
  168.                     n += strlen(hostport);
  169.                     n += strlen(path);
  170.                     n += strlen(spec);
  171.  
  172.                     p = malloc(n);
  173.  
  174.                     sprintf(p, CBLOCK,
  175.                         hostport, path, spec);
  176.  
  177.                     freez(hostport);
  178.                     freez(path);
  179.                     freez(spec);
  180.  
  181.                     return(p);
  182.                 } 
  183.             }
  184.         }
  185.     }
  186.     freez(url->dbuf);
  187.     freez(url->dvec);
  188.     return(NULL);
  189. }
  190.  
  191.  
  192. char *
  193. trust_url(struct http_request *http, struct client_state *csp)
  194. {
  195.     struct file_list *fl;
  196.     struct block_spec *b;
  197.     struct url_spec url[1], **tl, *t;
  198.     char *p, *h;
  199.     char *hostport, *path, *referrer;
  200.     struct http_request rhttp[1];
  201.     int n;
  202.  
  203.     if(((fl = csp->tlist) == NULL) || ((b  = fl->f) == NULL)) {
  204.         return(NULL);
  205.     }
  206.  
  207.     *url = dsplit(http->host);
  208.  
  209.     /* if splitting the domain fails, punt */
  210.     if(url->dbuf == NULL) return(NULL);
  211.  
  212.     memset(rhttp, '\0', sizeof(*rhttp));
  213.  
  214.     for(b = b->next; b ; b = b->next) {
  215.  
  216.         if((b->url->port == 0) || (b->url->port == http->port)) {
  217.             if((b->url->domain[0] == '\0') || (domaincmp(b->url, url) == 0)) {
  218.                 if((b->url->path == NULL) ||
  219. #ifdef REGEX
  220.                    (regexec(b->url->preg, http->path, 0, NULL, 0) == 0)
  221. #else
  222.                    (strncmp(b->url->path, http->path, b->url->pathlen) == 0)
  223. #endif
  224.                 ) {
  225.                     freez(url->dbuf);
  226.                     freez(url->dvec);
  227.  
  228.                     if(b->reject == 0) return(NULL);
  229.  
  230.                     hostport = URL(http->hostport);
  231.                     path     = URL(http->path);
  232.  
  233.                     if(csp->referrer) {
  234.                         referrer = URL(csp->referrer);
  235.                     } else {
  236.                         referrer = URL("undefined");
  237.                     }
  238.  
  239.                     n  = strlen(CTRUST);
  240.                     n += strlen(hostport);
  241.                     n += strlen(path);
  242.                     n += strlen(referrer);
  243.  
  244.                     p = malloc(n);
  245.  
  246.                     sprintf(p, CTRUST,
  247.                         hostport, path, referrer);
  248.  
  249.                     freez(hostport);
  250.                     freez(path);
  251.                     freez(referrer);
  252.  
  253.                     return(p);
  254.                 }
  255.             }
  256.         }
  257.     }
  258.  
  259.     freez(url->dbuf);
  260.     freez(url->dvec);
  261.  
  262.     if((csp->referrer == NULL)|| (strlen(csp->referrer) <= 9)) {
  263.         /* no referrer was supplied */
  264.         goto trust_url_not_trusted;
  265.     }
  266.  
  267.     /* forge a URL from the referrer so we can use
  268.      * convert_url() to parse it into its components.
  269.      */
  270.  
  271.     p = NULL;
  272.     p = strsav(p, "GET ");
  273.     p = strsav(p, csp->referrer + 9);    /* skip over "Referer: " */
  274.     p = strsav(p, " HTTP/1.0");
  275.  
  276.     parse_http_request(p, rhttp, csp);
  277.  
  278.     if(rhttp->cmd == NULL) {
  279.         freez(p);
  280.         goto trust_url_not_trusted;
  281.     }
  282.  
  283.     freez(p);
  284.  
  285.     *url = dsplit(rhttp->host);
  286.  
  287.     /* if splitting the domain fails, punt */
  288.     if(url->dbuf == NULL) goto trust_url_not_trusted;
  289.  
  290.     for(tl = trust_list; (t = *tl) ; tl++) {
  291.         if((t->port == 0) || (t->port == rhttp->port)) {
  292.             if((t->domain[0] == '\0') || domaincmp(t, url) == 0) {
  293.                 if((t->path == NULL) ||
  294. #ifdef REGEX
  295.                    (regexec(t->preg, rhttp->path, 0, NULL, 0) == 0)
  296. #else
  297.                    (strncmp(t->path, rhttp->path, t->pathlen) == 0)
  298. #endif
  299.                 ) {
  300.                     /* if the URL's referrer is from a trusted referrer, then
  301.                      * add the target spec to the trustfile as an unblocked
  302.                      * domain and return NULL (which means it's OK).
  303.                      */
  304.  
  305.                     FILE *fp;
  306.                     
  307.                     freez(url->dbuf);
  308.                     freez(url->dvec);
  309.  
  310.                     if((fp = fopen(trustfile, "a"))) {
  311.                         h = NULL;
  312.  
  313.                         h = strsav(h, "~");
  314.                         h = strsav(h, http->hostport);
  315.  
  316.                         p = http->path;
  317.                         if((*p++ == '/')
  318.                         && (*p++ == '~')) {
  319.                         /* since this path points into a user's home space
  320.                          * be sure to include this spec in the trustfile.
  321.                          */
  322.                             if((p = strchr(p, '/'))) {
  323.                                 *p = '\0';
  324.                                 h = strsav(h, http->path);
  325.                                 h = strsav(h, "/");
  326.                             }
  327.                         }
  328.  
  329.                         free_http_request(rhttp);
  330.  
  331.                         fprintf(fp, "%s\n", h);
  332.                         freez(h);
  333.                         fclose(fp);
  334.                     }
  335.                     return(NULL);
  336.                 }
  337.             }
  338.         }
  339.     }
  340.  
  341. trust_url_not_trusted:
  342.     free_http_request(rhttp);
  343.  
  344.     hostport = URL(http->hostport);
  345.     path     = URL(http->path);
  346.  
  347.     if(csp->referrer) {
  348.         referrer = URL(csp->referrer);
  349.     } else {
  350.         referrer = URL("undefined");
  351.     }
  352.  
  353.     n  = strlen(CTRUST);
  354.     n += strlen(hostport);
  355.     n += strlen(path);
  356.     n += strlen(referrer);
  357.  
  358.     p = malloc(n);
  359.     sprintf(p, CTRUST, hostport, path, referrer);
  360.  
  361.     freez(hostport);
  362.     freez(path);
  363.     freez(referrer);
  364.  
  365.     return(p);
  366. }
  367.  
  368. /* intercept_url() checks the URL `basename' against a list of URLs
  369.  * to snarf.  If it matches, it calls the associated function which
  370.  * returns an HTML page to send back to the client.
  371.  */
  372.  
  373. char *
  374. intercept_url(struct http_request *http, struct client_state *csp)
  375. {
  376.     char *basename;
  377.     struct interceptors *v;
  378.     
  379.     basename = strrchr(http->path, '/');
  380.  
  381.     if(basename == NULL) return(NULL);
  382.  
  383.     basename++; /* first char past the last slash */
  384.  
  385.     if(*basename) {
  386.         for(v = intercept_patterns; v->str; v++) {
  387.             if(strncmp(basename, v->str, v->len) == 0) {
  388.  
  389.                 return((v->interceptor)(http, csp));
  390.             }
  391.         }
  392.     }
  393.  
  394.     return(NULL);
  395. }
  396.  
  397. struct cookie_spec *
  398. cookie_url(struct http_request *http, struct client_state *csp)
  399. {
  400.     struct file_list *fl;
  401.     struct cookie_spec *b;
  402.     struct url_spec url[1];
  403.  
  404.     if(((fl = csp->clist) == NULL) || ((b  = fl->f) == NULL)) {
  405.         return(NULL);
  406.     }
  407.  
  408.     *url = dsplit(http->host);
  409.  
  410.     /* if splitting the domain fails, punt */
  411.     if(url->dbuf == NULL) return(NULL);
  412.  
  413.     for(b = b->next; b ; b = b->next) {
  414.         if((b->url->port == 0) || (b->url->port == http->port)) {
  415.             if((b->url->domain[0] == '\0') || (domaincmp(b->url, url) == 0)) {
  416.                 if((b->url->path == NULL) ||
  417. #ifdef REGEX
  418.                    (regexec(b->url->preg, http->path, 0, NULL, 0) == 0)
  419. #else
  420.                    (strncmp(b->url->path, http->path, b->url->pathlen) == 0)
  421. #endif
  422.                 ) {
  423.                     freez(url->dbuf);
  424.                     freez(url->dvec);
  425.                     return(b);
  426.                 }
  427.             }
  428.         }
  429.     }
  430.     freez(url->dbuf);
  431.     freez(url->dvec);
  432.     return(NULL);
  433. }
  434.  
  435. struct gateway *
  436. forward_url(struct http_request *http, struct client_state *csp)
  437. {
  438.     struct file_list *fl;
  439.     struct forward_spec *b;
  440.     struct url_spec url[1];
  441.  
  442.     if(((fl = csp->flist) == NULL) || ((b  = fl->f) == NULL)) {
  443.         return(gw_default);
  444.     }
  445.  
  446.     *url = dsplit(http->host);
  447.  
  448.     /* if splitting the domain fails, punt */
  449.     if(url->dbuf == NULL) return(gw_default);
  450.  
  451.     for(b = b->next; b ; b = b->next) {
  452.         if((b->url->port == 0) || (b->url->port == http->port)) {
  453.             if((b->url->domain[0] == '\0') || (domaincmp(b->url, url) == 0)) {
  454.                 if((b->url->path == NULL) ||
  455. #ifdef REGEX
  456.                    (regexec(b->url->preg, http->path, 0, NULL, 0) == 0)
  457. #else
  458.                    (strncmp(b->url->path, http->path, b->url->pathlen) == 0)
  459. #endif
  460.                 ) {
  461.                     freez(url->dbuf);
  462.                     freez(url->dvec);
  463.                     return(b->gw);
  464.                 }
  465.             }
  466.         }
  467.     }
  468.     freez(url->dbuf);
  469.     freez(url->dvec);
  470.     return(gw_default);
  471. }
  472.  
  473. /* dsplit() takes a domain and returns a pointer to a url_spec
  474.  * structure populated with dbuf, dcnt and dvec.  the other fields
  475.  * in the structure that is returned are zero.
  476.  *
  477.  */
  478.  
  479. struct url_spec
  480. dsplit(char *domain)
  481. {
  482.     struct url_spec ret[1];
  483.     char *v[BUFSIZ];
  484.     int size;
  485.     char *p;
  486.  
  487.     memset(ret, '\0', sizeof(*ret));
  488.  
  489.     if((p = strrchr(domain, '.'))) {
  490.         if(*(++p) == '\0') {
  491.             ret->toplevel = 1;
  492.         }
  493.     }
  494.  
  495.     ret->dbuf = strdup(domain);
  496.  
  497.     /* map to lower case */
  498.     for(p = ret->dbuf; *p ; p++) *p = tolower(*p);
  499.  
  500.     /* split the domain name into components */
  501.     ret->dcnt = ssplit(ret->dbuf, ".", v, SZ(v), 1, 1);
  502.  
  503.     if(ret->dcnt <= 0) {
  504.         memset(ret, '\0', sizeof(ret));
  505.         return(*ret);
  506.     }
  507.  
  508.     /* save a copy of the pointers in dvec */
  509.     size = ret->dcnt * sizeof(*ret->dvec);
  510.         
  511.     if((ret->dvec = malloc(size))) {
  512.         memcpy(ret->dvec, v, size);
  513.     }
  514.  
  515.     return(*ret);
  516. }
  517.  
  518. /* the "pattern" is a domain that may contain a '*' as a wildcard.
  519.  * the "fqdn" is the domain name against which the patterns are compared.
  520.  *
  521.  * domaincmp("a.b.c" , "a.b.c")    => 0 (MATCH)
  522.  * domaincmp("a*.b.c", "a.b.c")    => 0 (MATCH)
  523.  * domaincmp("b.c"   , "a.b.c")    => 0 (MATCH)
  524.  * domaincmp(""      , "a.b.c")    => 0 (MATCH)
  525.  */
  526.  
  527. int
  528. domaincmp(struct url_spec *pattern, struct url_spec *fqdn)
  529. {
  530.     char **pv, **fv;    /* vectors  */
  531.     int    pn,   fn;    /* counters */
  532.     char  *p,   *f;        /* chars    */
  533.     
  534.     pv = pattern->dvec;
  535.     pn = pattern->dcnt;
  536.  
  537.     fv = fqdn->dvec;
  538.     fn = fqdn->dcnt;
  539.  
  540.     while((pn > 0) && (fn > 0)) {
  541.         p = pv[--pn];
  542.         f = fv[--fn];
  543.  
  544.         while(*p && *f && (*p == tolower(*f))) {
  545.             p++, f++;
  546.         }
  547.  
  548.         if((*p != tolower(*f)) && (*p != '*')) return(1);
  549.     }
  550.  
  551.     if(pn > 0) return(1);
  552.  
  553.     return(0);
  554. }
  555.  
  556. /* intercept functions */
  557.  
  558. char *
  559. show_proxy_args(struct http_request *http, struct client_state *csp)
  560. {
  561.     char *s = NULL;
  562.  
  563.     s = strsav(s, proxy_args->header);
  564.     s = strsav(s, proxy_args->invocation);
  565.     s = strsav(s, proxy_args->gateways);
  566.  
  567.     if(csp->blist) {
  568.         s = strsav(s, csp->blist->proxy_args);
  569.     }
  570.  
  571.     if(csp->ilist) {
  572.         s = strsav(s, csp->ilist->proxy_args);
  573.     }
  574.  
  575.     if(csp->clist) {
  576.         s = strsav(s, csp->clist->proxy_args);
  577.     }
  578.  
  579.     if(csp->tlist) {
  580.         s = strsav(s, csp->tlist->proxy_args);
  581.     }
  582.  
  583.     if(csp->flist) {
  584.         s = strsav(s, csp->flist->proxy_args);
  585.     }
  586.     s = strsav(s, proxy_args->trailer);
  587.  
  588.     return(s);
  589. }
  590.  
  591. char *
  592. ij_blocked_url(struct http_request *http, struct client_state *csp)
  593. {
  594.     int n;
  595.     char *hostport, *path, *pattern, *p, *v[9];
  596.  
  597.     char *template =
  598.         "HTTP/1.0 200 OK\r\n"
  599.         "Pragma: no-cache\n"
  600.         "Last-Modified: Thu Jul 31, 1997 07:42:22 pm GMT\n"
  601.         "Expires:       Thu Jul 31, 1997 07:42:22 pm GMT\n"
  602.         "Content-Type: text/html\n\n"
  603.         "<html>\n"
  604.         "<head>\n"
  605.         "<title>Internet Junkbuster: Request for blocked URL</title>\n"
  606.         "</head>\n"
  607.         BODY
  608.         "<center><h1>"
  609.         BANNER
  610.         "</h1></center>"
  611.         "The " BANNER " Proxy "
  612.         "<A href=\"http://internet.junkbuster.com\">"
  613.         "(http://internet.junkbuster.com) </A>"
  614.         "intercepted the request for %s%s\n"
  615.         "because the URL matches the following pattern "
  616.         "in the blockfile: %s\n"
  617.         "</body>\n"
  618.         "</html>\n"
  619.         ;
  620.  
  621.     if((n = ssplit(http->path, "?+", v, SZ(v), 0, 0)) == 4) {
  622.         hostport = url_decode(v[1]);
  623.         path     = url_decode(v[2]);
  624.         pattern  = url_decode(v[3]);
  625.     } else {
  626.         hostport = strdup("undefined_host");
  627.         path     = strdup("/undefined_path");
  628.         pattern  = strdup("undefined_pattern");
  629.     }
  630.  
  631.     n  = strlen(template);
  632.     n += strlen(hostport);
  633.     n += strlen(path    );
  634.     n += strlen(pattern );
  635.  
  636.     if((p = malloc(n))) {
  637.         sprintf(p, template, hostport, path, pattern);
  638.     }
  639.  
  640.     freez(hostport);
  641.     freez(path    );
  642.     freez(pattern );
  643.  
  644.     return(p);
  645. }
  646.  
  647. char *
  648. ij_untrusted_url(struct http_request *http, struct client_state *csp)
  649. {
  650.     int n;
  651.     char *hostport, *path, *p, *v[9];
  652.     char buf[BUFSIZ];
  653.     struct url_spec **tl, *t;
  654.  
  655.  
  656.     char *template =
  657.         "HTTP/1.0 200 OK\r\n"
  658.         "Pragma: no-cache\n"
  659.         "Last-Modified: Thu Jul 31, 1997 07:42:22 pm GMT\n"
  660.         "Expires:       Thu Jul 31, 1997 07:42:22 pm GMT\n"
  661.         "Content-Type: text/html\n\n"
  662.         "<html>\n"
  663.         "<head>\n"
  664.         "<title>Internet Junkbuster: Request for untrusted URL</title>\n"
  665.         "</head>\n"
  666.         BODY
  667.         "<center><h1>"
  668.         BANNER
  669.         "</h1></center>"
  670.         "The " BANNER " Proxy "
  671.         "<A href=\"http://internet.junkbuster.com\">"
  672.         "(http://internet.junkbuster.com) </A>"
  673.         "intercepted the request for %s%s\n"
  674.         "because the URL is not trusted.\n"
  675.         "<br><br>\n"
  676.         ;
  677.  
  678.     if((n = ssplit(http->path, "?+", v, SZ(v), 0, 0)) == 4) {
  679.         hostport = url_decode(v[1]);
  680.         path     = url_decode(v[2]);
  681.         referrer = url_decode(v[3]);
  682.     } else {
  683.         hostport = strdup("undefined_host");
  684.         path     = strdup("/undefined_path");
  685.         referrer = strdup("undefined");
  686.     }
  687.  
  688.     n  = strlen(template);
  689.     n += strlen(hostport);
  690.     n += strlen(path    );
  691.  
  692.     if((p = malloc(n))) {
  693.         sprintf(p, template, hostport, path);
  694.     }
  695.  
  696.     freez(hostport);
  697.     freez(path    );
  698.  
  699.     strsav(p, "The referrer in this request was <strong>");
  700.     strsav(p, referrer);
  701.     strsav(p, "</strong><br>\n");
  702.  
  703.     p = strsav(p, "<h3>The following referrers are trusted</h3>\n");
  704.  
  705.     for(tl = trust_list; (t = *tl) ; tl++) {
  706.         sprintf(buf, "%s<br>\n", t->spec);
  707.         p = strsav(p, buf);
  708.     }
  709.  
  710.     if(trust_info->next) {
  711.         struct list *l;
  712.  
  713.         strcpy(buf,
  714.             "<p>"
  715.             "You can learn more about what this means "
  716.             "and what you may be able to do about it by "
  717.             "reading the following documents:<br>\n"
  718.             "<ol>\n"
  719.         );
  720.  
  721.         p = strsav(p, buf);
  722.         
  723.         for(l = trust_info->next; l ; l = l->next) {
  724.             sprintf(buf,
  725.                 "<li> <a href=%s>%s</a><br>\n",
  726.                     l->str, l->str);
  727.             p = strsav(p, buf);
  728.         }
  729.  
  730.         p = strsav(p, "</ol>\n");
  731.     }
  732.  
  733.     p = strsav(p, "</body>\n" "</html>\n");
  734.  
  735.     return(p);
  736. }
  737.