Drop PGP support.
[usenet/yapfaq.git] / yapfaq.pl
CommitLineData
dc88d139
TH
1#! /usr/bin/perl -W
2#
ace701c4 3# yapfaq Version 0.7 by Thomas Hochstein
7aaba0e0 4# (Original author: Marc Brockschmidt)
dc88d139 5#
7aaba0e0 6# This script posts any project described in its config-file. Most people
dc88d139
TH
7# will use it in combination with cron(8).
8#
9# Copyright (C) 2003 Marc Brockschmidt <marc@marcbrockschmidt.de>
7aaba0e0 10# Copyright (c) 2010 Thomas Hochstein <thh@inter.net>
dc88d139
TH
11#
12# It can be redistributed and/or modified under the same terms under
13# which Perl itself is published.
14
647af2ea 15my $Version = "0.8-prelease";
dc88d139 16
605916ef
TH
17# Please do not change this setting!
18# You may override the default .rc file (.yapfaqrc) by using "-c .rc file"
d60c2d5f 19my $RCFile = '.yapfaqrc';
605916ef 20# Valid configuration variables for use in a .rc file
0e741504 21my @ValidConfVars = ('NNTPServer','NNTPUser','NNTPPass','Sender','ConfigFile');
d60c2d5f 22
605916ef
TH
23################################### Defaults ###################################
24# Please do not change anything in here!
25# Use a runtime configuration file (.yapfaqrc by default) to override defaults.
7ef63844 26my %Config = (NNTPServer => "",
2507947f
TH
27 NNTPUser => "",
28 NNTPPass => "",
29 Sender => "",
0e741504 30 ConfigFile => "yapfaq.cfg");
dc88d139 31
605916ef 32################################# Main program #################################
dc88d139
TH
33
34use strict;
35use Net::NNTP;
40847f71 36use Net::Domain qw(hostfqdn);
dc88d139
TH
37use Date::Calc qw(Add_Delta_YM Add_Delta_Days Delta_Days Today);
38use Fcntl ':flock'; # import LOCK_* constants
4251e545 39use Getopt::Std;
dc88d139
TH
40my ($TDY, $TDM, $TDD) = Today(); #TD: Today's date
41
b9550622 42# read commandline options
4251e545 43my %Options;
86c0a100 44getopts('Vhvpdt:f:c:s:', \%Options);
b9550622 45# -V: print version / copyright information
a052296f
TH
46if ($Options{'V'}) {
47 print "$0 v $Version\nCopyright (c) 2003 Marc Brockschmidt <marc\@marcbrockschmidt.de>\nCopyright (c) 2010 Thomas Hochstein <thh\@inter.net>\n";
48 print "This program is free software; you may redistribute it and/or modify it under the same terms as Perl itself.\n";
49 exit(0);
50}
b9550622 51# -h: feed myself to perldoc
4251e545 52if ($Options{'h'}) {
ae3b1b79 53 exec ('perldoc', $0);
4251e545
TH
54 exit(0);
55};
b9550622 56# -f: set $Faq
4251e545
TH
57my ($Faq) = $Options{'f'} if ($Options{'f'});
58
d60c2d5f 59# read runtime configuration (configuration variables)
86c0a100
TH
60$RCFile = $Options{'c'} if ($Options{'c'});
61if (-f $RCFile) {
62 readrc (\$RCFile,\%Config);
63} else {
64 warn "$0: W: .rc file $RCFile does not exist!\n";
65}
d60c2d5f 66
b9550622 67# read configuration (configured FAQs)
dc88d139 68my @Config;
2507947f 69readconfig (\$Config{'ConfigFile'}, \@Config, \$Faq);
dc88d139 70
b9550622
TH
71# for each FAQ:
72# - parse configuration
73# - read status data
74# - if FAQ is due: call postfaq()
dc88d139
TH
75foreach (@Config) {
76 my ($LPD,$LPM,$LPY) = (01, 01, 0001); #LP: Last posting-date
77 my ($NPY,$NPM,$NPD); #NP: Next posting-date
78 my $SupersedeMID;
79
0c6ebe78 80 my ($ActName,$File,$PFreq,$Expire) =($$_{'name'},$$_{'file'},$$_{'posting-frequency'},$$_{'expires'});
dc88d139
TH
81 my ($From,$Subject,$NG,$Fup2)=($$_{'from'},$$_{'subject'},$$_{'ngs'},$$_{'fup2'});
82 my ($MIDF,$ReplyTo,$ExtHea)=($$_{'mid-format'},$$_{'reply-to'},$$_{'extraheader'});
83 my ($Supersede) =($$_{'supersede'});
4251e545 84
b9550622 85 # -f: loop if not FAQ to post
4251e545
TH
86 next if (defined($Faq) && $ActName ne $Faq);
87
b9550622 88 # read status data
dc88d139
TH
89 if (open (FH, "<$File.cfg")) {
90 while(<FH>){
91 if (/##;; Lastpost:\s*(\d{1,2})\.(\d{1,2})\.(\d{2}(\d{2})?)/){
92 ($LPD, $LPM, $LPY) = ($1, $2, $3);
93 } elsif (/^##;;\s*LastMID:\s*(<\S+@\S+>)\s*$/) {
94 $SupersedeMID = $1;
95 }
96 }
97 close FH;
98 } else {
74407146 99 warn "$0: W: Couldn't open $File.cfg: $!\n";
dc88d139
TH
100 }
101
102 $SupersedeMID = "" unless $Supersede;
103
0c6ebe78
TH
104 ($NPY,$NPM,$NPD) = calcdelta ($LPY,$LPM,$LPD,$PFreq);
105
b9550622 106 # if FAQ is due: get it out
4251e545
TH
107 if (Delta_Days($NPY,$NPM,$NPD,$TDY,$TDM,$TDD) >= 0 or ($Options{'p'})) {
108 if($Options{'d'}) {
109 print "$ActName: Would be posted now (but running in simulation mode [$0 -d]).\n" if $Options{'v'};
110 } else {
2507947f 111 postfaq(\$ActName,\$File,\$From,\$Subject,\$NG,\$Fup2,\$MIDF,\$ExtHea,\$Config{'Sender'},\$TDY,\$TDM,\$TDD,\$ReplyTo,\$SupersedeMID,\$Expire);
4251e545
TH
112 }
113 } elsif($Options{'v'}) {
114 print "$ActName: Nothing to do.\n";
dc88d139
TH
115 }
116}
117
118exit;
119
d60c2d5f
TH
120#################################### readrc ####################################
121# Takes a filename and the reference to an array which contains the valid options
122
123sub readrc{
124 my ($File, $Config) = @_;
125
126 print "Reading $$File.\n" if($Options{'v'});
127
128 open FH, "<$$File" or die "$0: Can't open $$File: $!";
129 while (<FH>) {
130 if (/^\s*(\S+)\s*=\s*'?(.*?)'?\s*(#.*$|$)/) {
131 if (grep(/$1/,@ValidConfVars)) {
132 $$Config{$1} = $2 if $2 ne '';
133 } else {
134 warn "$0: W: $1 is not a valid configuration variable (reading from $$File)\n";
135 }
136 }
137 }
138}
139
dc88d139 140################################## readconfig ##################################
4251e545
TH
141# Takes a filename, a reference to an array, which will hold hashes with
142# the data from $File, and - optionally - the name of the (single) FAQ to post
dc88d139
TH
143
144sub readconfig{
4251e545 145 my ($File, $Config, $Faq) = @_;
dc88d139
TH
146 my ($LastEntry, $Error, $i) = ('','',0);
147
b802358a 148 print "Reading configuration from $$File.\n" if($Options{'v'});
4251e545 149
74407146 150 open FH, "<$$File" or die "$0: E: Can't open $$File: $!";
dc88d139 151 while (<FH>) {
4251e545 152 next if (defined($$Faq) && !/^\s*=====\s*$/ && defined($$Config[$i]{'name'}) && $$Config[$i]{'name'} ne $$Faq );
dc88d139
TH
153 if (/^(\s*(\S+)\s*=\s*'?(.*?)'?\s*(#.*$|$)|^(.*?)'?\s*(#.*$|$))/ && not /^\s*$/) {
154 $LastEntry = lc($2) if $2;
155 $$Config[$i]{$LastEntry} .= $3 if $3;
156 $$Config[$i]{$LastEntry} .= "\n$5" if $5 && $5;
157 }
158 if (/^\s*=====\s*$/) {
159 $i++;
160 }
161 }
162 close FH;
163
164 #Check saved values:
165 for $i (0..$i){
4251e545 166 next if (defined($$Faq) && defined($$Config[$i]{'name'}) && $$Config[$i]{'name'} ne $$Faq );
dbca4ad8
TH
167 unless(defined($$Config[$i]{'name'}) && $$Config[$i]{'name'} =~ /^\S+$/) {
168 $Error .= "E: The name of your project \"$$Config[$i]{'name'}\" is not defined or contains whitespaces.\n"
dc88d139 169 }
dbca4ad8
TH
170 unless(defined($$Config[$i]{'file'}) && -f $$Config[$i]{'file'}) {
171 $Error .= "E: The file to post for your project \"$$Config[$i]{'name'}\" is not defined or does not exist.\n"
172 }
173 unless(defined($$Config[$i]{'from'}) && $$Config[$i]{'from'} =~ /\S+\@(\S+\.)?\S{2,}\.\S{2,}/) {
174 $Error .= "E: The From header for your project \"$$Config[$i]{'name'}\" seems to be incorrect.\n"
175 }
176 unless(defined($$Config[$i]{'ngs'}) && $$Config[$i]{'ngs'} =~ /^\S+$/) {
177 $Error .= "E: The Newsgroups header for your project \"$$Config[$i]{'name'}\" is not defined or contains whitespaces.\n"
178 }
179 unless(defined($$Config[$i]{'subject'})) {
180 $Error .= "E: The Subject header for your project \"$$Config[$i]{'name'}\" is not defined.\n"
dc88d139
TH
181 }
182 unless(!$$Config[$i]{'fup2'} || $$Config[$i]{'fup2'} =~ /^\S+$/) {
dbca4ad8 183 $Error .= "E: The Followup-To header for your project \"$$Config[$i]{'name'}\" contains whitespaces.\n"
dc88d139 184 }
dbca4ad8 185 unless(defined($$Config[$i]{'posting-frequency'}) && $$Config[$i]{'posting-frequency'} =~ /^\s*\d+\s*[dwmy]\s*$/) {
74407146 186 $Error .= "E: The Posting-frequency for your project \"$$Config[$i]{'name'}\" is invalid.\n"
dc88d139 187 }
5ddba442 188 unless(!$$Config[$i]{'expires'} || $$Config[$i]{'expires'} =~ /^\s*\d+\s*[dwmy]\s*$/) {
ac69c3ee
TH
189 warn "$0: W: The Expires for your project \"$$Config[$i]{'name'}\" is invalid - set to 3 month.\n";
190 $$Config[$i]{'expires'} = '3m'; # set default (3 month) if expires is unset or invalid
0c6ebe78 191 }
8f067a2f 192 unless(!$$Config[$i]{'mid-format'} || $$Config[$i]{'mid-format'} =~ /^<\S+\@(\S+\.)?\S{2,}\.\S{2,}>/) {
ac69c3ee
TH
193 warn "$0: W: The Message-ID format for your project \"$$Config[$i]{'name'}\" seems to be invalid - set to default.\n";
194 $$Config[$i]{'mid-format'} = '<%n-%d.%m.%y@'.hostfqdn.'>'; # set default if mid-format is invalid
40847f71 195 }
dc88d139 196 }
dbca4ad8 197 $Error .= "-" x 25 . 'program terminated' . "-" x 25 . "\n" if $Error;
dc88d139
TH
198 die $Error if $Error;
199}
200
0c6ebe78
TH
201################################# calcdelta #################################
202# Takes a date (year, month and day) and a time period (1d, 1w, 1m, 1y, ...)
203# and adds the latter to the former
204
205sub calcdelta {
206 my ($Year, $Month, $Day, $Period) = @_;
207 my ($NYear, $NMonth, $NDay);
208
209 if ($Period =~ /(\d+)\s*([dw])/) { # Is counted in days or weeks: Use Add_Delta_Days.
210 ($NYear, $NMonth, $NDay) = Add_Delta_Days($Year, $Month, $Day, (($2 eq "w")?$1 * 7: $1 * 1));
211 } elsif ($Period =~ /(\d+)\s*([my])/) { #Is counted in months or years: Use Add_Delta_YM
212 ($NYear, $NMonth, $NDay) = Add_Delta_YM($Year, $Month, $Day, (($2 eq "m")?(0,$1):($1,0)));
213 }
214 return ($NYear, $NMonth, $NDay);
215}
5a6670c7
TH
216
217################################ updatestatus ###############################
218# Takes a MID and a status file name
219# and writes status information to disk
220
221sub updatestatus {
222 my ($ActName, $File, $date, $MID) = @_;
223
224 print "$$ActName: Save status information.\n" if($Options{'v'});
225
226 open (FH, ">$$File.cfg") or die "$0: E: Can't open $$File.cfg: $!";
227 print FH "##;; Lastpost: $date\n";
228 print FH "##;; LastMID: $MID\n";
229 close FH;
230}
0c6ebe78 231
dc88d139
TH
232################################## postfaq ##################################
233# Takes a filename and many other vars.
234#
235# It reads the data-file $File and then posts the article.
236
237sub postfaq {
0c6ebe78 238 my ($ActName,$File,$From,$Subject,$NG,$Fup2,$MIDF,$ExtraHeaders,$Sender,$TDY,$TDM,$TDD,$ReplyTo,$Supersedes,$Expire) = @_;
dc88d139
TH
239 my (@Header,@Body,$MID,$InRealBody,$LastModified);
240
366322b2 241 print "$$ActName: Preparing to post.\n" if($Options{'v'});
4251e545 242
dc88d139
TH
243 #Prepare MID:
244 $$TDM = ($$TDM < 10 && $$TDM !~ /^0/) ? "0" . $$TDM : $$TDM;
245 $$TDD = ($$TDD < 10 && $$TDD !~ /^0/) ? "0" . $$TDD : $$TDD;
13ce8c26 246 my $Timestamp = time;
dc88d139
TH
247
248 $MID = $$MIDF;
ac69c3ee 249 $MID = '<%n-%d.%m.%y@'.hostfqdn.'>' if !defined($MID); # set to default if unset
dc88d139
TH
250 $MID =~ s/\%n/$$ActName/g;
251 $MID =~ s/\%d/$$TDD/g;
252 $MID =~ s/\%m/$$TDM/g;
253 $MID =~ s/\%y/$$TDY/g;
13ce8c26 254 $MID =~ s/\%t/$Timestamp/g;
dc88d139 255
dc88d139
TH
256 #Now get the body:
257 open (FH, "<$$File");
258 while (<FH>){
259 s/\r//;
260 push (@Body, $_), next if $InRealBody;
261 $InRealBody++ if /^$/;
8e1cb154 262 $LastModified = $1 if /^Last-modified: (\S+)$/i;
dc88d139
TH
263 push @Body, $_;
264 }
265 close FH;
266 push @Body, "\n" if ($Body[-1] ne "\n");
267
268 #Create Date- and Expires-Header:
269 my @time = localtime;
270 my $ss = ($time[0]<10) ? "0" . $time[0] : $time[0];
271 my $mm = ($time[1]<10) ? "0" . $time[1] : $time[1];
272 my $hh = ($time[2]<10) ? "0" . $time[2] : $time[2];
273 my $day = $time[3];
274 my $month = ($time[4]+1<10) ? "0" . ($time[4]+1) : $time[4]+1;
275 my $monthN = ("Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec")[$time[4]];
276 my $wday = ("Sun","Mon","Tue","Wed","Thu","Fri","Sat")[$time[6]];
277 my $year = (1900 + $time[5]);
278 my $tz = $time[8] ? " +0200" : " +0100";
7823ece9
TH
279
280 $$Expire = '3m' if !$$Expire; # set default if unset: 3 month
281
0c6ebe78 282 my ($expY,$expM,$expD) = calcdelta ($year,$month,$day,$$Expire);
dc88d139
TH
283 my $expmonthN = ("Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec")[$expM-1];
284
285 my $date = "$day $monthN $year " . $hh . ":" . $mm . ":" . $ss . $tz;
286 my $expdate = "$expD $expmonthN $expY $hh:$mm:$ss$tz";
0c6ebe78 287
dc88d139
TH
288 #Replace %LM by the content of the news.answer-pseudo-header Last-modified:
289 if ($LastModified) {
290 $$Subject =~ s/\%LM/$LastModified/;
291 }
292
4251e545
TH
293 # Test mode?
294 if($Options{'t'} and $Options{'t'} !~ /console/i) {
295 $$NG = $Options{'t'};
296 }
297
dc88d139
TH
298 #Now create the complete Header:
299 push @Header, "From: $$From\n";
300 push @Header, "Newsgroups: $$NG\n";
301 push @Header, "Followup-To: $$Fup2\n" if $$Fup2;
302 push @Header, "Subject: $$Subject\n";
303 push @Header, "Message-ID: $MID\n";
304 push @Header, "Supersedes: $$Supersedes\n" if $$Supersedes;
305 push @Header, "Date: $date\n";
306 push @Header, "Expires: $expdate\n";
307 push @Header, "Sender: $$Sender\n" if $$Sender;
308 push @Header, "Mime-Version: 1.0\n";
309 push @Header, "Reply-To: $$ReplyTo\n" if $$ReplyTo;
310 push @Header, "Content-Type: text/plain; charset=ISO-8859-15\n";
311 push @Header, "Content-Transfer-Encoding: 8bit\n";
312 push @Header, "User-Agent: yapfaq/$Version\n";
313 if ($$ExtraHeaders) {
314 push @Header, "$_\n" for (split /\n/, $$ExtraHeaders);
315 }
316
0e741504
TH
317 my @Article = (@Header, "\n", @Body);
318
b9550622 319 # post article
366322b2 320 print "$$ActName: Posting article ...\n" if($Options{'v'});
5a6670c7
TH
321 my $failure = post(\@Article);
322
323 if ($failure) {
324 print "$$ActName: Posting failed, ERROR.dat may have more information.\n" if($Options{'v'} && (!defined($Options{'t'}) || $Options{'t'} !~ /console/i));
325 } else {
326 updatestatus($ActName, $File, "$day.$month.$year", $MID) if !defined($Options{'t'});
327 }
dc88d139
TH
328}
329
330################################## post ##################################
331# Takes a complete article (Header and Body).
332#
333# It opens a connection to $NNTPServer and posts the message.
334
335sub post {
336 my ($ArticleR) = @_;
5a6670c7 337 my ($failure) = -1;
dc88d139 338
5a6670c7 339 # test mode - print article to console
4251e545 340 if(defined($Options{'t'}) and $Options{'t'} =~ /console/i) {
55bfbd3c 341 print "-----BEGIN--------------------------------------------------\n";
5a6670c7 342 print @$ArticleR;
55bfbd3c 343 print "------END---------------------------------------------------\n";
5a6670c7
TH
344 # pipe article to script
345 } elsif(defined($Options{'s'})) {
b855559e
TH
346 open (POST, "| $Options{'s'}") or die "$0: E: Cannot fork $Options{'s'}: $!\n";
347 print POST @$ArticleR;
348 close POST;
5a6670c7
TH
349 if ($? == 0) {
350 $failure = 0;
351 } else {
352 warn "$0: W: $Options{'s'} exited with status ", ($? >> 8), "\n";
353 $failure = $?;
354 }
355 # post article
356 } else {
357 my $NewsConnection = Net::NNTP->new($Config{'NNTPServer'}, Reader => 1) or die "$0: E: Can't connect to news server '$Config{'NNTPServer'}'!\n";
358 $NewsConnection->authinfo ($Config{'NNTPUser'}, $Config{'NNTPPass'}) if (defined($Config{'NNTPUser'}));
359 $NewsConnection->post();
360 $NewsConnection->datasend (@$ArticleR);
361 $NewsConnection->dataend();
362
363 if ($NewsConnection->ok()) {
364 $failure = 0;
365 # Posting failed? Save to ERROR.dat
366 } else {
367 warn "$0: W: Posting failed!\n";
368 open FH, ">>ERROR.dat";
369 print FH "\nPosting failed! Saving to ERROR.dat. Response from news server:\n";
370 print FH $NewsConnection->code();
371 print FH $NewsConnection->message();
372 print FH "\n";
373 print FH @$ArticleR;
374 print FH "-" x 80, "\n";
375 close FH;
376 }
377 $NewsConnection->quit();
dc88d139 378 }
5a6670c7 379 return $failure;
dc88d139
TH
380}
381
272b0243
TH
382__END__
383
384################################ Documentation #################################
385
386=head1 NAME
387
388yapfaq - Post Usenet FAQs I<(yet another postfaq)>
389
390=head1 SYNOPSIS
391
9817f98a 392B<yapfaq> [B<-Vhvpd>] [B<-t> I<newsgroups> | CONSOLE] [B<-f> I<project name>] [B<-s> I<program>] [B<-c> I<.rc file>]
272b0243
TH
393
394=head1 REQUIREMENTS
395
396=over 2
397
398=item -
399
400Perl 5.8 or later
401
402=item -
403
404Net::NNTP
405
406=item -
407
408Date::Calc
409
410=item -
411
412Getopt::Std
413
414=back
415
416Furthermore you need access to a news server to actually post FAQs.
417
418=head1 DESCRIPTION
419
420B<yapfaq> posts (one or more) FAQs to Usenet with a certain posting
421frequency (every n days, weeks, months or years), adding all necessary
422headers as defined in its config file (by default F<yapfaq.cfg>).
423
424=head2 Configuration
425
426F<yapfaq.cfg> consists of one or more blocks, separated by C<=====> on
427a single line, each containing the configuration for one FAQ as a set
227afd47
TH
428of definitions in the form of I<param = value>. Everything after a "#"
429sign is ignored so you may comment your configuration file.
272b0243
TH
430
431=over 4
432
433=item B<Name> = I<project name>
434
435A name referring to your FAQ, also used for generation of a Message-ID.
436
437This value must be set.
438
439=item B<File> = I<file name>
440
441A file containing the message body of your FAQ and all pseudo headers
442(subheaders in the news.answers style).
443
444This value must be set.
445
446=item B<Posting-frequency> = I<time period>
447
448The posting frequency defines how often your FAQ will be posted.
449B<yapfaq> will only post your FAQ if this period of time has passed
450since the last posting.
451
452You can declare that time period either in I<B<d>ays> or I<B<w>weeks>
453or I<B<m>onths> or I<B<y>ears>.
454
455This value must be set.
456
ac69c3ee 457=item B<Expires> = I<time period> (optional)
272b0243
TH
458
459The period of time after which your message will expire. An Expires
460header will be calculated adding this time period to today's date.
461
462You can declare this time period either in I<B<d>ays> or I<B<w>weeks>
463or I<B<m>onths> or I<B<y>ears>.
464
ac69c3ee 465This setting is optional; the default is 3 months.
272b0243
TH
466
467=item B<From> = I<author>
468
469The author of your FAQ as it will appear in the From header of the
470message.
471
472This value must be set.
473
474=item B<Subject> = I<subject>
475
476The title of your FAQ as it will appear in the Subject header of the
477message.
478
479You may use the special string C<%LM> which will be replaced with
480the contents of the Last-Modified subheader in your I<File>.
481
482This value must be set.
483
484=item B<NGs> = I<newsgroups>
485
486A comma-separated list of newsgroup(s) to post your FAQ to as it will
487appear in the Newsgroups header of the message.
488
489This value must be set.
490
ac69c3ee 491=item B<Fup2> = I<newsgroup | poster> (optional)
272b0243
TH
492
493A comma-separated list of newsgroup(s) or the special string I<poster>
494as it will appear in the Followup-To header of the message.
495
496This setting is optional.
497
ac69c3ee 498=item B<MID-Format> = I<pattern> (optional)
272b0243
TH
499
500A pattern from which the message ID is generated as it will appear in
501the Message-ID header of the message.
502
503You may use the special strings C<%n> for the I<Name> of your project,
13ce8c26
TH
504C<%d> for the date the message is posted, C<%m> for the month, C<%y>
505for the year and C<%t> for a time stamp (number of seconds since the
506epoch), respectively.
272b0243 507
ac69c3ee
TH
508This setting is optional; the default is '<%n-%d.%m.%y@I<YOURHOST>>'
509where I<YOURHOST> is the fully qualified domain name (FQDN) of the
510host B<yapfaq> is running on. Obviously that will only work if you
511have defined a reasonable hostname that the hostfqdn() function of
512Net::Domain can return.
272b0243 513
ac69c3ee 514=item B<Supersede> = I<yes> (optional)
272b0243
TH
515
516Add Supersedes header to the message containing the Message-ID header
517of the last posting.
518
519This setting is optional; you should set it to yes or leave it out.
520
ac69c3ee 521=item B<ExtraHeader> = I<additional headers> (optional)
272b0243
TH
522
523The contents of I<ExtraHeader> is added verbatim to the headers of
524your message so you can add custom headers like Approved.
525
526This setting is optional.
527
528=back
529
227afd47 530=head3 Example configuration file
272b0243
TH
531
532 # name of your project
533 Name = 'testpost'
534
535 # file to post (complete body and pseudo-headers)
536 # ($File.cfg contains data on last posting and last MID)
537 File = 'test.txt'
538
539 # how often your project should be posted
540 # use (d)ay OR (w)eek OR (m)onth OR (y)ear
541 Posting-frequency = '1d'
542
543 # time period after which the posting should expire
544 # use (d)ay OR (w)eek OR (m)onth OR (y)ear
ac69c3ee 545 # Expires = '3m'
272b0243
TH
546
547 # header "From:"
548 From = 'test@domain.invalid'
549
550 # header "Subject:"
551 # (may contain "%LM" which will be replaced by the contents of the
552 # Last-Modified pseudo header).
553 Subject = 'test noreply ignore'
554
555 # comma-separated list of newsgroup(s) to post to
556 # (header "Newsgroups:")
557 NGs = 'de.test'
558
559 # header "Followup-To:"
ac69c3ee 560 # Fup2 = 'poster'
272b0243
TH
561
562 # Message-ID ("%n" is $Name)
ac69c3ee 563 # MID-Format = '<%n-%d.%m.%y@domain.invalid>'
272b0243
TH
564
565 # Supersede last posting?
566 Supersede = yes
567
568 # extra headers (appended verbatim)
569 # use this for custom headers like "Approved:"
570 ExtraHeader = 'Approved: moderator@domain.invalid
571 X-Header: Some text'
572
573 # other projects may follow separated with "====="
574 =====
575
576 Name = 'othertest'
577 File = 'test.txt'
578 Posting-frequency = '2m'
579 From = 'My Name <my.name@domain.invalid>'
580 Subject = 'Test of yapfag <%LM>'
581 NGs = 'de.test,de.alt.test'
582 Fup2 = 'de.test'
583 MID-Format = '<%n-%m.%y@domain.invalid>'
584 Supersede = yes
585
227afd47
TH
586=head3 Status Information
587
272b0243
TH
588Information about the last post and about how to form message IDs for
589posts is stored in a file named F<I<project name>.cfg> which will be
590generated if it does not exist. Each of those status files will
591contain two lines, the first being the date of the last time the FAQ
592was posted and the second being the message ID of that incarnation.
593
227afd47
TH
594=head2 Runtime Configuration
595
596Apart from configuring which FAQ(s) to post you may (re)set some
597runtime configuration variables via the .rcfile (by default
598F<.yapfaqrc>). F<.yapfaqrc> must contain one definition in the form of
599I<param = value> on each line; everything after a "#" sign is ignored.
600
601If you omit some settings they will be set to default values hardcoded
602in F<yapfaq.pl>.
603
604B<Please note that all parameter names are case-sensitive!>
605
606=over 4
607
608=item B<NNTPServer> = I<NNTP server> (mandatory)
609
610Host name of the NNTP server to post to. Must be set (or omitted; the
611default is "localhost"); if set to en empty string, B<yapfaq> falls
612back to Perl's build-in defaults (contents of environment variables
613NNTPSERVER and NEWSHOST; if not set, default from Net::Config; if not
614set, "news" is used).
615
616=item B<NNTPUser> = I<user name> (optional)
617
618User name used for authentication with the NNTP server (I<AUTHINFO
619USER>).
620
621This setting is optional; if it is not set, I<NNTPPass> is ignored and
622no authentication is tried.
623
624=item B<NNTPPass> = I<password> (optional)
625
626Password used for authentication with the NNTP server (I<AUTHINFO
627PASS>).
628
629This setting is optional; it must be set if I<NNTPUser> is present.
630
631=item B<Sender> = I<Sender header> (optional)
632
633The Sender header that will be added to every posted message.
634
635This setting is optional.
636
637=item B<ConfigFile> = I<configuration file> (mandatory)
638
639The configuration file defining the FAQ(s) to post. Must be set (or
640omitted; the default is "yapfaq.cfg").
641
227afd47
TH
642=back
643
644=head3 Example runtime configuration file
645
646 NNTPServer = 'localhost'
647 NNTPUser = ''
648 NNTPPass = ''
649 Sender = ''
650 ConfigFile = 'yapfaq.cfg'
227afd47
TH
651
652=head3 Using more than one runtime configuration
653
654You may use more than one runtime configuration file with the B<-c>
655option (see below).
656
272b0243
TH
657=head1 OPTIONS
658
659=over 3
660
a052296f
TH
661=item B<-V> (version)
662
663Print out version and copyright information on B<yapfaq> and exit.
664
272b0243
TH
665=item B<-h> (help)
666
ae3b1b79 667Print this man page and exit.
272b0243
TH
668
669=item B<-v> (verbose)
670
671Print out status information while running to STDOUT.
672
673=item B<-p> (post unconditionally)
674
675Post (all) FAQs unconditionally ignoring the posting frequency setting.
676
677You may want to use this with the B<-f> option (see below).
678
679=item B<-d> (dry run)
680
681Start B<yapfaq> in simulation mode, i.e. don't post anything and don't
682update any status information.
683
684=item B<-t> I<newsgroup(s) | CONSOLE> (test)
685
686Don't post to the newsgroups defined in F<yqpfaq.cfg>, but to the
687newsgroups given after B<-t> as a comma-separated list or print the
688FAQs to STDOUT separated by lines of dashes if the special string
689C<CONSOLE> is given. This can be used to preview what B<yapfaq> would
690do without embarassing yourself on Usenet. The status files are not
691updated when this option is given.
692
693You may want to use this with the B<-f> option (see below).
694
695=item B<-f> I<project name>
696
697Just deal with one FAQ only.
698
699By default B<yapfaq> will work on all FAQs that are defined in
700F<yapfaq.cfg>, check whether they are due for posting and - if they
701are - post them. Consequently when the B<-p> option is set all FAQs
702will be posted unconditionally. That may not be what you want to
703achieve, so you can limit the operation of B<yapfaq> to the named FAQ
704only.
705
b855559e
TH
706=item B<-s> I<program> (pipe to script)
707
708Instead of posting the article(s) to Usenet pipe them to the external
709I<program> on STDIN (which may post the article(s) then). A return
710value of 0 will be considered success.
711
0e741504
TH
712For example, you may want to use the I<inews> utility from the INN package
713or the much more powerful replacement I<tinews.pl> from
714I<ftp://ftp.tin.org/tin/tools/tinews.pl> which is able to sign postings.
715
227afd47
TH
716=item B<-c> I<.rc file>
717
718Load another runtime configuration file (.rc file) than F<.yaofaq.rc>.
719
720You may for example define another usenet server to post your FAQ(s)
721to or load another configuration file defining (an)other FAQ(s).
722
272b0243
TH
723=back
724
725=head1 EXAMPLES
726
727Post all FAQs that are due for posting:
728
729 yapfaq
730
731Do a dry run, showing which FAQs would be posted:
732
733 yapfaq -dv
734
735Do a test run and print on STDOUT what the FAQ I<myfaq> would look
736like when posted, regardless whether it is due for posting or not:
737
738 yapfaq -pt CONSOLE -f myfaq
739
740Do a "real" test run and post the FAQ I<myfaq> to I<de.test>, but only
741if it is due:
742
743 yapfaq -t de.test -f myfaq
744
9817f98a
TH
745Post all FAQs (that are due for posting) using inews from INN:
746
747 yapfaq -s inews
748
749Do a dry run using a runtime configuration from .alternaterc, showing
750which FAQs would be posted:
751
752 yapfaq -dvc .alternaterc
753
272b0243
TH
754=head1 ENVIRONMENT
755
9817f98a
TH
756=over 4
757
758=item NNTPSERVER
759
760The default NNTP server to post to, used by the Net::NNTP module. You
761can also specify the server using the runtime configuration file (by
762default F<.yapfaqrc>).
763
764=back
272b0243
TH
765
766=head1 FILES
767
768=over 4
769
770=item F<yapfaq.pl>
771
772The script itself.
773
227afd47
TH
774=item F<.yapfaqrc>
775
776Runtime configuration file for B<yapfaq>.
777
272b0243
TH
778=item F<yapfaq.cfg>
779
780Configuration file for B<yapfaq>.
781
782=item F<*.cfg>
783
784Status data on FAQs.
785
786The status files will be created on successful posting if they don't
787already exist. The first line of the file will be the date of the last
788time the FAQ was posted and the second line will be the message ID of
789the last post of that FAQ.
790
791=back
792
793=head1 BUGS
794
795Many, I'm sure.
796
797=head1 SEE ALSO
798
799L<http://th-h.de/download/scripts.php> will have the current
800version of this program.
801
802=head1 AUTHOR
803
804Thomas Hochstein <thh@inter.net>
805
227afd47 806Original author (up to version 0.5b, dating from 2003):
272b0243
TH
807Marc Brockschmidt <marc@marcbrockschmidt.de>
808
272b0243
TH
809=head1 COPYRIGHT AND LICENSE
810
811Copyright (c) 2003 Marc Brockschmidt <marc@marcbrockschmidt.de>
812
813Copyright (c) 2010 Thomas Hochstein <thh@inter.net>
814
815This program is free software; you may redistribute it and/or modify it
816under the same terms as Perl itself.
817
818=cut
This page took 0.064422 seconds and 4 git commands to generate.