OSDN Git Service

>>530
[kita/kita.git] / kita / src / libkita / boardmanager.cpp
1 /***************************************************************************
2 *   Copyright (C) 2004 by Kita Developers                                 *
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 "boardmanager.h"
12 #include "qcp932codec.h"
13 #include "downloadmanager.h"
14 #include "cache.h"
15 #include "favoritethreads.h"
16 #include "thread.h"
17 #include "threadinfo.h"
18 #include "favoriteboards.h"
19 #include "kita_misc.h"
20 #include "threadindex.h"
21
22 #include <config.h>  /* VERSION */
23
24 #include <kdebug.h>
25 #include <kio/netaccess.h>
26 #include <kfilterdev.h>
27 #include <kio/slaveconfig.h>
28 #include <kdeversion.h>
29
30 #include <qfile.h>
31 #include <qdir.h>
32 #include <qregexp.h>
33 #include <qdatetime.h>
34 #include <qeucjpcodec.h>
35
36 using namespace Kita;
37
38
39 /*---------------------------------------------------------*/
40
41 /* BoardData */
42
43 BoardData::BoardData( const QString& boardName,
44                       const QString& hostname,
45                       const QString& rootPath,
46                       const QString& delimiter,
47                       const QString& bbsPath,
48                       const QString& ext,
49                       int boardtype )
50 {
51     m_readIdx = FALSE;
52     m_boardName = boardName;
53     m_rootPath = rootPath;
54     m_delimiter = delimiter;
55     m_bbsPath = bbsPath;
56     m_ext = ext;
57     m_type = boardtype;
58
59     /* set hostname and create URL of board */
60     setHostName( hostname );
61
62     /* create default key */
63     QStringList keyHosts = m_hostname;    
64     createKeys( keyHosts );
65
66     /* reset SETTING.TXT */
67     setSettingLoaded( FALSE );
68 }
69
70 BoardData::~BoardData()
71 {}
72
73
74 /* public */
75 void BoardData::setHostName( const QString& hostName )
76 {
77     m_hostname = hostName;
78
79     /* m_basePath = (hostname)/(rootPath)/(bbsPath)/ */
80     m_basePath = m_hostname + m_rootPath + m_bbsPath + "/";
81
82     switch( m_type ){
83
84     case Board_MachiBBS: /* m_cgiBasePath = (hostname)/(rootPath)/(delimiter)?BBS=(bbsPath) */
85         m_cgiBasePath = m_hostname + m_rootPath + m_delimiter + "?BBS=" + m_bbsPath.mid( 1 );
86         break;
87
88     /* m_cgiBasePath = (hostname)/(rootPath)/(delimiter)/(bbsPath)/ */
89     default:
90         m_cgiBasePath = m_hostname +  m_rootPath + m_delimiter + m_bbsPath + "/";       
91         break;
92     }
93 }
94
95
96 /*---------------------------*/
97 /* information */
98
99 /* public */
100 const bool BoardData::readIdx() const
101 {
102     return m_readIdx;
103 }
104
105 /* public */
106 void BoardData::setReadIdx( bool idx )
107 {
108     m_readIdx = idx;
109 }
110
111 /* public */
112 const QString& BoardData::boardName() const
113 {
114     return m_boardName;
115 }
116
117 /* public */
118 const QString& BoardData::hostName() const
119 {
120     return m_hostname;
121 }
122
123 /* public */
124 const QString& BoardData::rootPath() const
125 {
126     return m_rootPath;
127 }
128
129 /* public */
130 const QString& BoardData::delimiter() const
131 {
132     return m_delimiter;
133 }
134
135 /* public */
136 const QString& BoardData::bbsPath() const
137 {
138     return m_bbsPath;
139 }
140
141 /* public */
142 const QString& BoardData::ext() const
143 {
144     return m_ext;
145 }
146
147 /* public */
148 const int BoardData::type() const
149 {
150     return m_type;
151 }
152
153 /* public */
154 const QString& BoardData::basePath() const
155 {
156     return m_basePath;
157 }
158
159 /* public */
160 const QString& BoardData::cgiBasePath() const
161 {
162     return m_cgiBasePath;
163 }
164
165
166 /*---------------------------*/
167 /* SETTING.TXT */
168
169 /* public */
170 const QString BoardData::settingURL() const
171 {
172     return m_basePath + "SETTING.TXT";
173 }
174
175 /* public */
176 const bool BoardData::settingLoaded() const
177 {
178     return m_settingLoaded;
179 }
180
181 /* public */
182 const QString& BoardData::defaultName() const
183 {
184     return m_defaultName;
185 }
186
187 /* public */
188 const int BoardData::lineNum() const
189 {
190     return m_linenum;
191 }
192
193 /* public */
194 const int BoardData::msgCount() const
195 {
196     return m_msgCount;
197 }
198
199 /* public */
200 const KURL& BoardData::titleImgURL() const
201 {
202     return m_titleImgURL;
203 }
204
205 /* public */
206 void BoardData::setSettingLoaded( bool set )
207 {
208     m_settingLoaded = set;
209     if( ! set ){
210         m_defaultName = QString::null;
211         m_linenum = 0;
212         m_msgCount = 0;
213         m_titleImgURL = QString::null; 
214     }
215 }
216
217 /* public */
218 void BoardData::setDefaultName( const QString& newName )
219 {
220     m_defaultName = newName;
221 }
222
223 /* public */
224 void BoardData::setLineNum( int newLine )
225 {
226     m_linenum = newLine;
227 }
228
229 /* public */
230 void BoardData::setMsgCount( int msgCount )
231 {
232     m_msgCount = msgCount;
233 }
234
235 /* public */
236 void BoardData::setTitleImgURL( const KURL& url )
237 {
238     m_titleImgURL = url;
239 }
240
241
242 /*---------------------------*/
243 /* keys */
244
245 /* create keys of DB */ /* public */
246 void BoardData::createKeys( const QStringList& keyHostList )
247 {
248     /* reset keys */
249     m_keyBasePathList.clear();
250     m_keyCgiBasePathList.clear();
251     m_keyHostList.clear();
252    
253     m_keyHostList = keyHostList;
254
255     /* m_basePath = (hostname)/(rootPath)/(bbsPath)/ */    
256     for( unsigned int i = 0; i < m_keyHostList.count(); ++i ){
257         if( m_keyHostList[i].length() > 0 )
258             m_keyBasePathList += m_keyHostList[i] + m_rootPath + m_bbsPath + "/";
259     }
260
261     switch( m_type ){
262
263     case Board_MachiBBS: /* m_cgiBasePath = (hostname)/(rootPath)/(delimiter)?BBS=(bbsPath) */
264         for( unsigned int i = 0; i < m_keyHostList.count(); ++i )
265             m_keyCgiBasePathList += m_keyHostList[i] + m_rootPath + m_delimiter
266                 + "?BBS=" + m_bbsPath.mid( 1 );
267         break;
268
269     /* m_cgiBasePath = (hostname)/(rootPath)/(delimiter)/(bbsPath)/ */
270     default:
271         for( unsigned int i = 0; i < m_keyHostList.count(); ++i )
272             m_keyCgiBasePathList += m_keyHostList[i] +  m_rootPath + m_delimiter + m_bbsPath + "/";
273         break;
274     }
275 }
276
277 /* public */
278 const QStringList& BoardData::keyHostList() const
279 {
280     return m_keyHostList;
281 }
282
283 /* public */
284 const QStringList& BoardData::keyBasePathList() const
285 {
286     return m_keyBasePathList;
287 }
288
289 /* public */
290 const QStringList& BoardData::keyCgiBasePathList() const
291 {
292     return m_keyCgiBasePathList;
293 }
294
295
296
297
298 /*---------------------------------------------------------------*/
299 /*---------------------------------------------------------------*/
300 /*---------------------------------------------------------------*/
301
302 /* BoardManager */
303
304 QCp932Codec* BoardManager::m_cp932Codec = NULL;
305 QEucJpCodec* BoardManager::m_eucJpCodec = NULL;
306 BoardDataList BoardManager::m_boardDataList;
307 BoardData* BoardManager::m_previousBoardData = NULL;
308
309
310 BoardManager::BoardManager()
311 {
312     clearBoardData();
313 }
314
315
316 BoardManager::~BoardManager()
317 {
318     clearBoardData();
319 }
320
321 /* (hostname)/(rootPath)/(bbsPath)/ */ /* public */ /* static */
322 const QString BoardManager::boardURL( const KURL& url )
323 {
324     BoardData* bdata = getBoardData( url );
325     if( bdata == NULL ) return QString::null;
326
327     return bdata->basePath();
328 }
329
330 /* public */ /* static */
331 const QStringList BoardManager::allBoardURLList()
332 {
333     QStringList urlList;
334     urlList.clear();
335
336     for ( BoardDataList::Iterator it = m_boardDataList.begin(); it != m_boardDataList.end(); ++it )
337         urlList += (*it)->basePath();
338     
339     return urlList;
340 }
341
342 /* (hostname)/(rootPath) */ /* public */ /* static */
343 const QString BoardManager::boardRoot( const KURL& url )
344 {
345     BoardData* bdata = getBoardData( url );
346     if( bdata == NULL ) return QString::null;
347
348     return bdata->hostName() + bdata->rootPath(); 
349 }
350
351 /* (bbspath) */ /* public */ /* static */
352 const QString BoardManager::boardPath( const KURL& url )
353 {
354     BoardData* bdata = getBoardData( url );
355     if( bdata == NULL ) return QString::null;
356
357     return bdata->bbsPath();
358 }
359
360 /* ID of board for writing */ /* public */ /* static */
361 const QString BoardManager::boardID( const KURL& url )
362 {
363     BoardData* bdata = getBoardData( url );
364     if( bdata == NULL ) return QString::null;
365
366     return bdata->bbsPath().mid( 1 ); /* remove "/" */
367 }
368
369
370 /* (hostname)/(rootPath)/(bbsPath)/subject.txt */ /* public */ /* static */
371 const QString BoardManager::subjectURL( const KURL& url )
372 {
373     BoardData* bdata = getBoardData( url );
374     if( bdata == NULL ) return QString::null;
375
376     return bdata->basePath() + "subject.txt";
377 }
378
379
380 /* public */ /* static */
381 const QString BoardManager::boardName( const KURL& url )
382 {
383     BoardData* bdata = getBoardData( url );
384     if( bdata == NULL ) return QString::null;
385
386     return bdata->boardName();
387 }
388
389
390 /* public */ /* static */
391 const int BoardManager::type( const KURL& url )
392 {
393     BoardData* bdata = getBoardData( url );
394     if( bdata == NULL ) return Board_Unknown;
395
396     return bdata->type();
397 }
398
399
400 /*---------------------------*/
401 /* ThreadList */
402
403
404 /*  get list of pointers of Thread classes.     */
405 /*
406   Input:
407
408   url:  URL of board.
409   oldLogs: If TRUE, search cache and get list of pointer of old threads.
410   online: online or offline mode.
411
412   Output:
413
414   threadList: list of pointers of Thread classes.
415   oldLogList: list of pointers of old threads.
416
417                                                  */ /* public */ /* static */
418 void BoardManager::getThreadList(
419
420     /* input */
421     const KURL& url,
422     bool oldLogs,
423     bool online,
424     
425     /* output */
426     QPtrList< Thread >& threadList,
427     QPtrList< Thread >& oldLogList )
428 {
429     threadList.clear();
430     oldLogList.clear();
431
432     /* get all obtained threads list from cache */
433     if ( url.prettyURL() == "http://virtual/obtained/" ) {
434
435         QStringList bbslist = allBoardURLList();
436
437         /* search all cache dirs */
438         for ( QStringList::iterator it = bbslist.begin() ; it != bbslist.end(); ++it ) {
439
440             getCachedThreadList( ( *it ), threadList );
441         }
442
443         return;
444     }
445
446     /*-------------------------*/
447
448     BoardData* bdata = getBoardData( url );
449     if( bdata == NULL ) return;
450     
451     /* download subject.txt */
452     if( online ){
453
454         /* make directory */
455         QString cacheDir = Cache::baseDir() + Cache::serverDir( url ) + Cache::boardDir( url );
456         if( !Kita::mkdir( cacheDir ) ) return;
457         
458         KIO::SlaveConfig::self() ->setConfigData( "http",
459                                                   url.host() ,
460                                                   "UserAgent",
461                                                   QString( "Monazilla/1.00 (Kita/%1)" ).arg( VERSION ) );
462         QString subjectPath = Cache::getSubjectPath( url );
463 #if KDE_IS_VERSION( 3, 2, 0 )
464         KIO::NetAccess::download( subjectURL( url ), subjectPath, NULL );
465 #else
466         KIO::NetAccess::download( subjectURL( url ), subjectPath );
467 #endif
468
469         /* download SETTING.TXT */
470         loadBBSSetting( url );
471     }
472
473     /* open and read subject.txt */
474     readSubjectTxt( url, threadList );
475
476     /* read idx file */
477     if( !bdata->readIdx() ){
478         getThreadInfoFromIdx( url, threadList );
479         bdata->setReadIdx( TRUE );
480     }
481     
482     /* get old logs */
483     if( oldLogs ){
484             
485         QPtrList< Thread > tmpList;
486         tmpList.clear();
487         getCachedThreadList( url, tmpList );
488
489         for ( unsigned i = 0; i < tmpList.count(); i++ ) {
490                 
491             if( threadList.contains( tmpList.at( i ) ) == 0 ) oldLogList.append( tmpList.at( i ) );
492         }
493     }
494 }
495
496
497 /* read idx file and get information  */  /* private */ /* static */
498 void BoardManager::getThreadInfoFromIdx( const KURL& url, QPtrList< Thread >& threadList )
499 {
500     QString cacheDir = Cache::baseDir() + Cache::serverDir( url ) + Cache::boardDir( url );
501     QDir d( cacheDir );    
502     if ( d.exists() ) {
503         qDebug( "readIndexFile: %s", cacheDir.ascii() );
504     
505         /* get all file names */
506         QString ext = BoardManager::getBoardData( url )->ext();
507         QStringList flist = d.entryList( "*" + ext );
508
509         unsigned int count = threadList.count();    
510         for ( unsigned i = 0; i < count; i++ ) {
511
512             Kita::Thread* thread = threadList.at( i ); 
513             QString fname = KURL( thread->datURL() ).filename();
514             if( flist.contains( fname ) ){
515
516                 int readNum = QMAX( ThreadIndex::getReadNum( thread->datURL() ), 1 );
517                 KitaThreadInfo::setReadNum( thread->datURL(), readNum );
518                 qDebug( "%s : %d",(const char*)thread->threadName().local8Bit(), readNum );
519             }
520         }
521     }
522 }
523
524
525
526 /* read the cache dir & get the list of all threads. */ /* private */ /* static */
527 void BoardManager::getCachedThreadList( const KURL& url, QPtrList< Thread >& threadList )
528 {
529     QString cacheDir = Cache::baseDir() + Cache::serverDir( url ) + Cache::boardDir( url );
530     QDir d( cacheDir );    
531     if ( d.exists() ) {
532     
533         /* get all file names */
534         QString ext = BoardManager::getBoardData( url )->ext();
535         QString boardURL = BoardManager::getBoardData( url )->basePath();
536         QStringList flist = d.entryList( "*" + ext );
537                 
538         for ( QStringList::iterator it = flist.begin(); it != flist.end(); ++it ) {
539             if ( ( *it ) == QString::null ) continue;
540
541             QString datURL = boardURL + "dat/" + ( *it );
542
543             /* read idx file */
544             if( Kita::Thread::getByURLNew( datURL ) == NULL ){
545                 
546                 QString subject = Kita::ThreadIndex::getSubject( datURL );
547                 if ( subject == QString::null ) subject = "?";
548                 Kita::Thread::setName( datURL, subject );
549
550                 int resNum = QMAX( 1, Kita::ThreadIndex::getResNum( datURL ) );
551                 KitaThreadInfo::setResNum( datURL, resNum );
552
553                 int readNum = QMAX( 1, Kita::ThreadIndex::getReadNum( datURL ) );
554                 KitaThreadInfo::setReadNum( datURL, readNum );
555             }
556             
557             Kita::Thread* thread = Kita::Thread::getByURL( datURL );
558             threadList.append( thread );
559         }
560     }
561 }
562
563
564
565 /* open subject.txt and get list of Thread classes */ /* private */ /* static */ 
566 bool BoardManager::readSubjectTxt( const KURL& url, QPtrList< Thread >& threadList )
567 {
568     QString subjectPath = Cache::getSubjectPath( url );
569     QIODevice * device = KFilterDev::deviceForFile( subjectPath, "application/x-gzip" );
570     if( !device->open( IO_ReadOnly ) ) return FALSE;
571
572     QTextStream stream( device );
573
574     if ( BoardManager::type( url ) == Board_JBBS ) {
575         if ( !m_eucJpCodec ) m_eucJpCodec = new QEucJpCodec();  
576         stream.setCodec( m_eucJpCodec );
577     } else {
578         if ( !m_cp932Codec ) m_cp932Codec = new QCp932Codec();  
579         stream.setCodec( m_cp932Codec );
580     }
581
582     // parse subject.txt(only one format...)
583     // FIXME: need to refactoring
584     QRegExp regexp;
585     switch ( BoardManager::type( url ) ) {
586
587     case Board_MachiBBS:
588     case Board_JBBS:
589         regexp.setPattern( "(\\d+\\.cgi),(.*)\\((\\d+)\\)" );
590         break;
591
592     default:
593         regexp.setPattern( "(\\d+\\.dat)<>(.*)\\((\\d+)\\)" );
594         break;
595     }
596     QString line;
597
598     while ( ( line = stream.readLine() ) != QString::null ) {
599         int pos = regexp.search( line );
600         if ( pos != -1 ) {
601             QString fname = regexp.cap( 1 );
602             QString subject = regexp.cap( 2 );
603             QString num = regexp.cap( 3 );
604
605             QString datURL = boardURL( url ) + "dat/" + fname;
606             Kita::Thread::setName( datURL, subject );
607             Kita::Thread* thread = Kita::Thread::getByURL( datURL );
608             KitaThreadInfo::setResNum( datURL, num.toInt() );
609             threadList.append( thread );
610         }
611     }
612
613     device->close();
614
615     return TRUE;
616 }
617
618
619 /*---------------------------*/
620 /* BoardData */
621
622 /* reset all BoardData */ /* public */ /* static */
623 void  BoardManager::clearBoardData()
624 {
625     for ( BoardDataList::Iterator it = m_boardDataList.begin(); it != m_boardDataList.end(); ++it )
626         delete( *it );
627
628     m_boardDataList.clear();
629     m_previousBoardData = NULL;
630 }
631
632 /* enroll board        */
633
634 /* Input: board, boardName
635    Output: oldURL
636    
637    If board is already enrolled, return Board_enrollEnrolled and oldURL is QString::null.
638    If board is new board, return Board_enrollNew and oldURL is QString::null.
639    If board is moved, return Board_enrollMoved, and oldURL is old URL.
640
641    Note that board is NOT enrolled when board is moved.
642    To enroll new URL, call BoardManager::moveBoard(). 
643 */
644 /* public */ /* static */
645 int BoardManager::enrollBoard( const KURL& url, const QString& boardName, QString& oldURL )
646 {
647 //    qDebug( "enroll %s %s", ( const char * ) boardName.local8Bit(),url.prettyURL().ascii());
648
649     QString hostname;
650     QString rootPath;
651     QString delimiter;
652     QString bbsPath;
653     QString ext;
654     int type = parseBoardURL( url, hostname, rootPath, delimiter, bbsPath, ext );
655     oldURL = QString::null;
656
657     if( type == Board_Unknown ) return Board_enrollFailed;
658     
659     /* check if the board is enrolled or moved. */
660     for ( BoardDataList::Iterator it = m_boardDataList.begin(); it != m_boardDataList.end(); ++it ){
661         
662         if( ( *it )->boardName() == boardName
663             && ( *it )->type() == type
664             && ( *it )->bbsPath() == bbsPath ){
665             
666             if( ( *it )->hostName() ==  hostname 
667                 && ( *it )->rootPath() == rootPath ){ /* enrolled */
668 //              qDebug( "enrolled" );
669                 return Board_enrollEnrolled;
670             }
671             else{ /* moved */
672 //              qDebug( "moved" );              
673                 oldURL = ( *it )->basePath();
674                 return Board_enrollMoved;
675             }
676         }
677     }
678
679     /* enroll new board */
680     BoardData* bdata = new BoardData( boardName, hostname, rootPath, delimiter, bbsPath, ext, type );
681     m_boardDataList.append( bdata );
682
683     return Board_enrollNew;
684 }
685
686
687 /* private */ /* static */
688 int BoardManager::parseBoardURL( const KURL& url,
689                                   QString& hostname,
690                                   QString& rootPath,
691                                   QString& delimiter,
692                                   QString& bbsPath,
693                                  QString& ext )
694 {
695     int type = Board_Unknown;
696     
697     hostname = url.protocol() + "://" + url.host();
698     rootPath = QString::null;
699     delimiter = QString::null;
700     bbsPath = QString::null;
701     ext = QString::null;
702
703     /* MACHI : http:// *.machi.to/(bbsPath)/ */
704     if( url.host().contains( "machi.to" ) ){  
705         
706         delimiter = "/bbs/read.pl";
707         bbsPath = url.filename();
708         ext = ".cgi";
709         type = Board_MachiBBS;
710     }
711     
712     /* JBBS : http://jbbs.livedoor.jp/(bbsPath)/ */
713     else  if( url.host().contains( "jbbs.livedoor.jp" ) ){ 
714         
715         delimiter = "/bbs/read.cgi";
716         bbsPath = url.prettyURL().remove( hostname );
717         type = Board_JBBS;
718         ext = ".cgi";
719     }
720
721     /* test for Flash CGI/Mini Thread  */
722     else if( url.host().contains( "oooug.jp" ) ){ 
723         
724         delimiter = "/test/read.cgi";
725         bbsPath = url.filename();
726         rootPath = url.prettyURL().remove( hostname + "/" ).remove( bbsPath + "/" );
727         if( rootPath.length() == 0 ) rootPath = QString::null;
728         ext = ".dat";
729         type = Board_FlashCGI;
730     }
731     
732     /* 2ch : http://(hostname)/(rootPath)/(bbsPath)/ */
733     else { 
734         
735         delimiter = "/test/read.cgi";
736         bbsPath = url.filename();
737         rootPath = url.prettyURL().remove( hostname + "/" ).remove( bbsPath + "/" );
738         if( rootPath.length() == 0 ) rootPath = QString::null;
739         ext = ".dat";
740         type = Board_2ch;
741     }
742
743     /* For example, if bbsPath = "linux/", then m_bbsPath = "/linux" */
744     const QRegExp exp( "/$" );
745     rootPath.remove( exp );
746     bbsPath.remove( exp );
747     if( rootPath != QString::null && rootPath.at( 0 ) != '/' ) rootPath = "/" + rootPath;
748     if( bbsPath != QString::null && bbsPath.at( 0 ) != '/' ) bbsPath = "/" + bbsPath;
749     
750 /*
751     qDebug( "host : %s", hostname.ascii() );
752     qDebug( "root : %s", rootPath.ascii() );
753     qDebug( "deli : %s", delimiter.ascii() );
754     qDebug( "bbs  : %s", bbsPath.ascii() );
755 */
756
757     return type;
758 }
759
760
761 /* public */ /* static */
762 bool BoardManager::isEnrolled( const KURL& url )
763 {
764     if( getBoardData( url ) == NULL ) return FALSE;
765     return TRUE;
766 }
767
768
769 /* public */ /* static */
770 BoardData* BoardManager::getBoardData( const KURL& url )
771 {
772     QString urlstr = url.prettyURL();
773     
774     if( m_previousBoardData != NULL ){
775         int count =  m_previousBoardData->keyBasePathList().count();
776         for( int i = 0; i < count ; ++i ){
777             if( urlstr.contains(  m_previousBoardData->keyBasePathList()[i] )
778                 || urlstr.contains(  m_previousBoardData->keyCgiBasePathList()[i] ) ){
779
780                 return m_previousBoardData;
781             }
782         }
783     }
784     
785     for ( BoardDataList::Iterator it = m_boardDataList.begin(); it != m_boardDataList.end(); ++it ){
786
787         int count = (*it)->keyBasePathList().count();
788         for( int i = 0; i < count ; ++i ){
789             if( urlstr.contains( (*it)->keyBasePathList()[i] )
790                 || urlstr.contains( (*it)->keyCgiBasePathList()[i] ) ){
791
792             m_previousBoardData = (*it);
793             return (*it);
794             }
795         }
796     }
797     
798     return NULL;
799 }
800
801
802
803 /*--------------------------------*/
804 /* BBSHISTORY */
805
806
807 /* load the bbs history file ( BBSHISTORY ), and create keys of Data Base.  */
808 /* Before calling this, enroll the board by enrollBoard().                  */ 
809 /*
810     ex) If the host of board moved like :
811
812     http:://aaa.com -> http://bbb.com -> http://ccc.com -> http://ddd.com
813     
814     then, BBSHISTORY is
815
816     http://ccc.com
817     http://bbb.com
818     http://aaa.com
819
820 */ /* public */ /* static */
821 bool BoardManager::loadBBSHistory( const KURL& url )
822 {
823     BoardData* bdata = getBoardData( url );
824     if( bdata == NULL ) return FALSE;
825
826     QStringList keyHosts = bdata->hostName();  
827     
828     QFile file( Cache::getBBSHistoryPath( url ) );
829     if ( file.open( IO_ReadOnly ) ){
830
831         qDebug( "read BBSHISTRY : %s",Cache::getBBSHistoryPath( url ).ascii() );
832
833         QTextStream ts( &file );
834
835         QString line;
836         while( !ts.eof() ){
837
838             line = ts.readLine();
839             qDebug("line: %s",line.ascii());
840             keyHosts += line;
841         }
842
843         bdata->createKeys( keyHosts );
844         file.close();
845
846         return TRUE;
847     }
848
849     return FALSE;
850 }
851
852
853 /* public */ /* static */
854 bool BoardManager::moveBoard( const KURL& fromURL, const KURL& toURL )
855 {
856     QString oldhost = fromURL.protocol() + "://" + fromURL.host();    
857     QString newhost = toURL.protocol() + "://" + toURL.host();
858
859     const QRegExp exp( "/$" );
860     QString oldURL = fromURL.prettyURL();
861     QString newURL = toURL.prettyURL();
862     oldURL.remove( exp );
863     newURL.remove( exp );
864     oldURL += "/";
865     newURL += "/";
866     
867     qDebug( "moveBoard : %s -> %s", oldURL.ascii(), newURL.ascii() );
868
869     if( oldURL == newURL ) return FALSE;
870     
871     /* Is oldURL enrolled? */
872     BoardData* bdata = getBoardData( oldURL ); 
873     if( bdata == NULL ){
874
875         /* Is newURL enrolled? */
876         bdata = getBoardData( newURL ); 
877         if( bdata == NULL ) return FALSE;
878     }
879
880
881     /*---------------------------*/
882     /* updata BoardData */
883
884     /* get the path of old cache */
885     bdata->setHostName( oldhost );
886     QStringList keyHosts = bdata->keyHostList();
887     keyHosts.remove( oldhost );
888     keyHosts.prepend( oldhost );
889     bdata->createKeys( keyHosts );    
890     QString oldCachePath = Cache::baseDir() + Cache::serverDir( bdata->basePath() )
891         + Cache::boardDir( bdata->basePath() );
892
893     /* update URL */
894     bdata->setHostName( newhost );
895     
896     /* update keys */
897     /* The order of keyHosts will be like this:
898        
899       newhost      
900       oldhost      
901       foohost1
902       foohost2
903       
904     */
905     keyHosts = bdata->keyHostList();
906     keyHosts.remove( oldhost );
907     keyHosts.prepend( oldhost );
908     keyHosts.remove( newhost );
909     keyHosts.prepend( newhost );
910     bdata->createKeys( keyHosts );
911
912     
913     /*---------------------------*/
914     /* move cache dir */
915     
916     QDir qdir;
917
918     /* mkdir new server dir */
919     QString newCachePath = Cache::baseDir() + Cache::serverDir( bdata->basePath() );    
920     Kita::mkdir( newCachePath );
921     
922     /* backup old dir */
923     newCachePath += Cache::boardDir( bdata->basePath() );    
924     if( qdir.exists ( newCachePath ) ){
925         QString bkupPath = newCachePath;
926         bkupPath.truncate( bkupPath.length() -1 ); /* remove '/' */
927         bkupPath += "." + QString().setNum( QDateTime::currentDateTime().toTime_t() );
928         qDebug( "mv %s\n -> %s", newCachePath.ascii(), bkupPath.ascii() );
929         qdir.rename( newCachePath, bkupPath  );
930     }
931
932     /* move cache dir */
933     qDebug( "old: %s", oldCachePath.ascii() );
934     if( qdir.exists( oldCachePath ) ){
935         qDebug( "move: %s\n-> %s", oldCachePath.ascii(), newCachePath.ascii() );        
936         qdir.rename( oldCachePath, newCachePath );
937     }
938     else Kita::mkdir( newCachePath );
939
940     /* make old dir */
941     if ( ! qdir.exists( oldCachePath ) ) {
942         Kita::mkdir( oldCachePath );
943         /* create BBS_MOVED */
944         QString movedPath = oldCachePath + "/BBS_MOVED";
945         QFile file( movedPath );
946         if ( file.open( IO_WriteOnly ) ) {
947             qDebug( "write BBS_MOVED : %s", movedPath.ascii() );
948             QTextStream stream( &file );
949             stream << newURL << endl;
950         }
951         file.close();
952     }
953
954     /*---------------------------*/
955     /* update BBSHISTRY */
956     
957     QFile file( Cache::getBBSHistoryPath( bdata->basePath() ) );
958     if ( file.open( IO_WriteOnly ) ){
959
960         qDebug( "write BBSHISTRY : %s", Cache::getBBSHistoryPath( bdata->basePath() ).ascii() );
961         QTextStream ts( &file );
962         
963         keyHosts.remove( newhost );
964         for ( QStringList::iterator it = keyHosts.begin() ; it != keyHosts.end(); ++it ) {
965             ts << (*it) << endl;
966         }
967
968         file.close();
969     }
970
971     
972     /*---------------------------*/
973     /* update other information */
974     FavoriteThreads::replace( oldURL, newURL );
975     Kita::Thread::replace( oldURL, newURL );
976     KitaThreadInfo::replace( oldURL, newURL );
977     Kita::FavoriteBoards::replace( oldURL, newURL );
978
979     qDebug("%s",bdata->basePath().ascii() );
980     qDebug("%s",bdata->cgiBasePath().ascii() );
981     int count =  bdata->keyBasePathList().count();
982     for( int i = 0; i < count ; ++i ){
983         qDebug("key: %s",bdata->keyBasePathList()[i].ascii() );
984         qDebug("key: %s",bdata->keyCgiBasePathList()[i].ascii() );                  
985     }
986
987     return TRUE;
988 }
989
990
991 /*--------------------------------*/
992 /* SETTING.TXT  */
993
994
995 /* public */ /* static */
996 bool BoardManager::loadBBSSetting( const KURL& url, bool reload )
997 {
998     /* Is board enrolled ? */
999     BoardData* bdata = getBoardData( url );
1000     if( bdata == NULL ) return FALSE;
1001     if( bdata->type() != Board_2ch ) return FALSE;
1002     
1003     KURL settingURL = bdata->settingURL();
1004     QString path = Cache::getSettingPath( url );
1005     
1006     /* now loading */
1007     if( DownloadManager::isLoadingNow( settingURL ) ) return FALSE;
1008
1009     /* already loaded */
1010     if( bdata->settingLoaded() && !reload ) return TRUE;
1011
1012     /* reset names, linenum, etc. */
1013     bdata->setSettingLoaded( FALSE );
1014
1015     qDebug("LOAD SETTING.TXT :  %s",settingURL.prettyURL().ascii());
1016     qDebug("path: %s",path.ascii());
1017     
1018     /* download SETTING.TXT.*/
1019     DownloadManager::download( settingURL, path );
1020     bdata->setSettingLoaded( TRUE );
1021     
1022     return TRUE;
1023 }
1024
1025
1026 /* public */ /* static */
1027 QString BoardManager::getBBSDefaultName( const KURL& url )
1028 {
1029     BoardData* bdata = openSettingTxt( url );
1030     if( bdata == NULL ) return QString::null;
1031     return bdata->defaultName();
1032 }
1033
1034
1035 /* public */ /* static */
1036 int BoardManager::getBBSMaxLine( const KURL& url )
1037 {
1038     BoardData* bdata = openSettingTxt( url );
1039     if( bdata == NULL ) return 0;
1040     return bdata->lineNum();
1041 }
1042
1043 /* public */ /* static */
1044 int BoardManager::getBBSMsgCount( const KURL& url )
1045 {
1046     BoardData* bdata = openSettingTxt( url );
1047     if( bdata == NULL ) return 0;
1048     return bdata->msgCount();
1049 }
1050
1051
1052 /* public */ /* static */
1053 const KURL BoardManager::titleImgURL( const KURL& url )
1054 {
1055     BoardData* bdata = openSettingTxt( url );
1056     if( bdata == NULL ) return QString::null;
1057     return bdata->titleImgURL();
1058 }
1059
1060 /* open local SETTING.TXT, then get names, linenum, etc. */ /* private */ /* static */
1061 BoardData* BoardManager::openSettingTxt( const KURL& url )
1062 {
1063     BoardData* bdata = getBoardData( url );
1064     if( bdata == NULL ) return NULL;
1065     if( !bdata->settingLoaded() ) return NULL;
1066     if( DownloadManager::isLoadingNow( bdata->settingURL() ) ) return NULL;
1067     if( bdata->defaultName() != QString::null ) return bdata;
1068
1069     qDebug( "read SETTING.TXT");
1070     
1071     QFile file( Cache::getSettingPath( url ) );
1072     if ( file.open( IO_ReadOnly ) ){
1073
1074         QTextStream ts( &file );
1075         if( m_cp932Codec == NULL) m_cp932Codec = new QCp932Codec();
1076         ts.setCodec( m_cp932Codec );
1077
1078         QString line;
1079         while( !ts.eof() ){
1080
1081             line = ts.readLine();
1082 //          qDebug("line: %s\n",(const char *)line.local8Bit());
1083
1084             /* default name */
1085             QString key = "BBS_NONAME_NAME=";
1086             if( line.find( key ) != -1 ) bdata->setDefaultName( line.remove( key ) );
1087
1088             /* 0ch type */
1089             key = "NANASI_NAME=";
1090             if( line.find( key ) != -1 ) bdata->setDefaultName( line.remove( key ) );
1091
1092             /* line number */
1093             key = "BBS_LINE_NUMBER=";
1094             if( line.find( key ) != -1 ) bdata->setLineNum( line.remove( key ).toInt() * 2 );
1095
1096             /* msg count */
1097             key = "BBS_MESSAGE_COUNT=";
1098             if( line.find( key ) != -1 ) bdata->setMsgCount( line.remove( key ).toInt() );
1099             
1100             /* title image */
1101             key = "BBS_TITLE_PICTURE=";
1102             if( line.find( key ) != -1 ){
1103                 QString path = line.remove( key );
1104                 KURL titleImgURL = KURL( bdata->basePath(), path );
1105                 bdata->setTitleImgURL( titleImgURL );
1106             }
1107         }
1108             
1109         file.close();
1110     }
1111
1112     if( bdata->defaultName() == QString::null ) bdata->setDefaultName( "(default name)" );
1113
1114     qDebug( "name: %s", (const char *)bdata->defaultName().local8Bit());
1115     qDebug( "line num: %d", bdata->lineNum() );
1116     
1117     return bdata;
1118 }