Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 2568a89

Browse files
committedJan 28, 2015
copy of webnews archive from Starship Python
0 parents  commit 2568a89

File tree

7 files changed

+598
-0
lines changed

7 files changed

+598
-0
lines changed
 

‎.gitignore

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# compiled python
2+
*.py[co]
3+
build
4+
build-stamp
5+
# editor debris
6+
*~
7+
.#*
8+
._*
9+
.*.sw?
10+
# OS debris
11+
.DS_Store
12+
# ignore temp/state files
13+
.netrwhist
14+
#
15+
node_modules
16+
# don't include the sag couch library
17+
sag*
18+
# don't include the configuration file
19+
options.json
20+
tools/docopt
21+

‎LICENSE

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
The MIT License
2+
3+
Copyright (c) 1995-1997 Jim Tittsler http://tittsler.com
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in
13+
all copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21+
THE SOFTWARE.

‎README.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# webnews - a web-based newsreader
2+
3+
Some of the members of the
4+
[Tokyo PC Users Group](http://tokyopc.org/) complained
5+
that they couldn't access our local
6+
[newsgroups](http://tokyopc.org/tpc/newsgroups.html)
7+
through their firewall, so I put together a
8+
[PCGI](http://starship.python.net/crew/jbauer/persistcgi/index.html)
9+
program that queries the NNTP server and builds
10+
[simple HTML pages](<http://www.tokyopc.org/cgi-bin/webnews/newsgroup?G=tpc.list)
11+
on the fly.
12+
13+
## License: MIT

‎backupfile.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import os
2+
3+
def backupfile(filename, extension=".bak"):
4+
""" create a backupfile, returning (fin, fout) renaming original """
5+
6+
backname = filename+extension
7+
8+
try:
9+
os.remove(backname)
10+
except:
11+
pass # maybe nothing to delete
12+
13+
os.rename(filename, backname)
14+
15+
try:
16+
fin = open(backname, "r")
17+
except:
18+
print "Unable to open input file: %s" % backname
19+
20+
try:
21+
fout = open(filename, "w")
22+
except:
23+
print "Unable to create output file: %s" % filename
24+
25+
return (fin, fout)

‎ngupdate

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
#! /usr/bin/python
2+
# -*- python -*-
3+
# $Id: ngupdate,v 1.3 1998/04/10 08:06:55 tpc Exp tpc $
4+
#
5+
# 1997.11.21 7j1ajh@amsat.org add Expires header to newsgroups.html
6+
# 1997.11.20 7j1ajh@amsat.org update newsgroups.html to show activity
7+
# 1998.04.02 7j1ajh@amsat.org fix row matching regex so that it finds rows
8+
# with <FONT></FONT> in them
9+
10+
import sys, os, string, time, cgi, regex
11+
from nntplib import NNTP
12+
from time import ctime, time, localtime, strftime, asctime, gmtime
13+
from backupfile import backupfile
14+
15+
# number of seconds until the HTML page 'expires'
16+
valid_secs = 60*60*12 # 12 hours
17+
18+
# news server authentication
19+
user = 'account'
20+
password = 'password'
21+
22+
NEWS_SERVER = 'news.tpc.ml.org'
23+
#NEWS_SERVER = 'pengu.mww.dyn.ml.org'
24+
filenames = ('/home/tpc/www/tpc/newsgroups.html',
25+
'/home/tpc/www/member/newsgroups.html',
26+
'/home/tpc/www/exec/newsgroups.html')
27+
#filename = '/home/jwt/newsgroups.html'
28+
29+
def do_update(filename):
30+
try:
31+
news = NNTP(NEWS_SERVER)
32+
except:
33+
print "Can not open news server:", NEWS_SERVER
34+
raise SystemExit
35+
36+
try:
37+
resp = news.shortcmd('MODE READER')
38+
except:
39+
print "Can not communicate with server:", NEWS_SERVER
40+
raise SystemExit
41+
42+
resp = news.shortcmd('authinfo user '+user)
43+
if resp[:3] != '381':
44+
raise SystemExit
45+
resp = news.shortcmd('authinfo pass '+password)
46+
if resp[:3] != '281':
47+
raise SystemExit
48+
49+
(fin, fout) = backupfile(filename)
50+
51+
nowsecs = time()
52+
newstime = strftime("%H%M%S", localtime(nowsecs))
53+
datem1 = strftime("%y%m%d", localtime(nowsecs - (60*60*24)))
54+
datem7 = strftime("%y%m%d", localtime(nowsecs - (60*60*24*7)))
55+
datem21 = strftime("%y%m%d", localtime(nowsecs - (60*60*24*21)))
56+
57+
# copy the page up to the table comment
58+
while 1:
59+
line = fin.readline()
60+
if line == '': break
61+
if string.find(line, 'HTTP-EQUIV="Expires"') >= 0:
62+
fout.write('<META HTTP-EQUIV="Expires" CONTENT="%s"\n' % asctime(gmtime(nowsecs + valid_secs)))
63+
else:
64+
fout.write(line)
65+
if string.find(line, "##newsgroups table") >= 0: break
66+
67+
row = regex.symcomp('<TR><TD>.*</TD>\(<lead><TD>[^:]*://news.tpc.ml.org/\)\(<newsgroup>[^"]+\)\(<rest>.*\)')
68+
69+
while 1:
70+
line = fin.readline()
71+
if line == '': break
72+
if string.find(line, "</TABLE>") >= 0:
73+
break
74+
75+
if row.search(line) >= 0:
76+
newsgroup = row.group('newsgroup')
77+
idsm1 = 0
78+
idsm7 = 0
79+
idsm21 = 0
80+
try:
81+
resp, idsm1 = news.newnews(newsgroup, datem1, newstime)
82+
resp, idsm7 = news.newnews(newsgroup, datem7, newstime)
83+
resp, idsm21 = news.newnews(newsgroup, datem21, newstime)
84+
except:
85+
print "no such newsgroup:", newsgroup
86+
87+
fontm1 = ""
88+
fontm1e = ""
89+
fontm7 = ""
90+
fontm7e = ""
91+
if len(idsm1)>3:
92+
fontm1 = '<FONT COLOR="#FF0000">'
93+
fontm1e = '</FONT>'
94+
if len(idsm7)>10:
95+
fontm7 = '<FONT COLOR="#800000">'
96+
fontm7e = '</FONT>'
97+
line = "<TR><TD>%s%d%s/%s%d%s/%d</TD>%s%s%s\n" % (fontm1, len(idsm1), fontm1e, fontm7, len(idsm7), fontm7e, len(idsm21), row.group('lead'), newsgroup, row.group('rest'))
98+
fout.write(line)
99+
100+
# close the table and indicate update time
101+
fout.write("</TABLE><P>Last updated: %s</P>\n" % ctime(time()))
102+
103+
while 1:
104+
line = fin.readline()
105+
if line == '': break
106+
fout.write(line)
107+
108+
fin.close()
109+
fout.close()
110+
111+
for filename in filenames:
112+
do_update(filename)
113+

‎pcgiwebnews.py

Lines changed: 362 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,362 @@
1+
#!/usr/bin/python
2+
# pcgiwebnews.py - PCGI version of WebNews
3+
# 1997.09.27 Jim.Tittsler@tpc.ml.org
4+
# 1998.07.24 Jim.Tittsler@tpc.ml.org converted to PCGI
5+
"""Tokyo PC Users Group WebNews reader by Jim Tittsler"""
6+
7+
import sys, os, string, time, cgi, re
8+
from nntplib import NNTP
9+
from urllib import quote
10+
from rfc822 import parseaddr, parsedate
11+
12+
print "Content-type: text/html"
13+
print
14+
15+
NEWS_SERVER = 'news.tpc.ml.org'
16+
#NEWS_SERVER = 'pengu.mww.dyn.ml.org'
17+
default_show_articles = 30 # number of article headers to show on first entry
18+
NewsError = "Error communicating with server"
19+
20+
logo_html = """<HTML><HEAD></HEAD><BODY BGCOLOR="#FFFFFF">
21+
<A HREF="http://www.tpc.ml.org/" TARGET="_top"><IMG SRC="/tpc/images/tpcsml.gif" BORDER="0" ALT="TPC" ALIGN=LEFT></A>
22+
<B><FONT SIZE=+1>Tokyo PC</FONT></B>
23+
<B><FONT SIZE=+1>Users Group</FONT></B>
24+
<BR CLEAR=LEFT>
25+
<B><FONT SIZE="+1">%s</FONT></B><BR>
26+
<EM>%s</EM><P>
27+
<A HREF="mailto:%s@tpc.ml.org">Post new article</A><BR>
28+
<A HREF="http://www.tpc.ml.org/" TARGET="_top">[Home]</A>&nbsp;
29+
<A HREF="http://www.tpc.ml.org/tpc/newsgroups.html" TARGET="_top">[Newsgroups]</A>
30+
</BODY></HTML>"""
31+
32+
ttalk_logo_html = """<HTML><HEAD></HEAD><BODY BGCOLOR="#FFFFFF">
33+
<A HREF="http://www.ttalk.com/" TARGET="_top"><IMG SRC="http://ttalk.soholutions.com/images/tt.gif" BORDER="0" ALT="TTalk" ALIGN=LEFT WIDTH=59 HEIGHT=42></A>
34+
<B><FONT SIZE=+1>TTalk</FONT></B>
35+
<B>Broadcasting</B>
36+
<BR CLEAR=LEFT>
37+
<B><FONT SIZE="+1">%s</FONT></B><BR>
38+
<EM>%s</EM><P>
39+
<A HREF="mailto:%s@ttalk.soholutions.com">Post new article</A><BR>
40+
<A HREF="http://www.ttalk.com/" TARGET="_top">[Home]</A>&nbsp;
41+
<A HREF="http://ttalk.soholutions.com/%s" TARGET="_top">[Newsgroups]</A>
42+
</BODY></HTML>"""
43+
44+
interesting_headers = re.compile(r"""
45+
((?P<from>From:)
46+
|(?P<newsgroups>Newsgroups:\ tpc\.(?P<group>[-0-9a-zA-z\.]+)(?P<othergroups>.*))
47+
|(?P<subject>Subject:\ (?P<re>re:\s?)?(?P<real_subject>.*))
48+
|(?P<keep>Date:))""", re.X | re.I)
49+
50+
def logo(G="", D=""):
51+
"""The logo pane for the TPC WebNews reader"""
52+
# turn the newsgroup name into a mailing list name by changing first . to -
53+
if string.find(G, "ttalk") >= 0:
54+
if string.find(G, "staff") >= 0:
55+
return ttalk_logo_html % (G, D, string.replace(G, '.', '-'), "staff")
56+
else:
57+
return ttalk_logo_html % (G, D, string.replace(G, '.', '-'), "")
58+
else:
59+
return logo_html % (G, D, string.replace(G, '.', '-'))
60+
61+
def show_article_and_kids(index, depth, lines, childof,
62+
acc_string, pass_string, RESPONSE):
63+
art_nr, subject, poster, date, id, references, size, line_cnt = lines[index]
64+
name, email = parseaddr(poster)
65+
RESPONSE.write('<LI><A HREF="http:/cgi-bin/webnews/readnews?I=%s%s%s" TARGET="d">%s</A> (%s) %s, %s' % (quote(id), acc_string, pass_string, cgi.escape(subject), line_cnt, name, time.strftime('%b %d, %H:%M', parsedate(date))))
66+
if index in childof:
67+
RESPONSE.write('<UL>')
68+
for i in range(len(lines)):
69+
if childof[i] == index:
70+
show_article_and_kids(i, depth+1, lines, childof,
71+
acc_string, pass_string, RESPONSE)
72+
RESPONSE.write('</UL>')
73+
74+
def newsgroup(G='', F='', C='', A=None, P=None, RESPONSE=None):
75+
"""The article list for group G in framestyle F"""
76+
if G=='': return "Missing newsgroup name."
77+
group = G
78+
showframes = 1
79+
show_articles = default_show_articles
80+
if os.environ.has_key('HTTP_USER_AGENT'):
81+
browser = os.environ['HTTP_USER_AGENT']
82+
else:
83+
browser = "unknown"
84+
if string.find(browser, "Mozilla/") == 0:
85+
browser_version = string.atof(browser[8:string.index(browser, ' ')])
86+
if browser_version >= 2.00:
87+
showframes = 3
88+
if F != '':
89+
try:
90+
showframes = string.atoi(F)
91+
except AttributeError:
92+
showframes = 0
93+
if C != '':
94+
try:
95+
show_articles = string.atoi(C)
96+
except AttributeError:
97+
show_articles = default_show_articles
98+
99+
user = A
100+
acc_string = ''
101+
if A: acc_string = '&A=' + A
102+
password = P
103+
pass_string = ''
104+
if P: pass_string = '&P=' + P
105+
106+
lines = []
107+
RESPONSE.headers['expires'] = time.asctime(time.gmtime(time.time() + 60))
108+
RESPONSE.write( """<HTML><HEAD><TITLE>Tokyo PC Users Group: %s</TITLE></HEAD>""" % group)
109+
try:
110+
try:
111+
news = NNTP(NEWS_SERVER)
112+
except:
113+
RESPONSE.write( "<BODY><B>Can not connect to server:</B> ", NEWS_SERVER)
114+
raise NewsError
115+
116+
try:
117+
resp = news.shortcmd('MODE READER')
118+
except:
119+
RESPONSE.write( "<BODY><B>Can not communicate with server:</B> ", NEWS_SERVER)
120+
raise NewsError
121+
122+
if user:
123+
resp = news.shortcmd('authinfo user '+user)
124+
if resp[:3] == '381':
125+
if not password:
126+
RESPONSE.write( "<BODY><B>Can not fetch newsgroup</B>")
127+
raise NewsError
128+
else:
129+
resp = news.shortcmd('authinfo pass '+password)
130+
if resp[:3] != '281':
131+
RESPONSE.write( "<BODY><B>Can not fetch newsgroup</B>")
132+
raise NewsError
133+
134+
try:
135+
resp, count, first, last, name = news.group(group)
136+
except:
137+
RESPONSE.write( "<BODY><B>No such newsgroup:</B> " + group )
138+
raise NewsError
139+
140+
description = ""
141+
try:
142+
resp, lines = news.xgtitle(group)
143+
except:
144+
pass
145+
else:
146+
for line in lines:
147+
name, description = line
148+
149+
if showframes == 0:
150+
RESPONSE.write( '<BODY BGCOLOR="#FFFFFF"><H1>%s</H1>' % group)
151+
RESPONSE.write( "<EM>%s</EM><P>" % cgi.escape(description))
152+
elif showframes == 1 or showframes == 3:
153+
if description: description = "&D="+quote(description)
154+
RESPONSE.write( '<FRAMESET ROWS="33%,*">')
155+
RESPONSE.write( ' <FRAMESET COLS="220,*">')
156+
RESPONSE.write( ' <FRAME SRC="/cgi-bin/webnews/logo?G=%s%s" scrolling="auto">' % (group, description))
157+
RESPONSE.write( ' <FRAME SRC="/cgi-bin/webnews/newsgroup?G=%s&F=2%s%s#last" scrolling="yes"> ' % (group, acc_string, pass_string))
158+
RESPONSE.write( ' </FRAMESET>')
159+
if string.find(G, "ttalk") >= 0:
160+
RESPONSE.write( ' <FRAME SRC="http://ttalk.soholutions.com/welcome.html" scrolling="auto" name="d">')
161+
else:
162+
RESPONSE.write( ' <FRAME SRC="/webnews/welcome.html" scrolling="auto" name="d">')
163+
RESPONSE.write( '</FRAMESET><BODY BGCOLOR="#FFFFFF">')
164+
else:
165+
RESPONSE.write( '<BODY BGCOLOR="#FFFFFF">')
166+
167+
if showframes == 3:
168+
raise NewsError
169+
170+
if (show_articles > 0):
171+
ilast = string.atoi(last)
172+
ifirst = string.atoi(first)
173+
if ((ilast - ifirst + 1) > show_articles):
174+
first = "%d" % (ilast - show_articles + 1)
175+
RESPONSE.write( '<A HREF="/cgi-bin/webnews/newsgroup?G=%s&F=%d&C=0%s%s"><I>Retrieve earlier article headers</I></A> ' % (group, showframes, acc_string, pass_string))
176+
177+
try:
178+
resp, lines = news.xover(first, last)
179+
except:
180+
RESPONSE.write( "<B>Unable to get article list for:</B> " + group)
181+
raise NewsError
182+
183+
RESPONSE.write( '<UL TYPE="none">')
184+
185+
# pass 1: build a dictionary of message IDs
186+
ids = {}
187+
index = 0
188+
for line in lines:
189+
art_nr, subject, poster, date, id, references, size, line_cnt = line
190+
ids[id] = index
191+
index = index + 1
192+
193+
# pass 2: discover child articles
194+
childof = []
195+
subs = {}
196+
# subject_re_less = regex.symcomp("\([Rr]e:\)? *\(<real_subject>.*\)")
197+
subject_re_less = re.compile(r"(re:)?\s*(?P<real_subject>.*)")
198+
index = 0
199+
for line in lines:
200+
art_nr, subject, poster, date, id, references, size, line_cnt = line
201+
childof.append(-1)
202+
# if subject_re_less.match(subject) > 0:
203+
# subject = subject_re_less.group('real_subject')
204+
srl = subject_re_less.match(subject)
205+
if srl: subject = srl.group('real_subject')
206+
# if there are references, use them (most recent first)
207+
if len(references) > 0:
208+
references.reverse()
209+
for ref in references:
210+
if ids.has_key(ref):
211+
childof[index] = ids[ref]
212+
break
213+
# if no references (or referee not found), use subject
214+
if childof[index] == -1:
215+
if subs.has_key(subject) :
216+
childof[index] = subs[subject]
217+
else:
218+
subs[subject] = index
219+
index = index + 1
220+
221+
# index = 0
222+
# for line in lines:
223+
# art_nr, subject, poster, date, id, size, line_cnt, references = line
224+
# print index,childof[index],subject
225+
# index = index + 1
226+
227+
index = 0
228+
for seq in childof:
229+
if seq == -1:
230+
show_article_and_kids(index, 0, lines, childof,
231+
acc_string, pass_string, RESPONSE)
232+
index = index + 1
233+
234+
# art_nr, subject, poster, date, id, size, line_cnt, references = line
235+
# name, email = parseaddr(poster)
236+
# print '<LI><A HREF="http:/cgi-bin/readnews.cgi?%s" TARGET="d">%s</A> (%s) %s, %s' % (quote(id), subject, line_cnt, name, time.strftime('%b %d, %H:%M', parsedate(date)))
237+
238+
RESPONSE.write('<A NAME="last">&nbsp</A></UL>')
239+
240+
finally:
241+
if showframes != 2:
242+
if string.find(G, "ttalk") >= 0:
243+
RESPONSE.write( """<P><HR><P>A service of the
244+
<A HREF="http://www.soholutions.com/">SoHolutions</A>.""")
245+
else:
246+
RESPONSE.write( """<P><HR><P>A service of the
247+
<A HREF="http://www.tpc.ml.org/">Tokyo PC Users Group</A>.""")
248+
# print "<P><ADDRESS>",os.environ['HTTP_USER_AGENT'],"</ADDRESS>"
249+
RESPONSE.write( """</BODY></HTML>""")
250+
251+
def liven_url(matchobj):
252+
# if it was a URL-type match, but there is a zero length address part,
253+
# don't make it live (return the original string)
254+
if matchobj.group('uri') != None and matchobj.group('uri') == "":
255+
return matchobj.group(0)
256+
url = matchobj.group('url')
257+
text = url
258+
if matchobj.group('mailadr'): # if an assumed Email address,
259+
if url[0] in string.digits: # and it starts with a digit
260+
return matchobj.group(0) # don't make it live (msg id?)
261+
url = "mailto:" + url # otherwise add mailto: tag
262+
if matchobj.group('web'): # if a web link,
263+
url = url + '" target="fromtpc' # put it into a new window
264+
return matchobj.group('opening') + \
265+
'<A HREF="'+url+'">'+text+'</A>' + \
266+
matchobj.group('closing')
267+
268+
def readnews(I="", A=None, P=None, RESPONSE=None):
269+
"""Display article in HTML"""
270+
article = I
271+
user = A
272+
password = P
273+
274+
RESPONSE.write("""<HTML><HEAD><TITLE>Tokyo PC Users Group</TITLE></HEAD>
275+
<BODY BGCOLOR="#FFFFFF">""")
276+
277+
try:
278+
news = NNTP(NEWS_SERVER)
279+
except:
280+
RESPONSE.write("Can not connect to server: " + NEWS_SERVER)
281+
resp = news.shortcmd('MODE READER')
282+
283+
if user:
284+
resp = news.shortcmd('authinfo user '+user)
285+
if resp[:3] == '381':
286+
if not password:
287+
RESPONSE.write("<B>Can not fetch article</B><P>")
288+
else:
289+
resp = news.shortcmd('authinfo pass '+password)
290+
if resp[:3] != '281':
291+
RESPONSE.write("<B>Can not fetch article</B><P>")
292+
293+
try:
294+
resp, nr, id, subs = news.head(article)
295+
except:
296+
RESPONSE.write("Article %s not available" % quote(article))
297+
298+
RESPONSE.write('<TABLE WIDTH="100%" BGCOLOR="#CFCFCF"><TR><TD>')
299+
300+
# build up the header (so we know Subject: by Newsgroups: output time)
301+
from_line = ""
302+
newsgroup_line = ""
303+
subject_line = ""
304+
keep_lines = ""
305+
mail_subject = ""
306+
for line in subs:
307+
ihdr = interesting_headers.match(line)
308+
if ihdr:
309+
if ihdr.group('from'):
310+
name, email = parseaddr(line[6:])
311+
if name:
312+
from_line = 'From: <A HREF="mailto:%s%s">%s</A> &lt;%s&gt;<BR>' % (
313+
email, "%s", name, email)
314+
else:
315+
from_line = 'From: <A HREF="mailto:%s%s">%s</A><BR>' % (
316+
email, "%s", email)
317+
elif ihdr.group('newsgroups'):
318+
newsgroup_line = 'Newsgroups: <A HREF="mailto:tpc-%s@tpc.ml.org%s">tpc.%s</A>%s<BR>' % (
319+
ihdr.group('group'), "%s",
320+
ihdr.group('group'), ihdr.group('othergroups'))
321+
elif ihdr.group('subject'):
322+
subject_line = 'Subject: <B>%s</B><BR>' % line[9:]
323+
if ihdr.group('re'):
324+
mail_subject = "?subject="+line[9:]
325+
else:
326+
mail_subject = "?subject=Re: "+line[9:]
327+
elif ihdr.group('keep'):
328+
keep_lines = keep_lines+line+"<BR>"
329+
330+
if from_line:
331+
RESPONSE.write(from_line % mail_subject)
332+
if newsgroup_line:
333+
RESPONSE.write(newsgroup_line % mail_subject)
334+
RESPONSE.write(subject_line + keep_lines)
335+
336+
RESPONSE.write('</TD></TR></TABLE><P>')
337+
338+
try:
339+
resp, nr, id, subs = news.body(article)
340+
except:
341+
RESPONSE.write("Article %s body not available" % article)
342+
343+
RESPONSE.write("<CODE>")
344+
for line in subs:
345+
RESPONSE.write(re.sub(r'''(?i)(?x)
346+
(?P<opening>[<(";]?)
347+
(?P<url>(((?P<web>http:)|(news:)|(mailto:)|(telnet:))(?P<uri>\S*?))
348+
# a mail address is some non-ws characters followed by @
349+
# followed by a domain name that has at least one . in it
350+
|(?P<mailadr>\S+@(\S+\.)+\S+?))
351+
# either a URL or a mail address will not contain [)">\s]
352+
# and will not end with punctuation just before the whitespace
353+
(?P<closing>([)"'>\s]|$|([&.?,:;]\s)+))''',
354+
liven_url, line) + "<BR>")
355+
RESPONSE.write("</CODE>")
356+
357+
RESPONSE.write("</BODY></HTML>")
358+
resp = news.quit()
359+
360+
if __name__ == '__main__':
361+
newsgroup('tpc.unix', '2')
362+
# readnews('<slrn6np315.g6.Jim.Tittsler@mon.dskk.co.jp>')

‎pcgiwebnews.txt

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
pcgiwebnews.py
2+
3+
Jim Tittsler, Jim.Tittsler@tpc.ml.org
4+
5+
http://starship.python.net/crew/jtittsler/
6+
7+
1999.05.09
8+
9+
pcgiwebnews is a Persistent CGI program for accessing an NNRP news
10+
server and rendering the article list and the articles in a three pane
11+
HTML view. It was written to provide an alternative means of accessing
12+
our community news server for those that are prohibited by firewalls from
13+
using a real news client, or for people that just want to sample the
14+
groups and are intimidated by setting up a news client.
15+
16+
pcgiwebnews uses part of the Bobo object publishing technique that
17+
was a precursor to Zope. It effectively treats the news server as
18+
an underlying database.
19+
20+
Access to some newsgroups can require authentication. This can be
21+
accommodated by pcgiwebnews by building in account (A=) and password
22+
(P=) parameters in the newsgroup URLs.
23+
24+
The user posts articles by mailing them to a mail-to-news gateway.
25+
This allows him to use the features of his mail client (like
26+
spell checking), rather than forcing him to enter data into an
27+
HTML textbox.
28+
29+
Because of resource constraints on the machine where pcgiwebnews was
30+
originally hosted, the "front page" that initially greets the user is
31+
periodically updated by a cron job (ngupdate) rather than being
32+
*live*. ngupdate looks through a list of static HTML files for lines
33+
that match the newsgroup table and updates the number of articles
34+
posted in the past 1/7/21 days. This reduces load at the expense of
35+
not having the absolute latest information on the number of articles
36+
for each group. It would be worthwhile to make the front page *live*
37+
and also to keep track of articles seen by the user (with cookies or
38+
login) to highlight new messages.
39+
40+
.. PCGI http://starship.python.net/crew/jbauer/persistcgi/index.html
41+
.. Bobo http://www.digicool.com/
42+
.. Example installation http://www.tpc.ml.org
43+

0 commit comments

Comments
 (0)
Please sign in to comment.