架設 NEWS


常上新聞組的朋友一定在那裡學到不少東西吧﹖您又有否想過自己架一個 NEWS 伺服器呢﹖﹖其實在有些企業內部﹐也會透過 NEWS 伺服器進行公告、技術討論、工作彙報等事情。好消息是﹕要架設一個 NEWS 伺服器並不難﹐下面的步驟就是教您如何在本地網路中架一個 NEWS 伺服器。

設定news

如果您當初在安裝的時候選擇 everything 的話﹐相信您的機器上面已經有一個叫“inn”的 NEWS 伺服器了。請打﹕ setup --> System services --> [ * ] innd ﹐就可以讓 innd 在每次啟動的時候載入了。不過﹐如果還沒啟動的話﹐也可以使用 /etc/rc.d/init.d/innd start 來將之啟動啦。

如果您的 innd 已經成功啟動﹐在預設的情況下﹐您已經有‘control’、‘control.cancel’、‘junk’、‘test’者四個新聞群組了。只要您用 NEWS 客戶程式(如 outlook 等)﹐連上您的 NEWS 伺服器﹐就可以看到上面的群組了。

小心哦﹕上述的四個群組不能刪除哦﹐否則您的 innd 可能跑不起來。如果您不想用戶連接到它們﹐那麼參考後面的說明修改/etc/news/nnrp.access這個設定檔。例如﹕

192.168.0.*:Read Post:::*,!control,!control.cancel,!junk,!test,!to
(這裡我要特別多謝 Startac 兄 提供的意見﹗)

如果您要增加新的群組﹐只要輸入﹕
ctlinnd newgroup test.netman
就可以增加一個叫‘test.netman’的新聞組了。您再用您的客戶程式重新連接就可以看得到。

如果您輸入﹕
ctlinnd rmgroup test.netman
就會剛才建立的群組移除掉。

這樣看來﹐要建立起一個 NEWS 伺服器似乎一點也不難。不過﹐有幾個檔案我們還是要修改一下的﹕

/etc/news/inn.conf

organization: netman's news site
這是用來設定組織名稱的﹐您看一看原來的句子可能會有會心的微笑。

server:                 redhat52.siyongc.domain
指定 NEWS 伺服器的名稱。

pathhost:               redhat52.siyongc.domain
指定群組的 path 。

moderatormailer:        webmaster@siyongc.domain
指定管制群組的管理者信箱。

domain:                 siyongc.domain
指定網域名稱。

fromhost:               redhat52.siyongc.domain
指定文章的‘From’地址。

其它的設定﹐使用預設則可。

/etc/news/expire.ctl

/remember/:14
只記錄兩星期的文章標題。

*:A:1:10:never
在預設情況下﹐所有文章都會保留‘1 至 10’天﹔而允許標題‘永不過期(never)’。不過﹐您也可以指定某些群組的保留期限﹐如﹕
netman.*:M:1:35:90
test.*:A:never:never:never
這樣﹐以‘netman.’開頭的群組都會最多保留 90 天。而以‘test.’開頭的群組則永遠保留。

/etc/news/nnrp.access

*.siyongc.domain:Read Post:::*
192.168.0.*:Read Post:::*
這樣﹐所有從‘siyongc.domain’或‘192.168.0’這個網域連上來的使用者都可以‘讀’和‘刊登’文章。

在修改上面的檔案時﹐請注意在句子後面不能有空白鍵﹐如果您不知道有否﹐執行
inncheck -v
就可以得知。使用這個命令﹐我們還可以得知要設定 NEWS 伺服器要修改的檔案有哪些﹐以及它們有沒有設定錯誤。

和其他news伺服器連接

NEWS 系統有一個功能是﹕新聞群組可以從一個伺服器傳給另外一個伺服器﹐一直傳遞開去﹐從而達到一信傳千里的效果。要做到這個目的﹐您得在各伺服器之間建立餵信(newsfeed)功能。這需要雙方伺服器的極度配合方可達成。

有些朋友喜歡用上游、下游來稱呼參與餵信過程的雙方﹐不過您也可以稱之為餵方(feeder)和受方(receiver)。因為 news 並非如 DNS 那樣的樹狀層級架構。比如 server A 和 server B 互有信件對傳﹐那麼它們之間既是上游﹐也是下游﹐端看信件的傳遞方向而定。

