OSDN Git Service

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