激凹みから立ち直る方法

前回 http://oshiete.goo.ne.jp/qa/7765400.html

からあれこれしてフォームの値をクッキーに保存できるようになったのですが、バグが出てきました。

一、クッキーが存在しないとエラーが出る
ニ、Deta1関数を使って文字列の分解を試みるもうまく分解されない

この2つのバグを解決するにはどうしたら直せますか?

---以下ソース---

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

char *nameset[2],*valueset[2];
char *nameset2[2],*valueset2[2];

int Deta1(char *a,int b);
int Dcd(char *set,int a);

void get_Form(void);

void get_cookie(void);
void set_cookie(void);

int hen(char *buf, char *mae, char *ato);

void Page(int mode);

int main(void) {

char *nameset[2],*valueset[2];
char *nameset2[2],*valueset2[2];

printf("Content-type: text/html\n");
get_Form();
set_cookie();
get_cookie();
printf("\n");

Page(0);

}

int Deta1(char *a,int b){

int i=0,cn=0;

if(a[0]==NULL){
return(-1);
}

nameset[0]=a;

while((a[++i]!=NULL)&&(i<b)){

/* 項目の分解 */
if(a[i]=='='){
a[i]=NULL;
valueset[cn]=a+i+1;
}

/* データ項目で分解 */
else if(a[i]=='&'){
a[i]=NULL;
cn++;
nameset[cn]=a+i+1;
}
}

return cn+1;

}

int Dcd(char *set,int a){

int i,j;
char buf,*tmp;

if(a==0){
return -1;
}

tmp=(char*)malloc(a);
for(i=0,j=0;i<a;i++,j++){
if(set[i]=='+'){tmp[j]=' ';continue;}
if(set[i]!='%'){tmp[j]=set[i];continue;}
if(set[++i]>='A'){buf=set[i]-'A'+10;}
else{buf=set[i]-'0';}

buf*=16;
if(set[++i]>='A'){buf+=set[i]-'A'+10;}
else{buf+=set[i]-'0';}

tmp[j]=buf;
}

for(i=0;i<j;i++){
set[i]=tmp[i];
}
set[i]='\0';
free(tmp);

return 0;
}

void get_Form(void){

int a=0;
int i=0;

char *chr=NULL;

if ( getenv("CONTENT_LENGTH")!=NULL ){
a = atoi( getenv("CONTENT_LENGTH") );
}

chr=(char *)malloc(a+1);
scanf("%s",chr);
chr[a] = '\0';

if (a==0){
return ;
}

int deta1=Deta1(chr,a);

}

void get_cookie(void){

int i=0,cn=0;
int a=NULL;
char *b;

if( (getenv("HTTP_COOKIE"))!=NULL){
a=strlen(getenv("HTTP_COOKIE"));
}

if(a==NULL){

}

b=getenv("HTTP_COOKIE");

while((b[++i]!=NULL)&&(i<a)){
if(b[i]=='='){
b[i]=NULL;
nameset2[0]=b+i+1;
}

/* 項目の分解*/
if(b[i]=='-'){
b[i]=NULL;
valueset2[cn]=b+i+1;
}

/*データ項目で分解*/
else if(b[i]=='&'){
b[i]=NULL;
cn++;
nameset2[cn]=b+i+1;
}
}

for(i=0;i<cn+1;i++){
Dcd(nameset2[i],strlen(nameset2[i]));
Dcd(valueset2[i],strlen(valueset2[i]));
}

}


void set_cookie(void) {
time_t timer;
struct tm *tset;
char expires[256];

char *name="sskchat";
int kikan=86400*90;
char *set[2];
int i;

for(i=0;i<2;i++){
set[i]=NULL;
}

for(i=0;i<2;i++){
set[i]=valueset[i];
}

for(i=0;i<2;i++){
if(set[i]==NULL){
set[i]="no";
}
}

timer = time(NULL);
timer += kikan;
tset = gmtime(&timer);

strftime(expires, 255, "%a, %d-%b-%Y %H:%M:%S GMT", tset);

printf("Set-Cookie:%s=name-%s&mail-%s; expires=%s;\n",name,set[0],set[1],expires);

}

