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