Tizen 2.1 base
[platform/upstream/hplip.git] / prnt / filters / hpcac
1 #!/usr/bin/perl  -W
2 use strict;
3 use File::Temp qw(tempfile);
4 use File::Basename;
5 use Sys::Hostname;
6
7 #######################################################
8 #
9 ###################################################### 
10
11 my $arg_job                 = $ARGV[0];
12 my $arg_user                = $ARGV[1];
13 my $arg_title               = $ARGV[2];
14 my $arg_copies              = $ARGV[3];
15 my $arg_options             = $ARGV[4];
16
17 my $progname = basename( $0 );
18
19 # Set to 1 to capture the Postscript that you generate to a temp file
20 my $capture_postscript = 0;
21
22 # Set to 1 to print debug messages to CUPS error log
23 my $debug = 0;
24
25 my $capture_fh = undef;
26 my $capture_filename = undef;
27 my $procfile;
28
29 #----------------------------------------------------
30
31 ###################################################### 
32 # Various debug & error subs
33 # Error and debugging messages have to go to STDERR
34 ###################################################### 
35 sub abort_exit
36 {
37     my $mesg = shift;
38     print STDERR "ERROR: $progname - $mesg\n";
39     exit( 1 );
40 }
41
42 sub print_debug
43 {
44     return if( ! $debug );
45     my $output = shift;
46     print STDERR "DEBUG: $progname [PID $$] $output\n";
47 }
48
49 sub print_info
50 {
51     my $output = shift;
52     print STDERR "INFO: $progname [PID $$] $output\n";
53 }
54
55 sub print_warning
56 {
57     my $output = shift;
58     print STDERR "WARN: $progname [PID $$] $output\n";
59 }
60
61 ###################################################################
62 # Open a temp file to capture the Postscript that I send to the printer.
63 # Used for debugging
64 ###################################################################
65 sub open_capture
66 {
67     if( $capture_postscript ) {
68         ( $capture_fh, $capture_filename ) =
69             tempfile( "${progname}-$$-capture-XXXXXX", DIR => "/tmp", SUFFIX => ".ps", UNLINK => 0 );
70         if ( !defined $capture_fh ) {
71             # Not a fatal issue
72             print_warning( "Could not open capture file.  Will not save Postscript output." );
73             $capture_filename = undef;
74             $capture_postscript = 0;
75         }
76         else {
77             print_info( "Capturing Postscript to '$capture_filename'" );
78         }
79     }
80 }
81
82 ###################################################################
83 # Close the debugging capture log
84 ###################################################################
85 sub close_capture
86 {
87     if( $capture_postscript && defined $capture_fh ) {
88         close( $capture_fh ) or print_warning( "Could not close capture file '$capture_filename': $!" );
89     }
90 }
91
92 ###################################################################
93 # Print out the given line to STDOUT (print data stream)
94 # and optionally capture it for debugging.
95 ###################################################################
96 sub emit_line
97 {
98     my $line = shift;
99     print $line;
100
101     if( $capture_postscript && $capture_fh ) {
102         print $capture_fh $line;
103     }
104 }
105
106 ###################################################################
107 # Get the type of accounting that is used for the printer
108 # by looking at the PPD for specific values.
109 # Currently only support HPAccountingInfo: 1
110 ###################################################################
111 sub GetHPAccountingType
112 {
113     my $ppd = $ENV{"PPD"};
114     if( (! $ppd ) || (! -r $ppd ) ) {
115         return( "NONE", "NONE" );
116     }
117     
118     open( PPDFILE, "$ppd" ) or  return( "NONE", "NONE" );
119
120     my $accounting_type = "NONE";
121     my $accounting_mode = "NONE";
122     my $ppdline;
123     while( <PPDFILE> ) {
124         $ppdline = $_;
125         if( $ppdline =~ /^\*HPAccountingInfo:\s*(\d*)\s*$/ ) {
126             $accounting_type = "HPAccountingInfo";
127             $accounting_mode = $1;
128             print_debug("HPAccountingInfo found $accounting_mode");
129         }
130     }
131     close( PPDFILE );
132
133     return( $accounting_type, $accounting_mode );
134 }
135
136 ###################################################################
137 # The Postscript code containing the accounting info
138 # has starting and ending snippets.
139 ###################################################################
140 sub InsertFeatureStart
141 {
142     emit_line( "[{\n" );
143 }
144
145 sub InsertFeatureEnd
146 {
147     emit_line( "} stopped cleartomark\n" );
148 }
149
150 ###################################################################
151 sub GetUser
152 {
153     my $user;
154     if ( $arg_user ) {
155         $user = $arg_user;
156     }
157     else {
158         $user = "nobody";
159     }
160
161     return( $user );
162 }
163
164 ###################################################################
165 sub GetJob { }
166
167
168 ###################################################################
169 sub GetTitle { }
170
171 ###################################################################
172 sub GetOptions
173
174     my %opt_hash = ();
175     my $options = $arg_options;
176     my @opt_array = split( ' ', $options );
177     foreach my $option ( @opt_array ) {
178         my( $name, $value ) = split( /=/, $option, 2 );
179         if ( !$value ) {
180             $value = "";
181         }
182         print_debug( "Option $name='$value'" );
183         $opt_hash{$name} = $value;
184     }
185     return( \%opt_hash );
186 }
187
188 ###################################################################
189 # If a UUID wasn't given to us by CUPS, try a few methods
190 # to create one ourselves.
191 ###################################################################
192 sub CreateUUID
193 {
194     my $uuid_str = "";
195     eval {
196         require Data::UUID;
197         my $ug = new Data::UUID;
198         $uuid_str = $ug->create_str;
199     };
200     if ($@) {
201         $uuid_str = `uuidgen`;
202         $uuid_str =~ s/\r?\n?$//;
203     }
204
205     return( $uuid_str );
206 }
207
208 ###################################################################
209 # CUPS passes in an option value that contains a UUID.
210 # If it's there, use that and if not, try to create one ourselves
211 ###################################################################
212 sub GetUUID
213 {
214     my $uuid = "";
215     my $r_opt_hash = GetOptions();
216     # job-uuid=urn:uuid:018c1dab-3c0c-3edf-6036-1e87af479038
217
218     my $job_uuid = $$r_opt_hash{ "job-uuid" };
219     if ( $job_uuid ) {
220         if ( $job_uuid =~ /urn:uuid:(.*)/ ) {
221             $uuid = $1;
222             print_debug( "UUID in job-uuid=$uuid" );
223         }
224         else {
225             print_debug( "bad job-uuid" );
226             $uuid = CreateUUID();
227         }
228     }
229     else {
230         $uuid = CreateUUID();
231     }
232
233     if ( ! $uuid ) {
234         print_debug( "EPIC FAIL: Could not find nor generate UUID" );
235     }
236     
237     return( $uuid );
238 }
239
240 ###################################################################
241 # Get the system name
242 ###################################################################
243 sub GetSystem
244 {
245     my $system_name = hostname;
246     if( !$system_name ) {
247         $system_name = "unknown-system_name";
248     }
249
250     return( $system_name );
251
252 }
253
254 ###################################################################
255 # The domain value is really a Windows domain.  Since we're
256 # not using this in the Windows environment, we just
257 # use the hostname. The value is not used for Color Access Control
258 # filtering but it still has to be included in the job accounting info
259 ###################################################################
260 sub GetDomain
261 {
262     my $domain = hostname;
263     if( !$domain ) {
264         $domain = "unknown-domain";
265     }
266
267     return( $domain );
268 }
269
270 ###################################################################
271 # Gather up all of the information needed for the accounting data
272 # to be included in the print stream
273 ###################################################################
274 sub GetHPAccountingInfo
275 {
276     my( $jobname, $user, $system, $domain, $date, $uuid, $app, $appexe, $dept );
277
278     $jobname = $arg_job;
279     $user = GetUser();
280     $system = GetSystem();
281     $domain = GetDomain();
282     my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);
283     $year += 1900;
284     $mon += 1;
285     # YYYYMMDDhhmmss
286     $date = sprintf( "%s%02d%02d%02d%02d%02d", $year, $mon, $mday, $hour, $min, $sec);
287     $uuid = GetUUID(); 
288
289     # There is no way to get the name of the application that generated this PS file.
290     # Set it to this as a fallback.
291     $app = "HP Linux Printing";
292     $appexe = "HP Linux Printing";
293
294     $dept = $user;
295
296     return( $jobname, $user, $system, $domain, $date, $uuid, $app, $appexe, $dept );
297 }
298
299 ###################################################################
300 # Once the accounting data has been gathered, format it and
301 # include it in the print stream.
302 ###################################################################
303 sub InsertHPAccountingInfo
304 {
305
306     my ( $accounting_type, $accounting_mode ) = GetHPAccountingType();
307     print_debug( "Accounting Type=$accounting_type\tAccounting Mode=$accounting_mode");
308
309     if( ! ( ($accounting_type eq "HPAccountingInfo") && ($accounting_mode == 1)) ) {
310         print_debug( "Color Access Control not in effect." );
311         return;
312     }
313
314     InsertFeatureStart();
315     my( $jobname, $user, $system, $domain, $date, $uuid, $app, $appexe, $dept ) = GetHPAccountingInfo();
316
317     my $accounting_feature = "";
318     $accounting_feature = "%%BeginFeature: *HPAccountingInfo\n" .
319         "    currentpagedevice /StringCodeSet known\n" .
320         "    {\n" .
321         "        << /StringCodeSet (UTF8) >> setpagedevice\n" .
322         "        <<\n" .
323         "            /JobName ($jobname)\n" .
324         "            /JobAcct1 ($user)\n" .
325         "            /JobAcct2 ($system)\n" .
326         "            /JobAcct3 ($domain)\n" .
327         "            /JobAcct4 ($date)\n" .
328         "            /JobAcct5 ($uuid)\n" .
329         "            /JobAcct6 ($app)\n" .
330         "            /JobAcct7 ($appexe)\n" .
331         "            /JobAcct8 ($dept)\n" .
332         "        >> setuserparams\n" .
333         "    }\n" .
334         "    {\n" .
335         "        <<\n" .
336         "            /JobName ($jobname)\n" .
337         "            /JobAcct1 ($user)\n" .
338         "            /JobAcct2 ($system)\n" .
339         "            /JobAcct3 ($domain)\n" .
340         "            /JobAcct4 ($date)\n" .
341         "            /JobAcct5 ($uuid)\n" .
342         "            /JobAcct6 ($app)\n" .
343         "            /JobAcct7 ($appexe)\n" .
344         "            /JobAcct8 ($dept)\n" .
345         "        >> setuserparams\n" .
346         "    } ifelse\n" .
347         "%%EndFeature\n";
348
349     emit_line( $accounting_feature );
350     InsertFeatureEnd();
351 }
352
353 ###################################################################
354 # If the print job is from stdin, save a copy
355 ###################################################################
356 sub stdin2file
357 {
358     my ( $tmp_fh, $tmp_filename ) =
359         tempfile( "${progname}-$$-stdin-XXXXXX", SUFFIX => ".ps", DIR => "/tmp", UNLINK => 0 );
360     if ( !defined $tmp_fh ) {
361         abort_exit( "Cannot create tempfile '$tmp_filename'" );
362     }
363
364     print_debug( "Copying STDIN to $tmp_filename" );
365     while (<STDIN>)
366     {
367         print $tmp_fh $_;
368     }
369     close $tmp_fh or abort_exit( "Cannot close tempfile '$tmp_filename': $!" );
370     return( $tmp_filename );
371 }
372
373 ###################################################################
374 # If the print job is contained in a file, make a copy.
375 # A copy is made because the original file could be deleted
376 # before this print job is completed.
377 ###################################################################
378 sub copy_file
379 {
380         my $from = shift;
381
382         open (FROM, "<$from" ) or abort_exit( "Cannot read '$from': $!" );
383         my ( $tmp_fh, $tmp_filename ) =
384             tempfile( "${progname}-$$-copy-XXXXXX", SUFFIX => ".ps", DIR => "/tmp", UNLINK => 0 );
385         if ( !defined $tmp_fh ) {
386             abort_exit( "Couldn't create temporary file '$tmp_filename'" );
387         }
388
389         print_debug( "Copying $from to $tmp_filename" );
390         while (<FROM>) {
391             print $tmp_fh $_ or abort_exit( "Can't write to tempfile '$tmp_filename': $!" );
392         }
393         close $tmp_fh or abort_exit( "Cannot close tempfile '$tmp_filename': $!" );
394         close FROM;
395         return $tmp_filename;
396 }
397
398 ###################################################################
399 # Given the print job, look through it for the right place to
400 # insert the accounting info.  The document is assumed to be in
401 # the Adobe DSC format already and should contain the BeginProlog
402 # item.  Upstream CUPS filters will have converted the Postscript
403 # data into DSC format.
404 ###################################################################
405 sub processfile
406 {
407         my $fn = shift;
408         open (PSFILE, "<$fn") or abort_exit( "Cannot open '$fn': $!");
409         while (<PSFILE>)
410         {
411                 emit_line( $_ );
412                 if( /%%BeginProlog/ ) {
413                     InsertHPAccountingInfo();
414                 }
415         }
416         close PSFILE;
417 }
418
419 ###################################################################
420 # Main
421 ###################################################################
422
423 if ($#ARGV == 4)
424 {
425     $procfile = stdin2file();
426 }
427 elsif ($#ARGV == 5)
428 {
429     $procfile = copy_file($ARGV[5]);
430 } else {
431     abort_exit( "job-id user title copies options [file]");
432 }
433
434 for my $i ( 0 .. $#ARGV ) {
435     print_debug( "ARG $i=$ARGV[$i]")
436 }
437 print_debug("job-id=$arg_job user=$arg_user title=$arg_title copies=$arg_copies $arg_options $procfile");
438
439 my ($key, $value);
440 foreach $key ( sort keys %ENV) {
441     print_debug("ENV $key=$ENV{$key}");
442 }
443
444 print_debug( "Processing file");
445 open_capture();
446 processfile($procfile);
447 close_capture();
448
449 unlink($procfile) or print STDERR "ERROR: $progname - Couldn't remove '$procfile': $!\n";
450 exit 0;