void Page(int mode){

FILE *fp;

char *f1="!name!",*h1;
char *f2="!mail!",*h2;

if(valueset2[0]==NULL||strcmp("!name!",valueset2[0])==0){
h1="";
}

else{
h1=valueset2[0];
}

if(valueset2[1]==NULL||strcmp("!mail!",valueset2[1])==0){
h2="";
}

else{
h2=valueset2[1];
}

char buf[200];
char set[200];

fp = fopen("ren.html", "r+");

while( fgets( set, 200, fp ) != NULL ){
strcpy(buf,set);
while(hen(buf, f1, h1));
while(hen(buf, f2, h2));
printf("%s", buf);
}

fclose(fp);

}

int hen(char *buf, char *mae, char *ato){

char *nw;
size_t zen,go;

zen = strlen(mae);
go = strlen(ato);

if(zen == 0 || (nw = strstr(buf, mae)) == NULL){
return 0;
}

memmove(nw + go, nw + zen, strlen(buf) - (nw + zen - buf ) + 1);
memcpy(nw, ato, go);
return 1;
}

---ソースここまで---

---ren.htmlの内容---
<form action="first.exe" method="post">
名前:<input type="text" name="name" size="100" value="!name!"><br><br>
メール:<input type="text" name="mail" size="100" value="!mail!"><br><br>
本文:<textarea name="text" cols="70" rows="10"></textarea><br><br>
<input type="submit" value=" 送 信 "><br>
</form>

A 回答 (2件)

質問1



httpヘッダ出力パート
----------------------------------------
printf("Content-type: text/html\n");
get_Form();
set_cookie();
get_cookie();
printf("\n");
----------------------------------------

----------------------------------------
set_cookie();
get_cookie();
----------------------------------------
が"×"です。

クッキーの設定はhttpヘッダから可能ですが、参照は環境変数を介する必要があります(ソースコードもそうなってるはずですが)。
環境変数はクライアントからWebサーバに対して「CGIを介したプログラム」の起動オーダーが発生した際に、その実行プロセスの環境を構成するためにプロセス起動時に作り上げられるものです。(注:CGIはプログラムのことでは有りません。Webを介してプログラムを起動する仕組み→インターフェース:Common Gateway Interface のことです。だから標準入出力機能を持った言語なら何でもOKなのです。)
クッキーセット直後、同一プロセスからクッキーの環境変数を参照することは無意味です。クッキーはブラウザ側に送られても、環境変数はサーバ側にあって自動更新はされません。CGIプロセスの途中でサーバ側からクライアントのデータぶっこ抜き放題ならセキュリティもクソもあったもんじゃないでしょう。
あくまでも「クライアントの指示」でサーバ側のプロセス開始オーダーがあったときのみ必要なデータをクライアント側が送信し、サーバはそこからプロセスに必要なデータから環境変数群を構成してプログラムを開始するのです。
セットしたクッキーを参照したいなら、そのプロセス終了後に新たなプロセス(プログラムを別にする)で参照するしかないでしょう。

要するにクッキーを食わせるプログラムと、クッキーを参照するプログラムは分けて起動すること。

あと「サーバサイドのプログラムまたはプロセス」と「クライアントサイドのプログラムまたはプロセス」の区別をつけて考えることです。
さらに、HTTPをはじめとするTCP/IPについてもプロトコルとは何ぞや程度は・・・。 C/Sやネットプログラミングに限らず言語だけ知ってても仕組みを知らんと実装はなかなか難しいということですね。


質問2

関数「Data1」が挙動不審なので作り直してみました。
nameset[0]に全POSTデータを入れた後、共通のポインタから直にいじり倒しているので最終的にvaluesetの最後と同じになっています。
最適化はしてないので教科書みたいなリスト(しかも古いスタイル)になりますが・・・
---------------------------------------------------------
int Deta1(char *a,int b){
 int i,nMax;
 char *aTmp[30]; /* とりあえず30個まで耐えられるように */
 char *sTmp,*sSTW;

 sTmp = a;

 i=0;
 while((aTmp[i] = strtok(sTmp, "&")) != NULL){
  sTmp = NULL;
  i++;
 }
 nMax = i;

 for(i=0; i < nMax; i++){
  sSTW = aTmp[i];
  nameset[i] = strtok(sSTW, "=");
  valueset[i] = strtok(NULL, "=");
 }

 return nMax;
}
---------------------------------------------------------
インデントは全角にしてるのでコピペ時には注意
できれば関数名は別のを考えたほうがいいと思う。代入する変数名とかぶってるし。
あと、評判の悪いstrtok使ってるけど、この処理って基本半角文字だけだから適材適所ってことで
できれば値はエンコードされてるはずだからデコードして出したほうが理にかなってるかも

こんな感じで答えになってるかな・・・。

