5.ゲストブックを作ってみよう


HOME] [0章] [1章] [2章] [3章] [4章] [5章] [?章

(1)ゲストブックの仕組みと複数日本語コードの問題

 ゲストブックとは、ホームページを訪れた人々が、 次々にメッセージを書き込んでいくためのアプリです。 自動の掲示板などもこのバリエーションと考えることができます。

 この章では以下のようなゲストブックを作ってみましょう。
(1)フォームを含んだ書き込みページから書き込み用のCGIをコールする。
(2)CGIではデータファイルの先頭に新しい書き込みデータを追加する。
  (やはり、新しい書き込みは先頭にしたい)
(3)データファイルはHTMLにしておいて、読むときはCGIを使わない。
  (読み込みでCGIを使わないことでサーバーの負荷を軽減する)
 ただし、説明の簡便のため、同時アクセスの対策は省いてあります。 実際は、アクセスカウンタの章を参考にして、ロックを掛けた方がよいでしょう。
 また、実用にする時には、特にSSIを使えるサーバーでは、 タグを無効にした方がよいでしょう。この問題に関してはあちこちで 論じられているので、SSIについて書いてある書籍を参考にして下さい。 要するに、ゲストブックに危険なSSIのタグを埋め込まれると、 このページを読みに行った時に、サーバーがこれを解釈・実行してし まうからです。SSIが使えないサーバーでも、イメージタグで 巨大な画像を貼るといったことを、故意あるいは不注意でやられる ことも考えられます。  タグに対する対策は比較的簡単です。 Cの標準ライブラリにある文字列操作関数などを用いて、 ”<”は”&lt;”などに置き換えてしまえばよいからです。 この操作も説明の簡便のために省略します。

 ゲストブックのようなアプリには、複数日本語コードの問題が 生じます。これは次のようなものです。
 次々に書き込まれた、ゲストブックのデータを読むことを考えます。 ゲストブックの見出しに、私がシフトJISで日本語の見出しを書きます。 ここにAさんが、EUCコードで書き込みをすると、 シフトJISとEUCが混在したファイルになってしまいます。 こうなると、このゲストブックを表示した時には、 Aさんの書き込みは派手に文字化けしてしまいます。 まあ、たいていはシフトJISで書かれた私のページを表示した段階で ブラウザのコードはシフトJISになっているでしょうから、 こんなことはまれと思いますが。 ゲストブックのようなアプリでは、 データファイルのコードを統一したほうが良いでしょう。
 この問題は、以下の例では対処しています。

(2)簡単ゲストブック

<html>
<head>
<title>ゲストブック</title>
</head>
<body>
<center>
<h2>ゲストブック</h2>
みんなのスペースです<br>
たくさん書き込んでくださいね!<br>
<br>
<!--APPEND HERE-->
<hr>
</body>
</html>

 まず、左のようなゲストブックのデータファイルの初期ファイルを 作成します。これの<!--APPEND HERE-->の部分へ次々に 新しいメッセージを追加します。<!--APPEND HERE--> はブラウザにとってはコメントですので、 ユーザーはわざわざ見ようとしなければ見えないわけです。 このファイルには、nobodyが 書き込みアクセスしますので、666などnobodyが書き込める パーミッションを設定します。


<html>
<head>
<title>書き込んでね  </title>
</head>
<body>
<center>
<h2>書き込んでね!!</h2>
</center>
<form method="POST" action="gbkakiko.cgi">
        <p>ご意見<br>
        <textarea cols="40" rows="10" name="comment"> ここになんかかいてね!</textarea>
        <p>名前<br>
        <input type="text" name="name">
        <p>
        <input type="submit" value="send">
</form>
</body>
</html>

 次に、このような入力用のフォームを作ります。 データを単にgbkakiko.cgiに渡すだけのものです。 説明は要りませんね。


#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAXLEN 4096
main()
{
        char *inputstring,*cLength,*name[20],*value[20];
        int nfield,retcode;
        long i,length;
        char buf[MAXLEN];
        FILE *old,*new;
        char *oldname="gbdata.html",*newname="gbdata.temp",
                *mark="<!--APPEND HERE-->";
        int decode_form(),parse_form(),nkfsub();

        cLength=(char*)getenv("CONTENT_LENGTH");
        length=atol(cLength);
        if(length>MAXLEN) {printf("CONTENT_LENGTH>MAXLEN\n");exit(0);}
        inputstring=(char*)malloc(length+1);
        scanf("%s",inputstring);

        parse_form(inputstring,MAXLEN,name,value,&nfield);
        for(i=0;i<2;i++)
                decode_form(value[i],strlen(value[i]));
        
        old=fopen(oldname,"r");
        new=fopen(newname,"w");

        while(fgets(buf,MAXLEN,old)){
                if(strstr(buf,mark)){
                        fprintf(new,"%s\n",mark);
                        fputs("<hr>\n",new);
                        nkfsub("s",value[0],buf);
                        fputs(buf,new);
                        fputs("<div align=right>",new);
                        nkfsub("s",value[1],buf);
                        fputs(buf,new);
                        fputs("</div>",new);

                } else {
                        fputs(buf,new);
                }
        }

        fclose(old);
        fclose(new);

        rename(newname,oldname);

        printf("Content-type: text/html\n\n");
        printf("<HTML><HEAD></HEAD><BODY>\n");
        printf("<center><h2>Thank YOU!!</h2></center>\n");
        printf("\n</BODY></HTML>\n");

        free(inputstring);
        exit(0);
}


 これが、書き込みのためのCGIプログラムのソースです。 前述しましたが、同時アクセス問題と、SSIタグの問題には 説明の便のため対処してませんので、実際に使う時にはそれぞれ、 アクセスカウンタの章と、このページの始めの方を参考に して下さいな。

 まず、定法によって、フォームからのデータを 受け取ります。ここで用いている parse_form()とdecode_form()関数については、 「基本的な入出力」の章で説明してありますが、 この説明の後で再掲しておきます。
 次に、ゲストブックのデータファイルと 作業ファイルをオープンします。 同時アクセスに対処するためには、ここのとこの前に ロックを掛けて下さい。
 次のループで新しいデータファイルを作ります。 前に決めたマーク"<!--APPEND HERE-->" 以外の行は、左から右へと、作業ファイルにコピーします。 一方このマークに当ったら、フォームのデータを 作業ファイルに書き込みます。  この時に、nkfsub()でデータをシフトJISに変換します。 このサブルーチンはオリジナルのnkfを改変したものです。 「基本的な入出力」の章で使い方を書いておきましたので 参考にして下さい。 ダウンロードは ここからして下さい。Windowsの ために改行コードをCR/LFにしたものは ここにあります。
 後は、後始末をして、作業ファイルをデータファイルに コピーするだけです。

次に、フォームの処理のために上の例で使っているサブルーチンを のっけておきます。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int parse_form(char* s_in,long maxl,char* name[],char* value[],int *p_nfld)
{
        int i,cur_field;
        *p_nfld=0;
        i=0;
        cur_field=0;
        if(s_in[0]==NULL) return(-1);
        name[0]=s_in;
        while((s_in[++i]!=NULL)&&(i<maxl)){
                if(s_in[i]=='='){
                        s_in[i]=NULL;
                        value[cur_field]=s_in+i+1;
                }
                else if(s_in[i]=='&'){
                        s_in[i]=NULL;
                        cur_field++;
                        name[cur_field]=s_in+i+1;
                }
        }
        *p_nfld=cur_field+1;
        return(0);
}
        
int decode_form(char* s,long len)
{
        int i,j;
        char buf,*s1;
        if(len==0)return(-1);
        s1=(char*)malloc(len);
        for(i=0,j=0;i<len;i++,j++)
        {
                if(s[i]=='+'){s1[j]=' ';continue;}
                if(s[i]!='%') {s1[j]=s[i];continue;}
                buf=((s[++i]>='A') ? s[i]-'A'+10 : s[i]-'0');
                buf*=16;
                buf+=((s[++i]>='A') ? s[i]-'A'+10 : s[i]-'0');
                s1[j]=buf;
        }
        for(i=0;i<j;i++) s[i]=s1[i];
        s[i]='\0';
        free(s1);
        return(0);
}

Copyright 1999 Motoi Fujita