Coverage Report

Created: 2024-09-08 06:04

/src/qpdf/libqpdf/Pl_Base64.cc
Line
Count
Source (jump to first uncovered line)
1
#include <qpdf/Pl_Base64.hh>
2
3
#include <qpdf/QIntC.hh>
4
#include <qpdf/QUtil.hh>
5
#include <cstring>
6
#include <stdexcept>
7
8
static char
9
to_c(unsigned int ch)
10
0
{
11
0
    return static_cast<char>(ch);
12
0
}
13
14
static unsigned char
15
to_uc(int ch)
16
0
{
17
0
    return static_cast<unsigned char>(ch);
18
0
}
19
20
static int
21
to_i(int i)
22
0
{
23
0
    return static_cast<int>(i);
24
0
}
25
26
Pl_Base64::Pl_Base64(char const* identifier, Pipeline* next, action_e action) :
27
    Pipeline(identifier, next),
28
    action(action),
29
    pos(0),
30
    end_of_data(false),
31
    finished(false)
32
0
{
33
0
    reset();
34
0
}
35
36
void
37
Pl_Base64::write(unsigned char const* data, size_t len)
38
0
{
39
0
    if (finished) {
40
0
        throw std::logic_error("Pl_Base64 used after finished");
41
0
    }
42
0
    if (this->action == a_decode) {
43
0
        decode(data, len);
44
0
    } else {
45
0
        encode(data, len);
46
0
    }
47
0
}
48
49
void
50
Pl_Base64::decode(unsigned char const* data, size_t len)
51
0
{
52
0
    unsigned char const* p = data;
53
0
    while (len > 0) {
54
0
        if (!QUtil::is_space(to_c(*p))) {
55
0
            this->buf[this->pos++] = *p;
56
0
            if (this->pos == 4) {
57
0
                flush();
58
0
            }
59
0
        }
60
0
        ++p;
61
0
        --len;
62
0
    }
63
0
}
64
65
void
66
Pl_Base64::encode(unsigned char const* data, size_t len)
67
0
{
68
0
    unsigned char const* p = data;
69
0
    while (len > 0) {
70
0
        this->buf[this->pos++] = *p;
71
0
        if (this->pos == 3) {
72
0
            flush();
73
0
        }
74
0
        ++p;
75
0
        --len;
76
0
    }
77
0
}
78
79
void
80
Pl_Base64::flush()
81
0
{
82
0
    if (this->action == a_decode) {
83
0
        flush_decode();
84
0
    } else {
85
0
        flush_encode();
86
0
    }
87
0
    reset();
88
0
}
89
90
void
91
Pl_Base64::flush_decode()
92
0
{
93
0
    if (this->end_of_data) {
94
0
        throw std::runtime_error(getIdentifier() + ": base64 decode: data follows pad characters");
95
0
    }
96
0
    int pad = 0;
97
0
    int shift = 18;
98
0
    int outval = 0;
99
0
    for (size_t i = 0; i < 4; ++i) {
100
0
        int v = 0;
101
0
        char ch = to_c(this->buf[i]);
102
0
        if ((ch >= 'A') && (ch <= 'Z')) {
103
0
            v = ch - 'A';
104
0
        } else if ((ch >= 'a') && (ch <= 'z')) {
105
0
            v = ch - 'a' + 26;
106
0
        } else if ((ch >= '0') && (ch <= '9')) {
107
0
            v = ch - '0' + 52;
108
0
        } else if ((ch == '+') || (ch == '-')) {
109
0
            v = 62;
110
0
        } else if ((ch == '/') || (ch == '_')) {
111
0
            v = 63;
112
0
        } else if ((ch == '=') && ((i == 3) || ((i == 2) && (this->buf[3] == '=')))) {
113
0
            ++pad;
114
0
            this->end_of_data = true;
115
0
            v = 0;
116
0
        } else {
117
0
            throw std::runtime_error(getIdentifier() + ": base64 decode: invalid input");
118
0
        }
119
0
        outval |= v << shift;
120
0
        shift -= 6;
121
0
    }
122
0
    unsigned char out[3] = {
123
0
        to_uc(outval >> 16),
124
0
        to_uc(0xff & (outval >> 8)),
125
0
        to_uc(0xff & outval),
126
0
    };
127
128
0
    getNext()->write(out, QIntC::to_size(3 - pad));
129
0
}
130
131
void
132
Pl_Base64::flush_encode()
133
0
{
134
0
    int outval = ((this->buf[0] << 16) | (this->buf[1] << 8) | (this->buf[2]));
135
0
    unsigned char out[4] = {
136
0
        to_uc(outval >> 18),
137
0
        to_uc(0x3f & (outval >> 12)),
138
0
        to_uc(0x3f & (outval >> 6)),
139
0
        to_uc(0x3f & outval),
140
0
    };
141
0
    for (size_t i = 0; i < 4; ++i) {
142
0
        int ch = to_i(out[i]);
143
0
        if (ch < 26) {
144
0
            ch += 'A';
145
0
        } else if (ch < 52) {
146
0
            ch -= 26;
147
0
            ch += 'a';
148
0
        } else if (ch < 62) {
149
0
            ch -= 52;
150
0
            ch += '0';
151
0
        } else if (ch == 62) {
152
0
            ch = '+';
153
0
        } else if (ch == 63) {
154
0
            ch = '/';
155
0
        }
156
0
        out[i] = to_uc(ch);
157
0
    }
158
0
    for (size_t i = 0; i < 3 - this->pos; ++i) {
159
0
        out[3 - i] = '=';
160
0
    }
161
0
    getNext()->write(out, 4);
162
0
}
163
164
void
165
Pl_Base64::finish()
166
0
{
167
0
    if (this->pos > 0) {
168
0
        if (finished) {
169
0
            throw std::logic_error("Pl_Base64 used after finished");
170
0
        }
171
0
        if (this->action == a_decode) {
172
0
            for (size_t i = this->pos; i < 4; ++i) {
173
0
                this->buf[i] = '=';
174
0
            }
175
0
        }
176
0
        flush();
177
0
    }
178
0
    this->finished = true;
179
0
    getNext()->finish();
180
0
}
181
182
void
183
Pl_Base64::reset()
184
0
{
185
0
    this->pos = 0;
186
0
    memset(buf, 0, 4);
187
0
}