それにしても、あらためてCの文字列・配列処理は読み書きがめんどくさくて歳を感じてしまいました。
perlで作ったほうが開発効率いいと思う。 正規表現の変態さ加減なんかはもう・・・。

この回答への補足

<<クッキーセット直後、同一プロセスからクッキーの環境変数を参照することは無意味です。クッキーはブラウザ側に送られても、環境変数はサーバ側にあって自動更新はされません。CGIプロセスの途中でサーバ側からクライアントのデータぶっこ抜き放題ならセキュリティもクソもあったもんじゃないでしょう。
あくまでも「クライアントの指示」でサーバ側のプロセス開始オーダーがあったときのみ必要なデータをクライアント側が送信し、サーバはそこからプロセスに必要なデータから環境変数群を構成してプログラムを開始するのです。
セットしたクッキーを参照したいなら、そのプロセス終了後に新たなプロセス(プログラムを別にする)で参照するしかないでしょう。

私のプログラムでおかしなところはまさしくこれですかね
何度かバグらせまくってなんとなく感じてはいましたがはっきり形を教えていらだけましてありがとうございました

問題は方法が分かってもどのように書けばいいか分からない事ですね

<<あと「サーバサイドのプログラムまたはプロセス」と「クライアントサイドのプログラムまたはプロセス」の区別をつけて考えることです。

その区別すら分かっていないので調べる必要がありそうですね


<<科書みたいなリスト(しかも古いスタイル)になりますが・・

教科書みたいな書き方は非常にありがたいです
是非参考にさせていただきます
ありがとうございました


<<あと、評判の悪いstrtok使ってるけど、この処理って基本半角文字だけだから適材適所ってことで
できれば値はエンコードされてるはずだからデコードして出したほうが理にかなってるかも

ああ、なにやら扱いにくいと感じたのはそういう理由ですか
ちゃんと扱えれば動かせるはずだと思って使ってました

<<perlで作ったほうが開発効率いいと思う。 正規表現の変態さ加減なんかはもう・・・
皆さんそういわれますが最終的に戦略シュミレーションのブラウザゲームを作りたいのでperlではちょっと・・・。
といった理由なのでperlは使う気がないのですよ
ごめんなさいね

補足日時:2012/11/10 03:22
    • good
    • 0
この回答へのお礼

あれから作ってみて完全にバグを取り除けましたので同じ様な疑問をもった人のために書き込んでおきます。
(どうかけばいいか探してもグーグルで検索した時に見つからなかったため)
これでちゃんとしたチャットや掲示板が作れる♪

---ソースの内容---
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

char *nameset[3],*valueset[3];
char *nameset2[2],*valueset2[2];

int Deta1(char *a,int b);
int Dcd(char *set,int a);

void get_Form(void);
void set_cookie(void);
void get_cookie(void);

int hen(char *buf, char *mae, char *ato);
void Page(void);

void main(void){

printf("Content-type: text/html\n");

get_Form();
if(valueset[0]!=NULL){
set_cookie();
}
get_cookie();

printf("\n");

Page();

}

int Deta1(char *a,int b){

int i=0,cn=0;

if(a[0]==NULL){
return(-1);
}

nameset[0]=a;

while((a[++i]!=NULL)&&(i<b)){

/* 項目の分解 */
if(a[i]=='='){
a[i]=NULL;
valueset[cn]=a+i+1;
}

/* データ項目で分解 */
else if(a[i]=='&'){
a[i]=NULL;
cn++;
nameset[cn]=a+i+1;
}
}

return cn+1;

}

int Dcd(char *set,int a){

int i,j;
char buf,*tmp;

if(a==0){
return -1;
}

tmp=(char*)malloc(a);
for(i=0,j=0;i<a;i++,j++){
if(set[i]=='+'){tmp[j]=' ';continue;}
if(set[i]!='%'){tmp[j]=set[i];continue;}
if(set[++i]>='A'){buf=set[i]-'A'+10;}
else{buf=set[i]-'0';}

buf*=16;
if(set[++i]>='A'){buf+=set[i]-'A'+10;}
else{buf+=set[i]-'0';}

tmp[j]=buf;
}

for(i=0;i<j;i++){
set[i]=tmp[i];
}
set[i]='\0';
free(tmp);

return 0;
}

void get_Form(void){

int a=0;

char *chr=NULL;

if ( getenv("CONTENT_LENGTH")!=NULL ){
a = atoi( getenv("CONTENT_LENGTH") );
}

chr=(char *)malloc(a+1);
scanf("%s",chr);
chr[a] = '\0';

if (a==0){
return;
}

int deta1=Deta1(chr,a);

}

