OSDN Git Service

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