unauthorizedAccept option
[dyskinesia.git] / src / k8jshistory / k8jshistory.cpp
blob3e7f91ebf95e2089aa000914b51097ce39b80a82
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 <QDir>
14 #include "k8strutils.h"
15 #include "k8json.h"
17 #include "k8history.h"
18 //#include "k8jshistory.h"
21 //#define KH_DEBUG
24 ///////////////////////////////////////////////////////////////////////////////
25 #define KHISTORY_DATE_FORMAT "yyyy/MM/dd HH:mm:ss"
27 static QString dateToString (const QDateTime &dt) {
28 if (dt.isNull()) return QDateTime::currentDateTime().toString(KHISTORY_DATE_FORMAT);
29 return dt.toString(KHISTORY_DATE_FORMAT);
34 ///////////////////////////////////////////////////////////////////////////////
35 const uchar *HistoryMessage::parse (const uchar *ptr, int *maxleft, const QString &myUni) {
36 valid = false;
37 QVariant rec;
38 const uchar *res = K8JSON::parseRecord(rec, ptr, maxleft);
39 if (!res) return 0; // alas
40 if (rec.type() != QVariant::Map) return res; // alas
41 QVariantMap m(rec.toMap());
42 uni = m["uni"].toString().trimmed();
43 action = m["action"].toString();
44 text = m["text"].toString();
45 rec = m["timestamp"];
46 switch (rec.type()) {
47 case QVariant::Int:
48 case QVariant::UInt:
49 date = QDateTime::fromTime_t(rec.toUInt()).toLocalTime();
50 break;
51 default: date = QDateTime::currentDateTime();
53 if (!myUni.compare(uni, Qt::CaseInsensitive)) type = Outgoing;
54 else if (!uni.compare("info", Qt::CaseInsensitive)) type = Info;
55 else if (!uni.compare("server", Qt::CaseInsensitive)) type = Server;
56 else type = Incoming;
57 valid = true;
58 return res;
61 "uni": "psyc://psyced.org/~lynX",
62 "timestamp": 1235744956, //2009/02/27 16:29:16
63 "action": "expels",
64 "text": "and that friend is the president now"
68 ///////////////////////////////////////////////////////////////////////////////
69 HistoryFile::HistoryFile (const QString &fname, const QString &myUni) :
70 mCount(-1), mFile(fname+".json"), mMap(0), mSize(-1), mMyUni(myUni)
73 //qDebug() << fname;
77 HistoryFile::~HistoryFile () {
78 close();
82 bool HistoryFile::isOpen () const { return mFile.isOpen(); }
83 //bool HistoryFile::exists () const { return mFile.exists(); }
84 int HistoryFile::count () { return mCount; }
87 void HistoryFile::remove () {
88 close();
89 mFile.remove();
93 //bool HistoryFile::open (bool allowCreate)
94 bool HistoryFile::open (OpenMode newmode) {
95 close();
96 bool newFile = false;
97 if (!mFile.exists()) {
98 if (newmode != Write) return false;
99 QString dir(mFile.fileName());
100 int idx = dir.lastIndexOf('/');
101 if (idx > 0) {
102 dir.truncate(idx);
103 QDir d;
104 d.mkdir(dir);
106 newFile = true;
108 if (!mFile.open(newmode==Write?QIODevice::ReadWrite:QIODevice::ReadOnly)) return false;
109 if (newFile) {
110 mFile.write("//Dyskinesia JS history file\n");
111 mFile.write("//'timestamp' is UTC unixtime, but commented date is local\n");
113 mSize = mFile.size();
114 if (!(mMap = mFile.map(0, mSize))) {
115 close();
116 return false;
118 if (!scanMessages()) {
119 close();
120 return false;
122 return true;
126 bool HistoryFile::scanMessages () {
127 //fprintf(stderr, "scanning...\n");
128 mCount = 0;
129 int len = mSize;
130 const uchar *sj = K8JSON::skipBlanks(mMap, &len);
131 if (!sj || !sj[0]) {
132 // no messages at all
133 //fprintf(stderr, "empty file.\n");
134 mMsgOfs << mSize;
135 return true;
137 if (*sj++ != '[') {
138 // invalid ON file
139 //fprintf(stderr, "not a list (%u).\n", sj[-1]);
140 return false;
142 len--;
143 while (len > 0) {
144 sj = K8JSON::skipBlanks(sj, &len);
145 if (!sj || *sj != '{') {
146 // invalid ON file
147 //fprintf(stderr, "bad (%u) (%c).\n", sj[0], sj[0]);
148 return false;
150 mMsgOfs << (int)(sj-mMap); // save current message start
151 //fprintf(stderr, "ofs: 0x%08x\n", mMsgOfs[mCount]);
152 mCount++;
153 sj = K8JSON::skipRec(sj, &len);
154 if (!sj) return false; // invalid ON file
155 switch (*sj) {
156 case ',': break;
157 case ']':
158 // eof
159 mMsgOfs << (int)(sj-mMap); // save last message end
161 fprintf(stderr, "---------------------\n");
162 for (int f = 0; f < mMsgOfs.count(); f++) {
163 fprintf(stderr, "%3i: 0x%08x\n", f, (uint)(mMsgOfs[f]));
166 return true; // done
167 default: return false;
169 //fprintf(stderr, " end: 0x%08x\n", (uint)(sj-mMap));
170 len--; sj++; // skip ','
172 return false; // no eof
176 void HistoryFile::close () {
177 if (mFile.isOpen()) {
178 if (mMap) mFile.unmap(mMap);
179 mFile.close();
181 mCount = -1; mSize = -1;
182 mMap = 0;
183 mMsgOfs.clear();
188 *FIXME: check for errors!
190 bool HistoryFile::append (const HistoryMessage &msg) {
191 if (!mFile.isOpen()) return false;
192 if (mMap) {
193 mFile.unmap(mMap);
194 mMap = 0;
196 mFile.resize(mMsgOfs[mCount]); // trunc file
197 mFile.seek(mFile.size());
198 if (!mCount) {
199 // new file, no entries
200 mFile.write("[\n ");
201 } else mFile.write(",");
202 // save start of the new message
203 mMsgOfs[mCount] = mFile.size();
205 mFile.write("{//#");
206 QByteArray ba(QString::number(mCount).toAscii());
207 mFile.write(ba);
209 mFile.write("\n \"uni\": ");
210 ba = K8JSON::quote(msg.uni).toUtf8();
211 mFile.write(ba);
213 mFile.write(",\n \"timestamp\": ");
214 QDateTime utc(msg.date.toUTC());
215 ba = QString::number(utc.toTime_t()).toAscii();
216 mFile.write(ba);
217 mFile.write(", //");
218 ba = msg.date.toString(KHISTORY_DATE_FORMAT).toAscii();
219 mFile.write(ba);
220 mFile.write("\n ");
222 if (!msg.action.isEmpty()) {
223 mFile.write("\"action\": ");
224 ba = K8JSON::quote(msg.action).toUtf8();
225 mFile.write(ba);
226 mFile.write(",\n ");
228 mFile.write("\"text\": ");
229 ba = K8JSON::quote(msg.text).toUtf8();
230 mFile.write(ba);
231 mFile.write("\n }\n");
233 mCount++;
234 // save end of the written message
235 mMsgOfs << mFile.size();
236 mFile.write("]\n");
238 if (!(mMap = mFile.map(0, mSize))) {
239 close();
240 return false;
243 return true;
247 bool HistoryFile::read (int idx, HistoryMessage &msg) {
248 if (!mMap) return false;
249 if (idx < 0) idx += mCount;
250 if (idx < 0 || idx >= mCount) return false;
251 int left = mSize-(int)mMsgOfs[idx];
252 if (!msg.parse(mMap+mMsgOfs[idx], &left, mMyUni)) return false;
253 return true;