void set_cookie(void) {

time_t timer;
struct tm *tset;
char expires[256];

char *name="sskchat";
int kikan=86400*90;
char *set[2];
int i;

for(i=0;i<2;i++){
set[i]=valueset[i];
}

timer = time(NULL);
timer += kikan;
tset = gmtime(&timer);

strftime(expires, 255, "%a, %d-%b-%Y %H:%M:%S GMT", tset);

printf("Set-Cookie:%s=name-%s&mail-%s; expires=%s;\n",name,set[0],set[1],expires);
}

void get_cookie(void){

int i=0,cn=0;
int a=NULL;
char *b;

if((getenv("HTTP_COOKIE"))!=NULL){
a=strlen(getenv("HTTP_COOKIE"));

b=getenv("HTTP_COOKIE");

while((b[++i]!=NULL)&&(i<a)){
if(b[i]=='='){
b[i]=NULL;
nameset2[0]=b+i+1;
}

/* 項目の分解*/
if(b[i]=='-'){
b[i]=NULL;
valueset2[cn]=b+i+1;
}

/*データ項目で分解*/
else if(b[i]=='&'){
b[i]=NULL;
cn++;
nameset2[cn]=b+i+1;
}
}
}

for(i=0;i<4;i++){
if(nameset[i]!=NULL){
Dcd(nameset[i],strlen(nameset[i]));
}
if(valueset[i]!=NULL){
Dcd(valueset[i],strlen(valueset[i]));
}
if(nameset2[i]!=NULL){
Dcd(nameset2[i],strlen(nameset2[i]));
}
if(valueset2[i]!=NULL){
Dcd(valueset2[i],strlen(valueset2[i]));
}
}

}

void Page(void){

FILE *fp;
char buf[200];

char *f1="!name!",*h1="";
char *f2="!mail!",*h2="";

if((valueset[0]==NULL)&&(valueset2[0]!=NULL)){
h1=valueset2[0];
}

if(valueset[0]!=NULL){
h1=valueset[0];
}

if((valueset[1]==NULL)&&(valueset2[1]!=NULL)){
h2=valueset2[1];
}

if(valueset[1]!=NULL){
h2=valueset[1];
}

fp = fopen("ren.html", "r+");

while( fgets( buf, 200, fp ) != NULL ){
while(hen(buf, f1, h1));
while(hen(buf, f2, h2));
printf("%s", buf);
}

fclose(fp);

}

int hen(char *buf, char *mae, char *ato){

char *nw;
size_t zen,go;

zen = strlen(mae);
go = strlen(ato);

if(zen == 0 || (nw = strstr(buf, mae)) == NULL){
return 0;
}

memmove(nw + go, nw + zen, strlen(buf) - (nw + zen - buf ) + 1);
memcpy(nw, ato, go);
return 1;
}

---ren.htmlの内容---
<html>
<head>
<title>フォーム</title>
</head>
<body>
<form action="first.exe" method="post">
名前:<br><input type="text" name="name" size="100" value="!name!"><br><br>
メール:<br><input type="text" name="mail" size="100" value="!mail!"><br>
本文:<br><textarea name="text" cols="70" rows="10"></textarea><br><br>
<input type="submit" value=" 送 信 "><br>
</form>
</body>
</html>

お礼日時:2012/11/28 11:38

こまかく追い掛けてないけど


・getenvしたポインタの先をそのまま変えちゃって大丈夫?
・mallocとfreeは対になってますか?
・次のものが、 グローバルなものと、main関数ローカルなものがあるけど、Data2関数でグローバルを変えて、それをmain関数内でローカルの方でチェックした、とかないですか?
char *nameset[2],*valueset[2];
char *nameset2[2],*valueset2[2];

あと、エラーにはならないだろうけど、厳密には、NULLと'\0'は違います。

この回答への補足

・getenvしたポインタの先をそのまま変えちゃって大丈夫?
おそらくは・・・・

・mallocとfreeは対になってますか?
なっているはずです(ミスってなければ)

・次のものが、 グローバルなものと、main関数ローカルなものがあるけど、Data2関数でグローバルを変えて、それをmain関数内でローカルの方でチェックした、とかないですか?
char *nameset[2],*valueset[2];
char *nameset2[2],*valueset2[2];

そういえばおかしな処理していました
教えていただきありがとうございました

補足日時:2012/11/10 03:00
    • good
    • 0

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!