OSDN Git Service

>>564
[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* Kita::BoardManager::m_cp932Codec = NULL;
305 QEucJpCodec* Kita::BoardManager::m_eucJpCodec = NULL;
306 BoardDataList Kita::BoardManager::m_boardDataList;
307 BoardData* Kita::BoardManager::m_previousBoardData = NULL;
308 QString Kita::BoardManager::m_previousBoardURL;
309
310
311 BoardManager::BoardManager()
312 {
313     clearBoardData();
314 }
315
316
317 BoardManager::~BoardManager()
318 {
319     clearBoardData();
320 }
321
322 /* (hostname)/(rootPath)/(bbsPath)/ */ /* public */ /* static */
323 const QString BoardManager::boardURL( const KURL& url )
324 {
325     BoardData* bdata = getBoardData( url );
326     if( bdata == NULL ) return QString::null;
327
328     return bdata->basePath();
329 }
330
331 /* public */ /* static */
332 const QStringList BoardManager::allBoardURLList()
333 {
334     QStringList urlList;
335     urlList.clear();
336
337     for ( BoardDataList::Iterator it = m_boardDataList.begin(); it != m_boardDataList.end(); ++it )
338         urlList += (*it)->basePath();
339     
340     return urlList;
341 }
342
343 /* (hostname)/(rootPath) */ /* public */ /* static */
344 const QString BoardManager::boardRoot( const KURL& url )
345 {
346     BoardData* bdata = getBoardData( url );
347     if( bdata == NULL ) return QString::null;
348
349     return bdata->hostName() + bdata->rootPath(); 
350 }
351
352 /* (bbspath) */ /* public */ /* static */
353 const QString BoardManager::boardPath( const KURL& url )
354 {
355     BoardData* bdata = getBoardData( url );
356     if( bdata == NULL ) return QString::null;
357
358     return bdata->bbsPath();
359 }
360
361 /* ID of board for writing */ /* public */ /* static */
362 const QString BoardManager::boardID( const KURL& url )
363 {
364     BoardData* bdata = getBoardData( url );
365     if( bdata == NULL ) return QString::null;
366
367     return bdata->bbsPath().mid( 1 ); /* remove "/" */
368 }
369
370
371 /* (hostname)/(rootPath)/(bbsPath)/subject.txt */ /* public */ /* static */
372 const QString BoardManager::subjectURL( const KURL& url )
373 {
374     BoardData* bdata = getBoardData( url );
375     if( bdata == NULL ) return QString::null;
376
377     return bdata->basePath() + "subject.txt";
378 }
379
380
381 /* public */ /* static */
382 const QString BoardManager::boardName( const KURL& url )
383 {
384     BoardData* bdata = getBoardData( url );
385     if( bdata == NULL ) return QString::null;
386
387     return bdata->boardName();
388 }
389
390
391 /* public */ /* static */
392 const int BoardManager::type( const KURL& url )
393 {
394     BoardData* bdata = getBoardData( url );
395     if( bdata == NULL ) return Board_Unknown;
396
397     return bdata->type();
398 }
399
400
401 /*---------------------------*/
402 /* ThreadList */
403
404
405 /*  get list of pointers of Thread classes.     */
406 /*
407   Input:
408
409   url:  URL of board.
410   oldLogs: If TRUE, search cache and get list of pointer of old threads.
411   online: online or offline mode.
412
413   Output:
414
415   threadList: list of pointers of Thread classes.
416   oldLogList: list of pointers of old threads.
417
418                                                  */ /* public */ /* static */
419 void BoardManager::getThreadList(
420
421     /* input */
422     const KURL& url,
423     bool oldLogs,
424     bool online,
425     
426     /* output */
427     QPtrList< Thread >& threadList,
428     QPtrList< Thread >& oldLogList )
429 {
430     threadList.clear();
431     oldLogList.clear();
432
433     /* get all obtained threads list from cache */
434     if ( url.prettyURL() == "http://virtual/obtained/" ) {
435
436         QStringList bbslist = allBoardURLList();
437
438         /* search all cache dirs */
439         for ( QStringList::iterator it = bbslist.begin() ; it != bbslist.end(); ++it ) {
440
441             getCachedThreadList( ( *it ), threadList );
442         }
443
444         return;
445     }
446
447     /*-------------------------*/
448
449     BoardData* bdata = getBoardData( url );
450     if( bdata == NULL ) return;
451     
452     /* download subject.txt */
453     if( online ){
454
455         /* make directory */
456         QString cacheDir = Cache::baseDir() + Cache::serverDir( url ) + Cache::boardDir( url );
457         if( !Kita::mkdir( cacheDir ) ) return;
458         
459         KIO::SlaveConfig::self() ->setConfigData( "http",
460                                                   url.host() ,
461                                                   "UserAgent",
462                                                   QString( "Monazilla/1.00 (Kita/%1)" ).arg( VERSION ) );
463         QString subjectPath = Cache::getSubjectPath( url );
464 #if KDE_IS_VERSION( 3, 2, 0 )
465         KIO::NetAccess::download( subjectURL( url ), subjectPath, NULL );
466 #else
467         KIO::NetAccess::download( subjectURL( url ), subjectPath );
468 #endif
469
470         /* download SETTING.TXT */
471         loadBBSSetting( url );
472     }
473
474     /* open and read subject.txt */
475     readSubjectTxt( bdata, url, threadList );
476     
477     /* get old logs */
478     if( oldLogs ){
479             
480         QPtrList< Thread > tmpList;
481         tmpList.clear();
482         getCachedThreadList( url, tmpList );
483
484         for ( unsigned i = 0; i < tmpList.count(); i++ ) {
485                 
486             if( threadList.contains( tmpList.at( i ) ) == 0 ) oldLogList.append( tmpList.at( i ) );
487         }
488     }
489 }
490
491
492 /* read the cache dir & get the list of all threads. */ /* private */ /* static */
493 void BoardManager::getCachedThreadList( const KURL& url, QPtrList< Thread >& threadList )
494 {
495     QString cacheDir = Cache::baseDir() + Cache::serverDir( url ) + Cache::boardDir( url );
496     QDir d( cacheDir );    
497     if ( d.exists() ) {
498     
499         /* get all file names */
500         QString ext = BoardManager::getBoardData( url )->ext();
501         QString boardURL = BoardManager::getBoardData( url )->basePath();
502         QStringList flist = d.entryList( "*" + ext );
503                 
504         for ( QStringList::iterator it = flist.begin(); it != flist.end(); ++it ) {
505             if ( ( *it ) == QString::null ) continue;
506
507             QString datURL = boardURL + "dat/" + ( *it );
508
509             /* read idx file */
510             Kita::Thread* thread = Kita::Thread::getByURLNew( datURL );
511             if( thread == NULL ){
512
513                 thread = Kita::Thread::getByURL( datURL );
514                 if( thread == NULL ) continue;
515                 ThreadIndex::loadIndex( thread, datURL );
516             }
517
518             if( thread != NULL ) threadList.append( thread );
519         }
520     }
521 }
522
523
524
525 /* open subject.txt and get list of Thread classes */ /* private */ /* static */ 
526 bool BoardManager::readSubjectTxt( BoardData* bdata, const KURL& url, QPtrList< Thread >& threadList )
527 {
528     qDebug( "BoardManager::readSubjectTxt" );
529     
530     /* get all names of cached files to read idx.  */
531     QStringList cacheList;
532     if( !bdata->readIdx() ){
533         
534         QString cacheDir = Cache::baseDir() + Cache::serverDir( url ) + Cache::boardDir( url );
535         QDir d( cacheDir );    
536         if ( d.exists() ) {
537             QString ext = BoardManager::getBoardData( url )->ext();
538             cacheList = d.entryList( "*" + ext );
539         }
540     }
541
542     /* open subject.txt */
543     QString subjectPath = Cache::getSubjectPath( url );
544     QIODevice * device = KFilterDev::deviceForFile( subjectPath, "application/x-gzip" );
545     if( !device->open( IO_ReadOnly ) ) return FALSE;
546
547     QTextStream stream( device );
548
549     if ( BoardManager::type( url ) == Board_JBBS ) {
550         if ( !m_eucJpCodec ) m_eucJpCodec = new QEucJpCodec();  
551         stream.setCodec( m_eucJpCodec );
552     } else {
553         if ( !m_cp932Codec ) m_cp932Codec = new QCp932Codec();  
554         stream.setCodec( m_cp932Codec );
555     }
556
557     // parse subject.txt(only one format...)
558     // FIXME: need to refactoring
559     QRegExp regexp;
560     switch ( BoardManager::type( url ) ) {
561
562     case Board_MachiBBS:
563     case Board_JBBS:
564         regexp.setPattern( "(\\d+\\.cgi),(.*)\\((\\d+)\\)" );
565         break;
566
567     default:
568         regexp.setPattern( "(\\d+\\.dat)<>(.*)\\((\\d+)\\)" );
569         break;
570     }
571     QString line;
572
573     while ( ( line = stream.readLine() ) != QString::null ) {
574         int pos = regexp.search( line );
575         if ( pos != -1 ) {
576             QString fname = regexp.cap( 1 );
577             QString subject = regexp.cap( 2 );
578             QString num = regexp.cap( 3 );
579
580             /* get pointer of Thread class */
581             QString datURL = boardURL( url ) + "dat/" + fname;
582             Kita::Thread* thread = Kita::Thread::getByURL( datURL );
583             threadList.append( thread );
584
585             /* set thread name */           
586             thread->setThreadName( subject );
587
588             /* load index file */
589             if( !bdata->readIdx() ){
590
591                 if( cacheList.contains( fname ) ) ThreadIndex::loadIndex( thread, datURL );
592             }
593
594             /* update res num */
595             int newNum = num.toInt();
596             if( thread->readNum() ){ /* cache exists */
597                 int oldNum = thread->resNum();
598
599                 if( newNum > oldNum ){
600                     qDebug( "update ResNum old -> new : %d -> %d", oldNum, newNum );
601                     Kita::ThreadIndex::setResNum( datURL, newNum );
602                 }
603             }
604             thread->setResNum( newNum );
605         }
606     }
607
608     device->close();
609     bdata->setReadIdx( TRUE ); /* never read idx files again */
610
611     return TRUE;
612 }
613
614 /*---------------------------*/
615 /* BoardData */
616
617 /* reset all BoardData */ /* public */ /* static */
618 void  BoardManager::clearBoardData()
619 {
620     for ( BoardDataList::Iterator it = m_boardDataList.begin(); it != m_boardDataList.end(); ++it )
621         delete( *it );
622
623     m_boardDataList.clear();
624     m_previousBoardData = NULL;
625     m_previousBoardURL = QString::null;
626 }
627
628 /* enroll board        */
629
630 /* Input: board, boardName
631    Output: oldURL
632    
633    If board is already enrolled, return Board_enrollEnrolled and oldURL is QString::null.
634    If board is new board, return Board_enrollNew and oldURL is QString::null.
635    If board is moved, return Board_enrollMoved, and oldURL is old URL.
636
637    Note that board is NOT enrolled when board is moved.
638    To enroll new URL, call BoardManager::moveBoard(). 
639 */
640 /* public */ /* static */
641 int BoardManager::enrollBoard( const KURL& url, const QString& boardName, QString& oldURL )
642 {
643     int type = Board_Unknown;
644     
645     QString hostname;
646     QString rootPath;
647     QString delimiter;
648     QString bbsPath;
649     QString ext;
650     type = parseBoardURL( url, type, hostname, rootPath, delimiter, bbsPath, ext );
651     oldURL = QString::null;
652
653     if( type == Board_Unknown ) return Board_enrollFailed;
654     
655     /* check if the board is enrolled or moved. */
656     for ( BoardDataList::Iterator it = m_boardDataList.begin(); it != m_boardDataList.end(); ++it ){
657         
658         if( ( *it )->boardName() == boardName
659             && ( *it )->type() == type
660             && ( *it )->bbsPath() == bbsPath ){
661             
662             if( ( *it )->hostName() ==  hostname 
663                 && ( *it )->rootPath() == rootPath ){ /* enrolled */
664                 return Board_enrollEnrolled;
665             }
666             else{ /* moved */
667                 oldURL = ( *it )->basePath();
668                 return Board_enrollMoved;
669             }
670         }
671     }
672
673     /* enroll new board */
674     BoardData* bdata = new BoardData( boardName, hostname, rootPath, delimiter, bbsPath, ext, type );
675     m_boardDataList.append( bdata );
676
677     return Board_enrollNew;
678 }
679
680
681 /* parse board URL              */
682 /* output is board type.        */ /* private */ /* static */
683 int BoardManager::parseBoardURL(
684
685     /* input */
686     const KURL& url,
687     int type,  /* If type = Board_Unknown, type will be decided according to url. */
688
689     /* output */
690     QString& hostname,
691     QString& rootPath,
692     QString& delimiter,
693     QString& bbsPath,
694     QString& ext )
695 {
696     hostname = url.protocol() + "://" + url.host();
697     rootPath = QString::null;
698     delimiter = QString::null;
699     bbsPath = QString::null;
700     ext = QString::null;
701
702     /* decide type */
703     if( type == Board_Unknown ){
704         
705         if( url.host().contains( "machi.to" ) ) type = Board_MachiBBS;  
706         else if( url.host().contains( "jbbs.livedoor.jp" ) ) type = Board_JBBS;
707         else if( url.host().contains( "oooug.jp" ) ) type = Board_FlashCGI;     
708         else type = Board_2ch;
709     }
710
711     /* parse */
712     switch( type ){
713
714     case Board_MachiBBS:    /* MACHI : http:// *.machi.to/(bbsPath)/ */
715         
716         delimiter = "/bbs/read.pl";
717         bbsPath = url.filename();
718         ext = ".cgi";
719         break;
720
721     case Board_JBBS:  /* JBBS : http://jbbs.livedoor.jp/(bbsPath)/ */
722         
723         delimiter = "/bbs/read.cgi";
724         bbsPath = url.prettyURL().remove( hostname );
725         type = Board_JBBS;
726         ext = ".cgi";
727         break;
728
729     case Board_FlashCGI: /* test for Flash CGI/Mini Thread  */
730         
731         delimiter = "/test/read.cgi";
732         bbsPath = url.filename();
733         rootPath = url.prettyURL().remove( hostname + "/" ).remove( bbsPath + "/" );
734         if( rootPath.length() == 0 ) rootPath = QString::null;
735         ext = ".dat";
736         break;
737
738     default:  /* 2ch : http://(hostname)/(rootPath)/(bbsPath)/ */
739         
740         delimiter = "/test/read.cgi";
741         bbsPath = url.filename();
742         rootPath = url.prettyURL().remove( hostname + "/" ).remove( bbsPath + "/" );
743         if( rootPath.length() == 0 ) rootPath = QString::null;
744         ext = ".dat";
745         type = Board_2ch;
746         break;
747     }
748
749     /* For example, if bbsPath = "linux/", then m_bbsPath = "/linux" */
750     const QRegExp exp( "/$" );
751     rootPath.remove( exp );
752     bbsPath.remove( exp );
753     if( rootPath != QString::null && rootPath.at( 0 ) != '/' ) rootPath = "/" + rootPath;
754     if( bbsPath != QString::null && bbsPath.at( 0 ) != '/' ) bbsPath = "/" + bbsPath;
755
756     return type;
757 }
758
759
760 /* public */ /* static */
761 bool BoardManager::isEnrolled( const KURL& url )
762 {
763     if( getBoardData( url ) == NULL ) return FALSE;
764     return TRUE;
765 }
766
767
768 /* public */ /* static */
769 BoardData* BoardManager::getBoardData( const KURL& url )
770 {
771     if( url.isEmpty() ) return NULL;
772     QString urlstr = url.prettyURL();
773
774     /* cache */
775     if( m_previousBoardData != NULL && m_previousBoardURL == urlstr ) return m_previousBoardData;
776     
777 //    qDebug( "getBoardData %s", urlstr.ascii());
778     
779     for ( BoardDataList::Iterator it = m_boardDataList.begin(); it != m_boardDataList.end(); ++it ){
780
781         int count = (*it)->keyBasePathList().count();
782         for( int i = 0; i < count ; ++i ){
783             if( urlstr.contains( (*it)->keyBasePathList()[i] )
784                 || urlstr.contains( (*it)->keyCgiBasePathList()[i] ) ){
785
786             /* cache */
787             m_previousBoardData = (*it);
788             m_previousBoardURL = urlstr;
789             
790             return (*it);
791             }
792         }
793     }
794     
795     return NULL;
796 }
797
798
799
800 /*--------------------------------*/
801 /* BBSHISTORY */
802
803
804 /* load the bbs history file ( BBSHISTORY ), and create keys of Data Base.  */
805 /* Before calling this, enroll the board by enrollBoard().                  */ 
806 /*
807     ex) If the host of board moved like :
808
809     http:://aaa.com -> http://bbb.com -> http://ccc.com -> http://ddd.com
810     
811     then, BBSHISTORY is
812
813     http://ccc.com
814     http://bbb.com
815     http://aaa.com
816
817 */ /* public */ /* static */
818 bool BoardManager::loadBBSHistory( const KURL& url )
819 {
820     BoardData* bdata = getBoardData( url );
821     if( bdata == NULL ) return FALSE;
822
823     QStringList keyHosts = bdata->hostName();  
824     
825     QFile file( Cache::getBBSHistoryPath( url ) );
826     if ( file.open( IO_ReadOnly ) ){
827
828         qDebug( "read BBSHISTRY : %s",Cache::getBBSHistoryPath( url ).ascii() );
829
830         QTextStream ts( &file );
831
832         QString line;
833         while( !ts.eof() ){
834
835             line = ts.readLine();
836             qDebug("line: %s",line.ascii());
837             keyHosts += line;
838         }
839
840         bdata->createKeys( keyHosts );
841         file.close();
842
843         return TRUE;
844     }
845
846     return FALSE;
847 }
848
849
850 /* public */ /* static */
851 bool BoardManager::moveBoard( const KURL& fromURL, const KURL& toURL )
852 {
853     QString oldhost = fromURL.protocol() + "://" + fromURL.host();    
854     QString newhost = toURL.protocol() + "://" + toURL.host();
855
856     const QRegExp exp( "/$" );
857     QString oldURL = fromURL.prettyURL();
858     QString newURL = toURL.prettyURL();
859     oldURL.remove( exp );
860     newURL.remove( exp );
861     oldURL += "/";
862     newURL += "/";
863     
864     qDebug( "moveBoard : %s -> %s", oldURL.ascii(), newURL.ascii() );
865
866     if( oldURL == newURL ) return FALSE;
867     
868     /* Is oldURL enrolled? */
869     BoardData* bdata = getBoardData( oldURL ); 
870     if( bdata == NULL ){
871
872         /* Is newURL enrolled? */
873         bdata = getBoardData( newURL ); 
874         if( bdata == NULL ) return FALSE;
875     }
876
877
878     /*---------------------------*/
879     /* update BoardData */
880
881     /* get the path of old cache */
882     bdata->setHostName( oldhost );
883     QStringList keyHosts = bdata->keyHostList();
884     keyHosts.remove( oldhost );
885     keyHosts.prepend( oldhost );
886     bdata->createKeys( keyHosts );    
887     QString oldCachePath = Cache::baseDir() + Cache::serverDir( bdata->basePath() )
888         + Cache::boardDir( bdata->basePath() );
889
890     /* update URL */
891     bdata->setHostName( newhost );
892     
893     /* update keys */
894     /* The order of keyHosts will be like this:
895        
896       newhost      
897       oldhost      
898       foohost1
899       foohost2
900       
901     */
902     keyHosts = bdata->keyHostList();
903     keyHosts.remove( oldhost );
904     keyHosts.prepend( oldhost );
905     keyHosts.remove( newhost );
906     keyHosts.prepend( newhost );
907     bdata->createKeys( keyHosts );
908
909     
910     /*---------------------------*/
911     /* move cache dir */
912     
913     QDir qdir;
914     if( ! qdir.exists( oldCachePath ) ) return TRUE;
915     
916     /* mkdir new server dir */
917     QString newCachePath = Cache::baseDir() + Cache::serverDir( bdata->basePath() );    
918     Kita::mkdir( newCachePath );
919     
920     /* backup old dir */
921     newCachePath += Cache::boardDir( bdata->basePath() );    
922     if( qdir.exists ( newCachePath ) ){
923         QString bkupPath = newCachePath;
924         bkupPath.truncate( bkupPath.length() -1 ); /* remove '/' */
925         bkupPath += "." + QString().setNum( QDateTime::currentDateTime().toTime_t() );
926         qDebug( "mv %s\n -> %s", newCachePath.ascii(), bkupPath.ascii() );
927         qdir.rename( newCachePath, bkupPath  );
928     }
929
930     /* move cache dir */
931     qDebug( "old: %s", oldCachePath.ascii() );
932     if( qdir.exists( oldCachePath ) ){
933         qDebug( "move: %s\n-> %s", oldCachePath.ascii(), newCachePath.ascii() );        
934         qdir.rename( oldCachePath, newCachePath );
935     }
936     else Kita::mkdir( newCachePath );
937
938     /* make old dir */
939     if ( ! qdir.exists( oldCachePath ) ) {
940         Kita::mkdir( oldCachePath );
941         /* create BBS_MOVED */
942         QString movedPath = oldCachePath + "/BBS_MOVED";
943         QFile file( movedPath );
944         if ( file.open( IO_WriteOnly ) ) {
945             qDebug( "write BBS_MOVED : %s", movedPath.ascii() );
946             QTextStream stream( &file );
947             stream << newURL << endl;
948         }
949         file.close();
950     }
951
952     /*---------------------------*/
953     /* update BBSHISTRY */
954     
955     QFile file( Cache::getBBSHistoryPath( bdata->basePath() ) );
956     if ( file.open( IO_WriteOnly ) ){
957
958         qDebug( "write BBSHISTRY : %s", Cache::getBBSHistoryPath( bdata->basePath() ).ascii() );
959         QTextStream ts( &file );
960         
961         keyHosts.remove( newhost );
962         for ( QStringList::iterator it = keyHosts.begin() ; it != keyHosts.end(); ++it ) {
963             ts << (*it) << endl;
964         }
965
966         file.close();
967     }
968
969     
970     /*---------------------------*/
971     /* update other information */
972     FavoriteThreads::replace( oldURL, newURL );
973     Kita::Thread::replace( oldURL, newURL );
974     KitaThreadInfo::replace( oldURL, newURL );
975     Kita::FavoriteBoards::replace( oldURL, newURL );
976
977     qDebug("%s",bdata->basePath().ascii() );
978     qDebug("%s",bdata->cgiBasePath().ascii() );
979     int count =  bdata->keyBasePathList().count();
980     for( int i = 0; i < count ; ++i ){
981         qDebug("key: %s",bdata->keyBasePathList()[i].ascii() );
982         qDebug("key: %s",bdata->keyCgiBasePathList()[i].ascii() );                  
983     }
984
985     return TRUE;
986 }
987
988
989 /*--------------------------------*/
990 /* SETTING.TXT  */
991
992
993 /* public */ /* static */
994 bool BoardManager::loadBBSSetting( const KURL& url, bool reload )
995 {
996     /* Is board enrolled ? */
997     BoardData* bdata = getBoardData( url );
998     if( bdata == NULL ) return FALSE;
999     if( bdata->type() != Board_2ch ) return FALSE;
1000     
1001     KURL settingURL = bdata->settingURL();
1002     QString path = Cache::getSettingPath( url );
1003     
1004     /* now loading */
1005     if( DownloadManager::isLoadingNow( settingURL ) ) return FALSE;
1006
1007     /* already loaded */
1008     if( bdata->settingLoaded() && !reload ) return TRUE;
1009
1010     /* reset names, linenum, etc. */
1011     bdata->setSettingLoaded( FALSE );
1012
1013     qDebug("LOAD SETTING.TXT :  %s",settingURL.prettyURL().ascii());
1014     qDebug("path: %s",path.ascii());
1015     
1016     /* download SETTING.TXT.*/
1017     DownloadManager::download( settingURL, path );
1018     bdata->setSettingLoaded( TRUE );
1019     
1020     return TRUE;
1021 }
1022
1023
1024 /* public */ /* static */
1025 QString BoardManager::getBBSDefaultName( const KURL& url )
1026 {
1027     BoardData* bdata = openSettingTxt( url );
1028     if( bdata == NULL ) return QString::null;
1029     return bdata->defaultName();
1030 }
1031
1032
1033 /* public */ /* static */
1034 int BoardManager::getBBSMaxLine( const KURL& url )
1035 {
1036     BoardData* bdata = openSettingTxt( url );
1037     if( bdata == NULL ) return 0;
1038     return bdata->lineNum();
1039 }
1040
1041 /* public */ /* static */
1042 int BoardManager::getBBSMsgCount( const KURL& url )
1043 {
1044     BoardData* bdata = openSettingTxt( url );
1045     if( bdata == NULL ) return 0;
1046     return bdata->msgCount();
1047 }
1048
1049
1050 /* public */ /* static */
1051 const KURL BoardManager::titleImgURL( const KURL& url )
1052 {
1053     BoardData* bdata = openSettingTxt( url );
1054     if( bdata == NULL ) return QString::null;
1055     return bdata->titleImgURL();
1056 }
1057
1058 /* open local SETTING.TXT, then get names, linenum, etc. */ /* private */ /* static */
1059 BoardData* BoardManager::openSettingTxt( const KURL& url )
1060 {
1061     BoardData* bdata = getBoardData( url );
1062     if( bdata == NULL ) return NULL;
1063     if( !bdata->settingLoaded() ) return NULL;
1064     if( DownloadManager::isLoadingNow( bdata->settingURL() ) ) return NULL;
1065     if( bdata->defaultName() != QString::null ) return bdata;
1066
1067     qDebug( "read SETTING.TXT");
1068     
1069     QFile file( Cache::getSettingPath( url ) );
1070     if ( file.open( IO_ReadOnly ) ){
1071
1072         QTextStream ts( &file );
1073         if( m_cp932Codec == NULL) m_cp932Codec = new QCp932Codec();
1074         ts.setCodec( m_cp932Codec );
1075
1076         QString line;
1077         while( !ts.eof() ){
1078
1079             line = ts.readLine();
1080
1081             /* default name */
1082             QString key = "BBS_NONAME_NAME=";
1083             if( line.find( key ) != -1 ) bdata->setDefaultName( line.remove( key ) );
1084
1085             /* 0ch type */
1086             key = "NANASI_NAME=";
1087             if( line.find( key ) != -1 ) bdata->setDefaultName( line.remove( key ) );
1088
1089             /* line number */
1090             key = "BBS_LINE_NUMBER=";
1091             if( line.find( key ) != -1 ) bdata->setLineNum( line.remove( key ).toInt() * 2 );
1092
1093             /* msg count */
1094             key = "BBS_MESSAGE_COUNT=";
1095             if( line.find( key ) != -1 ) bdata->setMsgCount( line.remove( key ).toInt() );
1096             
1097             /* title image */
1098             key = "BBS_TITLE_PICTURE=";
1099             if( line.find( key ) != -1 ){
1100                 QString path = line.remove( key );
1101                 KURL titleImgURL = KURL( bdata->basePath(), path );
1102                 bdata->setTitleImgURL( titleImgURL );
1103             }
1104         }
1105             
1106         file.close();
1107     }
1108
1109     if( bdata->defaultName() == QString::null ) bdata->setDefaultName( "(default name)" );
1110
1111     qDebug( "name: %s", (const char *)bdata->defaultName().local8Bit());
1112     qDebug( "line num: %d", bdata->lineNum() );
1113     
1114     return bdata;
1115 }