前面我們已經知道架設好 innd 之後﹐可以用 ctlinnd newgroup 來增加群組。但技術上來說﹐它只是增加一個 active 群組﹐您可以在 /var/lib/news/active 檔案裡面找到。不過﹐請您不要胡亂修改它﹐否則您可能失掉整個群組。但如果您要為該群組做些說明﹐可以修改 /var/lib/news/newsgroups 這個檔﹐將 active 裡面的群組名稱寫進去﹐然後在它後面增加一些說明。例如﹕我先用下面命令建立三個群組﹕
ctlinnd newgroup netman.test
ctlinnd newgroup siyongc
ctlinnd newgroup private

然後修改 /var/lib/news/newsgroup :

netman.test	A test group for netman
siyongc		A public group for siyongc
private		A private group

上面是手工的方法﹐如果您的上游 news server 有許多已經建立起來的群組﹐您想您的 server 也能接收(或回送)它們的文章﹐您必須要先將群組建立起來﹐這恐怕是一件非常累人的事情。不過﹐下面教您一個方法﹐一次過將上游的群組名單全部抓回來。

在我們這例子中﹐假設上游 server 叫做 news1.siyongc.domain ﹐而下游 server 叫 news2.home.siyongc.domain 。這個 news1 已經跑起來好一段日子了﹐且也接受外部的餵信﹐這個我們暫時不管它。而 news2 則是剛建立好﹐且測試過可以勝任本地新聞的工作。那麼我們在 news2 的主機上依順序輸入﹕
service innd stop
chown news:news /var/lib/news/*
su - news
telnet news1.siyongc.domain nntp > /tmp/active.new
list active
quit
telnet news1.siyongc.domain nntp > /tmp/newsgroups.new
list newsgroups
quit

您在 telnet 過程中熒幕是沒有顯示回應結果的﹐如果您想確定一下看到什麼﹐那就只輸入 " > " 之前的句子看看吧。然後您要修改新下載回來的 active.new 和 newsgroups.new 兩個檔﹐把開頭的 5 或 6 行(至那個以 215 開頭的句子)和最後 2 行文字刪除掉﹐這樣您就擁有和上游一模一樣的群組名單了。但這還不夠完整﹐您還要執行下面命令﹐重設 active 和 newsgroups 檔案﹕
(繼續保持 news 身份﹐否則 su -news)
cd /var/lib/news
cp active active.bak
cp newsgroups newsgroups.bak
awk '{ $2 = "0000000000" ; $3 = "0000000001" ; print }' /tmp/active.new >> active
cat active | sort -o active
cat /tmp/newsgroups.new >> newsgroups
cat newsgroups | sort -o newsgroups

您可能需要手工的修改上面檔案﹐將您原來的群組和新群組重複的部份刪除掉。最後執行 inncheck -v 看看是否有地方需要修改﹐有則根據提示改之﹐直到再沒錯誤才繼續。否則﹐您的 NEWS 可能跑不起來哦﹐盡管執行 service innd start 後您會看到 OK﹐您用 service innd status 就可以確定了。切記切記﹗

好了﹐當群組名單建立起來之後﹐我們就可以正式修改 innd 的檔案﹐以接受餵方的信件了。除非真有必要﹐我建議您保留 news 的身份來設定所有的 innd 檔案。不過好消息是﹕受方只需設定一個檔案就可以了﹐您只需修改 /etc/news/incoming.conf 檔﹐給餵方起一個名稱(可隨意)﹐然後將它的地址寫好就行﹕

peer news1 {
  hostname:	news1.siyongc.domain
}

設定完之後﹐可以用 root 身份重新 stop 和 start 那個 innd 服務以確定設定生效。但這動作可能不是必須的﹐如果不影響正常運作﹐做一下也無妨就是了。

雖然您把受方的大門打開了﹐如果餵方沒有相應的設定﹐那麼您還是收不到任何信件的。接下來﹐就是餵方的設定啦。這裡﹐我假設您已搖身一變成為餵方 server 的管理員了。 :)

在一切正常的情形下﹐餵方只需修改兩個檔案﹕ /etc/news/nntpsend.ctl 和 /etc/news/newsfeeds 。讓我們逐個來吧。一如設定受方的 incoming.conf 那樣﹐您必須為受方定義一個名稱(可隨意)﹐然後將地址寫到 nntpsend.ctl 檔案裡面﹕

news2:news2.home.siyongc.domain::

就這麼簡單﹐一行而已﹐如果您還要其它設定﹐可以 man nntpsend.ctl 。不過﹐請記著第一個 ":" 號前的名字﹐在下面的 newsfeeds 檔案裡面要使用和它一樣的名稱﹕

news2\
	:netman.*,siyong,!private,!junk,!control/!foo\
	:Tf,Wnm:

好了﹐這個檔案有必要解釋一下了。

  1. 首先定義一個受方名稱﹐它和 nntpsend.ctl 裡面定義的要一致。
  2. 然後在第一個 ":" 和第二個 ":" 之間﹐指定有哪些群組要餵送﹐各群組名稱用 "﹐" 來分隔。如果群組前面用 "﹗" 就表示該群組絕對不進行餵送。
  3. 再來在第二個 ":" 和第三個 ":" 之間﹐指定了用哪些方式進行餵送。這裡設定是用 batch file 的方式﹐經由 innxmit 程式用 nntp 協定進行餵送。具體餵送方式的設定可以 man newsfeeds ﹐或參考 http://bbs.ee.ntu.edu.tw/boards/NetNews/7/4/index.html
  4. 如果還要其它設定﹐請自己參考資料﹐或 man newsfeeds 。

當一切設定妥當之後﹐在 news1 的相關群組發表一些文章﹐看看 /var/spool/news/outgoing 目錄下面是否有 news2 這個 batch file 檔案。如果沒有﹐就試試重新啟動 innd ﹕
su -
service innd stop
service innd start
(不建議用 restart)
service innd status (確定啟動正常)
exit (回到 news 身份)

如果 news2 檔案存在﹐可以 cat 一下看看是否有文章標號和 message-ID 出現。如果沒有可能要等一下﹐如果一直都沒有﹐那就可能是設定上某部份出現問題了(這個我就不是很確定了)。當萬事俱備之後﹐我們就可以進行餵送了﹕
nntpsend

然後看看 /var/log/news/nntpsend.log 有沒有錯誤信息。如果遇到什麼 "Ignoreing line ......" 的話﹐那可能是 batch file 的格式不對。如果用 innxmit 的話﹐請 man innxmit 看看格式規定是否和檔案一致。如果用其它的﹐請自行找資料。這就這要求您小心設定 newsfeeds 檔案了。

假如 log 信息沒錯誤﹐那麼我相信受方那邊已經成功收到更新文章了。如果沒有的話﹐那重新檢查所有步驟吧﹐或到各 BBS 找答案吧。不過﹐當 batchfile 建立起來之後﹐您可以可以手工的用 innxmit 來試試看﹕
innxmit -a -d news2.home.siyongc.domain news2

小心哦﹐這樣可能會把 batch file 移除掉哦﹐不過您可以重新啟動 innd 建立回來啦。

如果一切順利﹐那就開香賓慶祝一下吧﹗然後﹐您或許會希望 news2 回應的文章﹐也可以送回到 news1 去吧﹖現在相信難不到您啦﹐參考上面的設定﹐稍微改改餵方和受方名稱﹐然後換一台機器設而已啦。

剛才我們是用 nntpsend 這個程式手工進行餵送的﹐如果您沒時間親自執行這個動作﹐最簡單莫過於用 crontable 來做啦。只要用 root 身份執行 crontable -u news -e﹐然後加入﹕

0,10,20,30,40,50 * * * * /usr/bin/nntpsend

醬子就大功告成啦 ~~~ 哇~﹗好開心哦~~~﹗﹗ ^_^

設定 suck

好了﹐前面說的餵信方式﹐要經過雙方伺服器的配合才能完成﹐那麼有沒有法子不需要上游餵進來﹐而主動去抓文章的辦法呢﹖當然有啦﹗用 suck 就可以做到﹐下面就和大家談談如何用 suck 抓信吧﹐而且還能 post 回去哦~~~

這裡我要特別感謝 lman 兄<lman@ccns.ncku.edu.tw>的大力幫助﹐才讓我的測試順利完成。有興趣討論 suck 相關技術的朋友也可以找他研究一下的。

當然﹐首先要做的事情是取得 suck 這隻程式﹐我這裡使用的是 suck-4.2.3-1.i386.rpm ﹐從 http://rpmfind.net/linux/RPM/ 那裡抓的。直接用 rpm 安裝就好了﹐根本不用設定一大堆有的沒的﹐如 prefix 之類的東東。

不過﹐在真正動手設定 suck 之前﹐我們首先要修改 innd ﹐以符合這個特殊的環境。首先﹐您不能像前面教的被動餵信模式那樣保留長長的一列 /var/lib/news/active 記錄。因為 suck 會依據 /var/lib/suck/sucknewsrc 這個檔案來決定到上游抓信。這本來沒什麼問題﹐您可以手工的修改它(請參考 man suck)﹐但在我目前使用的這個 rpm 套件版本﹐sucknewsrc 卻會在執行 get.news.inn (後面就會談到) 之後把群組名稱同步為 active 記錄裡面的名稱(假如您使用預設的 -A 選項)。如果您的 active 是滿滿的一大串﹐那麼下次執行 get.news.inn 的時候﹐恐怕會嘗試抓所有的群組信件回來﹐這樣子﹐就算您不計較時間和頻寬﹐您也得每天至少準備 500MB 的空間(取決於名單長度)來裝那些信件。(tips:您也可以修改 /var/lib/suck/active-ignore 檔案來指定哪些群組不用去抓。)

這不同正常的被動模式那樣﹕可以通過 newsfeeds 和 incoming.conf 來限制群組名單哦﹐所以一定要小心設計 active﹐把不必要的群組拿掉。您可以手工刪除﹐也可以動一下腦筋去修改啦﹐比方我只想留 tw.bbs.comp 和本地的群組﹐我會這樣做﹕
cd /var/lib/news
cat active | grep "tw.bbs.comp" | awk '{ $2 = "0000000000" ; \
  $3 = "0000000001" ; print }' > active.tmp    # 同時完成歸零的動作
cat active.bak > active    # 這是上次抓上游 active 時﹐原來的本機群組備份)
cat active.tmp >> active

不過﹐如果您不想 suck 全部把 active 的東都更新至 sucknewsrc 去﹐您可以在 /var/lib/suck 目錄下建立一個 active-ignore 檔﹐把一些本機的群組﹐或那些不想到上游抓的群組名單﹐一行一個的寫進裡面去。

另外﹐您還得修改 /etc/news/newsfeeds ﹐為上游定義一個名稱﹐以及把您要回餵的群組寫進去。您可以參考前面所教的範例﹐不過﹐我現在的例子要用 news1 這個名稱哦﹗而且每次修改好 innd﹐別忘了讓 innd 重新讀入更新檔案或重新啟動。

等安裝好 suck 之後﹐就進入 /var/lib/suck 這個目錄工作了。您全部要做的只是編輯一個檔案﹐修改 get.news.inn ﹐主要是下面這幾句就夠了﹕

把您要抓的上游主機寫好﹕

REMOTE_HOST=news1.siyongc.domain

接著也給上游起一個名字﹐是用來把信件餵回上去的。要注意﹕這個名稱跟 newsfeeds 裡面所定義的要一至哦﹗

SITE=news1

如果您執行這個 script 沒問題的話﹐應該不用做什麼修改了。但如果您碰到“GROUP command not recognized, try the -M option”這樣的錯誤信息﹐那您還要修改另外兩行﹐在句子中插入一個 " -M " 選項﹕


......

    # download messages
    ${SUCK} ${REMOTE_HOST} -M -c -A -bp -hl ${LOCAL_HOST} -dt ${TMPDIR} -dm ${MSGDI
R} -dd ${BASEDIR} -HF ${HISTORY}

......

            # outgoing messages to post
            ${RPOST} ${REMOTE_HOST} -M -d -b ${OUTGOINGNEW} -p ${SPOOLDIR} -f \$
\$o=${OUTFILE} ${SCRIPT} \$\$i ${OUTFILE}

......

這裡﹐我將修改過的的 get.news.inn 這個 script 內容列出來﹐希望對您有所參考。因為﹐在測試過程中曾對它修改了好多次﹐最後才變成目前這個樣子 (它是用來抓 hinet 上面的 news 用的)﹕

#!/bin/sh

#BEFORE USING - check to ensure all the paths defined below are correct!!

#NOTE: this script probably needs to be run by root.  Most systems will
# not let a normal user run ctlinnd 

REMOTE_HOST=news.hinet.net
INN_DIR=/etc/news
INN_CONF=${INN_DIR}/inn.conf
LOCAL_HOST=localhost
LOCAL_TEST=$(cat ${INN_CONF} | grep fromhost | awk '{print $2}')
if [ "$LOCAL_TEST" != "" ]; then
	echo "Set LOCAL_INN to '$LOCAL_TEST'"
	LOCAL_INN=${LOCAL_TEST}
fi

SPOOLDIR=/var/spool/news/articles		# base directory for articles to be rposted
NEWSDIR=/usr				# base directory for news binaries 
BASEDIR=/var/lib/suck			# base directory for scripts and data files
FILTER=${BASEDIR}/newsfil.pl

CTLINND=${NEWSDIR}/bin/ctlinnd		# location of binary
SHLOCK=${NEWSDIR}/bin/shlock		# location of binary

TMPDIR=${BASEDIR}				# location for suck.* files
MSGDIR=${BASEDIR}/Msgs			# where to put MultiFile messages when getting them

SITE=hinet				# name of site from newsfeeds file

OUTGOING=/var/spool/news/outgoing/${SITE}	# location of the list of articles to upload
OUTGOINGNEW=${OUTGOING}.new			# file to contain the list temporarily
OUTGOINGFAIL=${OUTGOINGNEW}.fail		# file with failed xfers
SCRIPT=${BASEDIR}/put.news			# my filter for rpost
OUTFILE=/tmp/tmp$$				# used by rpost as article after it is filtered
LOCKFILE=${BASEDIR}/getnews.lock		# lock file to prevent multiple instances of script
NEWSGROUP=news				# which group owns the file in out.going, 
					# typically either news or uucp.

TESTHOST=testhost
RPOST=rpost
SUCK=suck

HISTORY=/var/lib/news/history		# location of history file

# if we are already running, abort 

trap 'rm -f ${LOCKFILE} ; echo "Aborting" ; exit 1' 1 2 3 15
${SHLOCK} -p $$ -f ${LOCKFILE}
if [ $? -ne 0 ]; then
	echo "Already running, can't run two at one time"
	exit
fi

# is the local host up and running so we can post messages we download?
${TESTHOST} ${LOCAL_HOST} -s
LOCAL_RESULT=$?

# is the remote host up and running so we can download messages?
${TESTHOST} ${REMOTE_HOST} -s
REMOTE_RESULT=$?

if [ ${REMOTE_RESULT} -eq 0 -a ${LOCAL_RESULT} -eq 0 ]; then
	
  # download messages
  if [ -x ${FILTER} ]; then
	${SUCK} ${REMOTE_HOST} -c -A -bp -hl ${LOCAL_HOST} -dt ${TMPDIR}\
	   -dm ${MSGDIR} -dd ${BASEDIR} -HF ${HISTORY} -y ${FILTER}
  else
	${SUCK} ${REMOTE_HOST} -c -A -bp -hl ${LOCAL_HOST} -dt ${TMPDIR}\
	   -dm ${MSGDIR} -dd ${BASEDIR} -HF ${HISTORY}
  fi

  SUCK_STATUS=$?

  if [ ${SUCK_STATUS} -eq 0 ]; then
	echo "Downloaded Articles"
  elif [ ${SUCK_STATUS} -eq 1 ]; then
	echo "No articles to download"
  elif [ ${SUCK_STATUS} -eq 2 ]; then
	echo "Unexpected answer from remote server to an issued command"
  elif [ ${SUCK_STATUS} -eq 4 ]; then
	echo "Can't do NNTP authorization"
  elif [ ${SUCK_STATUS} -eq -1 ]; then
	echo "General failure"
  fi

  # now upload messages

  # filtering outgoing articles

  if [ -s ${OUTGOING} ]; then
	echo "LOCAL_INN is ${LOCAL_INN}"
	 echo "OUTGOING is ${OUTGOING}"
	echo "Filtering ${OUTGOING} ..."
	cat ${OUTGOING} | grep ${LOCAL_INN} > ${OUTGOING}
  fi
  if [ -s ${OUTGOINGNEW} ]; then
	echo "OUTGOINGNEW is ${OUTGOINGNEW}"
	echo "Filtering ${OUTGOINGNEW} ..."
	cat ${OUTGOINGNEW} | grep ${LOCAL_INN} > ${OUTGOINGNEW}
  fi

  if [ -s ${OUTGOING}  -o -s ${OUTGOINGNEW} ]; then

	${TESTHOST} ${REMOTE_HOST} -s

	  if [ $? -ne 0 ]; then
		echo "Remote posting host not responding"
 	 else
		# this is needed by INND so that the outgoing file will be
		# properly flushed and we have a new blank file to work with
		# when we are done
		# First mv the current one to a new file name
		# Since innd already has the file open, it doesn't care 
		# about the rename.
		# The flush will ensure that all messages to be posted have
		# been written out, close off the old one (already renamed)
		# and create a new one.

		# if the outgoingnew already exists, it means we aborted last time
		# so don't try to do it again
		if [ ! -s ${OUTGOINGNEW} ]; then
		  mv ${OUTGOING} ${OUTGOINGNEW}
		  ${CTLINND} flush ${SITE}
		fi

		# outgoing messages to post
		echo "Posting outgoing articles..."
		${RPOST} ${REMOTE_HOST} -d -b ${OUTGOINGNEW}\
		   -p ${SPOOLDIR} -f \$\$o=${OUTFILE} ${SCRIPT} \$\$i ${OUTFILE}

		ERRLEV=$?
		if [ ${ERRLEV} -eq 0 ]; then
		  echo "Remotely posted articles"
		  rm ${OUTFILE}
		  elif [ ${ERRLEV} -eq 1 ]; then
		  echo "Error posting a message"
		elif [ ${ERRLEV} -eq 2 ]; then
		  echo "Unable to do NNTP authorization with remote server"
		elif [ ${ERRLEV} -eq 3 ]; then
		  echo "Unexpected answer from remote server to a command while doing NNTP authorization"
		elif [ ${ERRLEV} -eq -1 ]; then
		  echo "Fatal error"
		fi

		if [ -f ${OUTGOINGFAIL} ]; then
		  mv ${OUTGOINGFAIL} ${OUTGOINGNEW}	# so we can re do it
		  chown news.${NEWSGROUP} ${OUTGOINGNEW}
		  chmod 664 ${OUTGOINGNEW}
		fi
	fi
  fi	
	
  echo "Suck has finished the job, enjoy it!"
fi

rm -f ${LOCKFILE}

### added for restarting innd
### to make sure innd is running well.
echo "Trying to restart innd..."
sleep 5
/etc/rc.d/init.d/innd stop
sleep 10 
/etc/rc.d/init.d/innd start
innstatus=`/etc/rc.d/init.d/innd status`
chkinn=`echo ${innstatus} | grep 'pid'` 
if [ -z "$chkinn" ]; then
	echo "WARNING: something wrong with innd! Please fix it."
else
	echo "$innstatus"
fi
exit 0

不過﹐就算您的設定完全沒問題﹐根據我這裡的經驗﹐您還不能抓上游的文章啦。您必須還要幫 sucknewsrc 做一個歸零的動作﹕
cat sucknewsrc | awk '{ $2 = "1" ; print }' > sucknewsrc

然後再重新執行 get.news.inn 。

假如您有時間﹐應該 man suck 看看如何設定這個 sucknewsrc 的﹐這樣您就可以對群組有更大的控制空間了。

關於過濾

雖然您能夠成功抓上游的文章了﹐但通常來說﹐您是沒選擇的。也就是上面有什麼您就抓什麼﹐不管好的、壞的、想要的、不想要的﹐都通通會抓下來。這不僅讓您在閱讀標題時非常“不爽”﹐而且還會浪費許多磁碟空間。

這時候﹐您或許想用一些方法把那些不想要的文章過濾掉吧。剛好﹐最近在一位高手 AcE 兄的大力協助之下﹐寫了一個 script ﹐專門用來砍 news 文章用的。下面就和大家分享一下 ﹕
#!/usr/bin/perl -P
#
#~~~~~~~~~~~
#
#	Purpose:	Filtering BAD news articles.
# 	Author:	netman <netman@study-area.net>
#	Origin:	AcE <ace@coventive.com>
#
#	Version:	1.0.1
#	Date:	2000/10/31
#
#	License:	GNU/GPL <http://www.gnu.org/copyleft/gpl.html>
#
#~~~~~~~~~~~

if ( $ARGV[0] =~ /^$/) {
	printf "ERROR:  missing argument.\n";
	printf "	Syntax: $0 DIR_PATH\n\n";
	exit 1;
}
else { 
	opendir (FDIR, $ARGV[0]) or die "ERROR: Can't open $ARGV[0].\n\n";
	close FDIR;
}

printf "Feltering BAD articles under $ARGV[0] ...\n";

# List for Content filtering
$CList="/var/lib/suck/clist";

# List for 'Subject' filtering
$SList="/var/lib/suck/slist";

# List for 'From' filtering
$FList="/var/lib/suck/flist";

# List for 'Host' filtering
$HList="/var/lib/suck/hlist";

########## Starting Content feltering:
open(cList,"${CList}") or die "Can't open ${CList}.\n";
$index=0;
while()
{
  chop;
  $keys[$index]=$_;
  #printf "$keys[$index]\n";
  $index++;
}
close(cList);

for($i=0;$i<$index;$i++){
  $keyword=$keys[$i];
  printf "searching $keyword in Content.\n";
  system("grep -i -r '$keyword' $ARGV[0] > wait4kill-c");

  open(killList,"./wait4kill-c");
  while(){
    if($_ =~ /^([^:]+):.*$/){
		if($tt ne $1){
	       printf "Deleting $1 ...\n";
		   system("rm -f $1");
		   $tt=$1;
		}
     }

  }
  close(killList);
}
system("rm -f ./wait4kill-c");


########## Starting 'Newsgroups' feltering:
printf "Filtering Newsgroups...\n";
system("grep -r '^Newsgroups.*tw.*tw.*.tw' $ARGV[0] > wait4kill-n");

open(killList,"./wait4kill-n");
	while(){
     if($_ =~ /^(.+):Newsgroups.*$/){
       printf "Deleting $1 ...\n";
	   system("rm -f $1");
     }
	}
close(killList);
system("rm -f ./wait4kill-n");


########## Starting 'Subject' feltering:
open(sList,"${SList}") or die "Can't open ${SList}.\n";
$index=0;
while()
{
  chop;
  $keys[$index]=$_;
  #printf "$keys[$index]\n";
  $index++;
}
close(sList);


for($i=0;$i<$index;$i++){
  $keyword=$keys[$i];
  printf "Searching $keyword in \n";
  system("grep -r 'Subject:.*$keyword.*' $ARGV[0] > wait4kill-s");

  open(killList,"./wait4kill-s");
  while(){
    if($_ =~ /^(.+):Subject:.*$/){
       printf "Deleting $1 ...\n";
	   system("rm -f $1");
    }

  }
close(killList);
}
system("rm -f ./wait4kill-s");

########## Starting 'From' feltering:
open(fList,"${FList}") or die "Can't open ${FList}.\n";
$index=0;
while()
{
  chop;
  $keys[$index]=$_;
  #printf "$keys[$index]\n";
  $index++;
}
close(fList);

for($i=0;$i<$index;$i++){
  $keyword=$keys[$i];
  printf "Searching $keyword in \n";
  system("grep -r 'From:.*$keyword.*' $ARGV[0] > wait4kill-f");


  open(killList,"./wait4kill-f");
  while(){
     if($_ =~ /^(.+):From:.*$/){
       printf "Deleting $1 ...\n";
	   system("rm -f $1");
     }

  }

  close(killList);
}
system("rm -f ./wait4kill-f");

########## Starting 'NNTP-Posting-Host' feltering:
open(hList,"${HList}") or die "Can't open ${HList}.\n";
$index=0;
while()
{
  chop;
  $keys[$index]=$_;
  #printf "$keys[$index]\n";
  $index++;
}
close(hList);

for($i=0;$i<$index;$i++){
  $keyword=$keys[$i];
  printf "searching $keyword in \n";
  system("grep -r 'NNTP-Posting-Host:.*$keyword.*' $ARGV[0] > wait4kill-h");

  open(killList,"./wait4kill-h");
  while(){
     if($_ =~ /^(.+):NNTP-Posting-Host:.*$/){
       printf "Deleting $1 ...\n";
	   system("rm -f $1");
     }

  }
  close(killList);
}
system("rm -f ./wait4kill-h");

然後您針對 script 所需在 /var/lib/suck 目錄下建立清單﹐將您要過濾的字串相應填上﹐逐行寫就是了。下面是各檔的內容﹕

clist
這是針對文章內容而設的。那麼凡是文章包含有此清單上面的字串都會被砍掉。這是威力最大的﹐也最容易誤砍無辜者﹐所以不要列太多名稱。我通常建議您把那些廣告者所宣傳的網站、電話、地址、單位名稱、等具“唯一性”的字眼寫在此檔。
slist
這是針對標題而設的。如果您想對所有標題中的字串進行過濾﹐就將可以寫在這裡。例如什麼﹕空片、情趣用品、無碼、等等。
flist
這是針對發信人名稱而設的。如果有人常用什麼宣傳王、佈告王、廣告王、片片俱樂部等名稱來發信的話﹐加在這裡就對了。
hlist
這是針對發信主機而設的。如果您用 outlook 的話﹐可以用鼠標右點廣告文章的標題﹐然後看“內容”﹔或是用 netscape 的新聞閱讀器看文章的“source”﹐就可以找到一個“NNTP-Posting-Host”的句子﹐冒號後面就是該發信主機了。除非對方用非固定的連線或用程式修改發信文章﹐那麼您可以把所有從這台主機所發的文件砍掉。

如果您有 cross-post 的習慣﹐也就是一次過向不同的新聞群組發佈文章的話。不好意思﹐假如您要發佈的新聞群組中超過三個是用“tw”開頭的﹐就通不過這個 script了。不過﹐您可以修改其中關於 “Filtering Newsgroups”部份的 Regular Expression﹐以符合您的需求。

上面那個 newsfil.pl 只要寫好﹐連同那些過濾清單檔一起放在 /var/lib/suck 目錄下面﹐那麼當執行 get.news.inn 的時候﹐就會被執行起來。不過﹐它除了可以用來砍每次 suck 下載時的信件。而且也用來清理特定目錄裡面的新聞郵件﹐包括子目錄哦。只要執行 ./newsfil.pl /the/path/of/dir 就可以了﹐但要小心哦﹕如果不是新聞檔的存放目錄﹐那麼﹐只要檔案裡面有相關字串的話﹐都一律會被砍掉哦~~~ 小心小心﹗

好了﹐網中人把上面提到的相關檔案﹐都包在一個壓縮檔了。您只要 點這裡 將它下載回去﹐就可以慢慢針對自己的實際情形修改了。

測試

假如不牽涉餵信的問題﹐設定一個 NEWS 伺服器是非常簡單的﹐應該不至於有問題出現。不過﹐會得運用 inncheck -v 來檢查﹐會幫我們找到許多設定問題。不過有些東西﹐inncheck 也無能為力﹐例如﹐在 active 檔案裡面﹐留意是否要包含原來的那些群組﹐例如 control、control.cancel、juck 等幾個﹐別以為從上游抓下來的就可以代替哦。反而﹐newsgroups 那個檔案倒不是很重要﹐只要將群組描述清楚就可以了。

當您完成一些設定之後﹐其實沒有必要一定重新啟動 innd 的。您可以執行 ctlinnd -h 看看有哪些動作可以做﹐比如﹐重新讀取 active 檔﹕
ctlinnd reload active "update active only"
註﹕別少了最後那個 “reason”哦﹐就算您不想打字﹐也應該要有一對 " " 符號。

不過﹐如果您真的需要重新啟動 innd ﹐我建議不要用 restart﹐而是先 stop 再 start﹐這還不夠﹐還要再輸入 service innd status 看看新的 innd 是否跑起來了。我發現就算顯示 start [ OK ] 之後也未必一定 OK 哦~~~

在做餵信的時候﹐最關鍵是 newsfeeds 那個檔案﹐一定要留意格式是否寫對了﹐以及傳送模式(如Tf)和寫入信息(如Wnm)是否設對﹐這裡的設定要符合 nntpsend.ctl 的要求。我強烈建議您 man newsfeeds 看看啦。當然﹐還別忘了受方也要設定好 incoming.conf 哦。

如果您要用 suck 來抓信和送信﹐要注意的地方是 get.news.inn 檔案裡面的 SITE= 要和 newsfeed 裡面設定的名稱一致。因為送信時是主要參考 /var/spool/news/outgoing 目錄下的名稱﹐它必須由 innd 產生﹐而供 suck 來讀取。要是在 post 過程中遇到問題﹐或許會在該目錄下產生一個 xxxx.new (xxxx 是 site 名稱)﹐這樣會造成往後的文章都不能 post。這時候﹐只要將那些 *.new 砍掉就是了。

就算如此﹐最後有一點您還是最好留意一下的﹐就是看看跑完 get.inn.news 之後﹐您的 innd 是否能夠順利啟動。根據我這邊的經驗是﹐通常第一次跑 get.inn.news 之後﹐/var/lib/news/active 檔有可能給弄得亂七八糟﹐尤其是那些數字可能會超過或少過 10 位數﹔也有時候會把一些空白意外移除掉了。這些都會導至 innd 跑不起來﹐而且可惡的是執行 inncheck 並不能檢查出來。如果真的如此﹐我建議您“逐行”檢查 active 檔的準確性。

還有﹐在我目前使用的版本和設定來實作﹐我發現在下游 post 的第一篇文章是無法用 suck 上載到上游群組中去的。不知道為什麼﹐不過只要從第一篇往後的回應文章(RE﹕)都可以就是了。怪怪的﹐如果您能幫我解決這問題的話﹐一定要告訴我哦~~~﹗(不過﹐後來在別的機器上試過又沒有﹐很怪啦~~)

最後﹐如果您想對 NEWS 做更深入的了解﹐可以參閱 <<曾桑談如何架設 News Server (修正版)>> 這篇文章。(我是從 台大電機 Maxwell BBS 精華區 看來的﹐雖然著作日期舊了一點﹐但絕對值得一讀﹗)

 

 


www.study-area.org © 2001 Netman 網中人
Last Updated: March 09, 2001