OSDN Git Service

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