OSDN Git Service

refactoring & ID popup
[kita/kita.git] / kita / src / libkita / datmanager.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 "datmanager.h"
12 #include "qcp932codec.h"
13 #include "comment.h"
14 #include "access.h"
15 #include "../part/kitaconfig.h"
16 #include "../part/kita-utf8.h"
17 #include <qregexp.h>
18 #include <qsemaphore.h>
19
20 using namespace Kita;
21
22 /* UTF-16 */
23 #define UTF16_BRACKET  0xFF1E /* > */
24 #define UTF16_0 0xFF10 /* 0 */
25 #define UTF16_9 0xFF19 /* 9 */
26 #define UTF16_EQ 0xFF1D /* = */
27 #define UTF16_COMMA 0xFF0C /* , */
28
29 #define KITA_RESDIGIT 4
30
31 /*---------------------------------------------------*/
32
33 /* DatInfo stores information of *.dat */
34
35 DatInfo::DatInfo( const KURL& url ) : m_url( url )
36 {
37
38     QString idstr;
39     KURL logurl = url;
40     logurl.setProtocol( "k2ch" );
41     QString rawdata = Kita::Access::getCacheData( logurl );
42     m_linedat.clear();
43     m_subject = QString::null;
44     m_maxNum = 0;
45     m_spacestr = QString::null;
46     m_anclistmap.clear();    
47
48     if ( rawdata.length() ) {
49         QCp932Codec codec;
50         QString linedata = codec.toUnicode( rawdata );
51
52         QStringList lines = QStringList::split( "\n", linedata );
53         for ( QStringList::iterator it = lines.begin(); it != lines.end(); ++it, ++m_maxNum ) {
54
55             /* copy dat */
56             m_linedat += ( *it );
57
58             /* get id & copy */
59             idstr = "none";
60             QStringList list = QStringList::split( "<>", ( *it ), true );
61             if ( list.size() == 5 ) {
62                 /* copied from Comment::parseDateId */
63                 QRegExp regexp( "(\\d\\d)/(\\d\\d)/(\\d\\d) (\\d\\d):(\\d\\d)( ID:(.*))?" );
64                 if ( regexp.search( list[ 2 ] ) != -1 ) idstr = regexp.cap( 7 );
65
66                 /* get subject */
67                 if ( m_subject == QString::null && list[ 4 ] != QString::null ) m_subject = list[ 4 ];
68             }
69             m_idlist += idstr;
70         }
71     }
72 }
73
74
75 const QString& DatInfo::getDat( int num )
76 {
77
78     if ( num > m_maxNum ) return QString::null;
79
80     if ( m_linedat.at( num - 1 ) == m_linedat.end() ) return QString::null;
81
82     return *( m_linedat.at( num - 1 ) );
83 }
84
85
86 const QString& DatInfo::getId( int num )
87 {
88
89     if ( num > m_maxNum ) return QString::null;
90
91     if ( m_idlist.at( num - 1 ) == m_linedat.end() ) return QString::null;
92
93     return *( m_idlist.at( num - 1 ) );
94 }
95
96 /* convert dat to HTML */
97 QString DatInfo::getHtml(int num){
98
99     QString retstr = QString::null;
100     QString line = getDat(num);
101     bool showAddr = KitaConfig::showMailAddress();
102     
103     if(line != QString::null){
104
105         Kita::Comment comment( line );
106
107         /* check abone */
108         if(DatManager::checkAbone(getId(num),ABONECHK_BY_ID)) return retstr;
109         if(DatManager::checkAbone(comment.getName(),ABONECHK_BY_NAME)) return retstr;
110         if(DatManager::checkAbone(comment.getBody(),ABONECHK_BY_WORD)) return retstr;
111
112         retstr = comment.toHtml( num, showAddr );
113     }
114
115     return retstr;
116 }
117
118
119 /* Check if No.num has anchors to No.target */
120 /* For exsample, if No.num has an anchor ">>target",
121    then return TRUE.                               */
122 bool DatInfo::checkRes(const int target, const int num )
123 {
124     AncList anclist;
125     AncListMap::Iterator ancmapit = m_anclistmap.find(num);
126
127     /* If map is not set, then do parsing */
128     if(ancmapit == m_anclistmap.end() ){
129
130         QString line = getDat(num);
131         if(line == QString::null) return FALSE;
132
133         ANCNUM anctmp;
134         QString linkstr;
135         int refNum[2];
136         unsigned int pos;
137
138         /* dummy */
139         anclist.clear();
140         anctmp.from = 0;
141         anctmp.to = 0;
142         anclist += anctmp;
143
144         /* remove HTML tags */
145         QRegExp rex( "<[^>]*>" );
146         line.remove(rex);
147
148         const QChar *chpt = line.unicode();
149         unsigned int i;
150         unsigned int length = line.length();
151
152         for ( i = 0 ; i < length ; i++ ) {
153
154             if( chpt[ i ].unicode() == UTF16_BRACKET ||
155                 (chpt[ i] == '&' && chpt[ i+1] == 'g' && chpt[ i+2] == 't' && chpt[ i+3] == ';')){
156
157                 while(DatManager::parseResAnchor(chpt+i,length-i,linkstr,refNum,pos)){
158                     if(refNum[1] < refNum[0]) refNum[1] = refNum[0];
159                     anctmp.from = refNum[0];
160                     anctmp.to = refNum[1];
161                     anclist += anctmp;
162                     i += pos;
163                 }
164
165                 i += (pos-1);
166             }
167         }
168
169         ancmapit = m_anclistmap.insert(num,anclist);
170     }
171     /* end parsing   */
172     
173     for ( AncList::iterator it = (*ancmapit).begin(); it != (*ancmapit).end(); ++it ){
174         if ( target >= (*it).from && target <= (*it).to ) return TRUE;
175     }
176     
177     return FALSE;
178 }
179
180
181 /* Get HTML document of res tree.*/
182 /* For example,
183
184 >>1
185 |-->>4
186 |  |--->>10
187 |
188 |-->>20
189
190 */
191 QString DatInfo::getTreeByRes(const int rootnum, int& num, QString prestr )
192 {
193     QString retstr = QString::null ;    
194     num = 0;
195     int maxNum = getMaxResNumber();
196     QStringList strlists;
197
198     if(m_spacestr == QString::null){
199         QTextCodec * codec = QTextCodec::codecForName( "utf8" );
200         m_spacestr = ". ";
201         m_framestr1 = codec->toUnicode( KITAUTF8_FRAME1 ); /* |  */
202         m_framestr2 = codec->toUnicode( KITAUTF8_FRAME2 ); /* |- */
203         m_framestr3 = codec->toUnicode( KITAUTF8_FRAME3 ); /* L  */
204     }
205
206     /* find responses that refer to rootnum */
207     for ( int i = rootnum+1; i <= maxNum; i++ ){
208         if(checkRes(rootnum,i)){
209             num ++;
210             strlists += QString().setNum( i );
211         }
212     }
213
214     /* make HTML document */
215     if(num){
216         
217         for ( QStringList::iterator it = strlists.begin(); it != strlists.end(); ++it ){
218             QString tmpstr;
219             if((*it) == strlists.last()) tmpstr = m_framestr3;
220             else tmpstr = m_framestr2;
221             retstr += prestr + tmpstr + "<a href=\"#" + (*it) + "\">&gt;&gt;" + (*it) + "</a><br>";
222
223             /* call myself recursively */
224             if((*it) == strlists.last()) tmpstr = prestr + m_spacestr + m_spacestr + m_spacestr;
225             else tmpstr = prestr + m_framestr1  + m_spacestr;
226             int tmpnum;
227             retstr += getTreeByRes((*it).toInt(),tmpnum,tmpstr);
228             num += tmpnum;
229         }
230     }
231
232     return retstr;
233 }
234
235
236 /*---------------------------------------------------*/
237
238 /* DatManager manages all information about *.dat. */
239
240 DatInfoList DatManager::m_datInfo;
241 QSemaphore DatManager::m_semap( 1 );
242
243 /* private */
244 /* This function searches instance of DatInfo specified by url.
245    If instance does not exist, create instance. */
246 DatInfo* DatManager::getDatInfo( const KURL& url )
247 {
248
249     m_semap++;
250
251     if ( url.isEmpty() ) {
252         m_semap--;
253         return NULL;
254     }
255
256     int i = 0;
257     DatInfoList::Iterator it;
258
259     /* search */
260     if ( m_datInfo.count() ) {
261         for ( it = m_datInfo.begin(); it != m_datInfo.end(); ++it, i++ ) {
262
263             if ( url.host() == ( *it ) ->url().host() &&
264                     url.path() == ( *it ) ->url().path() ) {
265
266                 /* LRU */
267                 if ( i ) {
268                     m_datInfo.remove( it );
269                     m_datInfo.prepend( ( *it ) );
270                 }
271
272                 m_semap--;
273                 return ( *it );
274             }
275         }
276     }
277
278     /* not found */
279
280     /*create new DatInfo and insert it into list. */
281     KURL daturl = url.protocol() + "://" + url.host() + url.path();
282
283     DatInfo* datInfo;
284     datInfo = new DatInfo( daturl );
285     if ( datInfo->getMaxResNumber() == 0 ) { /* cache does not exist */
286         delete( datInfo );
287
288         m_semap--;
289         return NULL;
290     }
291
292     m_datInfo.prepend( datInfo );
293
294     /* delete the last item of list */
295     if ( m_datInfo.count() > DMANAGER_MAXQUEUE ) {
296         it = m_datInfo.fromLast();
297         m_datInfo.remove( it );
298
299         delete( *it );
300     }
301
302     m_semap--;
303     return datInfo;
304 }
305
306
307 /* public */
308 void DatManager::deleteDat( const KURL& url )
309 {
310     m_semap++;
311
312     DatInfoList::Iterator it;
313
314     for ( it = m_datInfo.begin(); it != m_datInfo.end(); ++it ) {
315         if ( url.host() == ( *it ) ->url().host() &&
316                 url.path() == ( *it ) ->url().path() ) {
317             m_datInfo.remove( it );
318             delete( *it );
319
320             m_semap--;
321             return ;
322         }
323     }
324
325     m_semap--;
326 }
327
328
329 /* public */
330 const QString& DatManager::getDat( const KURL& url, int num )
331 {
332     DatInfo * datInfo = getDatInfo( url );
333     if ( datInfo == NULL ) return QString::null;
334
335     return datInfo->getDat( num );
336 }
337
338 /* public */
339 const QString& DatManager::getSubject( const KURL& url )
340 {
341     DatInfo * datInfo = getDatInfo( url );
342     if ( datInfo == NULL ) return QString::null;
343
344     return datInfo->getSubject();
345 }
346
347
348 /* public */
349 int DatManager::getNumByID( const KURL& url, const QString& strid )
350 {
351
352     DatInfo * datInfo = getDatInfo( url );
353     if ( datInfo == NULL ) return 0;
354
355     int num = 0;
356     int maxNum = datInfo->getMaxResNumber();
357
358     for ( int i = 1; i <= maxNum; i++ ) {
359         if ( datInfo->getId( i ) == strid ) num ++;
360     }
361
362     return num;
363 }
364
365
366 /* public */
367 int DatManager::getMaxResNumber( const KURL& url )
368 {
369     DatInfo * datInfo = getDatInfo( url );
370     if ( datInfo == NULL ) return 0;
371
372     return datInfo->getMaxResNumber();
373 }
374
375
376 /* public */
377 QString DatManager::getHtml( const KURL& url, int startnum, int endnum )
378 {
379
380     DatInfo * datInfo = getDatInfo( url );
381     if ( datInfo == NULL ) return QString::null;
382
383     QString retstr = QString::null ;
384
385     for(int num = startnum; num <= endnum; num++)
386         retstr += datInfo->getHtml(num);
387
388     return retstr;
389 }
390
391
392 /* public */ /* This function is also called by kitadomtree. */
393 bool DatManager::checkAbone( const QString& str, int mode )
394 {
395
396     QStringList & strlist = KitaConfig::aboneIDList();
397     switch ( mode ) {
398     case ABONECHK_BY_ID: break;
399     case ABONECHK_BY_NAME: strlist = KitaConfig::aboneNameList(); break;
400     case ABONECHK_BY_WORD: strlist = KitaConfig::aboneWordList(); break;
401     default: return FALSE;
402     }
403
404     int i = -1;
405     for ( QStringList::iterator it = strlist.begin();
406             it != strlist.end(); ++it ) {
407         i = str.find( ( *it ) );
408         if ( i != -1 ) {
409             return TRUE;
410         }
411     }
412
413     return FALSE;
414 }
415
416
417 /* public */
418 QString DatManager::getHtmlByID(const KURL& url, const QString& strid, int &num ){
419
420     DatInfo* datInfo = getDatInfo(url);
421     if(datInfo == NULL) return QString::null;
422
423     QString retstr = QString::null ;    
424     num = 0;
425     int maxNum = datInfo->getMaxResNumber();
426
427     for ( int i = 1; i <= maxNum; i++ ) {
428         if(datInfo->getId(i) == strid){
429             num ++;
430             retstr += datInfo->getHtml(i);
431         }
432     }
433
434     return retstr;
435 }
436
437
438 /* public */
439 bool DatManager::checkID(const KURL& url, const QString& strid, int num ){
440
441     DatInfo* datInfo = getDatInfo(url);
442     if(datInfo == NULL) return FALSE;
443     
444     if(datInfo->getId(num) == strid) return TRUE;
445
446     return FALSE;
447 }
448
449
450 /* check keywords */ /* public */
451 bool DatManager::checkWord(const KURL& url,
452                             QStringList& stlist, int num,
453                             bool checkOR /* AND or OR search */
454     )
455 {
456     DatInfo* datInfo = getDatInfo(url);
457     if(datInfo == NULL) return FALSE;
458
459     QString str_text = datInfo->getDat(num);
460     if(str_text == QString::null) return FALSE;
461     
462     for ( QStringList::iterator it = stlist.begin(); it != stlist.end(); ++it ) {
463
464         if ( checkOR ) { /* OR */
465             if ( str_text.find( ( *it ), 0, FALSE ) != -1 ) {
466                 return TRUE;
467             }
468         } else { /* AND */
469             if ( str_text.find( ( *it ), 0, FALSE ) == -1 ) return FALSE;
470         }
471     }
472
473     if ( checkOR ) return FALSE;
474
475     return TRUE;
476 }
477
478
479 /*------------------------------------------*/
480 /* parsing function for anchor (>>number)   */
481 /*------------------------------------------*/ /* public */
482
483 /* This fuction parses res-anchor.                          
484
485    For example, if cdat = "&gt;12-20", then
486
487    linkstr = ">12-20",
488    refNum[0] = 12,
489    refNum[1] = 20,
490    pos = 9.
491
492    This function is also called in KitaDomTree::createResAnchor. */
493
494 bool DatManager::parseResAnchor(
495
496     /* input */
497     const QChar *cdat, const unsigned int length,
498
499     /* output */
500     QString& linkstr, int* refNum, unsigned int& pos ){
501
502     struct LocalFunc {
503         static bool isHYPHEN( unsigned short c )
504         {
505
506             /* UTF-16 */
507             if ( c == '-'
508                     || ( c >= 0x2010 && c <= 0x2015 )
509                     || ( c == 0x2212 )
510                     || ( c == 0xFF0D )      /* UTF8: 0xEFBC8D */
511                ) {
512                 return TRUE;
513             }
514
515             return FALSE;
516         }
517     };
518
519     bool ret = FALSE;
520     int i;
521     
522     linkstr = QString::null;
523     refNum[0] = 0;
524     refNum[1] = 0;    
525     pos = 0;
526
527     /* check '>' twice */
528     for ( i = 0;i < 2;i++ ) {
529         
530         if ( cdat[ pos ].unicode() == UTF16_BRACKET ) {
531             linkstr += cdat[ pos ];
532             pos++;
533         } else if ( cdat[ pos ] == '&' && cdat[ pos + 1 ] == 'g'  /* &gt; */
534                     && cdat[ pos + 2 ] == 't' && cdat[ pos + 3 ] == ';' ) {
535             linkstr += ">";
536             pos += 4;
537         }
538
539     }
540
541     /* check ',' */
542     if ( !pos ) {
543         if ( cdat[ pos ] == ',' || cdat[ pos ].unicode() == UTF16_COMMA ) {
544             linkstr += ",";
545             pos ++;
546         }
547     }
548
549     /* check '=' */
550     if ( !pos ) {
551         if ( cdat[ pos ] == '=' || cdat[ pos ].unicode() == UTF16_EQ ) {
552             linkstr += "=";
553             pos ++;
554         }
555     }
556
557     /* check numbers */
558     int hyphen = 0;
559
560     if( pos ){
561     
562         for ( i = 0 ; i < KITA_RESDIGIT + 1 && pos < length ; i++, pos++, ret = TRUE ) {
563
564             unsigned short c = cdat[ pos ].unicode();
565
566             if ( ( c < UTF16_0 || c > UTF16_9 )
567                  && ( c < '0' || c > '9' )
568                  && ( !LocalFunc::isHYPHEN( c )
569                       || ( i == 0 && LocalFunc::isHYPHEN( c ) )
570                       || ( hyphen && LocalFunc::isHYPHEN( c ) ) )
571                 ) break;
572
573             linkstr += cdat[ pos ];
574
575             if ( LocalFunc::isHYPHEN( c ) ) {
576                 hyphen = 1;
577                 i = -1;
578             } else {
579                 if ( c >= UTF16_0 ) c = '0' + cdat[ pos ].unicode() - UTF16_0;
580                 refNum[hyphen] *= 10;
581                 refNum[hyphen] += c - '0';
582             }
583         }
584     }
585
586     return ret;
587 }
588
589 /* public */
590 bool DatManager::checkRes(const KURL& url,const int target,const int num )
591 {
592     DatInfo* datInfo = getDatInfo(url);
593     if(datInfo == NULL) return FALSE;
594
595     return datInfo->checkRes(target,num);
596 }
597
598
599 /* public */
600 QString DatManager::getTreeByRes(const KURL& url, const int resNum, int &num ){
601
602     DatInfo* datInfo = getDatInfo(url);
603     if(datInfo == NULL) return QString::null;
604
605     QString retstr = QString::null ;    
606     num = 0;
607
608     QString tmp = QString().setNum( resNum );
609     retstr = "<a href=\"#" + tmp + "\">&gt;&gt;" + tmp + "</a><br>";
610     retstr += datInfo->getTreeByRes(resNum,num,"");
611
612     return retstr;
613 }
614