unauthorizedAccept option
[dyskinesia.git] / src / k8bhfhistory / k8bhfhistory.cpp
blob1f7807a663f147adddf57a3a004663a39a3394c8
1 /* coded by Ketmar // Vampire Avalon (psyc://ketmar.no-ip.org/~Ketmar)
2 * Understanding is not required. Only obedience.
4 * This program is free software. It comes without any warranty, to
5 * the extent permitted by applicable law. You can redistribute it
6 * and/or modify it under the terms of the Do What The Fuck You Want
7 * To Public License, Version 2, as published by Sam Hocevar. See
8 * http://sam.zoy.org/wtfpl/COPYING for more details.
9 */
10 #include <QDebug>
12 #include <QObject>
13 #include <QDir>
15 #include "k8strutils.h"
16 #include "k8json.h"
18 #include "k8history.h"
21 ///////////////////////////////////////////////////////////////////////////////
22 static const char sign[4] = {'B', 'H', 'F', '0'};
25 ///////////////////////////////////////////////////////////////////////////////
26 class QARC4Codec {
27 public:
28 struct A4Context {
29 int m[256]; // permutation table (unsigned char actually)
30 int x, y; // permutation indicies (unsigned char actually)
33 public:
34 QARC4Codec () {}
35 QARC4Codec (const QString &key) { setKey(key); }
36 QARC4Codec (const QByteArray &key) { setKey(key); }
37 ~QARC4Codec () {}
39 inline bool isReady () const { return mInited; }
41 // will reset codec
42 inline void setKey (const QString &key) { setKey(key.toUtf8()); }
43 inline void setKey (const QByteArray &key) { setup(key); }
45 inline void reset () { if (mInited) memcpy(&mCtx, &mStartCtx, sizeof(mCtx)); }
47 // process in-place
48 inline void processIP (QByteArray &data) { doBuffer(data.data(), data.constData(), data.size()); }
49 // copy
50 inline QByteArray process (const QByteArray &data) {
51 QByteArray res(data.size(), 0);
52 doBuffer(res.data(), data.constData(), data.size());
53 return res;
56 private:
57 void setup (const QByteArray &key);
58 void doBuffer (void *dest, const void *src, int len);
60 private:
61 bool mInited;
62 struct A4Context mCtx;
63 struct A4Context mStartCtx;
67 ///////////////////////////////////////////////////////////////////////////////
68 void QARC4Codec::setup (const QByteArray &key) {
69 int c = 0, k = 0, x, y, *m, a, b;
70 A4Context *ctx = &mCtx;
71 const unsigned char *kkk = (const unsigned char *)key.constData();
72 int keylen = key.size();
74 ctx->x = ctx->y = 0; m = ctx->m;
75 for (int f = 0; f < 256; ++f) m[f] = f;
76 // create permutation table
77 for (int f = 0; f < 256; ++f) {
78 unsigned char kc = keylen>0?kkk[k++]:0;
79 if (k >= keylen) k = 0;
80 a = m[f];
81 c = (unsigned char)(c+a+kc);
82 m[f] = m[c]; m[c] = a;
84 // discard first 256 bytes
85 x = ctx->x; y = ctx->y; m = ctx->m;
86 for (int f = 256; f > 0; --f) {
87 x = (unsigned char)((x+1)&0xff); a = m[x];
88 y = (unsigned char)((y+a)&0xff);
89 m[x] = b = m[y];
90 m[y] = a;
92 ctx->x = x; ctx->y = y;
93 memcpy(&mStartCtx, &mCtx, sizeof(mStartCtx));
94 mInited = true;
98 void QARC4Codec::doBuffer (void *dest, const void *src, int len) {
99 //void a4CryptCpy (A4Context *ctx, void *dest, const void *buf, int buflen)
100 int x, y, *m, a, b;
101 A4Context *ctx = &mCtx;
102 unsigned char *ddd = (unsigned char *)dest;
103 const unsigned char *bbb = (const unsigned char *)src;
105 if (!mInited) {
106 if (len > 0 && dest != src) memmove(dest, src, len);
107 return;
109 x = ctx->x; y = ctx->y; m = ctx->m;
110 for (int f = len; f > 0; --f, ++ddd, ++bbb) {
111 x = (unsigned char)((x+1)&0xff); a = m[x];
112 y = (unsigned char)((y+a)&0xff);
113 m[x] = b = m[y];
114 m[y] = a;
115 ddd[0] = bbb[0]^m[(unsigned char)((a+b)&0xff)];
117 ctx->x = x; ctx->y = y;
121 ///////////////////////////////////////////////////////////////////////////////
122 HistoryFile::HistoryFile (const QString &fname, const QString &myUni) :
123 mMode(Read), mName(fname), mMyUni(myUni), mCount(-1),
124 mFText(fname+".bht"), mFIdx(fname+".bhi")
129 HistoryFile::~HistoryFile () {
130 close();
134 bool HistoryFile::isOpen () const {
135 return mFText.isOpen();
139 int HistoryFile::count () {
140 return mCount;
144 void HistoryFile::remove () {
145 close();
146 mFText.remove();
147 mFIdx.remove();
151 static void createFileDir (const QString &fname) {
152 QString dir(fname);
153 int idx = dir.lastIndexOf('/');
154 if (idx > 0) {
155 dir.truncate(idx);
156 QDir d;
157 d.mkdir(dir);
162 bool HistoryFile::open (OpenMode newmode) {
163 bool newFile = false;
165 close();
166 if (newmode == Read) {
167 if (!mFText.open(QIODevice::ReadOnly)) return false;
168 if (!mFIdx.open(QIODevice::ReadOnly)) { mFText.close(); return false; }
169 } else {
170 newFile = !(mFText.exists() && mFIdx.exists());
171 if (newFile) createFileDir(mName);
172 if (!mFText.open(QIODevice::ReadWrite)) return false;
173 if (!mFIdx.open(QIODevice::ReadWrite)) {
174 mFText.close();
175 if (newFile) remove();
176 return false;
179 mMode = newmode;
180 if (!initialize(newFile)) {
181 close();
182 if (newFile) remove();
183 return false;
185 return true;
189 bool HistoryFile::initialize (bool newFile) {
190 char fsign[4];
192 mOffsets.clear();
193 if (newFile) {
194 // just create empty files
195 mCount = 0;
196 if (mFIdx.write(sign, 4) != 4) return false;
197 if (mFIdx.write((const char *)&mCount, 4) != 4) return false;
198 return true;
200 // read index
201 if (mFIdx.read(fsign, 4) != 4) return false;
202 if (memcmp(fsign, sign, 4) != 0) return false;
203 if (mFIdx.read((char *)&mCount, 4) != 4) return false;
204 if (mCount < 0 || mCount > 1024*1024*1024) return false;
205 // read offsets
206 if (mCount > 0) {
207 const qint32 *i;
208 QByteArray ofs(mFIdx.read(mCount*sizeof(qint32)));
209 if (ofs.size() != mCount*4) return false;
210 mOffsets.resize(mCount);
211 i = (const qint32 *)ofs.constData();
212 for (int f = 0; f < mCount; ++f, ++i) mOffsets[f] = *i;
215 return true;
219 void HistoryFile::close () {
220 if (mFIdx.isOpen()) {
221 mFIdx.close();
222 mFText.close();
223 mOffsets.clear();
224 mCount = 0;
225 mMode = Read;
230 static QByteArray &baPutQStr (QByteArray &ba, const QString &str) {
231 qint32 len = str.size();
232 if (len > 0) {
233 QByteArray u = str.toUtf8();
234 len = u.size();
235 ba.append((const char *)&len, sizeof(len));
236 ba.append(u);
237 } else {
238 ba.append((const char *)&len, sizeof(len));
240 return ba;
244 static int baGetQStr (const QByteArray &ba, int pos, QString &str) {
245 qint32 len;
246 //qDebug() << " strpos:" << pos;
247 if (pos < 0 || pos+4 > ba.size()) return -1;
248 memcpy(&len, ba.constData()+pos, 4);
249 //qDebug() << " strlen:" << len;
250 if (len < 0 || len > 1024*1024) return -1;
251 pos += 4;
252 if (pos+len > ba.size()) return -1;
253 str = QString::fromUtf8(ba.constData()+pos, len);
254 //qDebug() << " str:" << str;
255 pos += len;
256 return pos;
260 bool HistoryFile::append (const HistoryMessage &msg) {
261 unsigned char type;
262 qint64 ms;
263 qint32 bsz, tpos;
264 QByteArray ba;
266 if (!mFIdx.isOpen() || mMode != Write || !msg.valid) return false;
268 if (!mFText.seek(mFText.size())) return false;
269 ms = mFText.pos();
270 if (ms < 0 || ms > 0x7fffffffL) return false;
271 tpos = ms;
273 switch (msg.type) {
274 case HistoryMessage::Incoming: type = 0; break;
275 case HistoryMessage::Outgoing: type = 1; break;
276 case HistoryMessage::Server: type = 2; break;
277 case HistoryMessage::Info:
278 default: type = 3; break;
280 // type
281 ba.append((const char *)&type, 1);
282 // date
283 ms = msg.date.toUTC().toMSecsSinceEpoch();
284 ba.append((const char *)&ms, sizeof(ms));
285 // uni
286 baPutQStr(ba, msg.uni);
287 // action
288 baPutQStr(ba, msg.action);
289 // text
290 baPutQStr(ba, msg.text);
291 // encrypt
293 QARC4Codec a4(mMyUni.toLower()+QString::number(tpos, 10));
294 a4.processIP(ba);
296 bsz = ba.size();
297 if (mFText.write((const char *)&bsz, 4) != 4) return false;
298 if (mFText.write(ba) != bsz) return false;
299 // write offset
300 if (!mFIdx.seek(mCount*4+8)) return false;
301 if (mFIdx.write((const char *)&tpos, 4) != 4) return false;
303 ++mCount;
304 if (!mFIdx.seek(4)) { --mCount; return false; }
305 if (mFIdx.write((const char *)&mCount, 4) != 4) { --mCount; return false; }
307 mOffsets.append(tpos);
308 return true;
312 bool HistoryFile::read (int idx, HistoryMessage &msg) {
313 qint64 ms;
314 qint32 sz;
315 int pos;
316 QByteArray ba;
318 msg.clear();
319 if (!mFIdx.isOpen() || idx < 0 || idx >= mCount) return false;
320 if (!mFText.seek(mOffsets[idx])) return true;
321 if (mFText.read((char *)&sz, 4) != 4) return true;
322 if (sz < 21 || sz > 1024*1024*16) return true;
323 //qDebug() << "idx:" << idx << "size:" << sz;
324 ba = mFText.read(sz);
325 if (ba.size() != sz) return true;
326 // decrypt
328 QARC4Codec a4(mMyUni.toLower()+QString::number(mOffsets[idx], 10));
329 a4.processIP(ba);
331 // type
332 //qDebug() << " type:" << (unsigned int)(ba.constData()[0]);
333 switch (ba.constData()[0]) {
334 case 0: msg.type = HistoryMessage::Incoming; break;
335 case 1: msg.type = HistoryMessage::Outgoing; break;
336 case 2: msg.type = HistoryMessage::Server; break;
337 case 3: msg.type = HistoryMessage::Info; break;
338 default: return true;
340 // date
341 memcpy(&ms, ba.constData()+1, sizeof(ms));
342 //qDebug() << " date:" << ms;
343 if (ms < 0) return true;
344 msg.date.setTimeSpec(Qt::UTC);
345 msg.date.setMSecsSinceEpoch(ms);
346 msg.date = msg.date.toLocalTime();
347 //qDebug() << " date:" << msg.date;
349 pos = 1+sizeof(ms);
350 // uni
351 if ((pos = baGetQStr(ba, pos, msg.uni)) < 0) return true;
352 // action
353 if ((pos = baGetQStr(ba, pos, msg.action)) < 0) return true;
354 // text
355 if ((pos = baGetQStr(ba, pos, msg.text)) < 0) return true;
357 //qDebug() << " pos:" << pos << "ba.size:" << ba.size();
358 if (pos != ba.size()) return true;
359 if (msg.action.isEmpty()) msg.action = QString();
361 msg.valid = true;
362 return true;