1/* -*- c++ -*-
2 SPDX-FileCopyrightText: 2002 Marc Mutz <mutz@kde.org>
3
4 SPDX-License-Identifier: LGPL-2.0-or-later
5*/
6/**
7 @file
8 This file is part of the API for handling @ref MIME data and
9 defines a @ref uuencode @ref Codec class.
10
11 @brief
12 Defines the UUCodec class.
13
14 @authors Marc Mutz \<mutz@kde.org\>
15*/
16
17#include "kcodecsuuencode.h"
18
19#include <QDebug>
20
21#include <cassert>
22
23using namespace KCodecs;
24
25namespace KCodecs
26{
27class UUDecoder : public Decoder
28{
29 uint mStepNo;
30 uchar mAnnouncedOctetCount; // (on current line)
31 uchar mCurrentOctetCount; // (on current line)
32 uchar mOutbits;
33 bool mLastWasCRLF : 1;
34 bool mSawBegin : 1; // whether we already saw ^begin...
35 uint mIntoBeginLine : 3; // count #chars we compared against "begin" 0..5
36 bool mSawEnd : 1; // whether we already saw ^end...
37 uint mIntoEndLine : 2; // count #chars we compared against "end" 0..3
38
39 void searchForBegin(const char *&scursor, const char *const send);
40
41protected:
42 friend class UUCodec;
43 UUDecoder(Codec::NewlineType newline = Codec::NewlineLF)
44 : Decoder(newline)
45 , mStepNo(0)
46 , mAnnouncedOctetCount(0)
47 , mCurrentOctetCount(0)
48 , mOutbits(0)
49 , mLastWasCRLF(true)
50 , mSawBegin(false)
51 , mIntoBeginLine(0)
52 , mSawEnd(false)
53 , mIntoEndLine(0)
54 {
55 }
56
57public:
58 ~UUDecoder() override
59 {
60 }
61
62 bool decode(const char *&scursor, const char *const send, char *&dcursor, const char *const dend) override;
63 // ### really needs no finishing???
64 bool finish(char *&dcursor, const char *const dend) override
65 {
66 Q_UNUSED(dcursor);
67 Q_UNUSED(dend);
68 return true;
69 }
70};
71
72Encoder *UUCodec::makeEncoder(NewlineType newline) const
73{
74 Q_UNUSED(newline)
75 return nullptr; // encoding not supported
76}
77
78Decoder *UUCodec::makeDecoder(NewlineType newline) const
79{
80 return new UUDecoder(newline);
81}
82
83/********************************************************/
84/********************************************************/
85/********************************************************/
86
87void UUDecoder::searchForBegin(const char *&scursor, const char *const send)
88{
89 static const char begin[] = "begin\n";
90 static const uint beginLength = 5; // sic!
91
92 assert(!mSawBegin || mIntoBeginLine > 0);
93
94 while (scursor != send) {
95 uchar ch = *scursor++;
96 if (ch == begin[mIntoBeginLine]) {
97 if (mIntoBeginLine < beginLength) {
98 // found another char
99 ++mIntoBeginLine;
100 if (mIntoBeginLine == beginLength) {
101 mSawBegin = true; // "begin" complete, now search the next \n...
102 }
103 } else { // mIntoBeginLine == beginLength
104 // found '\n': begin line complete
105 mLastWasCRLF = true;
106 mIntoBeginLine = 0;
107 return;
108 }
109 } else if (mSawBegin) {
110 // OK, skip stuff until the next \n
111 } else {
112 // qWarning() << "UUDecoder: garbage before \"begin\", resetting parser";
113 mIntoBeginLine = 0;
114 }
115 }
116}
117
118// uuencoding just shifts all 6-bit octets by 32 (SP/' '), except NUL,
119// which gets mapped to 0x60
120static inline uchar uuDecode(uchar c)
121{
122 return (c - ' ') // undo shift and
123 & 0x3F; // map 0x40 (0x60-' ') to 0...
124}
125
126bool UUDecoder::decode(const char *&scursor, const char *const send, char *&dcursor, const char *const dend)
127{
128 // First, check whether we still need to find the "begin" line:
129 if (!mSawBegin || mIntoBeginLine != 0) {
130 searchForBegin(scursor, send);
131 } else if (mSawEnd) {
132 // or if we are past the end line:
133 scursor = send; // do nothing anymore...
134 return true;
135 }
136
137 while (dcursor != dend && scursor != send) {
138 uchar ch = *scursor++;
139 uchar value;
140
141 // Check whether we need to look for the "end" line:
142 if (mIntoEndLine > 0) {
143 static const char end[] = "end";
144 static const uint endLength = 3;
145
146 if (ch == end[mIntoEndLine]) {
147 ++mIntoEndLine;
148 if (mIntoEndLine == endLength) {
149 mSawEnd = true;
150 scursor = send; // shortcut to the end
151 return true;
152 }
153 continue;
154 } else {
155 // qWarning() << "UUDecoder: invalid line octet count looks like \"end\" (mIntoEndLine ="
156 // << mIntoEndLine << ")!";
157 mIntoEndLine = 0;
158 // fall through...
159 }
160 }
161
162 // Normal parsing:
163
164 // The first char of a line is an encoding of the length of the
165 // current line. We simply ignore it:
166 if (mLastWasCRLF) {
167 // reset char-per-line counter:
168 mLastWasCRLF = false;
169 mCurrentOctetCount = 0;
170
171 // try to decode the chars-on-this-line announcement:
172 if (ch == 'e') { // maybe the beginning of the "end"? ;-)
173 mIntoEndLine = 1;
174 } else if (ch > 0x60) {
175 // ### invalid line length char: what shall we do??
176 } else if (ch > ' ') {
177 mAnnouncedOctetCount = uuDecode(c: ch);
178 } else if (ch == '\n') {
179 mLastWasCRLF = true; // oops, empty line
180 }
181
182 continue;
183 }
184
185 // try converting ch to a 6-bit value:
186 if (ch > 0x60) {
187 continue; // invalid char
188 } else if (ch > ' ') {
189 value = uuDecode(c: ch);
190 } else if (ch == '\n') { // line end
191 mLastWasCRLF = true;
192 continue;
193 } else {
194 continue;
195 }
196
197 // add the new bits to the output stream and flush full octets:
198 switch (mStepNo) {
199 case 0:
200 mOutbits = value << 2;
201 break;
202 case 1:
203 if (mCurrentOctetCount < mAnnouncedOctetCount) {
204 *dcursor++ = (char)(mOutbits | value >> 4);
205 }
206 ++mCurrentOctetCount;
207 mOutbits = value << 4;
208 break;
209 case 2:
210 if (mCurrentOctetCount < mAnnouncedOctetCount) {
211 *dcursor++ = (char)(mOutbits | value >> 2);
212 }
213 ++mCurrentOctetCount;
214 mOutbits = value << 6;
215 break;
216 case 3:
217 if (mCurrentOctetCount < mAnnouncedOctetCount) {
218 *dcursor++ = (char)(mOutbits | value);
219 }
220 ++mCurrentOctetCount;
221 mOutbits = 0;
222 break;
223 default:
224 assert(0);
225 }
226 mStepNo = (mStepNo + 1) % 4;
227
228 // check whether we ran over the announced octet count for this line:
229 if (mCurrentOctetCount == mAnnouncedOctetCount + 1) {
230 // qWarning()
231 // << "UUDecoder: mismatch between announced ("
232 // << mAnnouncedOctetCount << ") and actual line octet count!";
233 }
234 }
235
236 // return false when caller should call us again:
237 return scursor == send;
238} // UUDecoder::decode()
239
240} // namespace KCodecs
241

source code of kcodecs/src/kcodecsuuencode.cpp