Created
June 25, 2014 22:53
-
-
Save wwwslinger/ac6b49cb991d2d5263a2 to your computer and use it in GitHub Desktop.
Dell 1600n Network Scanning script for Linux and Mac (from http://www.jon.demon.co.uk/dell1600n-net-scan/)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/perl -w | |
# Perl hack to do network scanning using Dell 1600n printer/scanner/fax/copier. | |
# Read LICENCE section below for terms and conditions. | |
# Run with no args for usage. | |
# $Id: dell1600n-net-scan.pl,v 1.64 2010-09-19 16:19:33 jon Exp $ | |
# | |
# Jon Chambers, 2005-05-19 | |
# | |
# Contains excellent and gratefully received patches from: | |
# Dani Gutiérrez (Ricoh FX200) | |
# Philip Roche (Xerox Phaser 6110) | |
# Laurent Ernes (Samsung CLX-2160N) | |
# Christophe Danker (Samsung SCX-4720FN) | |
# | |
use strict; | |
use IO::Socket; | |
use IO::Select; | |
use POSIX; | |
use Sys::Hostname; | |
use Time::HiRes qw( usleep ); | |
#========================================================================= | |
# VERSION | |
$main::version = "1.14"; | |
$main::cvsId = '$Id: dell1600n-net-scan.pl,v 1.64 2010-09-19 16:19:33 jon Exp $'; | |
#========================================================================= | |
# LICENCE | |
$main::licence = " | |
This software is open source. Feel free to copy and distribute as | |
you like. If you use it as the basis of other software then it would | |
be polite to credit me. If this software is useful to you then feel | |
free to send a nice postcard from wherever you are to | |
Jon Chambers, 30 Stephenson Rd, London W7 1NW, UK. | |
This program is provided in the hope that it will be useful. It comes | |
with no warranty. USE AT YOUR OWN RISK. | |
Jon Chambers (jon\@jon.demon.co.uk), 2007-11-17 | |
"; | |
#========================================================================= | |
# fill the nice globals with defaults | |
# uncomment the appropriate default for your model | |
$main::model = "1600n"; | |
#$main::model = "1815dn"; | |
#$main::model = "fx200"; | |
#$main::model = "6110mfp"; | |
#$main::model = "clx2160n"; | |
#$main::model = "scx4720fn"; | |
# get hostname (minus any domain part and non-alphanumerics) | |
$main::clientName = hostname(); | |
$main::clientName =~ s/\..*$//g; | |
$main::clientName =~ s/[^\w]//g; | |
# If defined then should be a 4-digit PIN number | |
#$main::clientPin = 1234; | |
$main::clientPin = undef; | |
$main::printerAddr = ""; | |
$main::printerPort = 1124; | |
$main::scanFileDir = "."; | |
$main::scanFilePrefix = "scan-"; | |
$main::softwareName = "dell1600n-net-scan"; | |
# if set then specifies a particular network interface | |
$main::bindAddr = undef; | |
# broadcast address too find scanners | |
$main::broadcastAddr = "255.255.255.255"; | |
# time to wait between re-registrations (seconds) | |
$main::scanWaitLoopTimeoutSec = 60; | |
# set non-0 to print lots of debug nonsense | |
$main::debug = 0; | |
# kernel-specific network stuff for now-defunct UPNP multicast | |
$main::IP_ADD_MEMBERSHIP_linux = 35; # Linux | |
#$main::IP_ADD_MEMBERSHIP_windows = 5; # Windows | |
# choose linux by default | |
$main::IP_ADD_MEMBERSHIP = $main::IP_ADD_MEMBERSHIP_linux; | |
# Command to send file as email attachment. | |
# (See PostProcessFile comments for substitutions.) | |
%main::emailCmd = ( "cmd" => "echo new scan | mutt &infiles; -s \"new scan\" &email;", | |
"inFilePrefix" => "-a ", | |
"delInFiles" => 1 ); | |
# The following options must match or things will go wrong. | |
$main::preferredFileType = 2; # ( 2=>TIFF, 4=>PDF, 8=>JPEG ) | |
$main::preferredFileCompression = 0x08; # ( 0x08 => CCIT Group 4, 0x20 => JPEG ) | |
$main::preferredFileComposition = 0x01; # ( 0x01 => TIFF/PDF, 0x40 => JPEG ) | |
$main::preferredResolution = 200; | |
# Profiles for Dell 1815dn and Xerox Phaser 6110mfp | |
# See comments above for legal values for type, compression and composition | |
%main::profiles = ( | |
"TIFF 100" => { "type" => 2, "cprss" => 0x08, "cmpsn" => 0x01, "res" => 100 }, | |
"TIFF 200" => { "type" => 2, "cprss" => 0x08, "cmpsn" => 0x01, "res" => 200 }, | |
"TIFF 300" => { "type" => 2, "cprss" => 0x08, "cmpsn" => 0x01, "res" => 300 }, | |
"PDF 100" => { "type" => 4, "cprss" => 0x08, "cmpsn" => 0x01, "res" => 100 }, | |
"PDF 200" => { "type" => 4, "cprss" => 0x08, "cmpsn" => 0x01, "res" => 200 }, | |
"PDF 300" => { "type" => 4, "cprss" => 0x08, "cmpsn" => 0x01, "res" => 300 }, | |
"JPEG 200" => { "type" => 8, "cprss" => 0x20, "cmpsn" => 0x40, "res" => 200 }, | |
"JPEG 300" => { "type" => 8, "cprss" => 0x20, "cmpsn" => 0x40, "res" => 300 }, | |
"COLOUR PDF 200" => { "type" => 8, "cprss" => 0x20, "cmpsn" => 0x40, "res" => 200, "profileOption" => "pdf" }, | |
"COLOUR PDF 300" => { "type" => 8, "cprss" => 0x20, "cmpsn" => 0x40, "res" => 300, "profileOption" => "pdf" }, | |
); | |
$main::emailAddr = undef; | |
# command to convert to PDF | |
#$main::pdfConvertCmd = undef; | |
# NOTE: convert is part of the imagemagick package | |
# NOTE2: zip compressed pdf files are not supported by Adobe Acrobat before | |
# version 3. | |
%main::pdfConvertCmd = ( "cmd" => "convert -compress zip &infiles; &outFile;", | |
"outFile" => "&scanFileDir;/&scanFilePrefix;×tamp;.pdf", | |
"delInFiles" => 1 ); | |
# if set then all scans will be converted to PDF | |
$main::forceToPdf = 0; | |
# if true then exit after single session | |
$main::singleSession = 1; | |
# instance number (concatenated with IP address to create uid for 1815dn and 6110mfp) | |
$main::instanceId = 0; | |
# Define optional commands here. | |
# These take the form of a hash (keyed by option name) of command hashes (in the | |
# same format as %main::pdfConvertCmd above) | |
# If the option is selected the command hash will be passed to | |
# PostProcessFile() (see comments in function for available substitutions) | |
%main::options = (); | |
# tgz option writes scanned files to a tgz archive. | |
# Not enormously useful but a fair usage example... | |
$main::options{ "tgz" } = { | |
"cmd" => "tar zcvf &outFile; &infiles;", | |
"outFile" => "&scanFileDir;/&scanFilePrefix;×tamp;.tgz", | |
"delInFiles" => 1, | |
"description" => "Write scanned files to a tgz archive" | |
}; | |
# gimp option opens files with the GIMP. | |
$main::options{ "gimp" } = { | |
"cmd" => "gimp &infiles;&", | |
"description" => "Open scanned files with the GIMP" | |
}; | |
# Not enormously useful but a fair usage example... | |
$main::options{ "multipage-tiff" } = { | |
"cmd" => "convert &infiles; &outFile; ", | |
"outFile" => "&scanFileDir;/&scanFilePrefix;×tamp;.tiff", | |
"delInFiles" => 1, | |
"description" => "Create multipage tiff document" | |
}; | |
# As %main::pdfConvertCmd but usable via options (and profile options) | |
$main::options{ "pdf" } = { | |
"cmd" => "convert -compress zip &infiles; &outFile;", | |
"outFile" => "&scanFileDir;/&scanFilePrefix;×tamp;.pdf", | |
"delInFiles" => 1, | |
"description" => "Convert all scans to PDF format" | |
}; | |
# to_web option moves scanned files to web tree | |
#$main::options{ "to_web" } = { | |
# "cmd" => "mv -v &infiles; /home/www/images/", | |
# "description" => "Move scanned files to web tree" | |
# }; | |
#========================================================================= | |
# Global state variables | |
# scan data storage | |
$main::dataBuf = ""; | |
# filenames scanned this session | |
@main::sessionFiles = (); | |
# PDF convert flag | |
$main::pdfConvert = 0; | |
# received scan metadata | |
$main::fileType = 0; # ( 2=>TIFF, 4=>PDF, 8=>JPEG ) | |
$main::widthPixels = 0; | |
$main::heightPixels = 0; | |
$main::xResolution = 0; | |
$main::yResolution = 0; | |
# our IP address (raw format) | |
$main::ipAddr = undef; | |
# which of the options (if any) is selected | |
$main::selectedOption = undef; | |
#========================================================================= | |
sub GetTimestamp() | |
# Return local timestamp string as YYYYMMDD-hhmmsss | |
{ | |
my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = | |
localtime(); | |
return sprintf( "%04d%02d%02d-%02d%02d%02d", | |
$year + 1900, | |
$mon + 1, | |
$mday, | |
$hour, | |
$min, | |
$sec ); | |
} # GetTimestamp | |
#========================================================================= | |
sub ListenForPrinters() | |
# listens for printers on multicast 239.255.255.250:1900 | |
# Now this code is just included as a curiosity - BroadcastDiscover | |
# is quicker and easier | |
{ | |
my $group = '239.255.255.250'; | |
my $port = 1900; | |
print "Listening on multicast group $group:$port\n"; | |
my $sock = IO::Socket::INET-> | |
new( Proto => 'udp', LocalPort => $port ) | |
|| die "Error opening socket"; | |
$sock->setsockopt( 0, | |
$main::IP_ADD_MEMBERSHIP, | |
pack("C8", split(/\./, "$group.0.0.0.0"))) | |
|| die "Couldn't set group: $!\n"; | |
while (1) { | |
my $data; | |
next unless $sock->recv( $data, 512 ); | |
print $data."\n"; | |
} | |
} # ListenForPrinters | |
#========================================================================= | |
sub BroadcastDiscover() | |
# Use UDP broadcast to discover devices | |
{ | |
print "Broadcasting to $main::broadcastAddr for $main::model-compatible scanners\n\n"; | |
my $sock = new IO::Socket::INET->new( Proto => 'udp', | |
LocalAddr => $main::bindAddr, | |
Broadcast => 1 | |
) | |
or die "Error opening UDP socket"; | |
my %packet = InitPacket( GetNormalPacketHeader() ); | |
if ( $main::model eq "1600n" ){ | |
AppendMessageToPacket( \%packet, 0x25, "std-scan-discovery-all", | |
0x02, 0 ); | |
} else { | |
# 1815dn-compatible (maybe works for fx200 too?) | |
AppendMessageToPacket( \%packet, 0x25, "std-scan-discovery-all", | |
0x02, 0 ); | |
AppendMessageToPacket( \%packet, 0x25, "std-scan-discovery-type", | |
0x06, 1 ); | |
} | |
my $sin = sockaddr_in( $main::printerPort, | |
inet_aton( $main::broadcastAddr ) ); | |
$sock->send( PackMessage( \%packet ), 0, $sin ) or | |
die "Nothing sent"; | |
# init a select object on our socket | |
my $sel = new IO::Select( $sock ); | |
my $numFound = 0; | |
while (1) { | |
my @ready = $sel->can_read( 5 ); | |
if ( ! @ready ){ | |
# no input yet (we hit the timeout) so exit | |
print "Finished querying for network scanners, found $numFound\n"; | |
exit( 0 ); | |
} | |
my $data; | |
if ( ! $sock->recv( $data, 1024 ) ){ | |
usleep( 100 ); | |
next; | |
} | |
ProcessReceivedPacket( \$data, $sock, "udp" ); | |
print "\n"; | |
$numFound++; | |
} # while | |
} # BroadcastDiscover | |
#========================================================================= | |
sub OpenUdpPort( $ ) | |
# Open udp socket to printer | |
{ | |
my ( $addr ) = @_; | |
my $sock = new IO::Socket::INET->new(PeerPort => $main::printerPort, | |
PeerAddr => $addr, | |
LocalAddr => $main::bindAddr, | |
Proto => 'udp' | |
) | |
or die "Can't connect to: $addr:$main::printerPort\n"; | |
# note our ip addr | |
$main::ipAddr = $sock->sockaddr(); | |
print "My IP address is ". join( ".", unpack( "C4", $main::ipAddr ))."\n"; | |
# sanity check (Windows will fail this) | |
if ( ! unpack( "V", $main::ipAddr ) ){ | |
print "Oh dear, WIN32 UDP sockets are bad... trying to determine local IP address...\n"; | |
my $tmpsock = new IO::Socket::INET->new(PeerPort => 5200, | |
PeerAddr => $addr, | |
LocalAddr => $main::bindAddr, | |
Proto => 'tcp' ) || | |
die "Error making TCP connection to $addr:5200"; | |
$main::ipAddr = $tmpsock->sockaddr(); | |
print "My IP address is ". join( ".", unpack( "C4", $main::ipAddr )). | |
"\n"; | |
} | |
# Work out the manufacturer from the model number supplied; | |
my $make; | |
if ( $main::model eq "fx200" ) { | |
$make = "Ricoh"; | |
} elsif ( $main::model eq "6110mfp" ) { | |
$make = "Xerox Phaser"; | |
} elsif ( $main::model eq "clx2160n" || $main::model eq "scx4720fn" ) { | |
$make = "Samsung"; | |
} else { | |
$make = "Dell"; | |
} | |
print "Registering with $make $main::model $addr:$main::printerPort as $main::clientName\n"; | |
return $sock; | |
} # OpenUdpPort | |
#========================================================================= | |
sub PostProcessFile( $ ) | |
# Performs post-processing on the current file list | |
# param 1 : reference to hash with members: | |
# cmd: post-process command (required) | |
# outFile : output file (optional) | |
# inFilePrefix : prefix to infile(s) (optional) | |
# delInFiles : if set and true then input files will be deleted | |
# | |
# The following substitutions will be made on cmd: | |
# &infiles; => list of input files (optionally prefixed by inFilePrefix) | |
# &outFile; => outFile | |
# | |
# The following substitutions will be made on outFile: | |
# &scanFileDir; => $main::scanFileDir | |
# &scanFilePrefix => $main::scanFilePrefix | |
# ×tamp; => the current timestamp | |
# | |
{ | |
my ( $in ) = @_; | |
# sanity check | |
if ( ! scalar @main::sessionFiles ){ | |
print "PostProcessFile: No files left to process\n"; | |
return; | |
} | |
my $cmd = $$in{ "cmd" }; | |
if ( ! defined( $cmd ) ){ return } | |
my $prefix = $$in{ "inFilePrefix" }; | |
if ( ! defined( $prefix ) ){ $prefix = "" } | |
my $outFile = $$in{ "outFile" }; | |
my $timestamp = GetTimestamp(); | |
# perform substitutions on outFile | |
if ( defined $outFile ){ | |
$outFile =~ s/&scanFileDir;/$main::scanFileDir/sg; | |
$outFile =~ s/&scanFilePrefix;/$main::scanFilePrefix/sg; | |
$outFile =~ s/×tamp;/$timestamp/sg; | |
} | |
# build post-process command | |
my $infiles = ""; | |
foreach my $file ( @main::sessionFiles ){ | |
$infiles .= $prefix . $file . " "; | |
} | |
$cmd =~ s/&infiles;/$infiles/sg; | |
if ( defined ( $outFile ) ){ | |
$cmd =~ s/&outFile;/$outFile/sg; | |
} | |
if ( defined ( $main::emailAddr ) ){ | |
$cmd =~ s/&email;/$main::emailAddr/sg; | |
} | |
print "Running: $cmd\n"; | |
my $ret = system( $cmd ); | |
if ( $ret != 0 ){ | |
print "WARNING: Got non-zero return code - this is generally bad...\n"; | |
} | |
if ( $$in{ "delInFiles" } ){ | |
foreach my $xxx ( @main::sessionFiles ){ | |
print "Deleting $xxx\n"; | |
unlink $xxx; | |
} | |
@main::sessionFiles = (); | |
} | |
if ( defined( $outFile ) ){ | |
push @main::sessionFiles, $outFile; | |
} | |
} # PostProcessFile() | |
#========================================================================= | |
sub ProcessReceivedPacket( $$$ ) | |
# Displays the contents of a packet received from the printer to screen | |
# and processes it as appropriate | |
# Processed data is removed from the packet. | |
# In "udp" mode the packet must be whole (ie: the data size must | |
# match that read from the header. In "tcp" mode, in case of a | |
# an incomplete packet the the function returns to allow more data | |
# to be read from the socket | |
# param 1 : reference to binary data | |
# param 2 : socket object (in case a reply is required) | |
# param 3 : mode, either "tcp" or "udp" | |
{ | |
my ( $data, $sock, $mode ) = @_; | |
if ( $main::debug ){ | |
print "** Processing packet of " . ( length ${$data} ) . " bytes\n"; | |
} | |
# init a reply packet ready for use | |
my %packet = InitPacket( GetReplyPacketHeader() ); | |
my $bLastPacket = 0; | |
my $bPrefsRequested = 0; | |
# process as much of the data as we can | |
while ( length ${$data} >= 8 ){ | |
# copy data into an array | |
my @datArray = unpack( "C*", ${$data} ); | |
# extract the header | |
my @header = splice( @datArray, 0, 8 ); | |
my $now = ctime( time() ); | |
chop $now; | |
if ( $main::debug ){ | |
print "$now: header: ".join( " ", @header )."\n"; | |
} | |
my $ok = 1; | |
if ( @header != 8 ){ | |
print "*** header less than 8 bytes\n"; | |
$ok = 0; | |
} | |
my $expectedSize = ($header[7]+($header[6]<<8) ); | |
my $actualSize = @datArray; | |
# if tcp mode then check whether we need more data | |
if ( ( $mode eq "tcp" ) && ( $actualSize < $expectedSize ) ){ | |
if ( $main::debug ){ | |
print "*** Incomplete packet (expect $expectedSize, ". | |
"got $actualSize)\n"; | |
} | |
return; | |
} | |
# if udp mode then we expect an exact match | |
if ( ( $mode eq "udp" ) && ( $expectedSize != $actualSize ) ) { | |
print "*** data size mismatch: (expect $expectedSize, got $actualSize)\n"; | |
$ok = 0; | |
} # if | |
my ( $cmdName, $cmdValue ); | |
if ( ! $ok ){ | |
# unrecognised data block : just HexDump it | |
print "Unexpected block format:\n"; | |
print HexDump ${$data}; | |
} else { | |
# remove the data that we will process from the start of the data buffer | |
${$data} = substr ${$data}, ( 8 + $expectedSize ); | |
# trim the excess elements from the end of @datArray | |
@datArray = @datArray[ 0..($expectedSize - 1) ]; | |
# loop until all the data has been processes | |
while ( @datArray ){ | |
# extract the command | |
my @cmdSub = splice( @datArray, 0, 3 ); | |
$cmdName = pack( "C*", | |
splice( @datArray, 0, | |
( ( $cmdSub[ 1 ] << 8 ) + | |
$cmdSub[ 2 ] )) ); | |
if ( $main::debug ){ | |
print " $cmdName ($cmdSub[0]): "; | |
} | |
# extract the payload | |
my @plSub = splice( @datArray, 0, 3 ); | |
my $plType = $plSub[ 0 ]; | |
my $plSize = ( $plSub[ 1 ] << 8 ) + $plSub[ 2 ]; | |
if ( $main::debug ){ | |
print "[$plType] "; | |
} | |
my @plArray = splice( @datArray, 0, $plSize ); | |
# extract payload in a manner appropriate to type | |
if ( $plType == 0x0b ){ | |
# treat as a string | |
$cmdValue = pack( "C*", @plArray ); | |
if ( $main::debug ){ print $cmdValue; } | |
} elsif ( ( ( $plType == 0x06 ) || ( $plType == 0x05 ) ) && | |
( @plArray == 4 ) ){ | |
# treat as an int | |
$cmdValue = ( ( $plArray[0] << 24 ) + | |
( $plArray[1] << 16 ) + | |
( $plArray[2] << 8 ) + | |
$plArray[3] ); | |
if ( $main::debug ){ print $cmdValue; } | |
} elsif ( ( $plType == 0x04 ) && ( @plArray == 2 ) ){ | |
# treat as a short | |
$cmdValue = ( ( $plArray[0] << 8 ) + | |
$plArray[1] ); | |
if ( $main::debug ){ print $cmdValue; } | |
} elsif ( ( $plType == 0x0a ) && ( @plArray == 4 ) ){ | |
# IP address | |
$cmdValue = $cmdValue = join( ".", @plArray ); | |
if ( $main::debug ){ print $cmdValue; } | |
} else { | |
# unknown type | |
$cmdValue = join( " ", @plArray ); | |
if ( $main::debug ){ print $cmdValue; } | |
} | |
if ( $main::debug ){ print "\n"; } | |
# respond appropriately (if we know how) | |
if ( $cmdName eq "std-scan-request-tcp-connection" ){ | |
ProcessTcpRequest(); | |
} elsif ( $cmdName eq "std-scan-session-open" ){ | |
my $respVal = | |
( $main::model eq "1815dn" | |
|| $main::model eq "6110mfp" | |
|| $main::model eq "fx200" | |
|| $main::model eq "clx2160n" | |
|| $main::model eq "scx4720fn" ) ? 1 : 0; | |
AppendMessageToPacket( \%packet, | |
0x22, | |
"std-scan-session-open-response", | |
0x05, | |
$respVal ); | |
} elsif ( $cmdName eq "std-scan-getclientpref" ){ | |
# make a note that client prefs have been requested but don't fill them in yet | |
$bPrefsRequested = 1; | |
} elsif ( $cmdName eq "std-scan-document-start" ){ | |
AppendMessageToPacket( \%packet, | |
0x22, | |
"std-scan-document-start-response", | |
0x05, | |
0 ); | |
# reset session file list | |
@main::sessionFiles = (); | |
} elsif ( $cmdName eq "std-scan-document-file-type" ){ | |
$main::fileType = $cmdValue; | |
} elsif ( $cmdName eq "std-scan-document-xresolution" ){ | |
$main::xResolution = $cmdValue; | |
} elsif ( $cmdName eq "std-scan-document-yresolution" ){ | |
$main::yResolution = $cmdValue; | |
} elsif ( $cmdName eq "std-scan-page-widthpixel" ){ | |
$main::widthPixels = $cmdValue; | |
} elsif ( $cmdName eq "std-scan-page-heightpixel" ){ | |
$main::heightPixels = $cmdValue; | |
} elsif ( $cmdName eq "std-scan-page-start" ){ | |
AppendMessageToPacket( \%packet, | |
0x22, | |
"std-scan-page-start-response", | |
0x05, | |
0 ); | |
# write out any pre-existing page data | |
if ( length $main::dataBuf ){ | |
OutputScanData(); | |
} | |
# reset the data buffer ready to store a page | |
$main::dataBuf = ""; | |
} elsif ( $cmdName eq "std-scan-page-end" ){ | |
AppendMessageToPacket( \%packet, | |
0x22, | |
"std-scan-page-end-response", | |
0x05, | |
0 ); | |
} elsif ( $cmdName eq "std-scan-document-end" ){ | |
AppendMessageToPacket( \%packet, | |
0x22, | |
"std-scan-document-end-response", | |
0x05, | |
0 ); | |
# write out data | |
OutputScanData(); | |
# reset the data buffer | |
$main::dataBuf = ""; | |
} elsif ( $cmdName eq "std-scan-session-end" ){ | |
AppendMessageToPacket( \%packet, | |
0x22, | |
"std-scan-session-end-response", | |
0x05, | |
0 ); | |
# shut down after the next send | |
$bLastPacket = 1; | |
# do PDF conversion | |
if ( $main::pdfConvert ){ | |
if ( defined( $main::pdfConvertCmd{"cmd"} ) ){ | |
PostProcessFile( \%main::pdfConvertCmd ); | |
} else { | |
print "*** \%main::pdfConvertCmd not set - ". | |
"skipping PDF conversion\n"; | |
} | |
} # if pdf | |
# do any extra requested option processing | |
if ( defined( $main::selectedOption ) ){ | |
PostProcessFile( \%{ $main::options{ $main::selectedOption } } ); | |
} | |
# email the result to somewhere if required | |
if ( defined( $main::emailAddr ) ){ | |
# just in case | |
if ( ! defined( $main::emailCmd{ "cmd" } ) ){ | |
print "WARNING: you must define \%main::emailCmd in the script for the email facility to work\n"; | |
} else { | |
PostProcessFile( \%main::emailCmd ); | |
} | |
} # if emailAddr | |
} elsif ( $cmdName eq "std-scan-scandata-error" ){ | |
# start of a chunk of binary scan data | |
my @binHead = splice( @datArray, 0, 8 ); | |
my $chunkSize = ( $binHead[ 6 ] << 8 ) + $binHead[ 7 ]; | |
if ( $main::debug ){ | |
print "Reading $chunkSize bytes of scan data\n"; | |
} | |
$main::dataBuf .= pack( "C*", | |
splice( @datArray, 0, | |
$chunkSize ) ); | |
if ( $main::debug ){ | |
print "(accumulated " . | |
( length $main::dataBuf ) . " bytes of data...)\n"; | |
} | |
} elsif ( $cmdName eq "std-scan-discovery-ip" ){ | |
print "IP Address: $cmdValue\n"; | |
} elsif ( $cmdName eq "std-scan-discovery-firmware-version" ){ | |
print "Firmware version: $cmdValue\n"; | |
} elsif ( $cmdName eq "std-scan-discovery-model-name" ){ | |
print "Model: $cmdValue\n"; | |
} elsif ( $cmdName eq "std-scan-getclientpref-application-name" ){ | |
# chop off leading '0' and trailing "\0"s | |
$cmdValue =~ s/^0([^\0]*)\0*$/$1/g; | |
if ( defined( $main::profiles{ $cmdValue } ) ){ | |
print "Selected profile ".$main::profiles{ $cmdValue }."\n"; | |
$main::preferredFileType = $main::profiles{ $cmdValue }{ "type" }; | |
$main::preferredFileCompression = $main::profiles{ $cmdValue }{ "cprss" }; | |
$main::preferredFileComposition = $main::profiles{ $cmdValue }{ "cmpsn" }; | |
$main::preferredResolution = $main::profiles{ $cmdValue }{ "res" }; | |
if ( defined( $main::profiles{ $cmdValue }{ "profileOption" } ) ) | |
{ | |
# override selected option | |
$main::selectedOption = $main::profiles{ $cmdValue }{ "profileOption" } | |
} | |
} elsif ( $cmdValue ne "" ) { | |
print "Ignoring unknown profile ".$cmdValue."\n"; | |
} | |
} # if | |
} # while | |
} # if | |
if ( $main::debug ){ print "\n"; } | |
} # while | |
# if prefs have been requested then fill them in | |
if ( $bPrefsRequested ){ | |
my ( $x1, $x2, $y1, $y2, $paperSizeDetect ); | |
if ( $main::model eq "1815dn" | |
|| $main::model eq "6110mfp" | |
|| $main::model eq "clx2160n" | |
|| $main::model eq "scx4720fn" ){ | |
if ( $main::preferredFileType == 8 ){ | |
# JPEG: currently set equal to TIFF value but may need to be different, Jon 2007-01-02 | |
( $x1, $x2, $y1, $y2, $paperSizeDetect ) = ( 0x40533333, 0x434eb333, 0x40533333, 0x4392d99a, 4 ); | |
} else { | |
# TIFF/PDF | |
( $x1, $x2, $y1, $y2, $paperSizeDetect ) = ( 0x40533333, 0x434eb333, 0x40533333, 0x4392d99a, 4 ); | |
} | |
} else { | |
( $x1, $x2, $y1, $y2, $paperSizeDetect ) = ( 0, 0, 0, 0, 0 ); | |
} | |
AppendMessageToPacket( \%packet, | |
0x22, | |
"std-scan-getclientpref-x1", | |
0x07, | |
$x1 ); | |
AppendMessageToPacket( \%packet, | |
0x22, | |
"std-scan-getclientpref-x2", | |
0x07, | |
$x2 ); | |
AppendMessageToPacket( \%packet, | |
0x22, | |
"std-scan-getclientpref-y1", | |
0x07, | |
$y1 ); | |
AppendMessageToPacket( \%packet, | |
0x22, | |
"std-scan-getclientpref-y2", | |
0x07, | |
$y2 ); | |
AppendMessageToPacket( \%packet, | |
0x22, | |
"std-scan-getclientpref-xresolution", | |
0x04, | |
$main::preferredResolution ); | |
AppendMessageToPacket( \%packet, | |
0x22, | |
"std-scan-getclientpref-yresolution", | |
0x04, | |
$main::preferredResolution ); | |
AppendMessageToPacket( \%packet, | |
0x22, | |
"std-scan-getclientpref-image-composition", | |
0x06, | |
$main::preferredFileComposition ); | |
AppendMessageToPacket( \%packet, | |
0x22, | |
"std-scan-getclientpref-brightness", | |
0x02, | |
0x80 ); | |
AppendMessageToPacket( \%packet, | |
0x22, | |
"std-scan-getclientpref-image-compression", | |
0x06, | |
$main::preferredFileCompression ); | |
AppendMessageToPacket( \%packet, | |
0x22, | |
"std-scan-getclientpref-file-type", | |
0x06, | |
$main::preferredFileType ); | |
AppendMessageToPacket( \%packet, | |
0x22, | |
"std-scan-getclientpref-paper-size-detect", | |
0x06, | |
$paperSizeDetect ); | |
AppendMessageToPacket( \%packet, | |
0x22, | |
"std-scan-getclientpref-scanner-type", | |
0x06, | |
0 ); | |
if ( $main::model eq "1815dn" | |
|| $main::model eq "6110mfp" | |
|| $main::model eq "clx2160n" | |
|| $main::model eq "scx4720fn" ) | |
{ | |
AppendMessageToPacket( \%packet, | |
0x22, | |
"std-scan-getclientpref-application-list", | |
0x0b, | |
GetProfileNameData() | |
); | |
} # if | |
} # if prefs requested | |
# send packet if some messages have been appended to it | |
if ( @{$packet{ "messages" }} > 0 ){ | |
if ( $main::debug ){ | |
print "Sending message with " . ( scalar( @{$packet{ "messages" }} ) ) . " items\n"; | |
} | |
$sock->send( PackMessage( \%packet ) ); | |
} | |
if ( $bLastPacket ){ | |
# initialise a clean socket shutdown | |
if ( $main::debug ){ | |
print "Shutting down TCP connection\n"; | |
} | |
$sock->shutdown( 2 ); | |
} | |
} # ProcessReceivedPacket | |
#========================================================================= | |
sub GetNormalPacketHeader() | |
# returns a "normal" packet header (eg: 02 00 01 02 00 00) | |
{ | |
if ( $main::model eq "1815dn" ){ | |
return pack( "C*", 0x02 ,0x01, 0x01, 0x02 ,0x00 ,0x00 ); | |
} elsif ( $main::model eq "fx200" ){ | |
return pack( "C*", 0x03 ,0x00, 0x01, 0x02 ,0x00 ,0x00 ); | |
} elsif ( $main::model eq "6110mfp" ){ | |
return pack( "C*", 0x04 ,0x00, 0x01, 0x02 ,0x00 ,0x00 ); | |
} elsif ( $main::model eq "clx2160n" || $main::model eq "scx4720fn" ){ | |
return pack( "C*", 0x01 ,0x00, 0x01, 0x02 ,0x00 ,0x00 ); | |
} else { | |
return pack( "C*", 0x02 ,0x00, 0x01, 0x02 ,0x00 ,0x00 ); | |
} | |
} # GetNormalPacketHeader | |
#========================================================================= | |
sub GetReplyPacketHeader() | |
# returns a "reply" packet header (eg: 02 00 02 02 00 00) | |
{ | |
if ( $main::model eq "1815dn" ){ | |
return pack( "C*", 0x02 ,0x01, 0x02, 0x02 ,0x00 ,0x00 ); | |
} elsif ( $main::model eq "fx200" ){ | |
return pack( "C*", 0x03 ,0x00, 0x02, 0x02 ,0x00 ,0x00 ); | |
} elsif ( $main::model eq "6110mfp" ){ | |
return pack( "C*", 0x04 ,0x00, 0x02, 0x02 ,0x00 ,0x00 ); | |
} elsif ( $main::model eq "clx2160n" || $main::model eq "scx4720fn" ){ | |
return pack( "C*", 0x01 ,0x00, 0x02, 0x02 ,0x00 ,0x00 ); | |
} else { | |
return pack( "C*", 0x02 ,0x00, 0x02, 0x02 ,0x00 ,0x00 ); | |
} | |
} # GetReplyPacketHeader | |
#========================================================================= | |
sub InitPacket( $ ) | |
# initialise a packet to send to printer | |
# param 1 : 6 byte header (eg: as from GetNormalPacketHeader() ) | |
# returns a hash containing an initialised packet | |
{ | |
my ( $header ) = @_; | |
die "Bad packet header" if ( length $header != 6 ); | |
my %packet = ( "header" => $header ); | |
@{$packet{ "messages" }} = (); | |
return %packet; | |
} # InitPacket | |
#========================================================================= | |
sub AppendMessageToPacket( $$$$$ ) | |
# appends a message to a packet | |
# param 1 : reference to packet (hash) | |
# param 2 : message name type | |
# param 3 : message name | |
# param 4 : message value type | |
# param 5 : message value | |
# dies in case of trouble | |
{ | |
my ( $nameType, $name, $valueType, $value ) = @_[1..4]; | |
my $message = pack ( "Cn", $nameType, length $name ) . $name; | |
if ( $valueType == 0x02 ){ | |
# unsigned char | |
$message .= pack( "CnC", $valueType, 1, $value ); | |
} elsif ( $valueType == 0x04 ){ | |
# unsigned short | |
$message .= pack( "Cnn", $valueType, 2, $value ); | |
} elsif ( $valueType == 0x07 || $valueType == 0x06 || $valueType == 0x05 ){ | |
# unsigned int | |
$message .= pack( "CnN", $valueType, 4, $value ); | |
} elsif ( $valueType == 0x0a ){ | |
# ip address type | |
$message .= pack( "Cn", $valueType, length $value ) . $value; | |
} elsif ( $valueType == 0x0b ){ | |
# char[] type | |
$message .= pack( "Cn", $valueType, length $value ) . $value; | |
} else { | |
die "Unknown value type: $valueType"; | |
} # if | |
push @{ $_[0] { "messages" }}, $message; | |
} # AppendMessageToPacket | |
#========================================================================= | |
sub HexDump( $ ) | |
# A poor man's hex dump | |
{ | |
my $ret = ""; | |
my $numBytes = 0; | |
foreach my $byte ( unpack( "C*", $_[0] ) ){ | |
$ret .= sprintf( "%02X ", $byte ); | |
if ( ! ( ( ++$numBytes ) % 16 ) ) { $ret .= "\n" } | |
} # foreach | |
if ( ( ++$numBytes ) % 16 ) { $ret .= "\n" } | |
return $ret; | |
} # HexDump | |
#========================================================================= | |
sub PackMessage( $ ) | |
# packs a printer message into binary format (ready to send) | |
# param 1 : reference to packet hash | |
# returns binary value | |
{ | |
my $payload; | |
# build the payload | |
foreach my $message ( @{ $_[0] { "messages" }} ){ | |
$payload .= $message; | |
} | |
my $packet = $_[0] { "header" } . | |
pack( "n", length $payload ) . | |
$payload; | |
if ( $main::debug ){ | |
print "Sending packet:\n" . HexDump( $packet ); | |
} | |
# return the full message | |
return $packet; | |
} # PackMessage | |
#========================================================================= | |
sub ProcessTcpRequest() | |
# opens a TCP/IP socket to $main::printerAddr and processes scan requests received | |
{ | |
my $sock = new IO::Socket::INET->new(PeerPort => $main::printerPort, | |
PeerAddr => $main::printerAddr, | |
LocalAddr => $main::bindAddr, | |
Proto => 'tcp' | |
) | |
or die "Can't connect to: $main::printerAddr:$main::printerPort (tcp/ip)\n"; | |
print "** Opened TCP/IP connection to $main::printerAddr:$main::printerPort\n"; | |
my $data = ""; | |
my $mesg; | |
# If this is 1815dn or 6110mfp mode then we must zero scan prefs in order to | |
# prompt the scanner to specify the profile name | |
if ( $main::model eq "1815dn" || $main::model eq "6110mfp"){ | |
$main::preferredFileType = 0; | |
$main::preferredFileCompression = 0; | |
$main::preferredFileComposition = 0; | |
$main::preferredResolution = 0; | |
} # if | |
my $isOpen = 1; | |
while ( $isOpen && defined( $sock->recv( $mesg, 2048, 0 ) ) ) { | |
# an empty mesg means a shutdown has occurred | |
if ( $mesg eq "" ){ | |
$sock->close(); | |
$isOpen = 0; | |
next; | |
} | |
# append to data buffer and process the result | |
$data .= $mesg; | |
ProcessReceivedPacket( \$data, $sock, "tcp" ); | |
} # while | |
print "** Closed TCP/IP connection to $main::printerAddr:$main::printerPort\n"; | |
# quit after single session if required | |
if ( $main::singleSession != 0 ){ exit 0 } | |
} # ProcessTcpRequest | |
#========================================================================= | |
sub OutputScanData() | |
# writes out contents of $main::dataBuf to file | |
{ | |
my $suffix = "dat"; | |
# format-specific stuff | |
if ( $main::fileType == 2 ){ | |
# TIFF | |
$suffix = "tif"; | |
$main::pdfConvert = $main::forceToPdf; | |
AddTiffHeaders(); | |
} elsif ( $main::fileType == 4 ){ | |
$suffix = "tif"; | |
$main::pdfConvert = 1; | |
AddTiffHeaders(); | |
} elsif ( $main::fileType == 8 ){ | |
# JPEG | |
$main::pdfConvert = $main::forceToPdf; | |
$suffix = "jpg"; | |
} else { | |
print "*** WARNING: Unexpected file format ($main::fileType)\n"; | |
} # if | |
my $fileName = "$main::scanFileDir/$main::scanFilePrefix" . | |
GetTimestamp() . ".$suffix"; | |
print "Writing data to $fileName\n"; | |
open SCANOUT, ">$fileName" or die "opening $fileName"; | |
# set output handle to raw binary mode | |
binmode( SCANOUT ); | |
print SCANOUT $main::dataBuf; | |
close SCANOUT; | |
# add this filename to the list | |
push @main::sessionFiles, $fileName; | |
} # OutputScanData | |
#========================================================================= | |
sub AddTiffHeaders() | |
# adds TIFF headers to data stored in $main::dataBuf; | |
{ | |
# build timestamp | |
my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = | |
localtime(); | |
my $stamp = sprintf( "%04d-%02d-%02d %02d:%02d:%02d", | |
$year + 1900, | |
$mon + 1, | |
$mday, | |
$hour, | |
$min, | |
$sec ); | |
# note our data size (before we modify it!) | |
my $dataSize = length $main::dataBuf; | |
# calculate offsets to Image File Directory and other bits | |
my $dataOffset = 8; | |
my $stampOffset = $dataOffset + length $main::dataBuf; | |
# align to word boundary | |
if ( $stampOffset % 2 ){ $stampOffset++ } | |
my $softwareNameOffset = $stampOffset + length( $stamp ) | |
+ 1; # don't forget NULL | |
if ( $softwareNameOffset % 2 ){ $softwareNameOffset++ } | |
my $xresOffset = $softwareNameOffset + length( $main::softwareName ) + 1; | |
if ( $xresOffset % 2 ){ $xresOffset++ } | |
my $yresOffset = $xresOffset + 8; | |
my $ifdOffset = $yresOffset + 8; | |
# we now have enough information to insert the file header | |
$main::dataBuf = pack( "CCCCV", 0x49, 0x49, 0x2A, 0x00, $ifdOffset ) . | |
$main::dataBuf; | |
# pad | |
if ( length ( $main::dataBuf ) % 2 ){ $main::dataBuf .= pack( "C", 0 ) } | |
# add timestamp string ( + NULL terminator ) | |
$main::dataBuf .= $stamp . pack( "C", 0 ); | |
# pad | |
if ( length ( $main::dataBuf ) % 2 ){ $main::dataBuf .= pack( "C", 0 ) } | |
# add software string name ( + NULL ) | |
$main::dataBuf .= $main::softwareName . pack( "C", 0 ); | |
# pad | |
if ( length ( $main::dataBuf ) % 2 ){ $main::dataBuf .= pack( "C", 0 ) } | |
# add x and y resolutions | |
$main::dataBuf .= pack( "VV", $main::xResolution, 1 ); | |
$main::dataBuf .= pack( "VV", $main::yResolution, 1 ); | |
# append field count | |
$main::dataBuf .= pack( "v", 14 ); | |
# NewSubFileType | |
$main::dataBuf .= pack( "vvVV", 0xfe, 4, 1, 2 ); | |
# ImageWidth | |
$main::dataBuf .= pack( "vvVV", 0x100, 4, 1, $main::widthPixels ); | |
# ImageLength | |
$main::dataBuf .= pack( "vvVV", 0x101, 4, 1, $main::heightPixels ); | |
# Compression ( 4 == CCIT Group 4) | |
$main::dataBuf .= pack( "vvVvv", 0x103, 3, 1, 4, 0 ); | |
# PhotometricInterpretation ( 0 = White Is Zero ) | |
$main::dataBuf .= pack( "vvVvv", 0x106, 3, 1, 0, 0 ); | |
# StripOffsets | |
$main::dataBuf .= pack( "vvVV", 0x111, 4, 1, 8 ); | |
# RowsPerStrip | |
$main::dataBuf .= pack( "vvVV", 0x116, 4, 1, $main::heightPixels ); | |
# StripByteCounts | |
$main::dataBuf .= pack( "vvVV", 0x117, 4, 1, $dataSize ); | |
# XResolution | |
$main::dataBuf .= pack( "vvVV", 0x11a, 5, 1, $xresOffset ); | |
# YResolution | |
$main::dataBuf .= pack( "vvVV", 0x11b, 5, 1, $yresOffset ); | |
# TbOptions | |
$main::dataBuf .= pack( "vvVV", 0x125, 4, 1, 0 ); | |
# ResolutionUnit | |
$main::dataBuf .= pack( "vvVvv", 0x128, 3, 1, 2, 0 ); | |
# Software | |
$main::dataBuf .= pack( "vvVV", 0x131, 2, length( $main::softwareName ), | |
$softwareNameOffset ); | |
# DateTime | |
$main::dataBuf .= pack( "vvVV", 0x132, 2, 20, $stampOffset ); | |
# end marker | |
$main::dataBuf .= pack( "V", 0 ); | |
} # AddTiffHeaders | |
#========================================================================= | |
sub RegisterWithScanner( $ ) | |
# registers with scanner | |
# param 1 : a UDP socket to the printer | |
{ | |
my ( $sock ) = @_; | |
my %packet = InitPacket( GetNormalPacketHeader() ); | |
AppendMessageToPacket( \%packet, 0x22, "std-scan-subscribe-user-name", | |
0x0b, $main::clientName ); | |
if ( $main::model eq "1815dn" || $main::model eq "6110mfp" || $main::model eq "clx2160n"){ | |
# this is the MD5 digest of 0000 | |
AppendMessageToPacket( \%packet, 0x22, "std-scan-subscribe-pin", | |
0x0b, "4a7d1ed414474e4033ac29ccb8653d9b" ); | |
} elsif ( defined( $main::clientPin ) ){ | |
AppendMessageToPacket( \%packet, 0x22, "std-scan-subscribe-pin", | |
0x06, $main::clientPin ); | |
} | |
AppendMessageToPacket( \%packet, 0x22, "std-scan-subscribe-ip-address", | |
0x0a, $main::ipAddr ); | |
if ( $main::model eq "1815dn" || $main::model eq "6110mfp"){ | |
my $uid = $main::ipAddr . pack( "U", $main::instanceId ); | |
AppendMessageToPacket( \%packet, 0x22, "std-scan-subscribe-uid", | |
0x0b, $uid ); | |
} | |
$sock->send( PackMessage( \%packet ) ); | |
} # RegisterWithScanner | |
#========================================================================= | |
sub GetProfileNameData | |
# returns packed array of 930 bytes containing names of profiles for Dell | |
# 1815dn and Xerox 6110mfp | |
{ | |
my @profdat = (); | |
my @names = sort keys %main::profiles; | |
for ( my $iProf = 0; $iProf < 30; $iProf++ ){ | |
my $profName; | |
if ( defined( $names[ $iProf ] ) ){ | |
$profName = $names[ $iProf ]; | |
push @profdat, 0x30; | |
} else { | |
$profName = ""; | |
push @profdat, 0; | |
} | |
my @elems = unpack( "C*", $profName ); | |
for ( my $iEl = 0; $iEl < 30; $iEl++ ){ | |
push @profdat, defined( $elems[ $iEl ] ) ? $elems[ $iEl ] : 0; | |
} | |
} | |
return pack( "C*", @profdat ); | |
} # GetProfileNameData | |
#========================================================================= | |
# parse args | |
my %options; | |
my $bHelp = 0; | |
for ( my $iArg = 0; $iArg < @ARGV; ++$iArg ){ | |
my $thisArg = $ARGV[ $iArg ]; | |
if ( $thisArg eq "--help" or $thisArg eq "-h" ){ | |
$bHelp = 1; | |
} elsif ( $thisArg eq "--find" ){ | |
$options{ "find" } = 1; | |
} elsif ( $thisArg eq "--debug" ){ | |
$main::debug = 1; | |
} elsif ( $thisArg eq "--1600n" ){ | |
$main::model = "1600n"; | |
} elsif ( $thisArg eq "--fx200" ){ | |
$main::model = "fx200"; | |
} elsif ( $thisArg eq "--1815dn" ){ | |
$main::model = "1815dn"; | |
} elsif ( $thisArg eq "--6110mfp" ){ | |
$main::model = "6110mfp"; | |
} elsif ( $thisArg eq "--clx2160n" ){ | |
$main::model = "clx2160n"; | |
} elsif ( $thisArg eq "--scx4720fn" ){ | |
$main::model = "scx4720fn"; | |
} elsif ( $thisArg eq "--single-session" or $thisArg eq "--single-doc" ){ | |
$main::singleSession = 1; | |
} elsif ( $thisArg eq "--multi-session" or $thisArg eq "--multi-doc" ){ | |
$main::singleSession = 0; | |
} elsif ( $thisArg eq "--force-pdf" ){ | |
$main::forceToPdf = 1; | |
} elsif ( $thisArg eq "--listen" ){ | |
die "--listen requires a parameter" unless | |
$options{ "listen" } = $ARGV[ ++$iArg ]; | |
} elsif ( $thisArg eq "--scan-dir" ){ | |
die "--scan-dir requires a parameter" unless | |
$main::scanFileDir = $ARGV[ ++$iArg ]; | |
} elsif ( $thisArg eq "--email" ){ | |
die "--email requires a parameter" unless | |
$main::emailAddr = $ARGV[ ++$iArg ]; | |
} elsif ( $thisArg eq "--scan-prefix" ){ | |
die "--scan-prefix requires a parameter" unless | |
$main::scanFilePrefix = $ARGV[ ++$iArg ]; | |
} elsif ( $thisArg eq "--name" ){ | |
die "--name requires a parameter" unless | |
$main::clientName = $ARGV[ ++$iArg ]; | |
} elsif ( $thisArg eq "--format" ){ | |
die "--format requires a parameter" unless | |
my $fmt = lc $ARGV[ ++$iArg ]; | |
if ( $fmt eq "tiff" ){ | |
$main::preferredFileType = 0x02; | |
$main::preferredFileCompression = 0x08; | |
$main::preferredFileComposition = 0x01; | |
} elsif ( $fmt eq "pdf" ){ | |
$main::preferredFileType = 0x04; | |
$main::preferredFileCompression = 0x08; | |
$main::preferredFileComposition = 0x01; | |
} elsif ( $fmt eq "jpeg" ){ | |
$main::preferredFileType = 0x08; | |
$main::preferredFileCompression = 0x20; | |
$main::preferredFileComposition = 0x40; | |
} else { | |
print "Ignoring unexpected format $fmt\n" | |
} | |
} elsif ( $thisArg eq "--resolution" ){ | |
die "--resolution requires a parameter" unless | |
$main::preferredResolution = $ARGV[ ++$iArg ]; | |
} elsif ( $thisArg eq "--bind" ){ | |
die "--bind requires a parameter" unless | |
$main::bindAddr = $ARGV[ ++$iArg ]; | |
} elsif ( $thisArg eq "--broadcast" ){ | |
die "--broadcast requires a parameter" unless | |
$main::broadcastAddr = $ARGV[ ++$iArg ]; | |
} elsif ( $thisArg eq "--instance-id" ){ | |
die "--instance-id requires a parameter" unless | |
$main::instanceId = $ARGV[ ++$iArg ]; | |
$main::instanceId += 0; | |
} elsif ( $thisArg eq "--option" ){ | |
die "--option requires a parameter" unless | |
$main::selectedOption = $ARGV[ ++$iArg ]; | |
if ( ! defined( $main::options{ $main::selectedOption } ) ){ | |
die "Unknown option: $main::selectedOption" | |
} | |
} else { | |
die "Unknown argument: $thisArg"; | |
} # if | |
} # for | |
# check usage | |
if ( $bHelp or ( ! %options ) ){ | |
print <<EOF | |
Usage: $0 <options> | |
Main Options: | |
--help : Show this help | |
--find : Discover Dell 1600n/1815dn using network broadcast | |
--listen <p> : Register and listen for requests from Dell 1600n/1815dn <p> | |
Sub Options: | |
--1600n : Use Dell 1600n-compatible protocol | |
--1815dn : Use Dell 1815dn-compatible protocol | |
--fx200 : Use Ricoh FX200-compatible protocol | |
--6110mfp : Use Xerox Phaser 6110MFP-compatible protocol | |
--clx2160n : Use Samsung CLX-2160N-compatible protocol | |
--scx4720fn : Use Samsung SCX-4720FN-compatible protocol | |
--scan-dir <d> : Scanned images will be scanned to this directory | |
--scan-prefix <p> : Scan filenames will be prefixed with <p> | |
--debug : Print lots of debug output | |
--email <a> : Email files to address <a> (requires \$main::emailCmd to be set) | |
--name <n> : Override client name (appears in scanner display) | |
--single-session : Exit after first scan session | |
--multi-session : Listen for scan documents until killed | |
--force-pdf : Convert all scans to PDF (requires \$main::pdfConvertCmd to be set) | |
--bind <i> : Bind to local IP address <i> | |
--broadcast <i> : Broadcast address (default: 255.255.255.255) used by --find. | |
Dell 1600n-specific Options: | |
--format <f> : Preferred scan format (tiff, pdf or jpeg) | |
--resolution <dpi>: Preferred resolution (100/200/300 for tiff/pdf, 200 for jpeg) | |
Dell 1815dn-specific Options: | |
--instance-id <id>: Unique instance id (in case of uid clash) | |
Other Options: | |
--option <o> : Select option <o>. The following are available: | |
EOF | |
; | |
foreach my $opt ( sort keys %main::options ){ | |
print " $opt = ".$main::options{ $opt }{ "description" }."\n"; | |
} | |
print <<EOF | |
$main::softwareName version $main::version ($main::cvsId) | |
$main::licence | |
EOF | |
; | |
exit 1; | |
} | |
# scan for printers | |
if ( defined( $options{ "find" } ) ){ | |
BroadcastDiscover(); | |
} | |
# register with scanner | |
if ( defined( $options{ "listen" } ) ){ | |
$main::printerAddr = $options{"listen" }; | |
my $sock = OpenUdpPort( $main::printerAddr ); | |
RegisterWithScanner( $sock ); | |
my $sel = new IO::Select( $sock ); | |
while (1) { | |
my @ready = $sel->can_read( $main::scanWaitLoopTimeoutSec ); | |
if ( ! @ready ){ | |
# no input yet (we hit the timeout) so re-register | |
if ( $main::debug ){ | |
my $now = ctime( time() ); | |
chop $now; | |
print "$now Re-registering with scanner\n"; | |
} | |
RegisterWithScanner( $sock ); | |
next; | |
} | |
my $data; | |
if ( ! $sock->recv( $data, 1024 ) ){ | |
usleep( 100 ); | |
next; | |
} | |
ProcessReceivedPacket( \$data, $sock, "udp" ); | |
} # while | |
} # if |
On a Mac (Mavericks):
$ brew link libpng freetype
$ brew install libtiff
$ brew install imagemagick --with-libtiff
Verify imagemagick, for example:
$ brew info imagemagick
imagemagick: stable 6.8.9-1 (bottled), HEAD
http://www.imagemagick.org
/usr/local/Cellar/imagemagick/6.8.9-1 (1436 files, 22M) *
Built from source with: --with-libtiff
From: https://github.com/Homebrew/homebrew/commits/master/Library/Formula/imagemagick.rb
==> Dependencies
Build: xz ✔, pkg-config ✔
Required: libtool ✔
Recommended: jpeg ✔, libpng ✔, freetype ✔
Optional: fontconfig ✘, libtiff ✔, little-cms ✘, little-cms2 ✘, libwmf ✘, librsvg ✘, liblqr ✘, openexr ✘, ghostscript ✘, webp ✘
Make sure convert is linked up:
$ which convert
Run and listen with defaults of 300 res and PDF format:
$ perl dell1600n-net-scan.pl --listen 192.168.1.73 --format pdf --resolution 300
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Assuming that your Dell 1600n's network address is myprinter then typical usage would be:
Start driver script like:
dell1600n-net-scan.pl --listen myprinter
Walk to the printer and start a scan by pressing "Start Scan" button and following the instructions.
Scanned images are written to the current directory unless the
--scan-dir
option is used to specify otherwise.