1 // This file is part of the brlaser printer driver.
2 //
3 // Copyright 2013 Peter De Wachter
4 //
5 // brlaser is free software: you can redistribute it and/or modify
6 // it under the terms of the GNU General Public License as published by
7 // the Free Software Foundation, either version 2 of the License, or
8 // (at your option) any later version.
9 //
10 // brlaser is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 // GNU General Public License for more details.
14 //
15 // You should have received a copy of the GNU General Public License
16 // along with brlaser.  If not, see <http://www.gnu.org/licenses/>.
17 
18 #include <stdio.h>
19 #include <signal.h>
20 #include <unistd.h>
21 #include <fcntl.h>
22 #include <cups/raster.h>
23 #include <algorithm>
24 #include <functional>
25 #include <string>
26 #include <array>
27 #include <map>
28 #include "config.h"
29 #include "job.h"
30 #include "debug.h"
31 
32 #ifndef O_BINARY
33 #define O_BINARY 0
34 #endif
35 
36 
37 namespace {
38 
39 cups_raster_t *ras;
40 volatile sig_atomic_t interrupted = 0;
41 
42 
sigterm_handler(int sig)43 void sigterm_handler(int sig) {
44   interrupted = 1;
45 }
46 
47 
next_line(std::vector<uint8_t> & buf)48 bool next_line(std::vector<uint8_t> &buf) {
49   if (interrupted) {
50     return false;
51   }
52   return cupsRasterReadPixels(ras, buf.data(), buf.size()) == buf.size();
53 }
54 
55 
plain_ascii_string(const char * str)56 bool plain_ascii_string(const char *str) {
57   bool result = true;
58   for (; result && *str; str++) {
59     result = *str >= 32 && *str <= 126;
60   }
61   return result;
62 }
63 
ascii_job_name(const char * job_id,const char * job_user,const char * job_name)64 std::string ascii_job_name(const char *job_id, const char *job_user, const char *job_name) {
65   std::array<const char *, 3> parts = {{
66     job_id,
67     job_user,
68     job_name
69   }};
70   std::string result;
71   for (const char *part : parts) {
72     if (*part && plain_ascii_string(part)) {
73       if (!result.empty()) {
74         result += '/';
75       }
76       result += part;
77     }
78   }
79   if (result.empty()) {
80     result = "brlaser";
81   }
82   const int max_size = 79;
83   if (result.size() > max_size) {
84     result.resize(max_size);
85   }
86   return result;
87 }
88 
build_page_params(const cups_page_header2_t & header)89 page_params build_page_params(const cups_page_header2_t &header) {
90   static const std::array<std::string, 6> sources = {{
91     "AUTO", "T1", "T2", "T3", "MP", "MANUAL"
92   }};
93   static const std::map<std::string, std::string> sizes = {
94     { "A4", "A4" },
95     { "A5", "A5" },
96     { "A6", "A6" },
97     { "B5", "B5" },
98     { "B6", "B6" },
99     { "EnvC5", "C5" },
100     { "EnvMonarch", "MONARCH" },
101     { "EnvPRC5", "DL" },
102     { "EnvDL", "DL" },
103     { "Executive", "EXECUTIVE" },
104     { "Legal", "LEGAL" },
105     { "Letter", "LETTER" }
106   };
107 
108   page_params p = { };
109   p.num_copies = header.NumCopies;
110   p.resolution = header.HWResolution[0];
111   p.economode = header.cupsInteger[10];
112   p.mediatype = header.MediaType;
113   p.duplex = header.Duplex;
114 
115   if (header.MediaPosition < sources.size())
116     p.sourcetray = sources[header.MediaPosition];
117   else
118     p.sourcetray = sources[0];
119 
120   auto size_it = sizes.find(header.cupsPageSizeName);
121   if (size_it != sizes.end())
122     p.papersize = size_it->second;
123   else
124     p.papersize = "A4";
125 
126   return p;
127 }
128 
129 }  // namespace
130 
131 
main(int argc,char * argv[])132 int main(int argc, char *argv[]) {
133   fprintf(stderr, "INFO: %s version %s\n", PACKAGE, VERSION);
134 
135   if (argc != 6 && argc != 7) {
136       fprintf(stderr, "ERROR: rastertobrlaser job-id user title copies options [file]\n");
137       fprintf(stderr, "INFO: This program is a CUPS filter. It is not intended to be run manually.\n");
138       return 1;
139   }
140   const char *job_id = argv[1];
141   const char *job_user = argv[2];
142   const char *job_name = argv[3];
143   // const int job_copies = atoi(argv[4]);
144   // const char *job_options = argv[5];
145   const char *job_filename = argv[6];
146   // const char *job_charset = getenv("CHARSET");
147 
148   signal(SIGTERM, sigterm_handler);
149   signal(SIGPIPE, SIG_IGN);
150 
151   int fd = STDIN_FILENO;
152   if (job_filename) {
153     fd = open(job_filename, O_RDONLY | O_BINARY);
154     if (fd < 0) {
155       fprintf(stderr, "ERROR: " PACKAGE ": Unable to open raster file\n");
156       return 1;
157     }
158   }
159 
160 #ifdef __OpenBSD__
161   if (pledge("stdio", nullptr) != 0) {
162     fprintf(stderr, "ERROR: " PACKAGE ": pledge failed\n");
163     return 1;
164   }
165 #endif
166 
167   ras = cupsRasterOpen(fd, CUPS_RASTER_READ);
168   if (!ras) {
169     fprintf(stderr, "DEBUG: " PACKAGE ": Cannot read raster data. Most likely an earlier filter in the pipeline failed.\n");
170     return 1;
171   }
172 
173   int pages = 0;
174   {
175     job job(stdout, ascii_job_name(job_id, job_user, job_name));
176     cups_page_header2_t header;
177     while (!interrupted && cupsRasterReadHeader2(ras, &header)) {
178       if (header.cupsBitsPerPixel != 1
179           || header.cupsBitsPerColor != 1
180           || header.cupsNumColors != 1
181           || header.cupsBytesPerLine > 10000) {
182         fprintf(stderr, "ERROR: " PACKAGE ": Page %d: Bogus raster data.\n", pages + 1);
183         dump_page_header(header);
184         return 1;
185       }
186       if (pages == 0) {
187         fprintf(stderr, "DEBUG: " PACKAGE ": Page header of first page\n");
188         dump_page_header(header);
189       }
190       job.encode_page(build_page_params(header),
191                       header.cupsHeight,
192                       header.cupsBytesPerLine,
193                       next_line);
194       fprintf(stderr, "PAGE: %d %d\n", ++pages, header.NumCopies);
195     }
196   }
197 
198   if (pages == 0) {
199     fprintf(stderr, "ERROR: " PACKAGE ": No pages were found.\n");
200     return 1;
201   }
202 
203   fflush(stdout);
204   if (ferror(stdout)) {
205     fprintf(stderr, "DEBUG: " PACKAGE ": Could not write print data. Most likely the CUPS backend failed.\n");
206     return 1;
207   }
208   return 0;
209 }
210