home *** CD-ROM | disk | FTP | other *** search
/ PC World Komputer 1995 November / PCWK1195.iso / inne / win95 / sieciowe / hotja32.lzh / hotjava / classsrc / net / www / protocol / news / newsfetcher.java < prev    next >
Text File  |  1995-08-11  |  13KB  |  509 lines

  1. /*
  2.  * @(#)newsFetcher.java    1.13 95/03/28 James Gosling, Jonathan Payne
  3.  * 
  4.  * Copyright (c) 1994 Sun Microsystems, Inc. All Rights Reserved.
  5.  * 
  6.  * Permission to use, copy, modify, and distribute this software and its
  7.  * documentation for NON-COMMERCIAL purposes and without fee is hereby
  8.  * granted provided that this copyright notice appears in all copies. Please
  9.  * refer to the file "copyright.html" for further important copyright and
  10.  * licensing information.
  11.  * 
  12.  * SUN MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF THE
  13.  * SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
  14.  * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE,
  15.  * OR NON-INFRINGEMENT. SUN SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY
  16.  * LICENSEE AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR
  17.  * ITS DERIVATIVES.
  18.  */
  19.  
  20. /*-
  21.  *    news stream opener
  22.  */
  23.  
  24. package net.www.protocol.news;
  25.  
  26. import java.io.*;
  27. import java.util.*;
  28. import net.nntp.*;
  29. import net.www.html.MessageHeader;
  30. import net.www.html.WWWClassLoader;
  31. import awt.*;
  32.  
  33. class newsFetcher extends Thread {
  34.     static Newsgroup Newsgroups[];
  35.     static String newsrcName;
  36.  
  37.     static NntpClient news;
  38.     String group;
  39.     String article;
  40.     Thread nntpOwner;        /* the nntp connection is inherently single
  41.                  * threaded: we have to lock out multiple
  42.                  * attempts to use it */
  43.  
  44.  
  45.     private synchronized void lockServer() {
  46.     while (nntpOwner != null) {
  47.         if (nntpOwner == Thread.currentThread())
  48.         return;
  49.         else if (nntpOwner.isAlive())
  50.         wait();
  51.         else
  52.         break;
  53.     }
  54.     nntpOwner = Thread.currentThread();
  55.     }
  56.  
  57.     private synchronized void unlockServer() {
  58.     if (nntpOwner == Thread.currentThread())
  59.         nntpOwner = null;
  60.     notifyAll();
  61.     }
  62.  
  63.     private synchronized void handoffLock(Thread t) {
  64.     if (nntpOwner == Thread.currentThread())
  65.         nntpOwner = t;
  66.     }
  67.  
  68.     static String classLoaderSrc;
  69.  
  70.     void fireUp(String fn, OutputStream os) {
  71.     try {
  72.         lockServer();
  73.         ps = new PrintStream(os);
  74.         if (news == null) {
  75.         String serverEnvName = System.getenv("NNTPSERVER");
  76.         if (serverEnvName == null)
  77.             serverEnvName = "newshost";
  78.         news = new NntpClient(serverEnvName);
  79.         }
  80.         article = null;
  81.         group = null;
  82.         if (fn != null) {
  83.         while (fn.startsWith("/"))
  84.             fn = fn.substring(1);
  85.         while (fn.endsWith("/"))
  86.             fn = fn.substring(0, fn.length() - 1);
  87.         if (fn.length() > 0) {
  88.             int firstc = fn.charAt(0);
  89.             int seppos = fn.indexOf('/');
  90.             int atpos = fn.indexOf('@');
  91.             if (atpos > 0) {
  92.             article = seppos > 0 && atpos > seppos
  93.                 ? fn.substring(seppos + 1)
  94.                 : fn;
  95.             } else if (seppos > 0) {
  96.             group = fn.substring(0, seppos);
  97.             article = fn.substring(seppos + 1);
  98.             } else
  99.             group = fn;
  100.         }
  101.         }
  102.         start();
  103.         handoffLock(this);
  104.     } catch(Object e) {
  105.         unlockServer();
  106.         throw e;
  107.     }
  108.     }
  109.  
  110.     static Newsgroup findGroup(String s) {
  111.     if (s != null && Newsgroups != null)
  112.         for (int i = Newsgroups.length; --i >= 0;) {
  113.         Newsgroup p = Newsgroups[i];
  114.         if (p.group.name.equals(s))
  115.             return p;
  116.         }
  117.     return null;
  118.     }
  119.  
  120.     static Newsgroup addGroup(String s) {
  121.     Newsgroup r = new Newsgroup(s + "!", news);
  122.     Newsgroup ng[];
  123.     if (Newsgroups != null) {
  124.         ng = new Newsgroup[Newsgroups.length + 1];
  125.         System.arraycopy(Newsgroups, 0, ng, 0, Newsgroups.length);
  126.     } else
  127.         ng = new Newsgroup[1];
  128.     ng[ng.length - 1] = r;
  129.     Newsgroups = ng;
  130.     return r;
  131.     }
  132.  
  133.     PrintStream ps;
  134.  
  135.     private String makeSafe(String s) {
  136.     int start = 0;
  137.     int pos;
  138.     while ((pos = s.indexOf("&", start)) >= 0) {
  139.         start = pos + 3;
  140.         s = s.substring(0, pos) + "&" + s.substring(pos + 1);
  141.     }
  142.     start = 0;
  143.     while ((pos = s.indexOf("<", start)) >= 0) {
  144.         start = pos + 3;
  145.         s = s.substring(0, pos) + "<" + s.substring(pos + 1);
  146.     }
  147.     return s;
  148.     }
  149.  
  150.     private void title(String s) {
  151.     s = makeSafe(s);
  152.     ps.print("<html><title>" + s + "</title>\n<body><H1>" + s + "</H1>\n");
  153.     }
  154.  
  155.     private void button(String label, String target) {
  156.     ps.print("<app class=net.www.protocol.news.InlineButton label=\""
  157.          + label + "\" href=" + target + classLoaderSrc + ">\n");
  158.     }
  159.  
  160.     private void StandardButtons(boolean allgroups) {
  161.     if (allgroups) {
  162.         button("All Groups", "news:/");
  163.         button("Catch up", "catchup");
  164.     }
  165.     button("Regenerate page", "regen");
  166.     button("Post", "post");
  167.     button("Mail", "mail");
  168.     button("Save", "saverc");
  169.     button("Rescan", "rescan");
  170.     }
  171.  
  172.     private void outStr(char s[], int i, int lim, boolean html) {
  173.     while (i < lim) {
  174.         int c = s[i++];
  175.         switch (c) {
  176.           case '<':
  177.         ps.print(html ? "<" : "<");
  178.         break;
  179.           case '&':
  180.         ps.print(html ? "&" : "&");
  181.         break;
  182.           default:
  183.         ps.write(c);
  184.         break;
  185.         }
  186.     }
  187.     }
  188.  
  189.     private void FormatArticle(InputStream is, int articlenum) {
  190.     boolean html = false;
  191.     Newsgroup thisNG = null;
  192.     MessageHeader mh = new MessageHeader(is);
  193.     String gp = mh.findValue("newsgroups");
  194.     if (gp == null || (gp.indexOf(",") >= 0 && group != null))
  195.         gp = group;
  196.     String Subject = mh.findValue("subject");
  197.     if (Subject == null) {
  198.         if (gp == null)
  199.         Subject = "News message";
  200.         else
  201.         Subject = "Message from " + gp;
  202.     }
  203.     thisNG = findGroup(gp);
  204.     title(Subject);
  205.     ps.print("<blockquote>\n");
  206.     String s;
  207.     if ((s = mh.findValue("from")) != null)
  208.         ps.print("<i>" + makeSafe(s) + "</i><br>\n");
  209.     if ((s = mh.findValue("organization")) != null)
  210.         ps.print(makeSafe(s) + "<br>\n");
  211.     if ((s = mh.findValue("date")) != null)
  212.         ps.print(makeSafe(s) + "<br>\n");
  213.     ps.print("<p>\n");
  214.     if (gp != null)
  215.         button("Group", "news:/" + gp);
  216.     if (thisNG != null && articlenum > 0) {
  217.         thisNG.markAsRead(articlenum);
  218.         if (articlenum > thisNG.group.firstArticle)
  219.         button("Previous", "prev");
  220.         if (articlenum < thisNG.group.lastArticle)
  221.         button("Next", "next");
  222.     }
  223.     if ((s = mh.findValue("content-type")) != null &&
  224.         s.startsWith("text/html"))
  225.         html = true;
  226.     StandardButtons(true);
  227.     ps.print("</blockquote>\n");
  228.     ps.print("<hr><pre>");
  229.     int c;
  230.     char line[] = new char[40];
  231.     int len = 0;
  232.     while ((c = is.read()) >= 0) {
  233.         if (c == '\n') {
  234.         int outed = 0;
  235.         int limit = len - 1;
  236.         if (!html) {
  237.             for (int i = 3; i < limit; i++) {
  238.             if (line[i] == ':') {
  239.                 int st = -1;
  240.                 if (line[i + 1] == '/' && line[i - 1] == 'p'
  241.                 && line[i - 2] == 't')
  242.                 {
  243.                 if (line[i - 3] == 't'
  244.                     && line[i - 4] == 'h')
  245.                 {
  246.                     st = 4;    /* seen http: */
  247.                 } else if (line[i - 3] == 'f') {
  248.                     st = 3;    /* seen ftp: */
  249.                 }
  250.                 } else if (line[i - 1] == 's'
  251.                        && line[i - 2] == 'w'
  252.                        && line[i - 3] == 'e'
  253.                        && line[i - 4] == 'n')
  254.                 {
  255.                 st = 4;    /* seen news: */
  256.                 }
  257.                 if (st > 0) {
  258.                 outStr(line, outed, i - st, false);
  259.                 outed = i - st;
  260.                 while (i < len
  261.                        && (c = line[i]) > ' ' && c != '>'
  262.                        && c != '"' && c != '\'' && c != ';'
  263.                        && c != ')' && c != ']' && c != '}')
  264.                 {
  265.                     i++;
  266.                 }
  267.                 while (i > outed && (line[i - 1] == ','
  268.                              || line[i - 1] == '.'))
  269.                 {
  270.                     i--;
  271.                 }
  272.                 ps.print("<a href=\""
  273.                      + new String(line, outed, i - outed)
  274.                      + "\">");
  275.                 outStr(line, outed, i, false);
  276.                 ps.print("</a>");
  277.                 outed = i;
  278.                 }
  279.             }
  280.             }
  281.         }
  282.         outStr(line, outed, len, html);
  283.         ps.write('\n');
  284.         len = 0;
  285.         } else {
  286.         if (len >= line.length) {
  287.             char nln[] = new char[line.length * 2];
  288.             System.arraycopy(line, 0, nln, 0, line.length);
  289.             line = nln;
  290.         }
  291.         line[len++] = (char) c;
  292.         }
  293.     }
  294.     ps.print("</pre>\n");
  295.     }
  296.  
  297.     private NewsDirectoryEntry dumpRange(Newsgroup ng, NewsDirectoryEntry root,
  298.                      int first, int last)
  299.     {
  300.     for (int j = first; j <= last; j++) {
  301.         try {
  302.         InputStream is = news.getHeader(j);
  303.         MessageHeader mh = new MessageHeader(is);
  304.         if (is == null)
  305.             ps.print("Null stream pointer\n");
  306.         else {
  307.             is.close();
  308.             String From = mh.findValue("from");
  309.             String mid = mh.findValue("message-id");
  310.             if (root != null && root.find(mid))
  311.             continue;
  312.             if (From != null) {
  313.             int lparen = From.indexOf('(');
  314.             if (lparen >= 0) {
  315.                 int rparen = From.indexOf(')', lparen);
  316.                 if (rparen >= 0)
  317.                 From = From.substring(lparen + 1, rparen);
  318.             }
  319.             }
  320.             NewsDirectoryEntry n = new NewsDirectoryEntry(
  321.                        mh.findValue("subject"), From, j,
  322.                        mid, mh.findValue("references"));
  323.             root = n.insert(root, true);
  324.         }
  325.         } catch(Exception e) {
  326.         if (ng != null)
  327.             ng.markAsRead(j);
  328.         }
  329.     }
  330.     return root;
  331.     }
  332.  
  333.     public void run() {
  334.     try {
  335.         if (classLoaderSrc == null) {
  336.         classLoaderSrc = " src=news:///";
  337.         try {
  338.             classLoaderSrc = " src=" + ((WWWClassLoader) this.getClass().getClassLoader()).ctx.toExternalForm();
  339.         } catch(Exception e) {
  340.         }
  341.         }
  342.         Newsgroup p = null;
  343.         if (Newsgroups == null) {
  344.         newsrcName = System.getenv("HOME") + File.separator + ".newsrc";
  345.         Newsgroups = Newsgroup.readNewsrcFile(newsrcName, news);
  346.         if (Newsgroups == null) {
  347.             newsrcName = System.getenv("HOME") + File.separator + ".hotjava"
  348.             + File.separator + "newsrc";
  349.             Newsgroups = Newsgroup.readNewsrcFile(newsrcName, news);
  350.         }
  351.         if (Newsgroups == null)
  352.             Newsgroups = new Newsgroup[0];
  353.         }
  354.         int articlenum = -1;
  355.         try {
  356.         articlenum = Integer.parseInt(article);
  357.         } catch(Exception e) {
  358.         }
  359.         if (article == null && group == null) {
  360.         title("Newsgroup Directory");
  361.         StandardButtons(false);
  362.         ps.print("<p>\n");
  363.         if (Newsgroups == null || Newsgroups.length == 0) {
  364.             ps.print("No subscribed newsgroups or .newsrc is unreadable.  If the news: protocol handler was dynamically loaded, it probably doesn't have the necessary authority to read .newsrc.\n<br>Goto the url news:<i>groupname</i> to read a new group");
  365.         } else
  366.             for (int i = 0; i < Newsgroups.length; i++) {
  367.             p = Newsgroups[i];
  368.             if (p.subscribed) {
  369.                 int unread = p.articles != null ? p.articles.size() : 0;
  370.                 if (unread > 0)
  371.                 ps.print("<br><app class=net.www.protocol.news.TotalUnReadIndicator group=" +
  372.                      p.group.name +
  373.                      classLoaderSrc +
  374.                      "><a href=news:/" + p.group.name +
  375.                      "> " + p.group.name
  376.                      + "</a>\n");
  377.             }
  378.             }
  379.         } else if (group != null) {
  380.         if (articlenum <= 0) {
  381.             int i;
  382.             boolean inConversation = false;
  383.             title("Newsgroup " + group);
  384.             StandardButtons(true);
  385.             boolean groupExists = false;
  386.             try {
  387.             news.setGroup(group);
  388.             groupExists = true;
  389.             } catch(Exception e) {
  390.             }
  391.             p = findGroup(group);
  392.             if (p == null) {
  393.             try {
  394.                 p = addGroup(group);
  395.             } catch(Exception e) {
  396.                 p = null;
  397.             }
  398.             }
  399.             NumberSet ns = p == null ? null : p.articles;
  400.             NewsDirectoryEntry root = null;
  401.             if (p != null && p.subscribed)
  402.             button("Unsubscribe", "unsubscribe");
  403.             else
  404.             button("Subscribe", "subscribe");
  405.             boolean readingUnread = true;
  406.             if (article != null && article.startsWith("upto")) {
  407.             int min = p.group.firstArticle;
  408.             if (min <= 0) {
  409.                 p.group = news.getGroup(p.group.name);
  410.                 min = p.group.firstArticle;
  411.             }
  412.             int max = p.group.lastArticle;
  413.             int hi = max;
  414.             readingUnread = false;
  415.             try {
  416.                 hi = Integer.parseInt(article.substring(4));
  417.             } catch(Exception e) {
  418.             }
  419.             if (hi > max)
  420.                 hi = max;
  421.             int lo = hi - 40;
  422.             if (lo < min)
  423.                 lo = min;
  424.             if (lo > min)
  425.                 button("Earlier",
  426.                    "news:/" + group + "/upto" + (lo + 5));
  427.             if (hi < max)
  428.                 button("Later",
  429.                    "news:/" + group + "/upto" + (hi + 35));
  430.             button("New or unread", "news:/" + group);
  431.             root = dumpRange(p, root, lo, hi);
  432.             } else {
  433.             button("View All", "news:/" + group + "/upto");
  434.             if (ns != null) {
  435.                 for (i = 0; i < ns.nranges; i++) {
  436.                 root = dumpRange(p, root,
  437.                          ns.starts[i], ns.ends[i]);
  438.                 }
  439.             }
  440.             }
  441.             // button("Catch up", "catchup");
  442.             ps.print("<hr>\n");
  443.             if (root == null) {
  444.             ps.print("No articles found.\n");
  445.             if (readingUnread) {
  446.                 ps.print("Use ");
  447.                 button("View All", "news:/" + group + "/upto");
  448.                 ps.print("to read all messages.\n");
  449.             }
  450.             }
  451.             addTree(root, true);
  452.         } else {
  453.             try {
  454.             news.setGroup(group);
  455.             FormatArticle(news.getArticle(articlenum), articlenum);
  456.             } catch(Exception e) {
  457.             ps.print("<hr>Error fetching article:<br> <i> " + e + "</i>\n");
  458.             }
  459.         }
  460.         } else {
  461.         /* group == null */
  462.         try {
  463.             FormatArticle(news.getArticle(article), -1);
  464.         } catch(Exception e) {
  465.             ps.print("<hr><H1>Error fetching article: " + e + "</H1>\n");
  466.         }
  467.         }
  468.         ps.print("</body></html>\n");
  469.         ps.close();
  470.         unlockServer();
  471.     } catch(Exception e) {
  472.         try {
  473.         ps.close();
  474.         } catch(Exception e2) {
  475.         }
  476.         try {
  477.         unlockServer();
  478.         } catch(Exception e3) {
  479.         }
  480.     }
  481.     }
  482.  
  483.     void addTree(NewsDirectoryEntry n, boolean toplevel) {
  484.     ps.print("<dl compact>\n");
  485.     while (n != null) {
  486.         ps.print("<dt><app class=net.www.protocol.news.ReadIndicator"
  487.              + " group=" + group
  488.              + " article=" + n.anum
  489.              + classLoaderSrc
  490.              + ">\n");
  491.         ps.print("<dd><a href=news:/" + group + "/" + n.anum + ">\n");
  492.         if (toplevel) {
  493.         ps.print(makeSafe(n.subject));
  494.         }
  495.         if (n.author != null) {
  496.         if (toplevel)
  497.             ps.print(", ");
  498.         ps.print("<i>" + makeSafe(n.author) + "</i>");
  499.         }
  500.         ps.print("</a>\n");
  501.         if (n.child != null) {
  502.         addTree(n.child, false);
  503.         }
  504.         n = n.next;
  505.     }
  506.     ps.print("</dl>\n");
  507.     }
  508. }
  509.