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.
15 #include "k8strutils.h"
18 #include "k8history.h"
21 ///////////////////////////////////////////////////////////////////////////////
22 static const char sign
[4] = {'B', 'H', 'F', '0'};
25 ///////////////////////////////////////////////////////////////////////////////
29 int m
[256]; // permutation table (unsigned char actually)
30 int x
, y
; // permutation indicies (unsigned char actually)
35 QARC4Codec (const QString
&key
) { setKey(key
); }
36 QARC4Codec (const QByteArray
&key
) { setKey(key
); }
39 inline bool isReady () const { return mInited
; }
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
)); }
48 inline void processIP (QByteArray
&data
) { doBuffer(data
.data(), data
.constData(), data
.size()); }
50 inline QByteArray
process (const QByteArray
&data
) {
51 QByteArray
res(data
.size(), 0);
52 doBuffer(res
.data(), data
.constData(), data
.size());
57 void setup (const QByteArray
&key
);
58 void doBuffer (void *dest
, const void *src
, int len
);
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;
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);
92 ctx
->x
= x
; ctx
->y
= y
;
93 memcpy(&mStartCtx
, &mCtx
, sizeof(mStartCtx
));
98 void QARC4Codec::doBuffer (void *dest
, const void *src
, int len
) {
99 //void a4CryptCpy (A4Context *ctx, void *dest, const void *buf, int buflen)
101 A4Context
*ctx
= &mCtx
;
102 unsigned char *ddd
= (unsigned char *)dest
;
103 const unsigned char *bbb
= (const unsigned char *)src
;
106 if (len
> 0 && dest
!= src
) memmove(dest
, src
, len
);
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);
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 () {
134 bool HistoryFile::isOpen () const {
135 return mFText
.isOpen();
139 int HistoryFile::count () {
144 void HistoryFile::remove () {
151 static void createFileDir (const QString
&fname
) {
153 int idx
= dir
.lastIndexOf('/');
162 bool HistoryFile::open (OpenMode newmode
) {
163 bool newFile
= false;
166 if (newmode
== Read
) {
167 if (!mFText
.open(QIODevice::ReadOnly
)) return false;
168 if (!mFIdx
.open(QIODevice::ReadOnly
)) { mFText
.close(); return false; }
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
)) {
175 if (newFile
) remove();
180 if (!initialize(newFile
)) {
182 if (newFile
) remove();
189 bool HistoryFile::initialize (bool newFile
) {
194 // just create empty files
196 if (mFIdx
.write(sign
, 4) != 4) return false;
197 if (mFIdx
.write((const char *)&mCount
, 4) != 4) return false;
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;
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
;
219 void HistoryFile::close () {
220 if (mFIdx
.isOpen()) {
230 static QByteArray
&baPutQStr (QByteArray
&ba
, const QString
&str
) {
231 qint32 len
= str
.size();
233 QByteArray u
= str
.toUtf8();
235 ba
.append((const char *)&len
, sizeof(len
));
238 ba
.append((const char *)&len
, sizeof(len
));
244 static int baGetQStr (const QByteArray
&ba
, int pos
, QString
&str
) {
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;
252 if (pos
+len
> ba
.size()) return -1;
253 str
= QString::fromUtf8(ba
.constData()+pos
, len
);
254 //qDebug() << " str:" << str;
260 bool HistoryFile::append (const HistoryMessage
&msg
) {
266 if (!mFIdx
.isOpen() || mMode
!= Write
|| !msg
.valid
) return false;
268 if (!mFText
.seek(mFText
.size())) return false;
270 if (ms
< 0 || ms
> 0x7fffffffL
) return false;
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;
281 ba
.append((const char *)&type
, 1);
283 ms
= msg
.date
.toUTC().toMSecsSinceEpoch();
284 ba
.append((const char *)&ms
, sizeof(ms
));
286 baPutQStr(ba
, msg
.uni
);
288 baPutQStr(ba
, msg
.action
);
290 baPutQStr(ba
, msg
.text
);
293 QARC4Codec
a4(mMyUni
.toLower()+QString::number(tpos
, 10));
297 if (mFText
.write((const char *)&bsz
, 4) != 4) return false;
298 if (mFText
.write(ba
) != bsz
) return false;
300 if (!mFIdx
.seek(mCount
*4+8)) return false;
301 if (mFIdx
.write((const char *)&tpos
, 4) != 4) return false;
304 if (!mFIdx
.seek(4)) { --mCount
; return false; }
305 if (mFIdx
.write((const char *)&mCount
, 4) != 4) { --mCount
; return false; }
307 mOffsets
.append(tpos
);
312 bool HistoryFile::read (int idx
, HistoryMessage
&msg
) {
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;
328 QARC4Codec
a4(mMyUni
.toLower()+QString::number(mOffsets
[idx
], 10));
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;
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;
351 if ((pos
= baGetQStr(ba
, pos
, msg
.uni
)) < 0) return true;
353 if ((pos
= baGetQStr(ba
, pos
, msg
.action
)) < 0) return true;
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();