From 6144f982cfd7e8b4362fd034e0bd1f99c7060b6c Mon Sep 17 00:00:00 2001 From: b Date: Thu, 17 Aug 2023 22:29:45 +0000 Subject: [PATCH] Started work towards preview, bbcode conversion started --- botm-common | 2 +- makefile | 18 +- makefile.1.mak | 18 +- post_common.1.pm | 656 +++++++++++++++++++++++++++++++++++++++++++++++ preview.1.pl | 190 ++++++++++++++ sendpost.1.pl | 241 ++--------------- settings.txt | 4 + 7 files changed, 899 insertions(+), 230 deletions(-) create mode 100644 post_common.1.pm create mode 100644 preview.1.pl diff --git a/botm-common b/botm-common index 576e2fd..70a5470 160000 --- a/botm-common +++ b/botm-common @@ -1 +1 @@ -Subproject commit 576e2fd6f22344e66fda21142b48ad6280e6e719 +Subproject commit 70a54705d7c930699fc7678b3b03a6f934e74397 diff --git a/makefile b/makefile index 731ea39..2337270 100644 --- a/makefile +++ b/makefile @@ -44,11 +44,14 @@ CONFIGURE_CMD = $(PERL) ./configure.pl $(CONFIGFILE) # keep these 2 lists in the same order!: GENERATE_FROM=\ -sendpost.1.pl +sendpost.1.pl\ +preview.1.pl\ +post_common.1.pm TO_GENERATE=\ -sendpost.pl - +sendpost.pl\ +preview.pl\ +post_common.pm DIR=\ $(BIN_PATH)\ @@ -63,10 +66,12 @@ SETUID=\ sendpost.pl EXEC=\ -sendpost.pl +sendpost.pl\ +preview.pl PERL_WRAP_EXEC=\ -sendpost +sendpost\ +preview BIN=\ @@ -74,7 +79,8 @@ $(EXEC)\ $(PERL_WRAP_EXEC) LIB=\ -botm-common/botm_common.pm +botm-common/botm_common.pm\ +post_common.pm all: $(BIN) setuid exec diff --git a/makefile.1.mak b/makefile.1.mak index 5a25251..110dbda 100644 --- a/makefile.1.mak +++ b/makefile.1.mak @@ -44,11 +44,14 @@ CONFIGURE_CMD = $(PERL) ./configure.pl $(CONFIGFILE) # keep these 2 lists in the same order!: GENERATE_FROM=\ -sendpost.1.pl +sendpost.1.pl\ +preview.1.pl\ +post_common.1.pm TO_GENERATE=\ -sendpost.pl - +sendpost.pl\ +preview.pl\ +post_common.pm DIR=\ $(BIN_PATH)\ @@ -63,10 +66,12 @@ SETUID=\ sendpost.pl EXEC=\ -sendpost.pl +sendpost.pl\ +preview.pl PERL_WRAP_EXEC=\ -sendpost +sendpost\ +preview BIN=\ @@ -74,7 +79,8 @@ $(EXEC)\ $(PERL_WRAP_EXEC) LIB=\ -botm-common/botm_common.pm +botm-common/botm_common.pm\ +post_common.pm all: $(BIN) setuid exec diff --git a/post_common.1.pm b/post_common.1.pm new file mode 100644 index 0000000..b173e58 --- /dev/null +++ b/post_common.1.pm @@ -0,0 +1,656 @@ +# Copyright (C) 2023 Balthasar Szczepański +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . + +package post_common; + +use strict; +#use warnings; +use utf8; + +use Encode::Locale; +use Encode ('encode', 'decode'); + +use Exporter; + +###PERL_EXPORT_VERSION: our $VERSION = 'x.x.x'; +our @ISA = qw(Exporter); +our @EXPORT = (); +our @EXPORT_OK = ( + 'prepare_post', + 'wget', + 'CODE', +); + +###PERL_LIB: use lib '/botm/lib/post'; +use botm_common ( + 'system_encoded', + 'write_postdata_file' +); + +###PERL_DEFAULT_PASSWORD: use constant DEFAULT_PASSWORD => 'password'; +###PERL_DEFAULT_SUBJECT: use constant DEFAULT_SUBJECT => 'Re: 1190: "Time"'; +###PERL_DEFAULT_USERNAME: use constant DEFAULT_USERNAME => 'username'; + +###PERL_WGET_RETRIES: use constant WGET_RETRIES => 3; +###PERL_WGET_TIMEOUT: use constant WGET_TIMEOUT => 60; +###PERL_WGET_USERAGENT: use constant WGET_USERAGENT => "post tool (http://bicyclesonthemoon.info/git-projects/?p=ott/post)"; + +###PERL_WGET: use constant WGET => '/usr/bin/wget'; + +###PERL_ENCODING_FILE: use constant ENCODING_FILE => 'UTF-8'; + + +use constant CODE => { + 'OK' => 0, + 'POST_INVALID' => 1, + 'POST_FAILED' => 2, + 'POST_NOT_ACCEPTED' => 3 +}; + +sub prepare_post { + (my $cmd_options, my $file_data) = @_; + my %post; + + if (($cmd_options->{'subject'}) ne '') { + $post{'subject'} = $cmd_options->{'subject'}; + } + elsif (($file_data->{'subject'}) ne '') { + $post{'subject'} = $file_data->{'subject'}; + } + else { + $post{'subject'} = DEFAULT_SUBJECT; + } + + if (($cmd_options->{'username'}) ne '') { + $post{'username'} = $cmd_options->{'username'}; + } + elsif (($file_data->{'username'}) ne '') { + $post{'username'} = $file_data->{'username'}; + } + else { + $post{'username'} = DEFAULT_USERNAME; + } + + if (($cmd_options->{'password'}) ne '') { + $post{'password'} = $cmd_options->{'password'}; + } + elsif (($file_data->{'password'}) ne '') { + $post{'password'} = $file_data->{'password'}; + } + else { + $post{'password'} = DEFAULT_PASSWORD; + } + + if (($cmd_options->{'content'}) ne '') { + $post{'content'} = $cmd_options->{'content'}; + } + elsif (($file_data->{'content'}) ne '') { + $post{'content'} = $file_data->{'content'}; + } + else { + $post{'content'} = ''; + } + + if (($cmd_options->{'no-bbcode'}) ne '') { + $post{'bbcode'} = 0; + } + elsif (($cmd_options->{'bbcode'}) ne '') { + $post{'bbcode'} = 1; + } + elsif (($file_data->{'bbcode'}) ne '') { + $post{'bbcode'} = $file_data->{'bbcode'}+0; + } + else { + $post{'bbcode'} = 1; + } + + if (($cmd_options->{'no-smilies'}) ne '') { + $post{'smilies'} = 0; + } + elsif (($cmd_options->{'smilies'}) ne '') { + $post{'smilies'} = 1; + } + elsif (($file_data->{'smilies'}) ne '') { + $post{'smilies'} = $file_data->{'smilies'}+0; + } + else { + $post{'smilies'} = 1; + } + + if (($cmd_options->{'no-urls'}) ne '') { + $post{'urls'} = 0; + } + elsif (($cmd_options->{'urls'}) ne '') { + $post{'urls'} = 1; + } + elsif (($file_data->{'urls'}) ne '') { + $post{'urls'} = $file_data->{'urls'}+0; + } + else { + $post{'urls'} = 1; + } + + if (($cmd_options->{'no-signature'}) ne '') { + $post{'signature'} = 0; + } + elsif (($cmd_options->{'signature'}) ne '') { + $post{'signature'} = 1; + } + elsif (($file_data->{'signature'}) ne '') { + $post{'signature'} = $file_data->{'signature'}+0; + } + else { + $post{'signature'} = 1; + } + + if (($cmd_options->{'no-notify'}) ne '') { + $post{'notify'} = 0; + } + elsif (($cmd_options->{'notify'}) ne '') { + $post{'notify'} = 1; + } + elsif (($file_data->{'notify'}) ne '') { + $post{'notify'} = $file_data->{'notify'}+0; + } + else { + $post{'notify'} = 0; + } + + if (($file_data->{'time'}) ne '') { + $post{'time'} = $file_data->{'time'}+0; + } + else { + $post{'time'} = 0; + } + + return %post; +} + +########## +# BBCODE # +########## + +# bbtree is the representation of a BBcode tag tree as a Perl hash +# each element can be a tag or text. +# text only has value +# tag can have properties and other tags +# +# index consists of multiple parts separated by dots +# top level index is "_" +# element's index is created by adding a number to the index +# starting from 0. +# EXAMPLE: +# element "_" can have other elements with indexes: +# "_.0", "_.1", "_.2", ... +# element "_.1" can have other elements with indexes: +# "_.1.0", "_.1.1", "_.1.2", ... +# and so on. +# +# properties also have index created by adding the property name +# to the element's base index separated by dot +# +# property ".type" dedermines type of element: +# - "text" - element is just normal text +# - "tag" - element is BBcode tag +# +# text has just 1 property: +# - ".text" - the actual text content +# +# tag has these properties: +# - ".text" - full text of BBcode opening tag +# - ".endtext"- full text of BBcode closing tag +# - ".name" - name of BBcode tag +# - ".value" - (optional) value of BBcode tag +# - ".count" - number, how many elements the tag contains inside +# - ".closed" - 1 or 0, if tag has a matching closing tag + +# initialise bbtree with top level +sub bbtree_init { + (my $bbtree, my $base_ind, my $print) = @_; + + %$bbtree = (); + $bbtree{$base_ind.'.name'} = $base_ind; + $bbtree{$base_ind.'.type'} = 'tag'; + $bbtree{$base_ind.'.count'} = 0; + + if ($print){ + print bbtree_debug(%bbtree, $ind); + } + + return ($base_ind, 0); +} + +# add new tag to bbtree, and enter new tag +sub bbtree_add_tag { + (my $bbtree, my $ind, my $level, my $name, my $value, my $text, my $print) = @_; + + my $ind_count = $ind.'.count'; + # index for NEW tag + my $new_ind = $ind.'.'.$bbtree->{$ind_count}; + + # increase count of CURRENT tag + $bbtree->{$ind_count} += 1; + + # save NEW tag properties + $bbtree->{$new_ind.'.type' } = 'tag'; + $bbtree->{$new_ind.'.name' } = $name; + $bbtree->{$new_ind.'.value' } = $value; + $bbtree->{$new_ind.'.text' } = $text; + + # NEW tag starts empty and open + $bbtree->{$new_ind.'.count' } = 0; + $bbtree->{$new_ind.'.closed'} = 0; + + if ($print) { + print bbtree_debug(%bbtree, $new_ind); + } + + # enter NEW tag + return ($new_ind, $level+1); +} + +# add new text to bbtree, don't enter +sub bbtree_add_text { + (my $bbtree, my $ind, my $level, my $text, my $print) = @_; + + my $ind_count = $ind.'.count'; + my $count = $bbtree->{$ind_count}; + + # if last element of CURRENT tag is text, + # then merge NEW text instead of adding separately + if ($count >= 0) { + my $ind_last = $ind.'.'.($count-1); + if ($bbtree->{$ind_last.'.type'} eq 'text') { + $bbtree->{$ind_last.'.text'} += $text; + $text = ''; + } + } + # text was not merged & actually exists, add normally + if ($text ne '') { + # index for NEW tag + my $new_ind = $ind.'.'.$count; + + # increase count of CURRENT tag + $bbtree->{$ind_count} = $count + 1; + + # save NEW text properties + $bbtree->{$new_ind.'.type' } = 'text'; + $bbtree->{$new_ind.'.text'} = $text; + } + + if ($print){ + print bbtree_debug(%bbtree, $ind); + } + return ($ind, $level); +} + +# close existing tag in bbtree, return to parent tag +sub bbtree_close_tag { + (my $bbtree, my $ind, my $level, my $text, my $print) = @_; + + $bbtree->{$ind.'endtext'} = $text; + # mark CURRENT tag as closed + $bbtree->{$ind.'closed'} = 1; + + if ($print){ + print bbtree_debug(%bbtree, $ind); + } + + # return to PARENT tag + if ($level > 0) { + $ind =~ s/\.[0-9]+$//; + return ($ind, $level-1); + } + else { + return ($ind, $level); + } +} + +# don't close existing tag in bbtree, return to parent tag +sub bbtree_drop_tag { + (my $bbtree, my $ind, my $level, my $text, my $print) = @_; + + $bbtree->{$ind.'endtext'} = $text; + # mark CURRENT tag as NOT closed + $bbtree->{$ind.'closed'} = 0; + + if ($print){ + print bbtree_debug(%bbtree, $ind); + } + + # return to PARENT tag + if ($level > 0) { + $ind =~ s/\.[0-9]+$//; + return ($ind, $level-1); + } + else { + return ($ind, $level); + } +} + +# create debug text for bbtree element currently in focus +sub bbtree_debug { + (my $bbtree, my $ind) = @_; + + # start with current index + my $debug = '['.$ind.']'; + + # element is tag, analyse further + if ($bbtree->{$ind.'.type'} eq 'tag') { + + # tag already closed, print name with "/" + if ($bbtree->{$ind.'.closed'}) { + $debug .= 'tag: [/'.$bbtree->{$ind.'.name'}.']'; + } + + # not closed but has end text + elsif ($bbtree->{$ind.'.endtext'} ne '') { + $debug .= 'mismatch: '.$bbtree->{$ind.'.endtext'}; + } + + # tag contains other elements, + # create debug text for last element instead + elsif ($bbtree->{$ind.'.count'} > 0) { + return bbtree_debug($bbtree, $ind.'.'.($bbtree->{$ind.'.count'}-1)); + } + + # empty, open tag, print name + else { + $debug .= 'tag: ['.$bbtree->{$ind.'.name'}; + # tag has value, print as well + if ($bbtree->{$ind.'.value'} ne '') { + $debug .= '='.$bbtree->{$ind.'.value'}; + } + $debug .= ']'; + } + } + + # element is text, just print + elsif ($bbtree->{$ind.'.type'} eq 'text') { + $debug .= 'text: '.$bbtree->{$ind.'.value'}; + } + + # unknown type, no idea what to print + else { + $debug .= '???'; + } + + $debug .= "\n"; + return $debug; +} + +sub bbcode_to_html { + (my $cmd_options, my $post) = @_; + + my $bbcode = $post->{'content'}; # BBcode text to convert + my $html = ''; + my $level; # how deep in the BB tree + my $ind; # current BB tree element's index + my %bbtree = (); # BB tree structure + my $tag; # current tag, full + my $tag_end; # current tag's closing mark + my $tag_name; # current tag's name + my $tag_value; # current tag's (optional) value + my $text; # current text + my $in_code = 0; # if (and how deep) in [code] tag + my $in_spoiler = 0; # if (and how deep) in [spoiler] tag + my $in_size = 0; # if (and how deep) in [size] tag + my $print = $cmd_options->{'verbose'} ne '' # allowed to print + + # initialise BB tree with top level + if ($print) { + print "Build BBcode tree:\n"; + } + ($ind, $level) = bbtree_init(\%bbtree, '_', $print); + + while ($bbcode ne '') { + + # found nearest tag + # [name] + # [/name] + # [name=value] + # [name="value"] + if($bbcode =~ m/(\[(\/?)([a-z]+|\*)(=(([^\[\]]*)|("[^"]*")))?\])/g) { + # assign detected parts: + $tag = $1; + $tag_end = $2; + $tag_name = $3; + $tag_value = $5; + # text before the tag: + $text = substr($bbcode, 0, pos($bbcode) - length($tag)); + # cut off already processed part: + $bbcode = substr ($bbcode, pos ($bbcode)); + } + # no tag found, only text left + else { + # mark no tag found + $tag = ''; + # take all text at once + $text = $bbcode; + $bbcode = ''; + } + + # there was text (before tag or alone) + if ($text ne '') { + if ($print) { + print 'text: '.$text."\n"; + } + # add new text to BB tree + bbtree_add_text(\%bbtree, $ind, $level, $text, $print); + } + + # no tag, skip + if ($tag eq '') { + next; + } + if ($print) { + print 'tag: '.$tag."\n"; + } + + # tag name not recognised + if ($tag_name !~ /^(quote|b|i|u|code|img|url|size|color|center|right|sub|sup|s|spoiler|list|\*)$/) { + if ($print) { + print 'invalid tag name "'.$tag_name."\"\n"; + } + # add bad tag to BB tree as text + bbtree_add_text(\%bbtree, $ind, $level, $tag, $print); + # skip + next; + } + + # opening tag if ($tag_end eq '') { + + # additional reasons for rejecting tag + if ( + # no tags in [code] except [code][/code] + (($in_code > 0) and ($tag_name ne 'code')) or + # no tags in [img] + ($bbtree{$ind.'.name'} eq 'img') or + # no nested [spoiler] (compatibility) + (($in_spoiler > 0) and ($tag_name eq 'spoiler')) or + # no nested [size] (compatibility) + (($in_size > 0) and ($tag_name eq 'size')) + ) { + if ($print) { + print "this tag forbidden here\n"; + } + # add bad tag as text + bbtree_add_text(\%bbtree, $ind, $level, $tag, $print); + # skip + next; + } + + # [*] tag is special case as it doesn't need closing + if ($tag_name eq '*') { + # [*] after [*] + # add implicit [/*] + if ($bbtree{$ind.'.name'} eq '*') { + if ($print) { + print "implicit [/*]\n"; + } + # add [/*] to BB tree + ($ind, $level) = bbtree_close_tag(\%bbtree, $ind, $level, '[/*]', $print); + } + # [*] not in [list], forbidden + elsif ($bbtree{$ind.'.name'} ne 'list') { + if ($print) { + print "this tag forbidden here\n"; + } + # add tag to BB tree as text + bbtree_add_text(\%bbtree, $ind, $level, $tag, $print); + # skip + next; + } + } + + # add tag to BB tree and enter + ($ind, $level) = bbtree_add_tag(\%bbtree, $ind, $level, $tag_name, $tag_value, $tag, $print); + # keep track of special case tags + if ($tag_name eq 'code') { + $in_code += 1; + } + elsif ($tag_name eq 'spoiler') { + $in_spoiler += 1; + } + elsif ($tag_name eq 'size') { + $in_size += 1; + } + } + # closing tag + else { + + # already on top level + if($level <= 0) { + if ($print) { + print "unmatched\n"; + } + # add bad tag to BB tree as text + bbtree_add_text(\%bbtree, $ind, $level, $tag, $print); + # skip + next; + } + + # unclosed [*] before [/list] + # add implicit [/*] + if (($tag_name eq 'list' ) and ($bbtree{$ind.'.name'} eq '*')) { + if ($print) { + print "implicit [/*]\n"; + } + # add [/*] to BB tree + ($ind, $level) = bbtree_close_tag(\%bbtree, $ind, $level, '[/*]' $print); + } + + # handle special case tags + if ($bbtree{$ind.'.name'} eq 'code') { + $in_code -= 1; + } + elsif ($bbtree{$ind.'.name'} eq 'spoiler') { + $in_spoiler -= 1; + } + elsif ($bbtree{$ind.'.name'} eq 'size') { + $in_size -= 1; + } + + # mismatched tag, but leave without closing + if ($tag_name ne $bbtree{$ind.'.name'}) { + if ($print) { + print "mismatched\n"; + } + # add bad tag to BB tree as text and close current tag + ($ind, $level) = bbtree_drop_tag(\%bbtree, $ind, $level, $tag, $print); + } + + # close current tag + ($ind, $level) = bbtree_close_tag(\%bbtree, $ind, $level, $tag, $print); + } + } + + if ($print) { + print "\nGenerate HTML from BBcode tree:\n"; + } + +} + +######## +# WGET # +######## + +sub wget { + (my $url, my $path, my $options, my $postdata) = @_; + + my @arg = ( + WGET, + '-t', WGET_RETRIES, + '--connect-timeout='.WGET_TIMEOUT, + ); + + unless ($options->{'verbose'} ne '') { + push(@arg, '-q'); + } + + if ($options->{'with_header'}) { + push(@arg, '--save-headers'); + } + + if ($options->{'cookie_path'} ne '') { + if (-f $options->{'cookie_path'}) { + push(@arg, '--load-cookies='.$options->{'cookie_path'}); + } + push(@arg, '--save-cookies='.$options->{'cookie_path'}); + } + else { + push(@arg, '--no-cookies'); + } + + if ($options->{'useragent'} ne '') { + push(@arg, '-U', $options->{'useragent'}); + } + + if ($options->{'referer'} ne '') { + push(@arg, '--referer='.$options->{'referer'}); + } + + if ($options->{'no-check-certificate'} ne '') { + push(@arg, '--no-check-certificate'); + } + + if ($options->{'postdata_path'} ne '') { + if (defined $postdata) { + write_postdata_file( + $options->{'postdata_path'}, + ENCODING_FILE, $options->{'encoding'}, + $postdata + ); + push(@arg, '--post-file='.$options->{'postdata_path'}); + } + } + + push(@arg, ( + $url, + '-O', $path + )); + + if ($options->{'verbose'} ne '') { + print WGET."\n"; + foreach my $a (@arg) { + print "$a\n"; + } + } + + my $r = system_encoded(WGET, @arg); + + return $r; +} + diff --git a/preview.1.pl b/preview.1.pl new file mode 100644 index 0000000..dfcde13 --- /dev/null +++ b/preview.1.pl @@ -0,0 +1,190 @@ +###RUN_PERL: + +# Copyright (C) 2023 Balthasar Szczepański +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . + +use strict; +use utf8; +use Getopt::Long; +use Encode::Locale ('decode_argv'); +use Encode ('encode', 'decode'); + +###PERL_LIB: use lib '/botm/lib/post'; +use botm_common ( + 'read_data_file', 'write_data_file' +); +use post_common ( + 'prepare_post', + 'CODE' +); + +###PERL_VERSION: use constant VERSION => 'x.x.x'; + +###PERL_ENCODING_FILE: use constant ENCODING_FILE => 'UTF-8'; + +use constant HELP_TEXT => + "preview [options] [file(s)]\n". + " -l, --username=USERNAME\n". + # " -p, --password=PASSWORD\n". + " -t, --subject=POST_SUBJECT\n". + " -c, --content=POST_CONTENT\n". + # " -e, --edit=POST_ID\n". + # " -a, --append\n". + "\n". + " -d, --data-only\n". + "\n". + " -B, --bbcode\n". + " -b, --no-bbcode\n". + " -S, --smilies\n". + " -s, --no-smilies\n". + " -U, --urls\n". + " -u, --no-urls\n". + " -G, --signature\n". + " -g, --no-signature\n". + " -N, --notify\n". + " -n, --no-notify\n". + "\n". + " -o, --output-file=OUTPUT_FILE\n". + "\n". + " -q, --quiet\n". + " -v, --verbose\n". + # " --keep-tempfile\n". + "\n". + " -h, --help\n". + " --version\n"; + +binmode STDIN, ':encoding(console_in)'; +binmode STDOUT, ':encoding(console_out)'; +binmode STDERR, ':encoding(console_out)'; +decode_argv(); + +my %options = (); + +Getopt::Long::Configure('bundling'); +GetOptions ( + 'username|l=s' => \$options{'username'}, + # 'password|p=s' => \$options{'password'}, + 'subject|t=s' => \$options{'subject'}, + 'content|c=s' => \$options{'content'}, + # 'edit|e=s' => \$options{'edit'}, + # 'append|a' => \$options{'append'}, + + 'data-only|d' => \$options{'data-only'}, + + 'bbcode|B' => \$options{ 'bbcode'}, + 'no-bbcode|b' => \$options{'no-bbcode'}, + 'smilies|S' => \$options{ 'smilies'}, + 'no-smilies|s' => \$options{'no-smilies'}, + 'urls|U' => \$options{ 'urls'}, + 'no-urls|u' => \$options{'no-urls'}, + 'signature|G' => \$options{ 'signature'}, + 'no-signature|g' => \$options{'no-signature'}, + 'notify|N' => \$options{ 'notify'}, + 'no-notify|n' => \$options{'no-notify'}, + + 'output-file|o=s' => \$options{'output-file'}, + + 'quiet|q' => \$options{'quiet'}, + 'verbose|v' => \$options{'verbose'}, + # 'keep-tempfile' => \$options{'keep-tempfile'}, + + 'help|h' => \$options{'help'}, + 'version' => \$options{'version'}, +); + +if ($options{'help'} ne '') { + print HELP_TEXT; + exit 0; +} +if ($options{'version'} ne '') { + print VERSION."\n"; + exit 0; +} + +my $time = time(); +my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = gmtime($time); +my $timetext = sprintf ( + "%04d-%02d-%02d %02d:%02d:%02d", + $year+1900, $mon+1, $mday, $hour, $min, $sec +); + +if ($options{'quiet'} ne '') { + $options{'verbose'} = ''; +} +if (($options{'output-file'} eq '') and ($options{'verbose'} eq '')) { + $options{'quiet'} = '1'; +} + +unless ($options{'quiet'} ne '') { + print "PREVIEW $$ $time - $timetext\n\n"; + if ($options{'verbose'} ne '') { + print "OPTIONS:\n"; + write_data_file(\*STDOUT, 'console_out', 0, \%options); + print "\n"; + } +} + +if ((scalar @ARGV) == 0) { + @ARGV = (\*STDIN); +} + +my $code = CODE->{'OK'}; +foreach my $arg (@ARGV) { + my $r; + + unless ($options{'quiet'} ne '') { + if ($arg == \*STDIN) { + print "STDIN\n"; + } + else { + print "FILE $arg\n"; + } + } + + my %data = (); + unless (($arg == \*STDIN) and ($options{'content'} ne '') and ($options{'data-only'} ne '')) + { + %data = read_data_file($arg, ENCODING_FILE, $options{'data-only'}); + unless (keys %data) { + unless ($options{'quiet'} ne '') { + print "NO DATA\n"; + } + if ($code == CODE->{'OK'}) { + $code = CODE->{'POST_INVALID'}; + } + print STDERR 'No data'; + unless ($arg == \*STDIN) { + print STDERR " in file $arg"; + } + print STDERR "\n"; + next; + } + } + if ($options{'verbose'} ne '') { + write_data_file(\*STDOUT, 'console_out', 0, \%data); + print "\n"; + } + + my %post = prepare_post(\%options, \%data); + if ($options{'verbose'} ne '') { + print "POST\n"; + write_data_file(\*STDOUT, 'console_out', 0, \%post); + print "\n"; + } + + # now generate preview +} + +exit $code; diff --git a/sendpost.1.pl b/sendpost.1.pl index 906eac1..6c1e4b4 100644 --- a/sendpost.1.pl +++ b/sendpost.1.pl @@ -23,18 +23,17 @@ use Encode ('encode', 'decode'); ###PERL_LIB: use lib '/botm/lib/post'; use botm_common ( - 'read_data_file', 'write_data_file', 'write_postdata_file', + 'read_data_file', 'write_data_file', 'read_header_file', 'merge_url', 'html_entity_decode', 'url_query_encode', - 'make_temp_path', - 'system_encoded' + 'make_temp_path' +); +use post_common ( + 'prepare_post', + 'wget', + 'CODE' ); - -use constant OK => 0; -use constant POST_INVALID => 1; -use constant POST_FAILED => 2; -use constant POST_NOT_ACCEPTED => 3; ###PERL_VERSION: use constant VERSION => 'x.x.x'; @@ -42,20 +41,12 @@ use constant POST_NOT_ACCEPTED => 3; ###PERL_ENCODING_OTT: use constant ENCODING_OTT => 'UTF-8'; ###PERL_ENCODING_MIRROR: use constant ENCODING_MIRROR => 'UTF-8'; -###PERL_DEFAULT_PASSWORD: use constant DEFAULT_PASSWORD => 'password'; -###PERL_DEFAULT_SUBJECT: use constant DEFAULT_SUBJECT => 'Re: 1190: "Time"'; -###PERL_DEFAULT_USERNAME: use constant DEFAULT_USERNAME => 'username'; - -###PERL_WGET_RETRIES: use constant WGET_RETRIES => 3; -###PERL_WGET_TIMEOUT: use constant WGET_TIMEOUT => 60; -###PERL_WGET_USERAGENT: use constant WGET_USERAGENT => "post tool (http://bicyclesonthemoon.info/git-projects/?p=ott/post)"; - -###PERL_WGET: use constant WGET => '/usr/bin/wget'; - ###PERL_TMP_PATH: use constant TMP_PATH => '/botm/tmp/post'; ###PERL_MIRROR_URL: use constant MIRROR_URL => 'https://1190.bicyclesonthemoon.info/ott'; +###PERL_WGET_USERAGENT: use constant WGET_USERAGENT => "post tool (http://bicyclesonthemoon.info/git-projects/?p=ott/post)"; + use constant HELP_TEXT => "sendpost [options] [file(s)]\n". " -l, --username=USERNAME\n". @@ -166,7 +157,7 @@ if ((scalar @ARGV) == 0) { @ARGV = (\*STDIN); } -my $code = OK; +my $code = CODE->{'OK'}; foreach my $arg (@ARGV) { my $r; @@ -187,8 +178,8 @@ foreach my $arg (@ARGV) { unless ($options{'quiet'} ne '') { print "NO DATA\n"; } - if ($code == OK) { - $code = POST_INVALID; + if ($code == CODE->{'OK'}) { + $code = CODE->{'POST_INVALID'}; } print STDERR 'No data'; unless ($arg == \*STDIN) { @@ -218,11 +209,11 @@ foreach my $arg (@ARGV) { # } unless ($options{'no-mirror'}) { $r = post_to_mirror(\%options, \%post); - if ($r == OK) { + if ($r == CODE->{'OK'}) { next; } } - if ($code == OK) { + if ($code == CODE->{'OK'}) { $code = $r; } } @@ -230,126 +221,6 @@ foreach my $arg (@ARGV) { exit $code; - -sub prepare_post { - (my $cmd_options, my $file_data) = @_; - my %post; - - if (($cmd_options->{'subject'}) ne '') { - $post{'subject'} = $cmd_options->{'subject'}; - } - elsif (($file_data->{'subject'}) ne '') { - $post{'subject'} = $file_data->{'subject'}; - } - else { - $post{'subject'} = DEFAULT_SUBJECT; - } - - if (($cmd_options->{'username'}) ne '') { - $post{'username'} = $cmd_options->{'username'}; - } - elsif (($file_data->{'username'}) ne '') { - $post{'username'} = $file_data->{'username'}; - } - else { - $post{'username'} = DEFAULT_USERNAME; - } - - if (($cmd_options->{'password'}) ne '') { - $post{'password'} = $cmd_options->{'password'}; - } - elsif (($file_data->{'password'}) ne '') { - $post{'password'} = $file_data->{'password'}; - } - else { - $post{'password'} = DEFAULT_PASSWORD; - } - - if (($cmd_options->{'content'}) ne '') { - $post{'content'} = $cmd_options->{'content'}; - } - elsif (($file_data->{'content'}) ne '') { - $post{'content'} = $file_data->{'content'}; - } - else { - $post{'content'} = ''; - } - - if (($cmd_options->{'no-bbcode'}) ne '') { - $post{'bbcode'} = 0; - } - elsif (($cmd_options->{'bbcode'}) ne '') { - $post{'bbcode'} = 1; - } - elsif (($file_data->{'bbcode'}) ne '') { - $post{'bbcode'} = $file_data->{'bbcode'}+0; - } - else { - $post{'bbcode'} = 1; - } - - if (($cmd_options->{'no-smilies'}) ne '') { - $post{'smilies'} = 0; - } - elsif (($cmd_options->{'smilies'}) ne '') { - $post{'smilies'} = 1; - } - elsif (($file_data->{'smilies'}) ne '') { - $post{'smilies'} = $file_data->{'smilies'}+0; - } - else { - $post{'smilies'} = 1; - } - - if (($cmd_options->{'no-urls'}) ne '') { - $post{'urls'} = 0; - } - elsif (($cmd_options->{'urls'}) ne '') { - $post{'urls'} = 1; - } - elsif (($file_data->{'urls'}) ne '') { - $post{'urls'} = $file_data->{'urls'}+0; - } - else { - $post{'urls'} = 1; - } - - if (($cmd_options->{'no-signature'}) ne '') { - $post{'signature'} = 0; - } - elsif (($cmd_options->{'signature'}) ne '') { - $post{'signature'} = 1; - } - elsif (($file_data->{'signature'}) ne '') { - $post{'signature'} = $file_data->{'signature'}+0; - } - else { - $post{'signature'} = 1; - } - - if (($cmd_options->{'no-notify'}) ne '') { - $post{'notify'} = 0; - } - elsif (($cmd_options->{'notify'}) ne '') { - $post{'notify'} = 1; - } - elsif (($file_data->{'notify'}) ne '') { - $post{'notify'} = $file_data->{'notify'}+0; - } - else { - $post{'notify'} = 0; - } - - if (($file_data->{'time'}) ne '') { - $post{'time'} = $file_data->{'time'}+0; - } - else { - $post{'time'} = 0; - } - - return %post; -} - sub post_to_mirror { (my $cmd_options, my $post) = @_; @@ -388,7 +259,7 @@ sub post_to_mirror { print 'INVALID ID '.$cmd_options->{'edit'}."\n"; } print STDERR 'Not a valid mirror post ID: '.$cmd_options->{'edit'}."\n"; - return POST_INVALID; + return CODE->{'POST_INVALID'}; } $query_data{'e'} = 'm'.$cmd_options->{'edit'}; } @@ -415,7 +286,7 @@ sub post_to_mirror { print "WGET FAIL $r\n"; } print STDERR "Failed to get edit page: wget: $r\n"; - return POST_FAILED; + return CODE->{'POST_FAILED'}; } unless (open($fh, '<:encoding('.ENCODING_MIRROR.')', encode('locale_fs', $tmp_path))) { @@ -423,7 +294,7 @@ sub post_to_mirror { print "FAIL open $tmp_path\n"; } print STDERR "Failed to open edit page. $tmp_path\n"; - return POST_FAILED; + return CODE->{'POST_FAILED'}; } %header = read_header_file($fh, ENCODING_MIRROR()); @@ -437,7 +308,7 @@ sub post_to_mirror { print 'FAIL '.$header{':status-code'}.' '.$header{':reason-phrase'}."\n"; } print STDERR 'Failed to get edit page: '.$header{':status-code'}.' '.$header{':reason-phrase'}."\n"; - return POST_FAILED; + return CODE->{'POST_FAILED'}; } if (($cmd_options->{'edit'} ne '') and ($cmd_options->{'append'} ne '')) { @@ -447,7 +318,7 @@ sub post_to_mirror { print "FAIL previous post\n"; } print STDERR 'Failed getting previous post content: '.$previous_post{'error'}."\n"; - return POST_FAILED; + return CODE->{'POST_FAILED'}; } if ($cmd_options->{'verbose'} ne '') { print "PREVIOUS POST\n"; @@ -516,7 +387,7 @@ sub post_to_mirror { print "WGET FAIL $r\n"; } print STDERR "Failed to get post submit response: wget: $r\n"; - return POST_FAILED; + return CODE->{'POST_FAILED'}; } unless (open($fh, '<:encoding('.ENCODING_MIRROR.')', encode('locale_fs', $tmp_path))) { @@ -524,7 +395,7 @@ sub post_to_mirror { print "FAIL open $tmp_path\n"; } print STDERR "Failed to open post submit response. $tmp_path\n"; - return POST_FAILED; + return CODE->{'POST_FAILED'}; } %header = read_header_file($fh, ENCODING_MIRROR()); @@ -538,7 +409,7 @@ sub post_to_mirror { print 'FAIL '.$header{':status-code'}.' '.$header{':reason-phrase'}."\n"; } print STDERR 'Failed to get post submit response page: '.$header{':status-code'}.' '.$header{':reason-phrase'}."\n"; - return POST_FAILED; + return CODE->{'POST_FAILED'}; } ($r, $s) = get_mirror_post_status($fh); @@ -549,7 +420,7 @@ sub post_to_mirror { print "FAIL $s\n"; } print STDERR "Post not accepted by mirror: $s.\n"; - return POST_NOT_ACCEPTED; + return CODE->{'POST_NOT_ACCEPTED'}; } unless ($cmd_options->{'quiet'} ne '') { @@ -567,7 +438,7 @@ sub post_to_mirror { unlink($tmp_path, $cookie_path, $postdata_path); } - return OK; + return CODE->{'OK'}; } sub get_mirror_post_status { @@ -674,67 +545,3 @@ sub get_mirror_previous_post { } return %post; } - -sub wget { - (my $url, my $path, my $options, my $postdata) = @_; - - my @arg = ( - WGET, - '-t', WGET_RETRIES, - '--connect-timeout='.WGET_TIMEOUT, - ); - - unless ($options->{'verbose'} ne '') { - push(@arg, '-q'); - } - - if ($options->{'with_header'}) { - push(@arg, '--save-headers'); - } - - if ($options->{'cookie_path'} ne '') { - if (-f $options->{'cookie_path'}) { - push(@arg, '--load-cookies='.$options->{'cookie_path'}); - } - push(@arg, '--save-cookies='.$options->{'cookie_path'}); - } - else { - push(@arg, '--no-cookies'); - } - - if ($options->{'useragent'} ne '') { - push(@arg, '-U', $options->{'useragent'}); - } - - if ($options->{'referer'} ne '') { - push(@arg, '--referer='.$options->{'referer'}); - } - - if ($options->{'postdata_path'} ne '') { - if (defined $postdata) { - write_postdata_file( - $options->{'postdata_path'}, - ENCODING_FILE, $options->{'encoding'}, - $postdata - ); - push(@arg, '--post-file='.$options->{'postdata_path'}); - } - } - - push(@arg, ( - $url, - '-O', $path - )); - - if ($options->{'verbose'} ne '') { - print WGET."\n"; - foreach my $a (@arg) { - print "$a\n"; - } - } - - my $r = system_encoded(WGET, @arg); - - return $r; -} - diff --git a/settings.txt b/settings.txt index b882072..f14c7a3 100644 --- a/settings.txt +++ b/settings.txt @@ -23,6 +23,8 @@ _SHEBANG: #!$0 _PERL_USE_2: use $0 $1; _PERL_CONSTANT: use constant $0 => $1; _PERL_CONSTANT_STR: @_PERL_CONSTANT($0,@_PERL_STR($1)) +_PERL_OUR: our $0 = $1; +_PERL_OUR_STR: @_PERL_OUR(\$$0,@_PERL_STR($1)) MAKE_TARGET = TARGET = $target @@ -41,6 +43,8 @@ MAKE_LIB_PATH = LIB_PATH = $lib_path MAKE_TMP_PATH = TMP_PATH = $tmp_path +PERL_EXPORT_VERSION = @_PERL_OUR_STR( VERSION, $_version) + PERL_DEFAULT_PASSWORD = @_PERL_CONSTANT_STR( DEFAULT_PASSWORD , $default_password) PERL_DEFAULT_SUBJECT = @_PERL_CONSTANT_STR( DEFAULT_SUBJECT , $default_subject) PERL_DEFAULT_USERNAME = @_PERL_CONSTANT_STR( DEFAULT_USERNAME , $default_username) -- 2.30.2