OSDN Git Service

c4e40caf422c6077ceb38e93c21c41c5b41bcc31
[kita/kita.git] / kita / src / libkita / thread.cpp
1 /***************************************************************************
2  *   Copyright (C) 2003 by Hideki Ikemoto                                  *
3  *   ikemo@users.sourceforge.jp                                            *
4  *                                                                         *
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  ***************************************************************************/
10
11 #include "thread.h"
12
13 #include <config.h>
14
15 #include <kurl.h>
16 #include <kio/slaveconfig.h>
17 #include <kio/jobclasses.h>
18 #include <kio/scheduler.h>
19 #include <kdebug.h>
20
21 #include <qwidget.h>
22 #include <qapplication.h>
23 #include <qregexp.h>
24
25 #include "board.h"
26 #include "qcp932codec.h"
27
28 using namespace Kita;
29
30 ThreadFactory* ThreadFactory::instance = 0;
31
32 Thread::Thread() : m_board( 0 )
33 {
34 }
35
36 Thread::Thread(const Board& board, const KURL& datURL, const QString threadName, int resNum)
37   : m_datURL(datURL), m_threadName(threadName), m_resNum(resNum)
38 {
39   Q_ASSERT( datURL.url().right( 3 ) == QString( "dat" ) );
40
41   m_datID = m_datURL.filename().section(".", 0, 0);
42   m_board = new Board( board.url(), board.name() );
43   m_boardID = m_board->id();
44 }
45
46 Thread::Thread(const Board* board, const KURL& datURL, const QString threadName, int resNum)
47   : m_datURL(datURL), m_threadName(threadName), m_resNum(resNum)
48 {
49   m_datID = m_datURL.filename().section(".", 0, 0);
50   m_board = new Board( board->url(), board->name() );
51   m_boardID = m_board->id();
52 }
53
54 Thread::Thread( const Thread& obj )
55 {
56   m_datURL = obj.m_datURL;
57   m_datID = obj.m_datID;
58   m_boardID = obj.m_boardID;
59   m_threadName = obj.m_threadName;
60   m_resNum = obj.m_resNum;
61   m_board = new Board( obj.m_board->url(), obj.m_board->name() );
62 }
63
64 Thread::~Thread()
65 {
66   delete m_board;
67 }
68
69 Thread& Thread::operator=( const Thread& obj )
70 {
71   m_datURL = obj.m_datURL;
72   m_datID = obj.m_datID;
73   m_boardID = obj.m_boardID;
74   m_threadName = obj.m_threadName;
75   m_resNum = obj.m_resNum;
76   m_board = new Board( obj.m_board->url(), obj.m_board->name() );
77
78   return *this;
79 }
80
81 const KURL& Thread::datURL() const
82 {
83   return m_datURL;
84 }
85
86 const KURL Thread::url() const
87 {
88   KURL url = KURL(m_datURL, "/test/read.cgi/");
89   url.addPath(m_boardID + "/");
90   url.addPath(m_datID + "/");
91
92   return url;
93 }
94
95 const QString& Thread::datID() const
96 {
97   return m_datID;
98 }
99
100 int Thread::resNum() const
101 {
102   return m_resNum;
103 }
104
105 const QString& Thread::name() const
106 {
107   return m_threadName;
108 }
109
110 const QString& Thread::boardName() const
111 {
112   return m_board->name();
113 }
114
115 const KURL& Thread::boardUrl() const
116 {
117   return m_board->url();
118 }
119
120 const QString Thread::boardId() const
121 {
122   return m_board->id();
123 }
124
125 const QString Thread::toXmlFragment() const
126 {
127   QString ret;
128
129   ret += "<thread xmlns=\"http://kita.sourceforge.jp/ns/thread\">\n";
130   ret += QString("<daturl>%1</daturl>\n").arg( m_datURL.url() );
131   ret += QString("<name>%1</name>\n").arg( m_threadName );
132   ret += QString("<resnum>%1</resnum>\n").arg( m_resNum );
133   ret += m_board->toXmlFragment();
134   ret += "</thread>\n";
135
136   return ret;
137 }
138
139 Thread Thread::fromXml( const QString& xml )
140 {
141   Thread thread;
142
143   ThreadXmlParser parser;
144   QXmlSimpleReader reader;
145   QXmlInputSource source;
146   source.setData( xml );
147   reader.setContentHandler( &parser );
148   reader.parse( &source );
149
150   if( parser.isValid() ) {
151     thread = parser.getThread();
152   } else {
153     kdError() << "inValid" << endl;
154   }
155
156   return thread;
157 }
158
159 bool Thread::test()
160 {
161   kdDebug() << "Thread::test()" << endl;
162   Thread thread = Thread::fromXml( "<thread xmlns=\"http://kita.sourceforge.jp/ns/thread\">\n"
163                                    "<daturl>http://pc.2ch.net/linux/dat/1022744633.dat</daturl>\n"
164                                    "<name>2ch browser thread</name>\n"
165                                    "<resnum>700</resnum>\n"
166                                    "<board xmlns=\"http://kita.sourceforge.jp/ns/board\">\n"
167                                    "<url>http://pc.2ch.net/linux/</url>\n"
168                                    "<name>Linux</name>\n"
169                                    "</board>\n"
170                                    "</thread>\n" );
171
172   // TODO: ¤³¤³¤Ç»È¤ï¤ì¤Æ¤¤¤ëkdError¤Î»³¤Ï²¿¤È¤«¤·¤¿¤¤¡£
173   if( thread.datURL().url() != "http://pc.2ch.net/linux/dat/1022744633.dat" ) {
174     kdError() << "inValid: line=" << __LINE__ << endl;
175   }
176   if( thread.url().url() != "http://pc.2ch.net/test/read.cgi/linux/1022744633/" ) {
177     kdError() << "inValid: line=" << __LINE__ << endl;
178   }
179   if( thread.name() != "2ch browser thread" ) {
180     kdError() << "inValid: line=" << __LINE__ << endl;
181   }
182   if( thread.resNum() != 700 ) {
183     kdError() << "inValid: line=" << __LINE__ << endl;
184   }
185   if( thread.getBoard()->url().url() != "http://pc.2ch.net/linux/" ) {
186     kdError() << "inValid: line=" << __LINE__ << endl;
187   }
188   if( thread.getBoard()->name() != "Linux" ) {
189     kdError() << "inValid: line=" << __LINE__ << endl;
190   }
191
192   Thread thread2 = Thread::fromXml( thread.toXmlFragment() );
193
194   if( thread2.datURL().url() != "http://pc.2ch.net/linux/dat/1022744633.dat" ) {
195     kdError() << "inValid: line=" << __LINE__ << endl;
196   }
197   if( thread2.url().url() != "http://pc.2ch.net/test/read.cgi/linux/1022744633/" ) {
198     kdError() << "inValid: line=" << __LINE__ << endl;
199   }
200   if( thread2.name() != "2ch browser thread" ) {
201     kdError() << "inValid: line=" << __LINE__ << endl;
202   }
203   if( thread2.resNum() != 700 ) {
204     kdError() << "inValid: line=" << __LINE__ << endl;
205   }
206   if( thread2.getBoard()->url().url() != "http://pc.2ch.net/linux/" ) {
207     kdError() << "inValid: line=" << __LINE__ << endl;
208   }
209   if( thread2.getBoard()->name() != "Linux" ) {
210     kdError() << "inValid: line=" << __LINE__ << endl;
211   }
212
213   kdDebug() << thread.toXmlFragment() << endl;
214
215   thread = Thread::fromXml( "<thread xmlns=\"http://kita.sourceforge.jp/ns/thread\">\n"
216                             "  <daturl>http://pc.2ch.net/linux/dat/1022744633.dat</daturl>\n"
217                             "  <name>2ch browser thread</name>\n"
218                             "  <resnum>700</resnum>\n"
219                             "  <board xmlns=\"http://kita.sourceforge.jp/ns/board\">\n"
220                             "    <url>http://pc.2ch.net/linux/</url>\n"
221                             "    <name>Linux</name>\n"
222                             "  </board>\n"
223                             "</thread>\n" );
224
225   if( thread.datURL().url() != "http://pc.2ch.net/linux/dat/1022744633.dat" ) {
226     kdError() << "inValid: line=" << __LINE__ << endl;
227   }
228   if( thread.url().url() != "http://pc.2ch.net/test/read.cgi/linux/1022744633/" ) {
229     kdError() << "inValid: line=" << __LINE__ << endl;
230   }
231   if( thread.name() != "2ch browser thread" ) {
232     kdError() << "inValid: line=" << __LINE__ << endl;
233   }
234   if( thread.resNum() != 700 ) {
235     kdError() << "inValid: line=" << __LINE__ << endl;
236   }
237   if( thread.getBoard()->url().url() != "http://pc.2ch.net/linux/" ) {
238     kdError() << "inValid: line=" << __LINE__ << endl;
239   }
240   if( thread.getBoard()->name() != "Linux" ) {
241     kdError() << "inValid: line=" << __LINE__ << endl;
242   }
243
244   return true;
245 }
246
247 bool ThreadXmlParser::startElement( const QString& namespaceURI, const QString& localName, const QString& qName, const QXmlAttributes& atts )
248 {
249   if( m_inBoard ) {
250     m_boardParser->startElement( namespaceURI, localName, qName, atts );
251   } else if( m_inThread ) {
252     if( localName == "daturl" ) {
253     } else if( localName == "name" ) {
254     } else if( localName == "resnum" ) {
255     } else if( localName == "board" ) {
256       m_inBoard = true;
257       m_boardParser = new BoardXmlParser();
258       m_boardParser->startElement( namespaceURI, localName, qName, atts );
259     } else {
260       // error
261       return false;
262     }
263   } else {
264     if( localName == "thread" ) {
265       m_inThread = true;
266     } else {
267       // error
268       return false;
269     }
270   }
271
272   return true;
273 }
274
275 // TODO: <resnum>¤ÏFavorites¤Ç¤Ï»È¤ï¤Ê¤¤¤³¤È¤Ë¤Ê¤Ã¤¿¤Î¤Çºï½ü¤¹¤ëɬÍפ¢¤ê¡£
276 bool ThreadXmlParser::endElement( const QString& namespaceURI, const QString& localName, const QString& qName )
277 {
278   if( localName == "board" ) {
279     m_inBoard = false;
280     m_boardParser->endElement( namespaceURI, localName, qName );
281     Board board = m_boardParser->getBoard();
282     m_board = new Board( board.url(), board.name() );
283
284     delete m_boardParser;
285     m_boardParser = 0;
286
287     // create board;
288   } else if( m_inBoard ) {
289     m_boardParser->endElement( namespaceURI, localName, qName );
290   } else if( localName == "thread" ) {
291     m_inThread = false;
292     m_thread = Kita::Thread( *m_board, KURL( m_datUrlStr ), m_threadName, m_resNum );
293     m_isValid = true;
294     // create thread;
295   } else if( localName == "daturl" ) {
296     m_datUrlStr = m_characters;
297   } else if( localName == "name" ) {
298     m_threadName = m_characters;
299   } else if( localName == "resnum" ) {
300     m_resNum = m_characters.toInt();
301   } else {
302     // error
303     return false;
304   }
305   return true;
306 }
307
308 bool ThreadXmlParser::characters( const QString& ch )
309 {
310   if( m_inBoard ) {
311     m_boardParser->characters( ch );
312   } else {
313     m_characters = ch;
314   }
315   return true;
316 }
317
318 QString ThreadAccess::get()
319 {
320   if ( m_thread.datURL().protocol() != "k2ch" ) {
321     KIO::SlaveConfig::self()->setConfigData("http",
322       m_thread.datURL().host(),
323       "UserAgent",
324       QString( "Monazilla/1.00 (Kita/%1)" ).arg( VERSION ) );
325   }
326
327   KIO::TransferJob* job = KIO::get(m_thread.datURL(), true, true);
328   m_currentJob = job;
329
330   connect(job, SIGNAL(data(KIO::Job*, const QByteArray&)),
331           SLOT(slotReceiveThreadData(KIO::Job*, const QByteArray&)));
332   connect(job, SIGNAL(result(KIO::Job*)), SLOT(slotThreadResult(KIO::Job*)));
333   connect(job, SIGNAL(redirection(KIO::Job *, const KURL&) ), SLOT(slotRedirection(KIO::Job *, const KURL&) ) );
334
335   // use 'HTTP-Headers' metadata.
336   job->addMetaData("PropagateHttpHeader", "true");
337
338   enter_loop();
339
340   QCp932Codec codec;
341   return codec.toUnicode( m_threadData );
342 }
343
344 // from netaccess.cpp
345 void qt_enter_modal( QWidget* widget );
346 void qt_leave_modal( QWidget* widget );
347
348 void ThreadAccess::enter_loop()
349 {
350   QWidget dummy( 0, 0, WType_Dialog | WShowModal );
351   dummy.setFocusPolicy( QWidget::NoFocus );
352   qt_enter_modal(&dummy);
353   qApp->enter_loop();
354   qt_leave_modal(&dummy);
355 }
356
357 void ThreadAccess::slotRedirection(KIO::Job *, const KURL & newURL)
358 {
359   kdDebug() << "Redirected to newURL: " << newURL.url() << endl;
360   m_thread = Kita::Thread( m_thread.getBoard(), newURL);
361   emit redirection( newURL.url() );
362 }
363
364 void ThreadAccess::slotReceiveThreadData( KIO::Job*, const QByteArray& data )
365 {
366   QCString cstr(data.data(), data.size()+1);
367   m_threadData.append(cstr);
368 }
369
370 void ThreadAccess::slotThreadResult(KIO::Job* job)
371 {
372   m_currentJob = 0;
373   if(job->error()) {
374     job->showErrorDialog();
375   } else {
376     m_header = job->queryMetaData("HTTP-Headers");
377   }
378   qApp->exit_loop();
379 }
380
381 void ThreadAccess::killJob()
382 {
383   if( m_currentJob ) m_currentJob->kill();
384 }
385
386 int ThreadAccess::serverTime()
387 {
388     // parse HTTP headers
389     QStringList headerList = QStringList::split( "\n", m_header );
390     QRegExp regexp( "Date: (...), (..) (...) (....) (..:..:..) .*" );
391     QString dateStr = headerList.grep(regexp)[0];
392     if( regexp.search(dateStr) == -1 ) {
393         // invalid date format
394         return QDateTime::currentDateTime().toTime_t();
395     } else {
396         // I hate this format ;p
397         QString usLocalDateStr = regexp.cap(1) + " " + regexp.cap(3) + " " +
398                                  regexp.cap(2) + " " + regexp.cap(5) + " " +
399                                  regexp.cap(4);
400
401         // 1970/01/01 00:00:00 GMT
402         QDateTime zeroTime(QDate(1970, 1, 1), QTime(0, 0));
403         return zeroTime.secsTo(QDateTime::fromString(usLocalDateStr));
404     }
405 }
406
407 void ThreadFactory::insertThread( Thread& thread )
408 {
409   if( ! instance ) {
410     instance = new ThreadFactory();
411   }
412
413   Thread* new_thread = new Thread( thread.getBoard(), thread.datURL(), thread.name(), thread.resNum() );
414   instance->m_dict.insert( thread.datURL().url(), new_thread );
415 }
416
417 Thread* ThreadFactory::getThread( QString& datURL )
418 {
419   if( ! instance ) {
420     instance = new ThreadFactory();
421   }
422
423   return instance->m_dict[ datURL ];
424 }
425
426 #include "thread.moc"