1 /***************************************************************************
2 * Copyright (C) 2004 by Kita Developers *
3 * ikemo@users.sourceforge.jp *
5 * This program is free software; you can redistribute it and/or modify *
6 * it under the terms of the GNU General Public License as published by *
7 * the Free Software Foundation; either version 2 of the License, or *
8 * (at your option) any later version. *
9 ***************************************************************************/
12 #include <QtCore/QByteArray>
13 #include <QtCore/QDataStream>
14 #include <QtCore/QDateTime>
15 #include <QtCore/QFile>
16 #include <QtCore/QRegExp>
18 #include <kdatetime.h>
21 #include <kio/jobuidelegate.h>
22 #include <kio/slaveconfig.h>
25 #include "boarddatabase.h"
30 #include "kita_misc.h"
37 Access::Access(const KUrl& datUrl) : m_datUrl(datUrl), m_currentJob(0)
47 BoardDatabase db(m_datUrl);
48 m_bbstype = db.type();
49 m_header = "HTTP/1.1 200 "; /* dummy header */
54 /* read data from cache, then emit data to DatInfo. */ /* public */
55 void Access::getcache()
60 Cache cache(m_datUrl);
61 QString cachePath = cache.getPath();
62 if (cachePath.isEmpty()) {
67 QFile file(cachePath);
68 if (file.open(QIODevice::ReadOnly)) {
69 orgData += file.readAll();
74 if (orgData.isEmpty()) return ;
75 m_dataSize = orgData.length();
80 case Board_MachiBBS: /* Machi BBS's data is already parsed as 2ch dat. */
83 QString tmpData = K2ch().datToUnicode(orgData);
84 QStringList tmpList = tmpData.split('\n');
85 emit receiveData(tmpList);
90 /* convert data stream into 2ch dat.
91 and emit receiveData SIGNAL. */
92 emitDatLineList(orgData);
97 /* write data to cache */ /* protected */
98 void Access::writeCacheData()
100 if (m_invalidDataReceived) return ;
101 if (m_threadData.isEmpty()) return ;
103 m_dataSize += m_threadData.length();
105 Cache cache(m_datUrl);
106 QString cachePath = cache.getPath();
107 if (!cachePath.isEmpty()) {
108 QFile file(QFile::encodeName(cachePath));
109 if (!file.open(QIODevice::WriteOnly))
111 QDataStream out(&file);
114 m_threadData.clear(); /* clear baffer */
119 /* update cache file */ /* public */
120 bool Access::getupdate(int readNum)
124 m_threadData.clear();
125 m_firstReceive = false;
126 m_invalidDataReceived = false;
129 /* set URL of data */
134 getUrl = getThreadUrl(m_datUrl);
136 getUrl += "&START=" + QString::number(m_readNum + 1);
141 getUrl = getThreadUrl(m_datUrl);
142 getUrl.replace("read.cgi", "rawmode.cgi"); /* adhoc... */
144 getUrl += '/' + QString::number(m_readNum + 1) + '-';
148 getUrl = m_datUrl.prettyUrl();
152 const QString useragent = QString("Monazilla/1.00 (Kita/%1)").arg(VERSION);
153 KIO::SlaveConfig::self() ->setConfigData("http",
155 "UserAgent", useragent);
158 KIO::TransferJob* job
159 = KIO::get(getUrl, KIO::Reload, KIO::HideProgressInfo);
162 connect(job, SIGNAL(data(KIO::Job*, const QByteArray&)),
163 SLOT(slotReceiveThreadData(KIO::Job*, const QByteArray&)));
164 connect(job, SIGNAL(result(KJob*)), SLOT(slotThreadResult(KJob*)));
166 // use 'HTTP-Headers' metadata.
167 job->addMetaData("PropagateHttpHeader", "true");
170 if (m_bbstype != Board_MachiBBS
171 && m_bbstype != Board_JBBS
173 m_firstReceive = true; /* remove first char (i.e. \n). see also slotReceiveThreadData() */
174 job->addMetaData("resume", QString::number(m_dataSize - 1));
175 job->addMetaData("AllowCompressedPage", "false");
181 void Access::slotThreadResult(KJob* job)
183 KIO::TransferJob *tjob = qobject_cast<KIO::TransferJob *>(job);
189 tjob->ui()->setWindow(0);
190 tjob->ui()->showErrorMessage();
192 m_header = tjob->queryMetaData("HTTP-Headers");
199 void Access::slotReceiveThreadData(KIO::Job*, const QByteArray& data)
201 QByteArray data_tmp(data);
203 // HACK: crash when data contains '\0'.
204 for (int i=0; i< data_tmp.size(); i++) {
205 if (data_tmp[ i ] == '\0') data_tmp[ i ] = ' ';
208 if (m_bbstype == Board_MachiBBS
209 || m_bbstype == Board_JBBS) {
211 emitDatLineList(data_tmp);
215 /* check if received data is invalid (or broken). */
216 if ((m_dataSize > 0 && responseCode() != 206)
217 || (m_firstReceive && data_tmp[ 0 ] != '\n')
218 || (m_dataSize == 0 && responseCode() != 200))
219 m_invalidDataReceived = true;
221 if (m_invalidDataReceived) return ;
223 /* If this is the first call at resumption, remove LF(\n) at head. */
224 if (m_firstReceive) {
225 data_tmp = data_tmp.mid(1);
227 m_firstReceive = false;
229 emitDatLineList(data_tmp);
233 /* convert data stream into 2ch dat.
234 and emit receiveData SIGNAL. */ /* private */
235 void Access::emitDatLineList(const QByteArray& dataStream)
237 QList<QByteArray> lineList;
238 QStringList datLineList;
239 if (dataStream.isEmpty()) return ;
241 bool endIsLF = false;
242 if (dataStream.at(dataStream.length() - 1) == '\n') endIsLF = true;
244 /* split the stream */
245 m_lastLine += dataStream;
246 lineList = m_lastLine.split('\n');
249 /* save the last line */
252 QList<QByteArray>::iterator lastit = lineList.end();
254 if (lastit != lineList.end()) {
256 m_lastLine = (*lastit);
257 lineList.removeLast();
263 /* convert lines into 2ch dat */
264 int count = lineList.count();
265 for (int i = 0; i < count ; ++i) {
267 if (! lineList[ i ].isEmpty()) {
271 int nextNum = m_readNum + 1;
279 line = machiBbs.datToUnicode(lineList[i]);
280 line2 = ParseMachiBBSOneLine(line, nextNum);
281 ba = machiBbs.datFromUnicode(line2);
288 line = jBbs.datToUnicode(lineList[i]);
289 line2 = ParseJBBSOneLine(line, nextNum);
290 ba = jBbs.datFromUnicode(line2);
297 line = flashCgi.datToUnicode(lineList[i]);
298 line2 = ParseFlashCGIOneLine(line);
299 ba = flashCgi.datFromUnicode(line2);
304 line = line2 = K2ch().datToUnicode(lineList[i]);
308 if (line2.isEmpty()) continue;
310 /* add abone lines */
311 const char aboneStr[] = "abone<><><>abone<>";
312 while (nextNum > m_readNum + 1) {
313 datLineList += aboneStr;
314 m_threadData += aboneStr + '\n';
319 if (m_bbstype == Board_MachiBBS) m_threadData += ba + '\n';
320 else m_threadData += lineList[ i ] + '\n';
323 datLineList += line2;
327 /* call DatInfo::slotReceiveData() */
328 emit receiveData(datLineList);
333 void Access::killJob()
335 if (m_currentJob) m_currentJob->kill();
338 void Access::stopJob()
340 if (m_currentJob) m_currentJob->kill(); /* emit result signal */
343 int Access::serverTime()
345 if (m_currentJob) m_header = m_currentJob->queryMetaData("HTTP-Headers");
346 //if (m_header.isEmpty()) return QDateTime::currentDateTime().toTime_t();
347 // parse HTTP headers
348 QStringList headerList = m_header.split('\n');
349 QRegExp regexp("Date: (.*)");
350 QStringList dateStrList = headerList.filter(regexp);
351 if (dateStrList.isEmpty() || regexp.indexIn(dateStrList[0]) == -1) {
352 // 'Date' header is not found. use current time.
353 return QDateTime::currentDateTime().toTime_t();
355 return KDateTime::fromString(regexp.cap(1), KDateTime::RFCDate)
360 int Access::responseCode()
362 if (m_currentJob) m_header = m_currentJob->queryMetaData("HTTP-Headers");
363 // parse HTTP headers
364 QStringList headerList = m_header.split('\n');
365 QRegExp regexp("HTTP/1\\.[01] ([0-9]+) .*");
366 QStringList dateStrList = headerList.filter(regexp);
367 if (dateStrList.isEmpty() || regexp.indexIn(dateStrList[0]) == -1) {
369 if (m_bbstype == Board_JBBS) return 200; /* adhoc... */
372 return regexp.cap(1).toInt();
377 int Access::dataSize() const
383 bool Access::invalidDataReceived() const
385 return m_invalidDataReceived;
391 QString OfflawAccess::get()
393 QString getUrl = datToOfflaw(m_datUrl.url());
394 KUrl kgetUrl(getUrl);
395 kgetUrl.addQueryItem("sid", Account::getSessionId());
397 m_threadData.clear();
398 m_invalidDataReceived = false;
400 KIO::SlaveConfig::self() ->setConfigData("http",
403 QString("Monazilla/1.00 (Kita/%1)").arg(VERSION));
405 KIO::TransferJob* job
406 = KIO::get(kgetUrl, KIO::Reload, KIO::HideProgressInfo);
409 connect(job, SIGNAL(data(KIO::Job*, const QByteArray&)),
410 SLOT(slotReceiveThreadData(KIO::Job*, const QByteArray&)));
411 connect(job, SIGNAL(result(KJob*)), SLOT(slotThreadResult(KJob*)));
413 // use 'HTTP-Headers' metadata.
414 job->addMetaData("PropagateHttpHeader", "true");
416 return QString(); /* dummy */
419 void OfflawAccess::slotThreadResult(KIO::Job* job)
423 job->ui()->setWindow(0);
424 job->ui()->showErrorMessage();
426 m_header = job->queryMetaData("HTTP-Headers");
429 if (!m_invalidDataReceived && !m_threadData.isEmpty()) {
436 void OfflawAccess::slotReceiveThreadData(KIO::Job*, const QByteArray& data)
438 QByteArray cstr(data);
440 if ((m_dataSize > 0 && responseCode() != 206)
441 || (m_dataSize == 0 && responseCode() != 200)) {
442 m_invalidDataReceived = true;
445 if (m_invalidDataReceived) return ;
447 // "+OK ....../1024K\tLocation:temp/\n"
448 if (m_threadData.isEmpty() && cstr[ 0 ] == '+') {
450 int index = cstr.indexOf('\n');
451 cstr = cstr.mid(index + 1);
453 emitDatLineList(cstr);