OSDN Git Service

・EUCtoSJIS が文字化けすることがあるバグを修正したのでバージョン更新。
[gikonavigoeson/gikonavi.git] / res / ExternalBoardPlugIn / ShitarabaJBBSPlugIn.dpr
1 library ShitarabaJBBSPlugIn;
2
3 {
4         ShitarabaJBBSPlugIn
5         \82µ\82½\82ç\82Î\8f\88\97\9d\83\86\83j\83b\83g
6         $Id: ShitarabaJBBSPlugIn.dpr,v 1.20 2004/03/19 15:19:00 yoffy Exp $
7 }
8
9 uses
10         Windows, SysUtils, Classes, Math, DateUtils, Dialogs,
11         IdURI,
12         PlugInMain in 'PlugInMain.pas',
13         ThreadItem in 'ThreadItem.pas',
14         BoardItem in 'BoardItem.pas',
15         FilePath in 'FilePath.pas',
16         Y_TextConverter in 'Y_TextConverter.pas',
17         MojuUtils in '..\..\MojuUtils.pas';
18
19 {$R *.res}
20
21 type
22         // =========================================================================
23         // TShitarabaThreadItem
24         // =========================================================================
25         TShitarabaThreadItem = class(TThreadItem)
26         private
27                 FIsTemporary    : Boolean;
28                 FDat                                    : TStringList;
29
30         public
31                 constructor     Create( inInstance : DWORD );
32                 destructor      Destroy; override;
33
34         private
35                 function        Download : TDownloadState;
36                 function        Write( inName : string; inMail : string; inMessage : string ) : TDownloadState;
37                 function        GetRes( inNo : Integer ) : string;
38                 function        GetDat( inNo : Integer ) : string;
39                 function        GetHeader( inOptionalHeader : string ) : string;
40                 function        GetFooter( inOptionalFooter : string ) : string;
41                 function        GetBoardURL : string;
42                 procedure       ArrangeDownloadData( start: Integer;var Data: TStringList);
43                 procedure       LoadDat;
44                 procedure       FreeDat;
45                 function        BrowsableURL : string;
46                 function        ReadURL : string;
47                 function        WriteURL : string;
48         end;
49
50         // =========================================================================
51         // TShitarabaBoardItem
52         // =========================================================================
53         TShitarabaBoardItem = class(TBoardItem)
54         private
55                 FIsTemporary    : Boolean;
56                 FDat                                    : TStringList;
57
58         public
59                 constructor     Create( inInstance : DWORD );
60                 destructor      Destroy; override;
61
62         private
63                 function        Download : TDownloadState;
64                 function        CreateThread( inSubject : string; inName : string; inMail : string; inMessage : string ) : TDownloadState;
65                 function        ToThreadURL( inFileName : string ) : string;
66                 procedure       EnumThread( inCallBack : TBoardItemEnumThreadCallBack );
67
68                 function        SubjectURL : string;
69         end;
70
71 const
72         LOG_DIR                                         = 'ShitarabaJBBS\';
73         SUBJECT_NAME                    = 'subject.txt';
74
75         PLUGIN_NAME                             = 'ShitarabaJBBS';
76         MAJOR_VERSION                   = 1;
77         MINOR_VERSION                   = 1;
78         RELEASE_VERSION         = 'alpha';
79         REVISION_VERSION        = 2;
80
81         SYNCRONIZE_MENU_CAPTION = '\82µ\82½\82ç\82ÎJBBS\94Â\8dX\90V';
82
83 var
84         SyncronizeMenu          : HMENU;
85
86 // =========================================================================
87 // \8eG\97p\8aÖ\90\94
88 // =========================================================================
89
90 // *************************************************************************
91 // \83e\83\93\83|\83\89\83\8a\82È\83p\83X\82Ì\8eæ\93¾
92 // *************************************************************************
93 function TemporaryFile : string;
94 var
95         tempPath : array [0..MAX_PATH] of       char;
96 begin
97
98         GetTempPath( SizeOf(tempPath), tempPath );
99         repeat
100                 Result := tempPath + IntToStr( Random( $7fffffff ) );
101         until not FileExists( Result );
102
103 end;
104
105 // *************************************************************************
106 // \82µ\82½\82ç\82Î\97p\83\8d\83O\83t\83H\83\8b\83_\8eæ\93¾
107 // *************************************************************************
108 function MyLogFolder : string;
109 var
110         folder : string;
111 begin
112
113         folder := LogFolder;
114         if Length( folder ) = 0 then
115                 Result := ''
116         else
117                 Result := folder + LOG_DIR;
118
119 end;
120
121 (*************************************************************************
122  *\83f\83B\83\8c\83N\83g\83\8a\82ª\91\8dÝ\82·\82é\82©\83`\83F\83b\83N
123  *************************************************************************)
124 function DirectoryExistsEx(const Name: string): Boolean;
125 var
126         Code: Integer;
127 begin
128         Code := GetFileAttributes(PChar(Name));
129         Result := (Code <> -1) and (FILE_ATTRIBUTE_DIRECTORY and Code <> 0);
130 end;
131
132 (*************************************************************************
133  *\83f\83B\83\8c\83N\83g\83\8a\8dì\90¬\81i\95¡\90\94\8aK\91w\91Î\89\9e\81j
134  *************************************************************************)
135 function ForceDirectoriesEx(Dir: string): Boolean;
136 begin
137         Result := True;
138         if Length(Dir) = 0 then
139                 raise Exception.Create('\83t\83H\83\8b\83_\82ª\8dì\90¬\8fo\97\88\82Ü\82¹\82ñ');
140         Dir := ExcludeTrailingPathDelimiter(Dir);
141         if (Length(Dir) < 3) or DirectoryExistsEx(Dir)
142                 or (ExtractFilePath(Dir) = Dir) then Exit; // avoid 'xyz:\' problem.
143         Result := ForceDirectoriesEx(ExtractFilePath(Dir)) and CreateDir(Dir);
144 end;
145
146 // \82Æ\82è\82 \82¦\82¸\82Ì\91ã\97p\95i\82È\82Ì\82Å chrWhite \82ð\8dl\97\82µ\82Ä\82¢\82È\82¢\82±\82Æ\82É\92\8d\88Ó\81I\81I\81I
147 procedure ExtractHttpFields(
148         const chrSep : TSysCharSet;
149         const chrWhite : TSysCharSet;
150         const strValue : string;
151         var strResult : TStringList;
152         unknownFlag : boolean = false
153 );
154 var
155         last, p, strLen : Integer;
156 begin
157
158         strLen := Length( strValue );
159         p := 1;
160         last := 1;
161
162         while p <= strLen do
163         begin
164
165                 if strValue[ p ] in chrSep then
166                 begin
167                         strResult.Add( Copy( strValue, last, p - last ) );
168                         last := p + 1;
169                 end;
170
171                 p := p + 1;
172
173         end;
174
175         if last <> p then
176                 strResult.Add( Copy( strValue, last, strLen - last + 1 ) );
177
178 end;
179
180 \rfunction HttpEncode(
181 \r       const strValue : string
182 ) : string;
183 var
184         i : Integer;
185         strLen : Integer;
186         strResult : string;
187         b : Integer;
188 const
189         kHexCode : array [0..15] of char = (
190                                 '0', '1', '2', '3', '4', '5', '6', '7',
191                                 '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' );
192 begin
193
194         strLen := Length( strValue );
195         i := 1;
196
197         while i <= strLen do
198         begin
199
200                 case strValue[ i ] of
201                 '0' .. '9', 'a' .. 'z', 'A' .. 'Z', '*', '-', '.', '@', '_':
202                         begin
203                                 strResult := strResult + strValue[ i ];
204                         end;
205                 else
206                         begin
207                                 b := Integer( strValue[ i ] );
208                                 strResult := strResult + '%'
209                                                                 + kHexCode[ b div $10 ]
210                                                                 + kHexCode[ b mod $10 ];
211                         end;
212                 end;
213
214                 i := i + 1;
215
216         end;
217
218         Result := strResult;
219
220 end;
221
222
223
224 // =========================================================================
225 // PlugIn
226 // =========================================================================
227
228 // *************************************************************************
229 // \83v\83\89\83O\83C\83\93\82ª(\90³\82µ\82­)\83\8d\81[\83h\82³\82ê\82½
230 // *************************************************************************
231 procedure OnLoad(
232         inInstance : DWORD                              // \83v\83\89\83O\83C\83\93\82Ì\83C\83\93\83X\83^\83\93\83X
233 ); stdcall;
234 begin
235
236         // \83v\83\89\83O\83C\83\93\83\81\83j\83\85\81[\82É\92Ç\89Á
237         SyncronizeMenu := AddPlugInMenu( inInstance, SYNCRONIZE_MENU_CAPTION );
238
239 end;
240
241 // *************************************************************************
242 // \83v\83\89\83O\83C\83\93\82Ì\83o\81[\83W\83\87\83\93\82ð\97v\8b\81\82³\82ê\82½
243 // *************************************************************************
244 procedure OnVersionInfo(
245         var outAgent            : PChar;        // \83o\81[\83W\83\87\83\93\82ð\88ê\90Ø\8aÜ\82Ü\82È\82¢\8f\83\90\88\82È\96¼\8fÌ
246         var outMajor            : DWORD;        // \83\81\83W\83\83\81[\83o\81[\83W\83\87\83\93
247         var outMinor            : DWORD;        // \83}\83C\83i\81[\83o\81[\83W\83\87\83\93
248         var outRelease  : PChar;        // \83\8a\83\8a\81[\83X\92i\8aK\96¼
249         var outRevision : DWORD         // \83\8a\83r\83W\83\87\83\93\83i\83\93\83o\81[
250 ); stdcall;
251 begin
252
253         try
254                 outAgent                := CreateResultString( PChar( PLUGIN_NAME ) );
255                 outMajor                := MAJOR_VERSION;
256                 outMinor                := MINOR_VERSION;
257                 outRelease      := CreateResultString( PChar( RELEASE_VERSION ) );
258                 outRevision     := REVISION_VERSION;
259         except
260                 outAgent                := nil;
261                 outMajor                := 0;
262                 outMinor                := 0;
263                 outRelease      := nil;
264                 outRevision     := 0;
265         end;
266
267 end;
268
269 // *************************************************************************
270 // \8ew\92è\82µ\82½ URL \82ð\82±\82Ì\83v\83\89\83O\83C\83\93\82Å\8eó\82¯\95t\82¯\82é\82©\82Ç\82¤\82©
271 // *************************************************************************
272 function OnAcceptURL(
273         inURL                   : PChar                         // \94»\92f\82ð\8bÂ\82¢\82Å\82¢\82é URL
274 ): TAcceptType; stdcall;        // URL \82Ì\8eí\97Þ
275 var
276         uri                             : TIdURI;
277         uriList         : TStringList;
278         foundPos        : Integer;
279 const
280         BBS_HOST                = 'jbbs.shitaraba.com';
281         THREAD_MARK     = '/bbs/read.cgi';
282 begin
283
284         try
285                 // \83z\83X\83g\96¼\82ª jbbs.shitaraba.com \82Å\8fI\82í\82é\8fê\8d\87\82Í\8eó\82¯\95t\82¯\82é\82æ\82¤\82É\82µ\82Ä\82¢\82é
286                 uri                     := TIdURI.Create( inURL );
287                 uriList := TStringList.Create;
288                 try
289                         ExtractHttpFields( ['/'], [], uri.Path, uriList );
290                         foundPos := AnsiPos( BBS_HOST, uri.Host );
291                         if (foundPos > 0) and (Length( uri.Host ) - foundPos + 1 = Length( BBS_HOST )) then begin
292                                 foundPos := ANsiPos( THREAD_MARK, inURL );
293                                 if foundPos > 0 then
294                                         Result := atThread
295                                 else if uriList.Count > 2 then  // \8dÅ\8cã\82ª '/' \82Å\95Â\82ß\82ç\82ê\82Ä\82é\82È\82ç 4
296                                         Result := atBoard
297                                 else
298                                         Result := atBBS;
299                         end else begin
300                                 Result := atNoAccept;
301                         end;
302                 finally
303                         uri.Free;
304                         uriList.Free;
305                 end;
306         except
307                 Result := atNoAccept;
308         end;
309
310 end;
311
312 // *************************************************************************
313 // \82µ\82½\82ç\82ÎJBBS\94Â\8dX\90V
314 // *************************************************************************
315 procedure OnBoardSyncronizeMenu(
316         inHandle        : HMENU                                 // \83\81\83j\83\85\81[\83n\83\93\83h\83\8b
317 ); stdcall;
318 var
319         category                        : string;
320         keyword                         : string;
321         savepath                        : string;
322         success                         : Boolean;
323         initdir                         : string;
324         filter                          : string;
325
326         agent                                   : PChar;
327         major                                   : DWORD;
328         minor                                   : DWORD;
329         release                         : PChar;
330         revision                        : DWORD;
331
332         url                                             : string;
333         modified                        : Double;
334         tmp                                             : PChar;
335         downResult              : string;
336         saveText                        : TStringList;
337         responseCode    : Longint;
338
339         found                                   : Integer;
340         found2                          : Integer;
341         board                                   : string;
342 const
343         CATEGORY_INPUT  = '\83J\83e\83S\83\8a\82ð\93ü\97Í\82µ\82Ä\82­\82¾\82³\82¢';
344         KEYWORD_INPUT           = '\8c\9f\8dõ\82·\82é\94Â\96¼\82ð\93ü\97Í\82µ\82Ä\82­\82¾\82³\82¢';
345         SAVEPATH_SELECT = '\95Û\91\82·\82é\83t\83@\83C\83\8b\96¼\82ð\93ü\97Í\82µ\82Ä\82­\82¾\82³\82¢';
346         BBS_HOST                = 'http://jbbs.shitaraba.com/';
347 label
348         NextBoard;
349 begin
350
351         category := 'computer';
352         success := InputQuery( SYNCRONIZE_MENU_CAPTION, CATEGORY_INPUT, category );
353         if not success then
354                 Exit;
355         keyword         := InputBox( SYNCRONIZE_MENU_CAPTION, KEYWORD_INPUT, '' );
356         if keyword = '' then
357                 Exit;
358
359         VersionInfo( agent, major, minor, release, revision );
360         try
361                 initdir := PreferencesFolder;
362                 filter  := '\82·\82Ä\82×\82Ì\83t\83@\83C\83\8b (*.*)|*.*';
363                 if agent = 'gikoNavi' then begin
364                         if DirectoryExists( initdir + 'Board\' ) then
365                                 initdir := initdir + 'Board\';
366                         filter  := '\94Â\83t\83@\83C\83\8b (*.txt)|*.txt';
367                 end;
368         finally
369                 DisposeResultString( agent );
370                 DisposeResultString( release );
371         end;
372         savepath        := keyword;
373         success         := PromptForFileName( savepath, filter, 'txt', SYNCRONIZE_MENU_CAPTION, initdir, True );
374         if not success then
375                 Exit;
376
377         InternalPrint( '\94Â\8fî\95ñ\82ð\8eæ\93¾\92\86\82Å\82·' );
378         try
379                 url :=
380                         'http://jbbs.shitaraba.com/bbs/left_menu.cgi?CATEGORY=' + category +
381                         '&WORD=' + HttpEncode( SJIStoEUC( keyword ) );
382
383                 responseCode := InternalDownload( PChar( url ), modified, tmp );
384                 try
385                         if (responseCode = 200) or (responseCode = 206) then begin
386                                 saveText := TStringList.Create;
387                                 saveText.Add( '[' + keyword + ']' );
388                                 downResult := EUCtoSJIS( tmp );
389                                 found := AnsiPos( '<a ', downResult );
390                                 while found > 0 do begin
391                                         downResult := Copy( downResult, found + 3, MaxInt );    // 3 = Length( '<a ' )
392                                         found := AnsiPos( 'href=', downResult );
393                                         if found = 0 then
394                                                 Break;
395
396                                         if downResult[ found + 5 ] = '"' then begin // 5 = Length( 'href=' )
397                                                 downResult := Copy( downResult, found + 6, MaxInt );    // 6 = Length( 'href="' )
398                                                 found := AnsiPos( '"', downResult );
399                                         end else begin
400                                                 downResult := Copy( downResult, found + 5, MaxInt );    // 5 = Length( 'href=' )
401                                                 found           := AnsiPos( ' ', downResult );
402                                                 found2  := AnsiPos( '>', downResult );
403                                                 if (found = 0) or (found2 = 0) then
404                                                         found := Max( found, found2 )
405                                                 else
406                                                         found := Min( found, found2 );
407                                         end;
408                                         board := Copy( downResult, 1, found - 1 );
409                                         downResult := Copy( downResult, found, MaxInt );
410
411                                         if board = BBS_HOST then
412                                                 goto NextBoard;
413                                         if AnsiPos( '../', board ) = 1 then
414                                                 board := BBS_HOST + Copy( board, 1 + 3, MaxInt );       // 3 = Length( '../' )
415
416                                         found := AnsiPos( '>', downResult );
417                                         if found = 0 then
418                                                 Break;
419                                         downResult := Copy( downResult, found + 1, MaxInt );    // 1 = Length( '>' )
420
421                                         found := AnsiPos( '</a>', downResult );
422                                         if found = 0 then
423                                                 Break;
424                                         board := Copy( downResult, 1, found - 1 ) + '=' + board;
425                                         downResult := Copy( downResult, found + 4, MaxInt );    // 4 = Length( '</a>' )
426
427                                         saveText.Add( board );
428 NextBoard:
429                                         found := AnsiPos( '<a ', downResult );
430                                 end;
431                                 saveText.SaveToFile( savepath );
432                                 saveText.Free;
433                         end else begin
434                                 InternalPrint( PChar( '\94Â\8fî\95ñ\82Ì\8eæ\93¾\82É\8e¸\94s\82µ\82Ü\82µ\82½(' + IntToStr( responseCode ) + ')' ) );
435                         end;
436                 finally
437                         DisposeResultString( tmp );
438                 end;
439                 InternalPrint( '\94Â\8fî\95ñ\82ð\8eæ\93¾\8a®\97¹' );
440         except
441                 InternalPrint( '\94Â\8fî\95ñ\82Ì\8eæ\93¾\82É\8e¸\94s\82µ\82Ü\82µ\82½' );
442         end;
443
444 end;
445
446 // \83\81\83j\83\85\81[\83n\83\93\83h\83\89
447 procedure OnPlugInMenu(
448         inHandle : HMENU                                        // \83\81\83j\83\85\81[\83n\83\93\83h\83\8b
449 ); stdcall;
450 begin
451
452         if inHandle = SyncronizeMenu then
453                 OnBoardSyncronizeMenu( inHandle );
454
455 end;
456
457 // =========================================================================
458 // TShitarabaThreadItem
459 // =========================================================================
460
461 // *************************************************************************
462 // \83R\83\93\83X\83g\83\89\83N\83^
463 // *************************************************************************
464 constructor TShitarabaThreadItem.Create(
465         inInstance      : DWORD
466 );
467 var
468         uri                                     : TIdURI;
469         uriList                 : TStringList;
470 begin
471
472         inherited;
473
474         OnDownload              := Download;
475         OnWrite                         := Write;
476         OnGetRes                        := GetRes;
477         OnGetDat                        := GetDat;
478         OnGetHeader             := GetHeader;
479         OnGetFooter             := GetFooter;
480         OnGetBoardURL   := GetBoardURL;
481
482         FilePath                        := '';
483         FIsTemporary    := False;
484         FDat                                    := nil;
485         URL                                             := BrowsableURL;
486
487         uri                     := TIdURI.Create( ReadURL );
488         uriList := TStringList.Create;
489         try
490                 ExtractHttpFields( ['/', '?'], [], uri.Path, uriList );
491                 FileName        := uriList[ 5 ] + '.dat';
492                 FilePath        := MyLogFolder + uriList[ 3 ] + '\' + uriList[ 4 ] + '\' + uriList[ 5 ] + '.dat';
493                 IsLogFile       := FileExists( FilePath );
494         finally
495                 uri.Free;
496                 uriList.Free;
497         end;
498
499 end;
500
501 // *************************************************************************
502 // \83f\83X\83g\83\89\83N\83^
503 // *************************************************************************
504 destructor TShitarabaThreadItem.Destroy;
505 begin
506
507         FreeDat;
508
509         // \88ê\8e\9e\83t\83@\83C\83\8b\82Ì\8fê\8d\87\82Í\8dí\8f\9c\82·\82é
510         if FIsTemporary then
511                 DeleteFile( FilePath );
512
513         inherited;
514
515 end;
516
517 // *************************************************************************
518 // \8ew\92è\82µ\82½ URL \82Ì\83X\83\8c\83b\83h\82Ì\83_\83E\83\93\83\8d\81[\83h\82ð\8ew\8e¦\82³\82ê\82½
519 // *************************************************************************
520 function TShitarabaThreadItem.Download : TDownloadState;
521 var
522         modified                        : Double;
523         tmp                                             : PChar;
524         downResult              : TStringList;
525         responseCode    : Longint;
526         logStream                       : TFileStream;
527         uri                                             : TIdURI;
528         uriList                         : TStringList;
529         datURL                          : string;
530                 tmpText: string;
531 begin
532
533         Result := dsError;
534
535         uri := TIdURI.Create( ReadURL );
536         uriList := TStringList.Create;
537         try
538                 ExtractHttpFields( ['/', '?'], [], uri.Path, uriList );
539                 FileName := uriList[ 5 ] + '.dat';
540                 // http://jbbs.shitaraba.com/bbs/rawmode.cgi/game/1578/1067968274/l100
541                 // protocol://host/1/2/3/4/5/uriList.Count - 1
542                 if MyLogFolder = '' then begin
543                         // \82Ç\82±\82É\95Û\91\82µ\82Ä\82¢\82¢\82Ì\82©\95ª\82©\82ç\82È\82¢\82Ì\82Å\88ê\8e\9e\83t\83@\83C\83\8b\82É\95Û\91
544                         FilePath                        := TemporaryFile;
545                         FIsTemporary    := True;
546                 end else begin
547                         FilePath                        := MyLogFolder + uriList[ 3 ] + '\' + uriList[ 4 ] + '\' + uriList[ 5 ] + '.dat';
548                         FIsTemporary    := False;
549                 end;
550         finally
551                 uri.Free;
552                 uriList.Free;
553         end;
554
555         // \95Û\91\97p\82Ì\83f\83B\83\8c\83N\83g\83\8a\82ð\8c@\82é
556         ForceDirectoriesEx( Copy( FilePath, 1, LastDelimiter( '\', FilePath ) ) );
557
558         // \93Æ\8e©\82É\83_\83E\83\93\83\8d\81[\83h\82â\83t\83B\83\8b\83^\83\8a\83\93\83O\82ð\8ds\82í\82È\82¢\8fê\8d\87\82Í
559         // InternalDownload \82É\94C\82¹\82é\82±\82Æ\82ª\8fo\97\88\82é
560         modified        := LastModified;
561         datURL          := ReadURL + IntToStr( Count + 1 ) + '-'; // \90V\92\85\82Ì\82Ý
562         responseCode := InternalDownload( PChar( datURL ), modified, tmp, 0 );
563
564         try
565                 if (responseCode = 200) or (responseCode = 206) then begin
566                         downResult := TStringList.Create;
567                         try
568                                                                 tmpText := CustomStringReplace( string( tmp ), '¡÷¡®', ',' );
569                                 downResult.Text := EUCtoSJIS( tmpText );
570                                                                 ArrangeDownloadData(Count, downResult);
571                                 if downResult.Count > 0 then begin
572                                         if FileExists( FilePath ) then
573                                                 logStream := TFileStream.Create( FilePath, fmOpenReadWrite or fmShareDenyWrite )
574                                         else
575                                                 logStream := TFileStream.Create( FilePath, fmCreate or fmShareDenyWrite );
576                                         try
577                                                 logStream.Position      := logStream.Size;
578                                                 logStream.Write( PChar( downResult.Text )^, Length( downResult.Text ) );
579                                         finally
580                                                 logStream.Free;
581                                         end;
582
583                                         if Count = 0 then
584                                                                                         // \90V\8bK
585                                                 Result := dsComplete
586                                         else
587                                                 // \92Ç\8bL
588                                                 Result := dsDiffComplete;
589
590                                                                                 Size                                    := Size + Length( downResult.Text );
591                                         // CGI \82©\82ç\82Í\90³\82µ\82¢\93ú\95t\82ª\93¾\82ç\82ê\82È\82¢\82Ì\82Å\8c»\8dÝ\82É\90Ý\92è
592                                         LastModified    := Now;
593
594
595
596                                         NewReceive              := Count + 1;
597                                         Count                                   := Count + downResult.Count;
598                                         NewResCount             := downResult.Count;
599
600
601
602                                 end else begin
603                                         Result := dsNotModify;
604                                 end;
605                         finally
606                                 downResult.Free;
607                         end;
608                 end else if responseCode = 304 then begin
609                         Result := dsNotModify;
610                 end;
611         finally
612                 DisposeResultString( tmp );
613         end;
614
615 end;
616 // *************************************************************************
617 // download\82µ\82Ä\82«\82½Dat\82Ì\82 \82Ú\81[\82ñ\82³\82ê\82½\95ª\82ð\95â\8f[\82µ\82Ä\81A
618 // \83\8c\83X\90\94\82Æ\88ê\92v\82·\82é\82æ\82¤\82É\82·\82é
619 // *************************************************************************
620 procedure       TShitarabaThreadItem.ArrangeDownloadData(
621         start: Integer; // \90V\8bK\81F\82O\81@\92Ç\8bL\81F\91O\89ñ\82Ü\82Å\82Ì\8eæ\93¾\90\94
622                 var Data: TStringList   //Dat\82Ì\83f\81[\83^
623 );
624 var
625         i: Integer;
626                 n: Integer;
627                 tmp: string;
628 begin
629         i := start;
630                 while i < Data.count + start do begin
631                         try
632                                         tmp := Copy(Data[i - start], 1 , AnsiPos('<>', Data[ i - start ] )-1 );
633                                                 try
634                                                         n := StrToInt(tmp);
635                                                                 if n > i + 1 then begin
636                                                                         Data.Insert(i - start, Format('%d<><><><><><>', [i+1]));
637                                                                 end;
638                                                                 Inc(i);
639                                                 except
640                                                         Inc(i);
641                         end;
642                                 except
643
644                                 end;
645                 end;
646
647 end;
648
649
650 // *************************************************************************
651 // \8f\91\82«\8d\9e\82Ý\82ð\8ew\8e¦\82³\82ê\82½
652 // *************************************************************************
653 function        TShitarabaThreadItem.Write(
654         inName                          : string;       // \96¼\91O(\83n\83\93\83h\83\8b)
655         inMail                          : string;       // \83\81\81[\83\8b\83A\83h\83\8c\83X
656         inMessage                       : string        // \96{\95
657 ) : TDownloadState;                             // \8f\91\82«\8d\9e\82Ý\82ª\90¬\8c÷\82µ\82½\82©\82Ç\82¤\82©
658 var
659         postData                        : string;
660         postResult              : PChar;
661         uri                                             : TIdURI;
662         uriList                         : TStringList;
663         responseCode    : Integer;
664 begin
665
666         uri                     := TIdURI.Create( URL );
667         uriList := TStringList.Create;
668         try
669                 ExtractHttpFields( ['/', '?'], [], uri.Path, uriList );
670
671                 // http://jbbs.shitaraba.com/bbs/read.cgi/game/1578/1067968274/l100
672                 postData        :=
673                         'NAME='                 + HttpEncode( SJIStoEUC( inName ) ) +
674                         '&MAIL='                + HttpEncode( SJIStoEUC( inMail ) ) +
675                         '&MESSAGE='     + HttpEncode( SJIStoEUC( inMessage ) ) +
676                         '&BBS='                 + uriList[ 4 ] +
677                         '&KEY='                 + uriList[ 5 ] +
678                         '&DIR='                 + uriList[ 3 ] +
679                         '&TIME='                + IntToStr( DateTimeToUnix( Now ) ) +
680                         '&submit='      + HttpEncode( SJIStoEUC( '\8f\91\82«\8d\9e\82Þ' ) );
681
682                 // \93Æ\8e©\82É\92Ê\90M\82µ\82È\82¢\8fê\8d\87\82Í InternalPost \82É\94C\82¹\82é\82±\82Æ\82ª\8fo\97\88\82é
683                 responseCode := InternalPost( PChar( WriteURL ), PChar( postData ), postResult );
684                 try
685                         if (responseCode = 200) or
686                                 ((responseCode = 302) and (Length( Trim( postResult ) ) = 0)) then begin
687                                 Result := dsComplete
688                         end else begin
689                                 Result := dsError;
690                                 if Assigned( InternalPrint ) then
691                                         InternalPrint( postResult );
692                         end;
693                 finally
694                         DisposeResultString( postResult );
695                 end;
696         finally
697                 uri.Free;
698                 uriList.Free;
699         end;
700
701 end;
702
703 // *************************************************************************
704 // \83\8c\83X\94Ô\8d\86 inNo \82É\91Î\82·\82é html \82ð\97v\8b\81\82³\82ê\82½
705 // *************************************************************************
706 function TShitarabaThreadItem.GetRes(
707         inNo            : Integer               // \97v\8b\81\82³\82ê\82½\83\8c\83X\94Ô\8d\86
708 ) : string;                                             // \91Î\89\9e\82·\82é HTML
709 var
710         res                     : string;
711         tmp                     : PChar;
712 begin
713
714         // \93Æ\8e©\82É\83t\83B\83\8b\83^\83\8a\83\93\83O\82ð\8ds\82í\82È\82¢\8fê\8d\87\82Í
715         // InternalAbon \82¨\82æ\82Ñ Dat2HTML \82É\94C\82¹\82é\82±\82Æ\82ª\8fo\97\88\82é
716                 {
717         LoadDat;
718         if FDat = nil then begin
719                 // \83\8d\83O\82É\91\8dÝ\82µ\82È\82¢\82Ì\82Å\82±\82Ì\82Ü\82Ü\8fI\97¹
720                 Result := '';
721                 Exit;
722         end;
723         res := Copy( FDat[ inNo - 1 ], AnsiPos( '<>', FDat[ inNo - 1 ] ) + 2, MaxInt );
724                 }
725                 res := GetDat( inNo );
726         tmp := InternalAbonForOne( PChar( res ), PChar( FilePath ),inNo );
727         try
728                 Result := Dat2HTML( string( tmp ), inNo );
729         finally
730                 DisposeResultString( tmp );
731         end;
732
733 end;
734
735 // *************************************************************************
736 // \83\8c\83X\94Ô\8d\86 inNo \82É\91Î\82·\82é Dat \82ð\97v\8b\81\82³\82ê\82½
737 // *************************************************************************
738 function TShitarabaThreadItem.GetDat(
739         inNo            : Integer               // \97v\8b\81\82³\82ê\82½\83\8c\83X\94Ô\8d\86
740 ) : string;                                             // \82Q\82¿\82á\82ñ\82Ë\82é\82ÌDat\8c`\8e®
741 var
742         res                     : string;
743                 tmp                     : array[1..5] of string;
744                 i                       : Integer;
745 begin
746
747         // \93Æ\8e©\82É\83t\83B\83\8b\83^\83\8a\83\93\83O\82ð\8ds\82í\82È\82¢\8fê\8d\87\82Í
748         // InternalAbon \82¨\82æ\82Ñ Dat2HTML \82É\94C\82¹\82é\82±\82Æ\82ª\8fo\97\88\82é
749         LoadDat;
750         if FDat = nil then begin
751                 // \83\8d\83O\82É\91\8dÝ\82µ\82È\82¢\82Ì\82Å\82±\82Ì\82Ü\82Ü\8fI\97¹
752                 Result := '';
753                 Exit;
754         end;
755                 try
756
757                 res := Copy( FDat[ inNo - 1 ], AnsiPos( '<>', FDat[ inNo - 1 ] ) + 2, MaxInt );
758                                 //\96\96\94ö\82ÉID\82ª\95\\8e¦\82³\82ê\82Ä\82¢\82é\82Ì\82Å\82»\82ê\82ð\93\8a\8de\93ú\82Ì\82Æ\82±\82ë\82É\93ü\82ê\82é
759                                 // \96¼\91O<>\83\81\81[\83\8b<>\93ú\95t<>\96{\95¶<>\83X\83\8c\83^\83C<>ID
760                                 for i := 0 to 4 do begin
761                                         tmp[ i + 1 ] := Copy( res, 1, AnsiPos('<>', res) - 1 );
762                                         Delete( res, 1, AnsiPos('<>', res) + 1 );
763                 end;
764                                 // \96¼\91O<>\83\81\81[\83\8b<>\93ú\95tID<>\96{\95¶<>\83X\83\8c\83^\83C
765                 Result := tmp[1] + '<>' + tmp[2] + '<>' + tmp[3] + ' ' + res + '<>'+ tmp[4] + '<>' +tmp[5];
766         except
767                         Result := '';
768         end;
769
770 end;
771
772 // *************************************************************************
773 // \83X\83\8c\83b\83h\82Ì\83w\83b\83_ html \82ð\97v\8b\81\82³\82ê\82½
774 // *************************************************************************
775 function TShitarabaThreadItem.GetHeader(
776         inOptionalHeader        : string
777 ) : string;
778 begin
779
780         // \93Æ\8e©\82É\83t\83B\83\8b\83^\83\8a\83\93\83O\82ð\8ds\82í\82È\82¢\8fê\8d\87\82Í
781         // InternalHeader \82É\94C\82¹\82é\82±\82Æ\82ª\8fo\97\88\82é
782         Result := InternalHeader(
783                 '<meta http-equiv="Content-type" content="text/html; charset=Shift_JIS">' +
784                 inOptionalHeader );
785
786
787         // GetRes \82ð\8cÄ\82Î\82ê\82é\82±\82Æ\82ª\97\\91z\82³\82ê\82é\82Ì\82Å FDat \82ð\90\90¬\82µ\82Ä\82¨\82­
788         try
789                 FreeDat;
790                 LoadDat;
791         except
792         end;
793
794 end;
795
796 // *************************************************************************
797 // \83X\83\8c\83b\83h\82Ì\83t\83b\83^ html \82ð\97v\8b\81\82³\82ê\82½
798 // *************************************************************************
799 function TShitarabaThreadItem.GetFooter(
800         inOptionalFooter : string
801 ) : string;
802 begin
803
804         // \93Æ\8e©\82É\83t\83B\83\8b\83^\83\8a\83\93\83O\82ð\8ds\82í\82È\82¢\8fê\8d\87\82Í
805         // InternalFooter \82É\94C\82¹\82é\82±\82Æ\82ª\8fo\97\88\82é
806         Result := InternalFooter( inOptionalFooter );
807
808         // \82à\82¤ GetRes \82Í\8cÄ\82Î\82ê\82È\82¢\82Æ\8ev\82¤\82Ì\82Å FDat \82ð\8aJ\95ú\82µ\82Ä\82¨\82­
809         try
810                 FreeDat;
811         except
812         end;
813
814 end;
815
816 // *************************************************************************
817 // \82±\82Ì ThreadItem \82ª\91®\82·\82é\94Â\82Ì URL \82ð\97v\8b\81\82³\82ê\82½
818 // *************************************************************************
819 function        TShitarabaThreadItem.GetBoardURL : string;
820 var
821         uri                                             : TIdURI;
822         uriList                         : TStringList;
823 begin
824
825         uri                     := TIdURI.Create( ReadURL );
826         uriList := TStringList.Create;
827         try
828                 ExtractHttpFields( ['/', '?'], [], uri.Path, uriList );
829                 FileName := uriList[ 5 ] + '.dat';
830                 // http://jbbs.shitaraba.com/bbs/read.cgi/game/1578/1067968274/l100
831                 // http://jbbs.shitaraba.com/game/1000/subject.txt
832                 Result          := CreateResultString(
833                         uri.Protocol + '://' + uri.Host + '/' + uriList[ 3 ] + '/' + uriList[ 4 ] + '/' );
834         finally
835                 uri.Free;
836                 uriList.Free;
837         end;
838
839 end;
840
841 // *************************************************************************
842 // FDat \82Ì\90\90¬
843 // *************************************************************************
844 procedure       TShitarabaThreadItem.LoadDat;
845 begin
846
847         if FDat = nil then begin
848                 if IsLogFile then begin
849                         // dat \82Ì\93Ç\82Ý\8d\9e\82Ý
850                         FDat := TStringList.Create;
851                         FDat.LoadFromFile( FilePath );
852                 end;
853         end;
854
855 end;
856
857 // *************************************************************************
858 // FDat \82Ì\8aJ\95ú
859 // *************************************************************************
860 procedure       TShitarabaThreadItem.FreeDat;
861 begin
862
863         if FDat <> nil then begin
864                 FDat.Free;
865                 FDat := nil;
866         end;
867
868 end;
869
870 // *************************************************************************
871 // \88À\91S\82È\83u\83\89\83E\83U\95\\8e¦\97p\82Ì URL
872 // *************************************************************************
873 function        TShitarabaThreadItem.BrowsableURL : string;
874 var
875         uri                             : TIdURI;
876         uriList         : TStringList;
877         foundPos        : Integer;
878         dir                             : string;
879 begin
880
881         foundPos := AnsiPos( '?', URL );
882         if foundPos > 0 then begin
883                 // \8b\8c\8e®
884                 uri := TIdURI.Create( URL );
885                 uriList := TStringList.Create;
886                 try
887                         ExtractHttpFields( ['/'], [], uri.Path, uriList );
888                         dir := uriList[ 1 ];
889                         ExtractHttpFields( ['&'], [], Copy( URL, foundPos + 1, MaxInt ), uriList );
890                         Result :=
891                                 uri.Protocol + '://' + uri.Host + '/bbs/read.cgi/' +
892                                 dir + '/' + uriList.Values[ 'BBS' ] + '/' + uriList.Values[ 'KEY' ] + '/l100';
893                 finally
894                         uri.Free;
895                         uriList.Free;
896                 end;
897         end else begin
898                 if Copy( URL, Length( URL ), 1 ) = '/' then
899                         uri := TIdURI.Create( URL )
900                 else
901                         uri := TIdURI.Create( URL + '/' );
902                 uriList := TStringList.Create;
903                 try
904                         ExtractHttpFields( ['/'], [], uri.Path, uriList );
905                         Result :=
906                                 uri.Protocol + '://' + uri.Host + '/bbs/read.cgi/' +
907                                 uriList[ 3 ] + '/' + uriList[ 4 ] + '/' + uriList[ 5 ] + '/l100';
908                 finally
909                         uri.Free;
910                         uriList.Free;
911                 end;
912         end;
913
914 end;
915
916 // *************************************************************************
917 // \88À\91S\82È( '/' \82Å\8fI\82í\82é )\93Ç\82Ý\8d\9e\82Ý\82Ì URL
918 // *************************************************************************
919 function        TShitarabaThreadItem.ReadURL : string;
920 var
921         uri                             : TIdURI;
922         uriList         : TStringList;
923         foundPos        : Integer;
924         dir                             : string;
925 begin
926
927         foundPos := AnsiPos( '?', URL );
928         if foundPos > 0 then begin
929                 // \8b\8c\8e®
930                 uri := TIdURI.Create( URL );
931                 uriList := TStringList.Create;
932                 try
933                         ExtractHttpFields( ['/'], [], uri.Path, uriList );
934                         dir := uriList[ 1 ];
935                         ExtractHttpFields( ['&'], [], Copy( URL, foundPos + 1, MaxInt ), uriList );
936                         // http://jbbs.shitaraba.com/bbs/read.cgi?DIR=game&BBS=1578&KEY=1067968274
937                         Result :=
938                                 uri.Protocol + '://' + uri.Host + '/bbs/rawmode.cgi/' +
939                                 dir + '/' + uriList.Values[ 'BBS' ] + '/' + uriList.Values[ 'KEY' ] + '/';
940                 finally
941                         uri.Free;
942                         uriList.Free;
943                 end;
944         end else begin
945                 if Copy( URL, Length( URL ), 1 ) = '/' then
946                         uri := TIdURI.Create( URL )
947                 else
948                         uri := TIdURI.Create( URL + '/' );
949                 uriList := TStringList.Create;
950                 try
951                         ExtractHttpFields( ['/'], [], uri.Path, uriList );
952                         // http://jbbs.shitaraba.com/bbs/read.cgi/game/1578/1067968274/l100
953                         Result :=
954                                 uri.Protocol + '://' + uri.Host + '/bbs/rawmode.cgi/' +
955                                 uriList[ 3 ] + '/' + uriList[ 4 ] + '/' + uriList[ 5 ] + '/';
956                 finally
957                         uri.Free;
958                         uriList.Free;
959                 end;
960         end;
961
962 end;
963
964 // *************************************************************************
965 // \88À\91S\82È( '/' \82Å\8fI\82í\82é )\8f\91\82«\8d\9e\82Ý\82Ì URL
966 // *************************************************************************
967 function        TShitarabaThreadItem.WriteURL : string;
968 var
969         uri                     : TIdURI;
970         uriList : TStringList;
971 begin
972
973         if Copy( URL, Length( URL ), 1 ) = '/' then
974                 uri := TIdURI.Create( URL )
975         else
976                 uri := TIdURI.Create( URL + '/' );
977         uriList := TStringList.Create;
978         try
979                 ExtractHttpFields( ['/', '?'], [], uri.Path, uriList );
980                 // http://jbbs.shitaraba.com/bbs/read.cgi/game/1578/1067968274/l100
981                 Result          :=
982                         uri.Protocol + '://' + uri.Host + '/bbs/write.cgi/' +
983                         uriList[ 3 ] + '/' + uriList[ 4 ] + '/' + uriList[ 5 ] + '/';
984         finally
985                 uri.Free;
986                 uriList.Free;
987         end;
988
989 end;
990
991 // *************************************************************************
992 // TThreadItem \82ª\90\90¬\82³\82ê\82½\8fê\8d\87\82Ì\8f\88\92u(TShitarabaThreadItem \82ð\90\90¬\82·\82é)
993 // *************************************************************************
994 procedure ThreadItemOnCreateOfTShitarabaThreadItem(
995         inInstance : DWORD
996 );
997 var
998         threadItem : TShitarabaThreadItem;
999 begin
1000
1001         threadItem := TShitarabaThreadItem.Create( inInstance );
1002         ThreadItemSetLong( inInstance, tipContext, DWORD( threadItem ) );
1003
1004 end;
1005
1006 // *************************************************************************
1007 // TThreadItem \82ª\94j\8aü\82³\82ê\82½\8fê\8d\87\82Ì\8f\88\92u(TShitarabaThreadItem \82ð\94j\8aü\82·\82é)
1008 // *************************************************************************
1009 procedure ThreadItemOnDisposeOfTShitarabaThreadItem(
1010         inInstance : DWORD
1011 );
1012 var
1013         threadItem : TShitarabaThreadItem;
1014 begin
1015
1016         threadItem := TShitarabaThreadItem( ThreadItemGetLong( inInstance, tipContext ) );
1017         threadItem.Free;
1018
1019 end;
1020
1021 // =========================================================================
1022 // TShitarabaBoardItem
1023 // =========================================================================
1024
1025 // *************************************************************************
1026 // \83R\83\93\83X\83g\83\89\83N\83^
1027 // *************************************************************************
1028 constructor TShitarabaBoardItem.Create(
1029         inInstance      : DWORD
1030 );
1031 var
1032         uri                                     : TIdURI;
1033         uriList                 : TStringList;
1034 begin
1035
1036         inherited;
1037
1038         OnDownload                                              := Download;
1039         OnCreateThread                          := CreateThread;
1040         OnEnumThread                                    := EnumThread;
1041         OnFileName2ThreadURL    := ToThreadURL;
1042
1043         FilePath                        := '';
1044         FIsTemporary    := False;
1045         FDat                                    := nil;
1046
1047         uri                     := TIdURI.Create( SubjectURL );
1048         uriList := TStringList.Create;
1049         try
1050                 ExtractHttpFields( ['/', '?'], [], uri.Path, uriList );
1051                 // http://jbbs.shitaraba.com/game/1000/subject.txt
1052                 FilePath        := MyLogFolder + uriList[ 1 ] + '\' + uriList[ 2 ] + '\' + uri.Document;
1053                 IsLogFile       := FileExists( FilePath );
1054         finally
1055                 uri.Free;
1056                 uriList.Free;
1057         end;
1058
1059 end;
1060  
1061 // *************************************************************************
1062 // \83f\83X\83g\83\89\83N\83^
1063 // *************************************************************************
1064 destructor TShitarabaBoardItem.Destroy;
1065 begin
1066
1067         if FDat <> nil then begin
1068                 try
1069                         FDat.Free;
1070                         FDat := nil;
1071                 except
1072                 end;
1073         end;
1074
1075         // \88ê\8e\9e\83t\83@\83C\83\8b\82Ì\8fê\8d\87\82Í\8dí\8f\9c\82·\82é
1076         if FIsTemporary then
1077                 DeleteFile( FilePath );
1078
1079         inherited;
1080
1081 end;
1082
1083 // *************************************************************************
1084 // \8ew\92è\82µ\82½\83X\83\8c\88ê\97\97\82Ì\83_\83E\83\93\83\8d\81[\83h\82ð\97v\8b\81\82³\82ê\82½
1085 // *************************************************************************
1086 function TShitarabaBoardItem.Download : TDownloadState;
1087 var
1088         modified                        : Double;
1089         downResult              : PChar;
1090         responseCode    : Longint;
1091         uri                                             : TIdURI;
1092         uriList                         : TStringList;
1093         i                                                       : Integer;
1094                 tmpText : String;
1095 begin
1096
1097         Result := dsError;
1098
1099         if FDat <> nil then begin
1100                 try
1101                         FDat.Free;
1102                         FDat := nil;
1103                 except
1104                 end;
1105         end;
1106         FDat            := TStringList.Create;
1107         uri                     := TIdURI.Create( SubjectURL );
1108         uriList := TStringList.Create;
1109         // \93Æ\8e©\82É\83_\83E\83\93\83\8d\81[\83h\82â\83t\83B\83\8b\83^\83\8a\83\93\83O\82ð\8ds\82í\82È\82¢\8fê\8d\87\82Í
1110         // InternalDownload \82É\94C\82¹\82é\82±\82Æ\82ª\8fo\97\88\82é
1111         modified                        := LastModified;
1112         responseCode    := InternalDownload( PChar( uri.URI ), modified, downResult );
1113         try
1114                 if responseCode = 200 then begin
1115                         try
1116                                 // \83p\83X\82ð\8eZ\8fo
1117                                 ExtractHttpFields( ['/', '?'], [], uri.Path, uriList );
1118                                 if MyLogFolder = '' then begin
1119                                         // \82Ç\82±\82É\95Û\91\82µ\82Ä\82¢\82¢\82Ì\82©\95ª\82©\82ç\82È\82¢\82Ì\82Å\88ê\8e\9e\83t\83@\83C\83\8b\82É\95Û\91
1120                                         FilePath                        := TemporaryFile;
1121                                         FIsTemporary    := True;
1122                                 end else begin
1123                                         FilePath                        := MyLogFolder + uriList[ 1 ] + '\' + uriList[ 2 ] + '\' + uri.Document;
1124                                         FIsTemporary    := False
1125                                 end;
1126
1127                                 // \95Û\91\97p\82Ì\83f\83B\83\8c\83N\83g\83\8a\82ð\8c@\82é
1128                                 ForceDirectoriesEx( Copy( FilePath, 1, LastDelimiter( '\', FilePath ) ) );
1129
1130                                 // EUC \82ð Shift_JIS \82É
1131                                                                 tmpText := CustomStringReplace( string( downResult ), '¡÷¡®', ',');
1132                                 FDat.Text := EUCtoSJIS( tmpText );
1133                                 // \82µ\82½\82ç\82ΠJBBS \82Í\83`\83F\83b\83N\97p\82É\90æ\93ª\82Æ\8dÅ\8fI\8ds\82ª\93¯\82
1134                                 i := FDat.Count - 1;
1135                                 if i > 0 then   // 1 \8cÂ\82¾\82Á\82½\82ç\8fã\89º\82à\82­\82»\82à\96³\82¢\82©\82ç 0 \82Í\8aÜ\82Ü\82¸
1136                                         if FDat[ 0 ] = FDat[ i ] then
1137                                                 FDat.Delete( i );
1138                                 // \95Û\91
1139                                 FDat.SaveToFile( FilePath );
1140
1141                                 IsLogFile                       := True;
1142                                 RoundDate                       := Now;
1143                                 LastModified    := modified;
1144                                 LastGetTime             := Now;
1145                         finally
1146                                 uri.Free;
1147                                 uriList.Free;
1148                         end;
1149                         Result := dsComplete;
1150                 end;
1151         finally
1152                 DisposeResultString( downResult );
1153         end;
1154
1155 end;
1156
1157 // *************************************************************************
1158 // \83X\83\8c\97§\82Ä\82ð\8ew\8e¦\82³\82ê\82½
1159 // *************************************************************************
1160 function        TShitarabaBoardItem.CreateThread(
1161         inSubject                       : string;       // \83X\83\8c\83^\83C
1162         inName                          : string;       // \96¼\91O(\83n\83\93\83h\83\8b)
1163         inMail                          : string;       // \83\81\81[\83\8b\83A\83h\83\8c\83X
1164         inMessage                       : string        // \96{\95
1165 ) : TDownloadState;                             // \8f\91\82«\8d\9e\82Ý\82ª\90¬\8c÷\82µ\82½\82©\82Ç\82¤\82©
1166 var
1167         postURL                         : string;
1168         postData                        : string;
1169         postResult              : PChar;
1170         uri                                             : TIdURI;
1171         uriList                         : TStringList;
1172         responseCode    : Integer;
1173 begin
1174
1175         uri                     := TIdURI.Create( URL );
1176         uriList := TStringList.Create;
1177         try
1178                 ExtractHttpFields( ['/', '?'], [], uri.Path, uriList );
1179
1180                 // http://jbbs.shitaraba.com/bbs/write.cgi/game/1578/new/
1181                 // http://jbbs.shitaraba.com/game/1000/subject.txt
1182                 postURL         :=
1183                         uri.Protocol + '://' + uri.Host + '/bbs/write.cgi/' +
1184                         uriList[ 1 ] + '/' + uriList[ 2 ] + '/new/';
1185                 postData        :=
1186                         'SUBJECT='      + HttpEncode( SJIStoEUC( inSubject ) ) +
1187                         '&NAME='                + HttpEncode( SJIStoEUC( inName ) ) +
1188                         '&MAIL='                + HttpEncode( SJIStoEUC( inMail ) ) +
1189                         '&MESSAGE='     + HttpEncode( SJIStoEUC( inMessage ) ) +
1190                         '&BBS='                 + uriList[ 4 ] +
1191                         '&DIR='                 + uriList[ 3 ] +
1192                         '&TIME='                + IntToStr( DateTimeToUnix( Now ) ) +
1193                         '&submit='      + HttpEncode( SJIStoEUC( '\90V\8bK\8f\91\82«\8d\9e\82Ý' ) );
1194
1195                 // \93Æ\8e©\82É\92Ê\90M\82µ\82È\82¢\8fê\8d\87\82Í InternalPost \82É\94C\82¹\82é\82±\82Æ\82ª\8fo\97\88\82é
1196                 responseCode := InternalPost( PChar( postURL ), PChar( postData ), postResult );
1197                 try
1198                         if (responseCode = 200) or
1199                                 ((responseCode = 302) and (Length( Trim( postResult ) ) = 0)) then begin
1200                                 Result := dsComplete
1201                         end else begin
1202                                 Result := dsError;
1203                                 if Assigned( InternalPrint ) then
1204                                         InternalPrint( postResult );
1205                         end;
1206                 finally
1207                         DisposeResultString( postResult );
1208                 end;
1209         finally
1210                 uri.Free;
1211                 uriList.Free;
1212         end;
1213
1214 end;
1215
1216 // *************************************************************************
1217 // \83X\83\8c\88ê\97\97\82Ì URL \82©\82ç\83X\83\8c\83b\83h\82Ì URL \82ð\93±\82«\8fo\82·
1218 // *************************************************************************
1219 function TShitarabaBoardItem.ToThreadURL(
1220         inFileName      : string        // \83X\83\8c\83b\83h\83t\83@\83C\83\8b\96¼
1221 ) : string;                                                     // \83X\83\8c\83b\83h\82Ì URL
1222 var
1223         threadURL               : string;
1224         uri                                     : TIdURI;
1225         uriList                 : TStringList;
1226         found                           : Integer;
1227 begin
1228
1229         found := AnsiPos( '.', inFileName );
1230         if found > 0 then
1231                 inFileName := Copy( inFileName, 1, found - 1 );
1232
1233         uri                     := TIdURI.Create( SubjectURL );
1234         uriList := TStringList.Create;
1235         try
1236                 try
1237                         ExtractHttpFields( ['/', '?'], [], uri.Path, uriList );
1238                         threadURL       := uri.Protocol + '://' + uri.Host + '/bbs/read.cgi/' +
1239                                 uriList[ 1 ] + '/' + uriList[ 2 ] + '/' + inFileName + '/l100';
1240                         Result          := threadURL;
1241                 finally
1242                         uri.Free;
1243                         uriList.Free;
1244                 end;
1245         except
1246                 Result := '';
1247         end;
1248
1249 end;
1250
1251 // *************************************************************************
1252 // \82±\82Ì\94Â\82É\82¢\82­\82Â\82Ì\83X\83\8c\82ª\82 \82é\82©\97v\8b\81\82³\82ê\82½
1253 // *************************************************************************
1254 procedure       TShitarabaBoardItem.EnumThread(
1255         inCallBack      : TBoardItemEnumThreadCallBack
1256 );
1257 var
1258         uri                                     : TIdURI;
1259         uriList                 : TStringList;
1260 begin
1261
1262         try
1263                 if FDat = nil then begin
1264                         FDat            := TStringList.Create;
1265                         uri                     := TIdURI.Create( SubjectURL );
1266                         uriList := TStringList.Create;
1267                         try
1268                                 // \83p\83X\82ð\8eZ\8fo
1269                                 ExtractHttpFields( ['/', '?'], [], uri.Path, uriList );
1270                                 // http://jbbs.shitaraba.com/game/1000/subject.txt
1271                                 FilePath        := MyLogFolder + uriList[ 1 ] + '\' + uriList[ 2 ] + '\' + uri.Document;
1272                                 if FileExists( FilePath ) then
1273                                         // \93Ç\82Ý\8d\9e\82Ý
1274                                         FDat.LoadFromFile( FilePath );
1275                         finally
1276                                 uri.Free;
1277                                 uriList.Free;
1278                         end;
1279                 end;
1280
1281                 // \93Æ\8e©\82É\83t\83B\83\8b\83^\83\8a\83\93\83O\82ð\8ds\82í\82È\82¢\8fê\8d\87\82Í EnumThread \82É\94C\82¹\82é\82±\82Æ\82ª\8fo\97\88\82é
1282                 inherited EnumThread( inCallBack, CustomStringReplace( FDat.Text, ',', '<>' ) );
1283         except
1284         end;
1285
1286 end;
1287
1288 // *************************************************************************
1289 // \83X\83\8c\88ê\97\97\82Ì URL \82ð\8b\81\82ß\82é
1290 // *************************************************************************
1291 function        TShitarabaBoardItem.SubjectURL : string;
1292 var
1293         uri                     : TIdURI;
1294         uriList : TStringList;
1295 begin
1296
1297         uri                     := TIdURI.Create( URL );
1298         uriList := TStringList.Create;
1299         try
1300                 if uri.Document <> SUBJECT_NAME then begin
1301                         if Copy( URL, Length( URL ), 1 ) = '/' then
1302                                 Result := URL + SUBJECT_NAME
1303                         else
1304                                 Result := URL + '/' + SUBJECT_NAME;
1305                 end else begin
1306                         // \82±\82±\82É\82Í\97\88\82È\82¢\82Æ\8ev\82¤\82¯\82Ç
1307                         Result := URL;
1308                 end;
1309         finally
1310                 uri.Free;
1311                 uriList.Free;
1312         end;
1313
1314 end;
1315
1316 // *************************************************************************
1317 // TBoardItem \82ª\90\90¬\82³\82ê\82½\8fê\8d\87\82Ì\8f\88\92u(TShitarabaBoardItem \82ð\90\90¬\82·\82é)
1318 // *************************************************************************
1319 procedure BoardItemOnCreateOfTShitarabaBoardItem(
1320         inInstance : DWORD
1321 );
1322 var
1323         boardItem : TShitarabaBoardItem;
1324 begin
1325
1326         boardItem := TShitarabaBoardItem.Create( inInstance );
1327         BoardItemSetLong( inInstance, bipContext, DWORD( boardItem ) );
1328
1329 end;
1330
1331 // *************************************************************************
1332 // TBoardItem \82ª\94j\8aü\82³\82ê\82½\8fê\8d\87\82Ì\8f\88\92u(TShitarabaBoardItem \82ð\94j\8aü\82·\82é)
1333 // *************************************************************************
1334 procedure BoardItemOnDisposeOfTShitarabaBoardItem(
1335         inInstance : DWORD
1336 );
1337 var
1338         boardItem : TShitarabaBoardItem;
1339 begin
1340
1341         boardItem := TShitarabaBoardItem( BoardItemGetLong( inInstance, bipContext ) );
1342         boardItem.Free;
1343
1344 end;
1345
1346
1347
1348 // =========================================================================
1349 // \83G\83\93\83g\83\8a\83|\83C\83\93\83g
1350 // =========================================================================
1351 procedure DLLEntry(
1352         ul_reason_for_call : DWORD
1353 );
1354 var
1355         module : HMODULE;
1356 begin
1357
1358         case ul_reason_for_call of
1359                 DLL_PROCESS_ATTACH:
1360                 begin
1361                         Randomize;
1362
1363                         module := GetModuleHandle( nil );
1364
1365                         LoadInternalAPI( module );
1366                         LoadInternalFilePathAPI( module );
1367                         LoadInternalThreadItemAPI( module );
1368                         LoadInternalBoardItemAPI( module );
1369
1370                         // ===== \83C\83\93\83X\83^\83\93\83X\82Ì\8eæ\82è\88µ\82¢\82ð TThreadItem \82©\82ç TShitarabaThreadItem \82É\95Ï\8dX\82·\82é
1371                         ThreadItemOnCreate      := ThreadItemOnCreateOfTShitarabaThreadItem;
1372                         ThreadItemOnDispose     := ThreadItemOnDisposeOfTShitarabaThreadItem;
1373                         // ===== \83C\83\93\83X\83^\83\93\83X\82Ì\8eæ\82è\88µ\82¢\82ð TBoardItem \82©\82ç TShitarabaBoardItem \82É\95Ï\8dX\82·\82é
1374                         BoardItemOnCreate               := BoardItemOnCreateOfTShitarabaBoardItem;
1375                         BoardItemOnDispose      := BoardItemOnDisposeOfTShitarabaBoardItem;
1376                 end;
1377                 DLL_PROCESS_DETACH:
1378                 begin
1379                         RemovePlugInMenu( SyncronizeMenu );
1380                 end;
1381                 DLL_THREAD_ATTACH:
1382                         ;
1383                 DLL_THREAD_DETACH:
1384                         ;
1385         end;
1386
1387 end;
1388
1389 exports
1390         OnLoad,
1391         OnVersionInfo,
1392         OnAcceptURL,
1393         OnPlugInMenu;
1394
1395 begin
1396
1397         try
1398                 DllProc := @DLLEntry;
1399                 DLLEntry( DLL_PROCESS_ATTACH );
1400         except end;
1401
1402 end.