forked from chromium/chromium
-
Notifications
You must be signed in to change notification settings - Fork 0
/
ppd_line_reader.cc
197 lines (167 loc) · 6 KB
/
ppd_line_reader.cc
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chromeos/printing/ppd_line_reader.h"
#include <memory>
#include <string>
#include <utility>
#include "base/bind.h"
#include "base/strings/string_util.h"
#include "net/base/completion_once_callback.h"
#include "net/base/io_buffer.h"
#include "net/filter/gzip_header.h"
#include "net/filter/gzip_source_stream.h"
#include "net/filter/source_stream.h"
namespace chromeos {
namespace {
constexpr char kPPDMagicNumberString[] = "*PPD-Adobe:";
// Return true if contents has a valid Gzip header.
bool IsGZipped(const std::string& contents) {
const char* unused;
return net::GZipHeader().ReadMore(contents.data(), contents.size(),
&unused) ==
net::GZipHeader::COMPLETE_HEADER;
}
// Return true if c is a newline in the ppd sense, that is, either newline or
// carriage return.
bool IsNewline(char c) {
return c == '\n' || c == '\r';
}
// Source stream that reads from a string. A reference is taken to the string
// used by StringSourceStream; it must not be modified while the
// StringSourceStream exists.
class StringSourceStream : public net::SourceStream {
public:
explicit StringSourceStream(const std::string& src)
: SourceStream(TYPE_UNKNOWN), src_(src) {}
// This source always reads sychronously, so never uses the callback.
int Read(net::IOBuffer* dest_buffer,
int buffer_size,
net::CompletionOnceCallback) override {
if (buffer_size < 0)
return net::ERR_INVALID_ARGUMENT;
if (!MayHaveMoreBytes())
return net::OK;
const size_t read_size =
std::min(src_.size() - read_ofs_, static_cast<size_t>(buffer_size));
memcpy(dest_buffer->data(), src_.data() + read_ofs_, read_size);
read_ofs_ += read_size;
return read_size;
}
std::string Description() const override { return ""; }
bool MayHaveMoreBytes() const override { return read_ofs_ < src_.size(); }
private:
size_t read_ofs_ = 0;
const std::string& src_;
};
class PpdLineReaderImpl : public PpdLineReader {
public:
PpdLineReaderImpl(const std::string& ppd_contents, size_t max_line_length)
: max_line_length_(max_line_length),
read_buf_(base::MakeRefCounted<net::IOBuffer>(kReadBufCapacity)) {
input_ = std::make_unique<StringSourceStream>(ppd_contents);
if (IsGZipped(ppd_contents)) {
input_ = net::GzipSourceStream::Create(std::move(input_),
net::SourceStream::TYPE_GZIP);
}
}
~PpdLineReaderImpl() override = default;
bool NextLine(std::string* line) override {
line->reserve(max_line_length_);
// Outer loop controls retries; if we fail to read a line, we'll try again
// after the next newline.
while (true) {
line->clear();
while (line->size() <= max_line_length_) {
char c = NextChar();
if (Eof()) {
return !line->empty();
} else if (IsNewline(c)) {
return true;
}
line->push_back(c);
}
// Exceeded max line length, skip the rest of this line, try for another
// one.
if (!SkipToNextLine()) {
return false;
}
}
}
bool Error() const override { return error_; }
private:
// Chunk size of reads to the underlying source stream.
static constexpr int kReadBufCapacity = 500;
// Skip input until we hit a newline (which is discarded). If
// we encounter eof before a newline, false is returned.
bool SkipToNextLine() {
while (true) {
char c = NextChar();
if (Eof()) {
return false;
}
if (IsNewline(c)) {
return true;
}
}
}
// Consume and return the next char from the source stream. If there is no
// more data to be had, set eof. Eof() should be checked before the returned
// value is used.
char NextChar() {
if (read_ofs_ == read_buf_size_) {
// Grab more data from the underlying stream.
read_ofs_ = 0;
// Since StringSourceStream never uses the callback, and filter streams
// are only supposed to use the callback if the underlying source stream
// uses it, we should never see the callback used.
int result = input_->Read(
read_buf_.get(), kReadBufCapacity,
base::BindOnce([](int) { LOG(FATAL) << "Unexpected async read"; }));
if (result == 0) {
eof_ = true;
return '\0';
} else if (result < 0) {
eof_ = true;
error_ = true;
}
read_buf_size_ = result;
}
return read_buf_->data()[read_ofs_++];
}
bool Eof() const { return eof_; }
// Maximum allowable line length from the source. Any lines longer than this
// will be silently discarded.
size_t max_line_length_;
// Buffer for reading from the source stream.
scoped_refptr<net::IOBuffer> read_buf_;
// Number of bytes actually in the buffer.
int read_buf_size_ = 0;
// Offset into read_buf for the next char.
int read_ofs_ = 0;
// Have we hit the end of the source stream?
bool eof_ = false;
// Did we encounter an error while reading?
bool error_ = false;
// The input stream we're reading bytes from. This may be a gzip source
// stream or string source stream depending on the source data.
std::unique_ptr<net::SourceStream> input_;
};
constexpr int PpdLineReaderImpl::kReadBufCapacity;
} // namespace
// static
std::unique_ptr<PpdLineReader> PpdLineReader::Create(
const std::string& contents,
size_t max_line_length) {
return std::make_unique<PpdLineReaderImpl>(contents, max_line_length);
}
// static
bool PpdLineReader::ContainsMagicNumber(const std::string& contents,
size_t max_line_length) {
auto line_reader = PpdLineReader::Create(contents, max_line_length);
std::string line;
return line_reader->NextLine(&line) &&
base::StartsWith(line, kPPDMagicNumberString,
base::CompareCase::SENSITIVE);
}
} // namespace chromeos