#!/usr/bin/perl # # add airprint records to match any existing printers # use strict; use warnings; # use the version of Net::Bonjour that comes with this code, as the # current CPAN distro dies when fetching TXT records. BEGIN { use Cwd; use File::Basename; use File::Spec; my $binary_dir = dirname( $0 ); if ( $binary_dir !~ m@/@ ) { $binary_dir = File::Spec->join( getcwd(), $binary_dir ); } # not really important, just pickiness $binary_dir =~ s@^(.+)/.$@$1@; unshift @INC, $binary_dir; } use Net::Bonjour 0.97; # there doesn't appear to be a way to republish the printers without a # prefix. dns-sd says it's done so, but the records don't appear in the local # browse domains. my $prefix = 'AirPrint '; # First, find some printers. my %printers; my $res = new Net::Bonjour( 'ipp', 'tcp', 'local' ); $res->discover(); # stash what we've found, but skip anything starting with our prefix. for my $entry ( $res->entries ) { $printers{$entry->name} = $entry unless $entry->name =~ /$prefix/; } my @printers = sort keys %printers; # Now republish all the printers we found for my $printer ( @printers ) { # XXX check if we've already exported this as an airprint printer #my $airprinted = get_dnssd_record( "AirPrint ". $printer, '_universal._ipp._tcp' && next; # get the current DNS-SD attributes my $record = { $printers{$printer}->all_attrs }; # add the URF entries to the record $record->{pdl} .= ',image/urf'; $record->{URF} = 'none'; # publish the new record publish_dnssd_record( $printer, $record ); } sub publish_dnssd_record { my $printer = shift; # need to clean out the \\032 crap. Not sure if there's other # stuff I should be cleaning out. $printer =~ s/\\032/ /g; my $record = shift; my @args; for my $key ( sort keys %{$record} ) { my $string = "$key="; my $value = $record->{$key}; $value =~ s@\\@\\\\@g; $value =~ s@(\s)@\\$1@g; $string .= $value; push @args, $string; } # Ideally I'd do this with Net::MDNS, but right now that doesn't # build on MacOS (at least not the CPAN version). This forks off a # process per printer, and they remain running after the script # has exited. You can get rid of them with 'killall -TERM dns-sd' my @cmd = ( '/usr/bin/dns-sd', '-R', $prefix . $printer, '_ipp._tcp,_universal', '.', '631', @args ); my $pid = fork(); if ( !defined( $pid )) { print STDERR "Fork failed: $!"; } if ( $pid == 0 ) { # child exec( @cmd ) or print STDERR "dns-sd -R failed\n"; } } # Better approach: # set up a dns-sd listener (e.g. run dns-sd in a fork # any time a printer shows up, spin up a dns-sd child to republish it as an airprint printer # if a printer is removed, remove the corresponding dns-sd child