#!/usr/bin/perl
# @(#) RpSend.pl  Remote-printer send program, intended to capture appsocket
#                 (port 9100) print streams and upload them to a server from
#                 which they can subsequently be downloaded for printing.
#
# Copyright (c) 2020 Graham Jenkins <grahjenk@cpan.org>. All rights reserved.
# This program is free software; you can redistribute it and/or modify it under
# the same terms as Perl itself. Revised: 2020-05-10

use strict;             # This program can be installed in /usr/local/bin and
use warnings;           # called through an xinetd program thus:
use Getopt::Std;        #  service appsocket
use File::Basename;     #  { 
use File::Spec;         #   socket_type = stream
use Sys::Syslog;        #   protocol    = tcp
use LWP::UserAgent qw();#   wait        = no
use Compress::Raw::Lzma;#   user        = lp
use MIME::Lite;         #   group       = sys
use Sys::Hostname::Long;#   server      = /usr/local/bin/RpSend.pl
use File::Spec;         #   server_args = MyPrinter@aol.com
use vars qw($VERSION);  #   disable     = no
$VERSION = "1.03";      #  }
                        # It will then upload print jobs sent to port 9100 and
                        # email a download-URL to the designated email address.
use File::Temp qw/tempfile/;
use Env qw/TEADATAFILE TEAUSERNAME/;

# Compress subroutine; accepts string as parameter, compresses it if possible
sub compress {
  my ($z,$o);
  $z=new Compress::Raw::Lzma::EasyEncoder(AppendOutput=>1,Preset=>9,Extreme=>1);
  if ( ($z->code($_[0],$o)       ==LZMA_OK        ) &&
       ($z->flush($o,LZMA_FINISH)==LZMA_STREAM_END)    ) { $_[0]=$o; return(1) }
  else                                                   {           return(0) }
}

# Collect option(s), check usage
my (%opts,$BadOption);
$Getopt::Std::STANDARD_HELP_VERSION=1;
getopts ('c',\%opts) or $BadOption="Y";
if ( defined($BadOption) || ($#ARGV != 0) || ($ARGV[0]!~m/\@/) ) { 
  die "Usage: ".basename($0)." [-c] email-address\n".
      " e.g.: ".basename($0)." -c MyPrinter\@aol.com\n"
}

# Collect user email if possible
my $user="";
if ( defined($TEAUSERNAME) ) {
  if ( my @p=getpwnam($TEAUSERNAME) ) {
    if ( open(CF,File::Spec->catdir($p[7],".RpSend.cf"))) {
      while ( <CF> ) {if ( ( $_=~m/\@/)&&($_!~m/^#/) ) {$user=$_;chomp($user)}}
    }
  }
}
 
# Generate sequence-number, slurp from input, then fork so that we can be
# called again while compression and upload proceed
my ($sec,$min,$hour) = localtime(time);
my $seq=substr(100+$hour,1).substr(100+$min,1).substr(100+$sec,1);
if ( defined($TEADATAFILE) ) { $seq=basename($TEADATAFILE) }
syslog("info","Receiving Seq ".$seq);
my $data;
if ( defined($TEADATAFILE) ) {
  open(IN,$TEADATAFILE); $data=do { local $/; <IN> }; close(IN);
  fork and exit(-1)
}
else {
  $data = do { local $/; <STDIN> };
  fork and exit
}

# Compress, copy to temporary file and upload
compress($data) if defined ($opts{'c'});
my $size=length($data);
syslog("info","Uploading Seq $seq $size bytes");
my ($fh,$tmp)=tempfile(UNLINK=>1); print $fh $data; close $fh; undef($data);
my $ua = LWP::UserAgent->new();
my $response = $ua->post('https://file.io',Content_Type => 'form-data',
                                           Content => [ file => [ $tmp ], ], );
# Compose and send the message
if ( $response->is_success ) {
  my $url=$response->decoded_content;
  $url=substr($url,index($url,"https://"));
  $url=substr($url,0,index($url,'"'));
  my $msg = MIME::Lite->new(From    => getpwuid($<).'@'.hostname_long,
                            To      => $ARGV[0],
                            Subject => "Remote Printer Data for Seq: $seq",
                            Data    => "Seq: $seq $size bytes =$user= $url");
  $msg->send;
  syslog("info","Uploaded  Seq $seq Link $url")
} else {
  syslog("info","Upload    Seq $seq Failed!")
}

__END__

=head1 NAME

RpSend - Remote printing client utility

=head1 README

Remote-printer send program, intended to capture appsocket (port 9100)
print streams and upload them to a server from which they can subsequently
be downloaded for printing.

=head1 DESCRIPTION

C<RpSend> intercepts a job sent to an AppSocket printer and uploads
the data file to an ephemeral file-sharing website. A link to the 
file location is then sent to a designated address so that the file
can be downloaded and printed at a remote location.

Interception is effected by an appropriate xinetd (or equivalent)
service as shown in the program listing.

=head1 USAGE

The program can be invoked as follows:

=over 5

RpSend [-c] email-address

=back

e.g. RpSend MyPrinter@aol.com

If the [-c] option is used, the data stream will be compressed
before transmission.

C<RpSend> can be called using the Tea4CUPS package by appending to
its configuration file lines like:

  prehook_spool : /usr/local/bin/RpSend MyPrinter@aol.com
  keepfiles     : no

If C<RpSend> is called via Tea4CUPS, it will attempt to read the
sender's email address from a file called C<.RpSend.cf> in
his home directory. 

C<RpSend> always forks the compression and transmission operations
so that it may be called repeatedly without significant delays.

=head1 SCRIPT CATEGORIES

Networking
UNIX/System_administration

=head1 AUTHOR

Graham Jenkins <grahjenk@cpan.org>

=head1 COPYRIGHT

Copyright (c) 2020 Graham Jenkins. All rights reserved.
This program is free software; you can redistribute it
and/or modify it under the same terms as Perl itself.

=cut
