GCC Code Coverage Report
Directory: src/ Exec Total Coverage
File: src/utils/base64.cpp Lines: 22 85 25.9 %
Date: 2021-03-17 Branches: 5 39 12.8 %

Line Branch Exec Source
1
/*
2
   +----------------------------------------------------------------------+
3
   | PHP HTML Embedded Scripting Language Version 3.0                     |
4
   +----------------------------------------------------------------------+
5
   | Copyright (c) 1997-2000 PHP Development Team (See Credits file)      |
6
   +----------------------------------------------------------------------+
7
   | This program is free software; you can redistribute it and/or modify |
8
   | it under the terms of one of the following licenses:                 |
9
   |                                                                      |
10
   |  A) the GNU General Public License as published by the Free Software |
11
   |     Foundation; either version 2 of the License, or (at your option) |
12
   |     any later version.                                               |
13
   |                                                                      |
14
   |  B) the PHP License as published by the PHP Development Team and     |
15
   |     included in the distribution in the file: LICENSE                |
16
   |                                                                      |
17
   | This program is distributed in the hope that it will be useful,      |
18
   | but WITHOUT ANY WARRANTY; without even the implied warranty of       |
19
   | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the        |
20
   | GNU General Public License for more details.                         |
21
   |                                                                      |
22
   | You should have received a copy of both licenses referred to here.   |
23
   | If you did not, or have any questions about PHP licensing, please    |
24
   | contact [email protected].                                                |
25
   +----------------------------------------------------------------------+
26
   | Author: Jim Winstead ([email protected])                                  |
27
   +----------------------------------------------------------------------+
28
   */
29
30
#include "utils/base64.h"
31
32
#include "utils/cast.h"
33
34
#include "debug.h"
35
36
static char base64_table[] =
37
{
38
    'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
39
    'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
40
    'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
41
    'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
42
    '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/', '\0'
43
};
44
static const char base64_pad = '=';
45
46
unsigned char *php3_base64_encode(const unsigned char *restrict const string,
47
                                  int length,
48
                                  int *restrict const ret_length)
49
{
50
    if (string == nullptr)
51
        return nullptr;
52
    const unsigned char *current = string;
53
    int i = 0;
54
    unsigned char *const result = static_cast<unsigned char *>(
55
        calloc(CAST_SIZE((length + 3 - length % 3) * 4 / 3 + 1)
56
        * sizeof(unsigned char), 1));
57
    if (result == nullptr)
58
        return nullptr;
59
60
    while (length > 2)
61
    { /* keep going until we have less than 24 bits */
62
        result[i++] = base64_table[current[0] >> 2];
63
        result[i++] = base64_table[((current[0] & 0x03)
64
                      << 4) + (current[1] >> 4)];
65
        result[i++] = base64_table[((current[1] & 0x0f)
66
                      << 2) + (current[2] >> 6)];
67
        result[i++] = base64_table[current[2] & 0x3f];
68
69
        current += 3;
70
        length -= 3; /* we just handle 3 octets of data */
71
    }
72
73
    /* now deal with the tail end of things */
74
    if (length != 0)
75
    {
76
        result[i++] = base64_table[current[0] >> 2];
77
        if (length > 1)
78
        {
79
            result[i++] = base64_table[((current[0] & 0x03) << 4)
80
                          + (current[1] >> 4)];
81
            result[i++] = base64_table[(current[1] & 0x0f) << 2];
82
            result[i++] = base64_pad;
83
        }
84
        else
85
        {
86
            result[i++] = base64_table[(current[0] & 0x03) << 4];
87
            result[i++] = base64_pad;
88
            result[i++] = base64_pad;
89
        }
90
    }
91
    if (ret_length != nullptr)
92
    {
93
        *ret_length = i;
94
    }
95
    result[i] = '\0';
96
    return result;
97
}
98
99
/* as above, but backwards. :) */
100
2
unsigned char *php3_base64_decode(const unsigned char *restrict const string,
101
                                  const int length,
102
                                  int *restrict const ret_length)
103
{
104
2
    const unsigned char *current = string;
105
    int ch;
106
2
    int i = 0;
107
2
    int j = 0;
108
    int k;
109
110
    unsigned char *result = static_cast<unsigned char *>(
111
2
        calloc(length + 1, 1));
112
113
2
    if (result == nullptr)
114
        return nullptr;
115
116
    /* run through the whole string, converting as we go */
117
2
    while ((ch = *current++) != '\0')
118
    {
119
        if (ch == base64_pad)
120
            break;
121
122
        /* When Base64 gets POSTed, all pluses are interpreted as spaces.
123
           This line changes them back.  It's not exactly the Base64 spec,
124
           but it is completely compatible with it (the spec says that
125
           spaces are invalid).  This will also save many people considerable
126
           headache.  - Turadg Aleahmad <[email protected]>
127
           */
128
129
        if (ch == ' ') ch = '+';
130
131
        const char *const chp = strchr(base64_table, ch);
132
        if (chp == nullptr)
133
            continue;
134
        ch = CAST_S32(chp - base64_table);
135
136
        switch (i % 4)
137
        {
138
            case 0:
139
                result[j] = CAST_U8(ch << 2U);
140
                break;
141
            case 1:
142
                result[j++] |= CAST_U8(ch >> 4U);
143
                result[j] = CAST_U8((ch & 0x0f) << 4U);
144
                break;
145
            case 2:
146
                result[j++] |= CAST_U8(ch >>2U);
147
                result[j] = CAST_U8((ch & 0x03) << 6U);
148
                break;
149
            case 3:
150
                result[j++] |= CAST_U8(ch);
151
                break;
152
            default:
153
                break;
154
        }
155
        i++;
156
    }
157
158
2
    k = j;
159
    /* mop things up if we ended on a boundary */
160
2
    if (ch == base64_pad)
161
    {
162
        switch (i % 4)
163
        {
164
            case 0:
165
            case 1:
166
                free(result);
167
                return nullptr;
168
            case 2:
169
                k++;
170
                // copy from 3. is it should be here?
171
                result[k++] = 0;
172
                break;
173
            case 3:
174
                result[k++] = 0;
175
                break;
176
            default:
177
                break;
178
        }
179
    }
180
2
    if (ret_length != nullptr)
181
    {
182
2
        *ret_length = j;
183
    }
184
2
    result[k] = '\0';
185
2
    return result;
186
}
187
188
std::string encodeBase64String(std::string value)
189
{
190
    int sz = 0;
191
    const unsigned char *const str = reinterpret_cast<unsigned char*>(
192
        const_cast<char*>(value.c_str()));
193
    unsigned char *const buf = php3_base64_encode(
194
        str, CAST_S32(value.size()), &sz);
195
    if (buf == nullptr)
196
        return std::string();
197
198
    value = std::string(reinterpret_cast<char*>(buf), sz);
199
    free(buf);
200
    return value;
201
}
202
203
2
std::string decodeBase64String(std::string value)
204
{
205
2
    int sz = 0;
206
    const unsigned char *const str = reinterpret_cast<unsigned char*>(
207
2
        const_cast<char*>(value.c_str()));
208
2
    unsigned char *const buf = php3_base64_decode(
209
4
        str, CAST_S32(value.size()), &sz);
210
211
2
    if (buf != nullptr)
212
8
        value = std::string(reinterpret_cast<char*>(buf), sz);
213
    else
214
        value.clear();
215
2
    free(buf);
216
2
    return value;
217
}