OSDN Git Service

remove unused variable
[kita/kita.git] / kita / src / libkita / datinfo.cpp
1 /**************************************************************************
2 *   Copyright (C) 2003 by Hideki Ikemoto , (c)2004 by 421                 *
3 *   ikemo@wakaba.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 <qregexp.h>
12 #include <qstringlist.h>
13 #include <klocale.h>
14 #include <dom/html_document.h>
15 #include <dom/html_element.h>
16 #include <dom/html_table.h>
17 #include <dom/dom_text.h>
18
19 #include "datinfo.h"
20 #include "datmanager.h"
21 #include "parsemisc.h"
22 #include "access.h"
23 #include "thread.h"
24 #include "threadinfo.h"
25 #include "kitaconfig.h"
26 #include "kita-utf8.h"
27 #include "kita-utf16.h"
28 #include "kita_misc.h"
29
30 using namespace Kita;
31
32 #define RESDAT_DEFAULTSIZE 10
33 #define RESDAT_DELTA       1000
34
35
36 /*------------------------------------------------------*/
37 /* DatInfo stores & handles all information about *.dat */
38
39 DatInfo::DatInfo( const KURL& url ) : m_access ( 0 )
40 {
41     QString refstr;
42     m_datURL = Kita::ParseMisc::parseURL( url, refstr);
43     m_thread = Kita::Thread::getByURLNew( m_datURL );
44
45     /* japanese strings */
46     m_spacestr = ". ";
47     m_framestr1 = ParseMisc::utf8ToUnicode( KITAUTF8_FRAME1 ); /* |  */
48     m_framestr2 = ParseMisc::utf8ToUnicode( KITAUTF8_FRAME2 ); /* |- */
49     m_framestr3 = ParseMisc::utf8ToUnicode( KITAUTF8_FRAME3 ); /* L  */
50
51     initPrivate();
52 }
53
54 DatInfo::~DatInfo()
55 {
56     deleteAccessJob();
57 }
58
59
60 /* Init  */
61 /* Usually, don't call this. */ /* public */
62 void DatInfo::init()
63 {
64     QMutexLocker locker( &m_mutex );
65
66     return initPrivate();
67 }
68
69 /* private */
70 void DatInfo::initPrivate()
71 {
72
73     /* stop & delete dat loader */
74     deleteAccessJob();
75
76     /* init variables */
77     m_readNum = 0;
78     m_rawData = QString::null;
79     m_subject = QString::null;
80     m_broken = FALSE;
81     m_lock = 0;
82     m_nowLoading = FALSE;
83     m_lastLine = QString::null;
84     m_kokoyonNum = KitaThreadInfo::readNum( m_datURL.prettyURL() );
85
86     /* clear ResDatVec */
87     m_resDatVec.clear();
88     increaseResDatVec( RESDAT_DEFAULTSIZE );
89
90     /* reset Abone */
91     resetAbonePrivate();
92
93     /* create dat loader */
94     m_access = new Kita::Access( m_datURL );
95
96     connect( m_access, SIGNAL( receiveData( const QString& ) ),
97              SLOT( slotReceiveData( const QString& ) ) );
98     connect( m_access, SIGNAL( finishLoad() ),SLOT( slotFinishLoad() ) );
99
100     /* get dat from cahce & copy it to buffer */
101     copyRawDataToBuffer(m_access->getcache());
102     copyRawDataToBuffer(QString::null); /* copy the last line */
103 }
104
105
106 /* private */
107 void DatInfo::resetResDat(RESDAT& resdat)
108 {
109
110     resdat.set = FALSE;
111     resdat.parsed = FALSE;
112     resdat.broken = FALSE;
113     resdat.setAnclist = FALSE;
114     resdat.anclist.clear();
115     resdat.checkAbone = FALSE;
116     resdat.abone = FALSE;
117     resdat.marked = FALSE;
118 }
119
120
121 /* private */
122 void DatInfo::increaseResDatVec( int delta )
123 {
124     int size = m_resDatVec.size();
125     RESDAT resdat;
126     resetResDat(resdat);
127     m_resDatVec.resize( size + delta, resdat);
128 }
129
130
131 /* delete dat loader */ /* private */
132 void DatInfo::deleteAccessJob()
133 {
134     if ( m_access )
135     {
136         m_access->killJob();
137         delete m_access;
138         m_access = NULL;
139     }
140 }
141
142 /* copy raw lines to buffer */
143
144 /*  !! NOTICE!!
145    You need to call this function last
146    with rawData = QString::null in order
147    to copy the last line to buffer.      */ /* private */
148 void DatInfo::copyRawDataToBuffer(const QString& rawData)
149 {
150     QStringList linelist;
151     int basenum = m_readNum + 1;
152
153     // hack for machiBBS
154     if ( Kita::boardType( m_datURL.url() ) == Board_MachiBBS && m_lastLine == QString::null && m_access->responseCode() == 200 )
155     {
156         basenum = 1;
157         m_readNum = 0;
158     }
159
160     bool endIsLF = FALSE;
161     if ( rawData != QString::null )
162     {
163
164         if(rawData.at(rawData.length()-1) == '\n') endIsLF = TRUE;
165
166         /* split the raw data */
167         m_lastLine += rawData;
168         linelist = QStringList::split( "\n", m_lastLine );
169
170         /* backup the last line */
171         QStringList::iterator lastit = linelist.end();
172         lastit--;
173         if( lastit != linelist.end() )
174         {
175             m_lastLine = (*lastit);
176             linelist.remove(lastit);
177
178             /* If the last char of rawData is LF, then
179                add LF to m_lastLine, because LF was
180                removed by above QStringList::split().  */
181             if(endIsLF) m_lastLine += '\n';
182         }
183         else m_lastLine = QString::null;
184
185     }
186     else
187     { /* If rawData == null, then copy the last line */
188
189         if ( m_lastLine != QString::null
190                 && m_lastLine.contains( "<>" ) == 4
191                 && m_lastLine.at(m_lastLine.length()-1) == '\n' )
192         {
193
194             m_lastLine.truncate(m_lastLine.length()-1);/* remove LF */
195             linelist += m_lastLine;
196             m_lastLine = QString::null;
197         }
198     }
199
200     /* copy lines to buffer */
201     for ( QStringList::iterator it = linelist.begin();
202             it != linelist.end(); ++it, ++basenum )
203     {
204         if((*it) != QString::null)
205         {
206             QString line = ParseMisc::qcpToUnicode((*it));
207             copyOneLineToResDat(line,basenum);
208         }
209     }
210
211     /* update thread info */
212     if ( !m_thread && m_readNum ) {
213         m_thread = Kita::Thread::getByURL( m_datURL.prettyURL() );
214     }
215     if( m_thread ){
216         if( m_thread->resNum() < m_readNum ) m_thread->setResNum( m_readNum );
217         KitaThreadInfo::setReadNum( m_datURL.prettyURL(), m_readNum );
218     }
219 }
220
221
222
223 /* copy one line to resdat.            */
224 /* This private function is called in
225    copyRawDataToBuffer.                */ /* private */
226 bool DatInfo::copyOneLineToResDat( const QString& line, int num )
227 {
228
229     if ( num <= 0 ) return FALSE;
230     if ( line == QString::null ) return FALSE;
231
232     /* If resdat vector is short, then resize the vector. */
233     while( (int) m_resDatVec.size() <= num ) increaseResDatVec( RESDAT_DELTA );
234
235     /* reset and  set new data */
236     RESDAT& resdat = m_resDatVec[ num ];
237     resetResDat(resdat);
238
239     resdat.set = TRUE;
240     resdat.linestr = line;
241     if(m_readNum < num) m_readNum = num;
242     m_rawData += line + '\n';
243
244     if(num == 1) parseDat ( num ); /* to get subject */
245
246     /* is this dat file broken ? */
247     if ( line.contains( "<>" ) != 4 )
248     {
249         resdat.broken = TRUE;
250         m_broken = TRUE;
251     }
252
253     return TRUE;
254 }
255
256
257
258 /* public */
259 const KURL& DatInfo::url()
260 {
261     QMutexLocker locker( &m_mutex );
262
263     return m_datURL;
264 }
265
266
267
268 /*--------------------------------------*/
269 /* cache handling functions             */
270
271 /* Update cache  */
272
273 /* When Kita::Access received new data,
274    slotReceiveData is called.           */
275
276 /* When Kita::Access fineshed loading,
277    slotFinishLoad is called, and
278    DatInfo emits the finishLoad signal to the parent object  */ /* public */
279 bool DatInfo::updateCache(const QObject* parent)
280 {
281     QMutexLocker locker( &m_mutex );
282     if ( m_access == NULL ) return FALSE;
283     if ( m_nowLoading ) return FALSE;
284
285     m_nowLoading = TRUE;
286     m_lock++; /* By locking, DatManager can't delete this while loading. */
287
288     connect( this, SIGNAL( receiveData() ),
289              parent,SLOT( slotReceiveData() ) );
290
291     connect( this, SIGNAL( finishLoad() ),
292              parent,SLOT( slotFinishLoad() ) );
293
294     m_access->getupdate();
295
296     return TRUE;
297 }
298
299
300 /* slot called when Kita::Access
301    received new data              */      /* private  slot */
302 void DatInfo::slotReceiveData(const QString& newLine)
303 {
304
305     int rescode = m_access->responseCode();
306
307     if(rescode != 200 && rescode != 206) return;
308
309     copyRawDataToBuffer(newLine);
310
311     emit receiveData();
312 }
313
314
315 /* slot called when Kita::Access
316    finished loading new dat */      /* private  slot */
317 void DatInfo::slotFinishLoad()
318 {
319
320     /* copy the last line */
321     copyRawDataToBuffer(QString::null);
322
323     /* finish loading session & emit signal to the parent object */
324     m_nowLoading = FALSE;
325     emit finishLoad();
326
327     /* disconnect signals */
328     disconnect( SIGNAL( receiveData() ));
329     disconnect( SIGNAL( finishLoad() ) );
330
331     if(m_lock) m_lock--;
332 }
333
334
335 /* public */
336 int DatInfo::getResponseCode()
337 {
338     QMutexLocker locker( &m_mutex );
339     if ( m_access == NULL ) return 0;
340
341     return m_access->responseCode();
342 }
343
344
345 /* public */
346 int DatInfo::getServerTime()
347 {
348     QMutexLocker locker( &m_mutex );
349     if ( m_access == NULL ) return 0;
350
351     return m_access->serverTime();
352 }
353
354
355 /* public */
356 bool DatInfo::deleteCache()
357 {
358     QMutexLocker locker( &m_mutex );
359     if ( m_access == NULL ) return FALSE;
360     if ( m_nowLoading ) return FALSE;
361
362     bool ret = FALSE;
363     if( m_thread ) ret = Kita::Access::deleteLog( m_thread );
364     KitaThreadInfo::removeThreadInfo( m_datURL.prettyURL() );
365     if(ret) initPrivate();
366
367     return ret;
368 }
369
370
371 /* public */
372 bool DatInfo::isLoadingNow()
373 {
374
375     QMutexLocker locker( &m_mutex );
376
377     return m_nowLoading;
378 }
379
380
381
382 /* public */
383 void DatInfo::stopLoading()
384 {
385
386     /* Don't lock the mutex here !!!
387        It will cause deadlock , because
388        Kita::Access::stopJob() calls KitaThreadView::slotFinishLoad() back,
389        then slotFinishLoad calls another functions in DatInfo. */
390     if ( m_access == NULL ) return;
391     if (! m_nowLoading ) return;
392
393     m_access->stopJob();
394 }
395
396
397
398
399 /*---------------------------------------------------*/
400 /* locking , unlocking functions                     */
401 /*                                                   */
402 /* If m_lock is not 0, DatManager can't delete this. */
403 /* Don't forget to call unlock() after locking this. */
404
405 /* They are public */
406
407 void DatInfo::lock()
408 {
409     QMutexLocker locker( &m_mutex );
410
411     m_lock++;
412 }
413
414 void DatInfo::unlock()
415 {
416     QMutexLocker locker( &m_mutex );
417
418     if(m_lock) m_lock--;
419 }
420
421 int DatInfo::isLocked()
422 {
423     QMutexLocker locker( &m_mutex );
424
425     return m_lock;
426 }
427
428
429 /*------------------------------------------------------*/
430 /* get subject, linedata,  id, body, name, HTML, etc.   */
431
432 /* They are public */
433
434 const QString& DatInfo::getSubject()
435 {
436     QMutexLocker locker( &m_mutex );
437
438     return m_subject;
439 }
440
441 const QString& DatInfo::getRawDat()
442 {
443     QMutexLocker locker( &m_mutex );
444
445     return m_rawData;
446 }
447
448 const QString& DatInfo::getDat( int num )
449 {
450     QMutexLocker locker( &m_mutex );
451
452     if ( num <= 0 ||  (int) m_resDatVec.size() <= num ) return QString::null;
453     if ( ! m_resDatVec[ num ].set ) return QString::null;
454
455     return m_resDatVec[ num ].linestr;
456 }
457
458 const QString& DatInfo::getId( int num )
459 {
460     QMutexLocker locker( &m_mutex );
461     if(!parseDat(num)) return QString::null;
462
463     return m_resDatVec[ num ].id;
464 }
465
466 const QString& DatInfo::getBody( int num )
467 {
468     QMutexLocker locker( &m_mutex );
469     if(!parseDat(num)) return QString::null;
470
471     return m_resDatVec[ num ].body;
472 }
473
474 const QString& DatInfo::getName( int num )
475 {
476     QMutexLocker locker( &m_mutex );
477     if(!parseDat(num)) return QString::null;
478
479     return m_resDatVec[ num ].name;
480 }
481
482 /* plain (parsed) strings of name  */
483 QString DatInfo::getPlainName( int num )
484 {
485     QMutexLocker locker( &m_mutex );
486     if(!parseDat(num)) return QString::null;
487
488     return m_resDatVec[ num ].parsedName;
489 }
490
491
492 /* plain (parsed) strings of body  */
493 QString DatInfo::getPlainBody(int num)
494 {
495     QMutexLocker locker( &m_mutex );
496     if(!parseDat(num)) return QString::null;
497
498     QString bodytext;
499     QString line = m_resDatVec[ num ].body;
500
501     ParseMisc::parseBODYdatText(PARSEMODE_TEXT,line,bodytext);
502
503     return bodytext;
504 }
505
506
507 /* plain (parsed) strings of title */
508 QString DatInfo::getPlainTitle(int num)
509 {
510     QMutexLocker locker( &m_mutex );
511     if(!parseDat(num)) return QString::null;
512
513     bool showAddr = KitaConfig::showMailAddress();
514     QString titletext;
515     ParseMisc::parseTITLEdatText(PARSEMODE_TEXT,num,showAddr,m_resDatVec[ num ],titletext);
516
517     return titletext;
518 }
519
520
521 /*-----------------------------------------*/
522 /* HTML data                               */
523
524 /* HTML strings */
525 /* Note that this function checks Abone internally. */ /* public */
526 QString DatInfo::getHtml(int startnum, int endnum)
527 {
528     QMutexLocker locker( &m_mutex );
529
530     QString retstr = QString::null;
531     bool showAddr = KitaConfig::showMailAddress();
532
533     for(int num = startnum; num <= endnum; num++)
534     {
535
536         if(!parseDat(num)) continue;
537         if(checkAbonePrivate(num)) retstr += aboneHTML( num );
538         else if(m_resDatVec[ num ].broken) retstr += brokenHTML( num );
539         else retstr += ParseMisc::ResDatToHtml(m_resDatVec[ num ],  num, showAddr );
540     }
541
542     return retstr;
543 }
544
545
546 /* return HTML strings that have ID = strid. */
547 /* Note that this function checks Abone internally. */ /* public */
548 QString DatInfo::getHtmlByID(const QString& strid, int &count )
549 {
550     QMutexLocker locker( &m_mutex );
551
552     QString retstr = QString::null;
553     bool showAddr = KitaConfig::showMailAddress();
554
555     count = 0;
556
557     for ( int i = 1; i <= m_readNum; i++ )
558     {
559
560         if(!parseDat(i)) continue;
561
562         if(m_resDatVec[i].id == strid)
563         {
564             count ++;
565             if(checkAbonePrivate(i)) retstr += aboneHTML(i);
566             else if(m_resDatVec[ i ].broken) retstr += brokenHTML(i);
567             else retstr += ParseMisc::ResDatToHtml(m_resDatVec[i], i, showAddr);
568         }
569     }
570
571     return retstr;
572 }
573
574
575 /* private */
576 QString DatInfo::aboneHTML(int num)
577 {
578
579     QString tmpstr = QString( "<dl><dt>" );
580     tmpstr += QString().setNum( num ) + " "  + ParseMisc::utf8ToUnicode( KITAUTF8_ABONE );
581     tmpstr += "</dt><dd>";
582     tmpstr += ParseMisc::utf8ToUnicode( KITAUTF8_ABONE );;
583     tmpstr += "<br/><br/></dd></dl>";
584
585     return tmpstr;
586 }
587
588
589 /* private */
590 QString DatInfo::brokenHTML(int num)
591 {
592
593     QString tmpstr = QString( "<dl><dt>" );
594     tmpstr += QString().setNum( num ) + " "  + ParseMisc::utf8ToUnicode( KITAUTF8_NAME );
595     tmpstr += "</dt><dd>";
596     tmpstr += ParseMisc::utf8ToUnicode( KITAUTF8_KOWARE );;
597     tmpstr += "<br/><br/></dd></dl>";
598
599     return tmpstr;
600 }
601
602
603 /*-------------------------------*/
604 /* Get HTML document of res tree.*/
605 /* For example, when rootnum = 1,
606  
607 >>1 
608 |-->>4
609 |  |--->>10
610 |
611 |-->>20, and return count = 3.  */
612
613 /* Note that this function checks Abone internally. */ /* public */
614 QString DatInfo::getTreeByRes(const int rootnum, int& count)
615 {
616     QMutexLocker locker( &m_mutex );
617     return getTreeByResPrivate(rootnum,FALSE,count);
618 }
619
620 /*---------------------------------------*/
621 /* Get HTML document of reverse res tree.*/
622 /* For example, when rootnum = 10,
623  
624 >>10 
625 |-->>5
626 |  |--->>2
627 |
628 |-->>6, and returns count = 3.  */
629
630 /* Note that this function checks Abone internally. */ /* public */
631 QString DatInfo::getTreeByResReverse(const int rootnum, int& count)
632 {
633     QMutexLocker locker( &m_mutex );
634     return getTreeByResPrivate(rootnum,TRUE,count);
635 }
636
637
638 /* private */
639 QString DatInfo::getTreeByResPrivate(
640     const int rootnum,
641     bool reverse,  /* reverse search */
642     int& count)
643 {
644
645     QString tmp = QString().setNum( rootnum );
646     QString retstr = "<a href=\"#" + tmp + "\">&gt;&gt;" + tmp + "</a><br>";
647
648     retstr += getTreeByResCore(rootnum,reverse,count,"");
649
650     return retstr;
651 }
652
653 /* private */
654 QString DatInfo::getTreeByResCore(
655     const int rootnum,
656     bool reverse,  /* reverse search */
657     int& count, QString prestr)
658 {
659     if(!parseDat(rootnum)) return QString::null;
660     if( checkAbonePrivate(rootnum) ) return QString::null;
661
662     QString retstr = QString::null ;
663     count = 0;
664     QStringList strlists;
665
666     if( !reverse )
667     {
668
669         /* collect responses that have anchor to rootnum */
670         for ( int i = rootnum+1; i <= m_readNum; i++ )
671         {
672             if(checkAbonePrivate( i ) ) continue;
673             if(checkRes(i,rootnum))
674             {
675                 count ++;
676                 strlists += QString().setNum( i );
677             }
678         }
679
680     }
681     else
682     { /* collect responses for which rootnum has anchors */
683
684         setAncList(rootnum);
685         AncList& anclist = m_resDatVec[ rootnum ].anclist;
686         for ( AncList::iterator it = anclist.begin(); it != anclist.end(); ++it )
687         {
688             for( int i = (*it).from; i <= QMIN(rootnum-1,(*it).to) ; i++)
689             {
690                 if(checkAbonePrivate(i) ) continue;
691                 count ++;
692                 strlists += QString().setNum( i );
693             }
694         }
695     }
696
697     /* make HTML document */
698     if(count)
699     {
700
701         for ( QStringList::iterator it = strlists.begin(); it != strlists.end(); ++it )
702         {
703             QString tmpstr;
704             if((*it) == strlists.last()) tmpstr = m_framestr3;  /* 'L' */
705             else tmpstr = m_framestr2;  /* '|-' */
706
707             retstr += prestr + tmpstr + "<a href=\"#" + (*it) + "\">&gt;&gt;" + (*it) + "</a><br>";
708
709             /* call myself recursively */
710             int tmpnum;
711             tmpstr = prestr;
712             if((*it) == strlists.last()) tmpstr += m_spacestr + m_spacestr + m_spacestr; /* "   " */
713             else tmpstr += m_framestr1  + m_spacestr; /* "| " */
714             retstr += getTreeByResCore((*it).toInt(),reverse,tmpnum,tmpstr);
715             count += tmpnum;
716         }
717     }
718
719     return retstr;
720 }
721
722
723
724 /*----------------------------------------------*/
725 /* Check if No.num has anchors to No.target     */
726 /* For exsample, if target = 4, and No.num have
727    an anchor >>4, or >>2-6, etc.,
728    then return TRUE.                            */  /* private */
729 bool DatInfo::checkRes(const int num, const int target )
730 {
731     if(! parseDat(num)) return FALSE;
732
733     setAncList(num);
734     AncList& anclist = m_resDatVec[ num ].anclist;
735
736     for ( AncList::iterator it = anclist.begin(); it != anclist.end(); ++it )
737     {
738         if ( target >= (*it).from && target <= (*it).to ) return TRUE;
739     }
740
741     return FALSE;
742
743 }
744
745
746 /*--------------------------*/
747 /* set AncList              */ /* private */
748
749 /* This function sets anchor list.
750    For example, a res has anchors >>2-3 and >>4, then
751    
752    anclist[0].from = 2,
753    anclist[0].to = 3,
754    anclist[1].from = 4,
755    anclist[1].to = 4.   
756 */
757 void DatInfo::setAncList(int num)
758 {
759
760     if(! parseDat(num)) return;
761     if( m_resDatVec[ num ].setAnclist ) return;
762
763     m_resDatVec[ num ].setAnclist = TRUE;
764
765     QString linkstr;
766     int refNum[2];
767     unsigned int pos;
768     ANCNUM anctmp;
769
770     AncList& anclist = m_resDatVec[ num ].anclist;
771     anclist.clear();
772
773     QString line = m_resDatVec[ num ].body;
774
775     /* remove HTML tags */
776     QRegExp rex( "<[^>]*>" );
777     line.remove(rex);
778
779     const QChar *chpt = line.unicode();
780     unsigned int i;
781     unsigned int length = line.length();
782
783     /* parse body */
784     for ( i = 0 ; i < length ; i++ )
785     {
786
787         if( chpt[ i ].unicode() == UTF16_BRACKET || /* > */
788                 (chpt[ i] == '&' && chpt[ i+1] == 'g' && chpt[ i+2] == 't' && chpt[ i+3] == ';') /* "&gt;" */
789           )
790         {
791             while(ParseMisc::parseResAnchor(chpt+i,length-i,linkstr,refNum,pos))
792             {
793                 if(refNum[1] < refNum[0]) refNum[1] = refNum[0];
794                 anctmp.from = refNum[0];
795                 anctmp.to = refNum[1];
796                 anclist += anctmp;
797                 i += pos;
798             }
799
800             i += (pos-1);
801         }
802     }
803
804     /* parse name */
805     line = m_resDatVec[ num ].name;
806     chpt = line.unicode();
807     i = 0;
808     length = line.length();
809
810     while(ParseMisc::parseResAnchor(chpt+i,length-i,linkstr,refNum,pos))
811     {
812         if(refNum[1] < refNum[0]) refNum[1] = refNum[0];
813         anctmp.from = refNum[0];
814         anctmp.to = refNum[1];
815         anclist += anctmp;
816         i += pos;
817     }
818 }
819
820
821
822 /*----------------------------------*/
823 /*  Get DOM element                 */
824
825 /* This function returns the element
826    of DOM.
827  
828    This function checks Abone
829    internally. So, if the res is
830    aboned,the output is "abone" node.
831    
832    If the res is broken, the output
833    is "broken" node.
834                                     */
835 /* public */
836 bool DatInfo::getDomElement(
837
838     /* input */
839     int num,
840     DOM::HTMLDocument& hdoc, /* root of HTML document */
841
842     /* output */
843     DOM::Element& retelm)
844 {
845
846     QMutexLocker locker( &m_mutex );
847     if(!parseDat(num)) return FALSE;
848
849     QString tmpstr;
850     QString status = "normal";
851     DOM::Element bodynode, titlenode, tmpelm;
852     DOM::HTMLTableElement table;
853     bool showAddr = KitaConfig::showMailAddress();
854     bool showAA = KitaConfig::showAA();
855     bool abone = checkAbonePrivate( num );
856
857     RESDAT& resdat = m_resDatVec[ num ];
858
859     if ( !resdat.broken && !abone )
860     {
861
862         /* title node */
863         table = hdoc.createElement( "TABLE" );
864         titlenode = table;
865         tmpelm = table.insertRow( 0 );
866         ParseMisc::parseTITLEdat(PARSEMODE_DOM,hdoc,num,showAddr,resdat,tmpelm,tmpstr);
867
868         /* body node */
869         bodynode = hdoc.createElement( "DIV" );
870         {
871             /* put the span node at the head of each line */
872             if ( showAA )
873             {
874
875                 tmpelm = bodynode.appendChild( hdoc.createElement( "SPAN" ) );
876                 {
877                     tmpelm.setAttribute( "style", "color: white" );
878                     tmpelm.appendChild( hdoc.createTextNode( "" ) );
879                 }
880             }
881
882             ParseMisc::parseBODYdat(PARSEMODE_DOM,resdat.body,hdoc,showAA,bodynode,tmpstr);
883         }
884
885     }
886     else
887     { /* abone or data is broken */
888
889         QString namestr;
890         QString bodystr;
891
892         if(abone)
893         { /* "abone" node */
894             namestr = ParseMisc::utf8ToUnicode( KITAUTF8_ABONE );
895             bodystr = ParseMisc::utf8ToUnicode( KITAUTF8_ABONE );
896             status = "abone";
897         }
898         else
899         { /* "broken" node */
900             namestr = ParseMisc::utf8ToUnicode( KITAUTF8_NAME );
901             bodystr = ParseMisc::utf8ToUnicode( KITAUTF8_KOWARE );
902             status = "broken";
903         }
904
905         /* title node */
906         table = hdoc.createElement( "TABLE" );
907         titlenode = table;
908         tmpelm = table.insertRow( 0 );
909         tmpelm = tmpelm.appendChild( hdoc.createElement( "TD" ) );
910         tmpelm.appendChild( hdoc.createTextNode(
911                                    QString().setNum( num ) + " " + namestr ) );
912
913         /* body node */
914         bodynode = hdoc.createElement( "DIV" );
915         tmpelm = bodynode.appendChild( hdoc.createElement( "SPAN" ) );
916         {
917             tmpelm.setAttribute( "style", "color: red" );
918             tmpelm.appendChild( hdoc.createTextNode( bodystr ) );
919         }
920
921     }
922
923
924     /*-----------------------------*/
925
926     retelm = hdoc.createElement( "DIV" );
927     {
928         retelm.setAttribute( "kita_type", "res" );
929         retelm.setAttribute( "kita_status", status );
930         retelm.setAttribute( "id", QString().setNum( num ) );
931         retelm.setAttribute( "kita_rname", resdat.name );
932         retelm.setAttribute( "kita_rid", resdat.id );
933
934         titlenode.setAttribute( "class", "res_title" );
935         bodynode.setAttribute( "class", "res_body" );
936         retelm.appendChild( titlenode );
937         retelm.appendChild( bodynode );
938     }
939
940     return TRUE;
941 }
942
943
944
945 /*-----------------------*/
946 /* several information */
947
948 /* public */
949 int DatInfo::getReadNum()
950 {
951     QMutexLocker locker( &m_mutex );
952
953     return m_readNum;
954 }
955
956
957 /* return number of responses that have ID = strid. */
958 /* Note that this function checks Abone internally. */ /* public */
959 int DatInfo::getNumByID( const QString& strid )
960 {
961     QMutexLocker locker( &m_mutex );
962
963     int count = 0;
964
965     for ( int i = 1; i <= m_readNum; i++ )
966     {
967
968         if( !parseDat(i) ) continue;
969         if( checkAbonePrivate( i ) ) continue;
970
971         if ( m_resDatVec[i].id == strid ) count++;
972     }
973
974     return count;
975 }
976
977 /* public */
978 int DatInfo::getKokoyonNum()
979 {
980     QMutexLocker locker( &m_mutex );
981
982     return QMIN(m_kokoyonNum, m_readNum);
983 }
984
985 /* public */
986 void DatInfo::setKokoyonNum(int num)
987 {
988     QMutexLocker locker( &m_mutex );
989
990     num = QMIN(num, m_readNum);
991
992     m_kokoyonNum = num;
993     KitaThreadInfo::setReadNum( m_datURL.prettyURL(), num );
994 }
995
996 /* public */
997 int DatInfo::getDatSize()
998 {
999     QMutexLocker locker( &m_mutex );
1000
1001     return m_rawData.length();
1002 }
1003
1004
1005 /* public */
1006 bool DatInfo::isResValid(int num)
1007 {
1008     QMutexLocker locker( &m_mutex );
1009
1010     return parseDat(num);
1011 }
1012
1013 /* public */
1014 bool DatInfo::isBroken()
1015 {
1016     QMutexLocker locker( &m_mutex );
1017
1018     return m_broken;
1019 }
1020
1021 /* public */
1022 bool DatInfo::isResBroken(int num)
1023 {
1024     QMutexLocker locker( &m_mutex );
1025     if(!parseDat(num)) return FALSE;
1026
1027     return m_resDatVec[ num ].broken;
1028 }
1029
1030 /* ID = strid ? */ /* public */
1031 bool DatInfo::checkID(const QString& strid, int num )
1032 {
1033     QMutexLocker locker( &m_mutex );
1034     if(!parseDat(num)) return FALSE;
1035
1036     if(m_resDatVec[num].id  == strid) return TRUE;
1037
1038     return FALSE;
1039 }
1040
1041
1042 /* Are keywords included ? */ /* public */
1043 bool DatInfo::checkWord(QStringList& stlist, /* list of keywords */
1044                         int num,
1045                         bool checkOR /* AND or OR search */
1046                        )
1047 {
1048     QMutexLocker locker( &m_mutex );
1049     if(!parseDat(num)) return FALSE;
1050
1051     QString str_text = m_resDatVec[ num ].body;
1052
1053     for ( QStringList::iterator it = stlist.begin(); it != stlist.end(); ++it )
1054     {
1055
1056         QRegExp regexp( (*it) );
1057         regexp.setCaseSensitive( FALSE );
1058
1059         if ( checkOR )
1060         { /* OR */
1061             if ( str_text.find( regexp, 0 ) != -1 )
1062             {
1063                 return TRUE;
1064             }
1065         }
1066         else
1067         { /* AND */
1068             if ( str_text.find( regexp, 0 ) == -1 ) return FALSE;
1069         }
1070     }
1071
1072     if ( checkOR ) return FALSE;
1073
1074     return TRUE;
1075 }
1076
1077 /* public */
1078 bool DatInfo::isMarked( int num )
1079 {
1080     QMutexLocker locker( &m_mutex );
1081
1082     return m_resDatVec[ num ].marked;
1083 }
1084
1085
1086 /* public */
1087 void DatInfo::setMark( int num, bool mark )
1088 {
1089     QMutexLocker locker( &m_mutex );
1090
1091     m_resDatVec[ num ].marked = mark;
1092 }
1093
1094
1095
1096
1097 /*--------------------------------*/
1098 /* abone functions                */
1099
1100
1101 /*-----------------------*/
1102 /* reset abone.          */
1103
1104 /* call this when config
1105    of abone changed.     */  /* public */
1106
1107 void DatInfo::resetAbone()
1108 {
1109     QMutexLocker locker( &m_mutex );
1110
1111     return resetAbonePrivate();
1112 }
1113
1114 /* private */
1115 void DatInfo::resetAbonePrivate()
1116 {
1117     for ( int i = 1; i <  (int) m_resDatVec.size(); i++ ) m_resDatVec[ i ].checkAbone = FALSE;
1118
1119     m_aboneByID = ( !KitaConfig::aboneIDList().empty() );
1120     m_aboneByName = ( !KitaConfig::aboneNameList().empty() );
1121     m_aboneByBody = ( !KitaConfig::aboneWordList().empty() );
1122     m_aboneByRes = m_aboneByID | m_aboneByName | m_aboneByBody;
1123 }
1124
1125
1126 /*--------------*/
1127 /* check abone  */ /* public */
1128
1129 bool DatInfo::checkAbone(int num)
1130 {
1131     QMutexLocker locker( &m_mutex );
1132
1133     return checkAbonePrivate(num);
1134 }
1135
1136
1137 /* private */
1138 bool DatInfo::checkAbonePrivate(int num)
1139 {
1140     if(!parseDat(num)) return FALSE;
1141
1142     if ( m_resDatVec[ num ].checkAbone ) return m_resDatVec[ num ].abone;
1143
1144     m_resDatVec[ num ].checkAbone = TRUE;
1145     bool checktmp = FALSE;
1146
1147     if( m_aboneByID )
1148         checktmp = checkAboneCore(m_resDatVec[ num ].id,KitaConfig::aboneIDList());
1149
1150     if(!checktmp && m_aboneByName )
1151         checktmp = checkAboneCore(m_resDatVec[ num ].parsedName,KitaConfig::aboneNameList());
1152
1153     if(!checktmp && m_aboneByBody )
1154         checktmp = checkAboneCore(m_resDatVec[ num ].body,KitaConfig::aboneWordList());
1155
1156     if(!checktmp && m_aboneByRes )
1157     {
1158         setAncList(num);
1159         AncList& anclist = m_resDatVec[ num ].anclist;
1160
1161         for ( AncList::iterator it = anclist.begin();
1162                 it != anclist.end() && !checktmp ; ++it )
1163         {
1164
1165             int refNum = (*it).from;
1166             int refNum2 = (*it).to;
1167
1168             /* I don't want to enter loop... */
1169             if ( refNum >= num ) continue;
1170             if ( refNum2 >= num ) refNum2 = num - 1;
1171
1172             for (int  i = refNum; i <= refNum2; i++ )
1173             {
1174                 if (checkAbonePrivate(i))
1175                 {
1176                     checktmp = TRUE;
1177                     break;
1178                 }
1179             }
1180         }
1181     }
1182
1183     m_resDatVec[ num ].abone = checktmp;
1184
1185     return m_resDatVec[ num ].abone;
1186 }
1187
1188 /* private */
1189 bool DatInfo::checkAboneCore(const QString& str, QStringList& strlist)
1190 {
1191     if(strlist.count())
1192     {
1193
1194         int i;
1195         for ( QStringList::iterator it = strlist.begin();
1196                 it != strlist.end(); ++it )
1197         {
1198             i = str.find( ( *it ) );
1199             if ( i != -1 )
1200             {
1201                 return TRUE;
1202             }
1203         }
1204     }
1205
1206     return FALSE;
1207 }
1208
1209
1210
1211 /*---------------------------------------*/
1212 /* simple parsing function               */
1213
1214 /* This function parses struct RESDAT by
1215    ParseMisc::parseResDat. In short, this
1216    splits the raw date into name, id,
1217    date, and bodytext, etc.
1218  
1219    Note that this function keeps data as
1220    the raw data. So, you need to parse them
1221    later by getPlainBody, getHtml,
1222    getDomElement, etc. 
1223                                          */ /* private */
1224 bool DatInfo::parseDat( int num )
1225 {
1226
1227     if ( num <= 0 ||  (int) m_resDatVec.size() <= num ) return FALSE;
1228     if( m_resDatVec[ num ].parsed ) return TRUE;
1229
1230     QString tmpstr;
1231     if( !ParseMisc::parseResDat( m_resDatVec[ num ], tmpstr ) ) return FALSE;
1232
1233     /* get subject */
1234     if ( m_subject == QString::null && tmpstr != QString::null )
1235     {
1236         m_subject = tmpstr;
1237         Kita::Thread::setName( m_datURL.prettyURL(), m_subject );
1238     }
1239
1240     return TRUE;
1241 }