実践Linux                 TOPへ  Cプログラミング目次へ

CGI  2009年7月更新

●HTMLフォーム
<HTML>
<HEAD>
</HEAD>

<BODY>

<FORM ACTION="http://www.kaisha.com/cgi-bin/test.cgi" METHOD=POST>
名前 <INPUT TYPE=text NAME="名前" SIZE=30><BR>
E-mail <INPUT TYPE=text NAME="E-mail" SIZE=48><BR><BR><BR>
内容 <Textarea NAME="コメント" ROWS=6 COLS=48></Textarea><BR><BR>
<INPUT TYPE=submit VALUE="送信"><INPUT TYPE=reset VALUE="リセット">
</FORM>

</BODY>
</HTML>

●Makefile
プログラムソースをtest.cで保存して、Makefileを以下の内容で準備し、makeを実行する。
字下げはTabキーで行うこと。

# Makefile for test

CC = gcc
TARGETS = test.cgi
OBJ = ${SRC:.c=.o}
SRC = test.c

$(TARGETS):${OBJ}
  ${CC} ${OBJ} -o ${TARGETS}

${OBJ}:${SRC}
  ${CC} ${SRC} -c -o ${OBJ}

clean:
  rm -f *.o $(TARGETS)

●CGI  SELinuxをOFFにしてテストしてください。
フォームの内容を表示するとともにdata.txt(所有者をapacheにしておく)に書き込み、さらにメールで送信。

//postメソッド Shift_JISで保存(フォームのHTMLに合わせる)

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

#define KOU (20) //フォームの数(nameとvalue配列の確保)
#define MAXLEN (4096) //フォーム全体の最大文字数(制限)
#define SUBJECT "Form Mail"
#define ADDRESS "info@mail.kaisha.com"

/*フォームを = と & で切り分けるルーチン。*/
int parse_form(char* line, char* name[], char* value[], int *p_nfld)
{
int i, cur_field;

*p_nfld = 0;
i = 0;
cur_field = 0;
if(line[0] == NULL) return(-1);
name[0] = line;
while((line[++i] != NULL) && (i < MAXLEN)){
if(line[i] == '='){
line[i] = NULL;
value[cur_field] = line + i + 1;
}
else if(line[i] == '&'){
line[i] = NULL;
cur_field ++;
name[cur_field] = line + i + 1;
}
}
*p_nfld = cur_field + 1;
return(0);
}

/*16進のコードをデコード。
文字列表現された「%16進数(2桁)」というところに当たると、ここの部分を通常のバイナリに置き換える。*/
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;}
//以下は'%'のとき(続く2文字の処理)
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);
}

/* メール送信 */
void send_mail(char* buf)
{
int pid;
int pipefds[2];
FILE *fp;

pipe(pipefds); //pipeでパイプを作成してから、子プロセスを作成。pipefds[1]に対して出力すると、その出力内容が pipefds[0]に送られる。

if ((pid=fork()) == 0) { //子プロセス(/bin/mailを実行する)
dup2(pipefds[0], 0); //パイプの受信用ファイルディスクリプタをdup2で複製し、execを行う。

close(pipefds[1]);

execl("/bin/mail", "mail", "-s", SUBJECT, ADDRESS, 0 ); //「/bin/mail」は「# which mail」コマンドで調べておく。

printf("Error: Can not exec mail.\n"); //execに失敗した場合
exit(1);
}

else { //親プロセス
close(pipefds[0]);

if ((fp = fdopen(pipefds[1], "w")) == NULL) {printf("メールを送信できませんでした。\n"); exit(0);}

fprintf(fp, buf); //メール本文
fprintf(fp, ".\n"); //メール本文を pipeに書き込み終えたら、最後に . だけの行を書き込む。

fclose(fp);

waitpid(pid, NULL, 0);
}

printf("メールを送信しました。\n");
}

main(int argc, char *argv[], char *envp[])
{
char *LineBuffer;
char m_buf[MAXLEN]; //メール用
int i;
char *name[KOU], *value[KOU];
long len;
int nfield;
FILE *fp;
time_t t;
struct tm *local_t;

time(&t);
local_t = localtime(&t);

len=atol((char*)getenv("CONTENT_LENGTH"));
if(len > MAXLEN) {printf("CONTENT_LENGTH > MAXLEN\n"); exit(0);}

LineBuffer=(char*)malloc(len+1);
scanf("%s",LineBuffer); //フォーム内容を取得

printf("Content-type: text/html\n\n");
puts("<HTML><HEAD><META http-equiv=\"Content-Type\" content=\"text/html; charset=Shift_JIS\"></HEAD><BODY>post3<BR><BR>");

puts(LineBuffer); //フォーム内容を生表示
puts("<BR><BR>");

for (i = 0; (char *)NULL != envp[i]; ++i)
printf("ENV[%d]=[%s]<BR>\n", i, envp[i]); //全環境変数を表示

puts("<BR>");
puts("#################################################################################<BR><BR>");

parse_form(LineBuffer, name, value, &nfield);

sprintf(m_buf, "%s", "");
for(i = 0; i <nfield; i++){
decode_form(name[i], strlen(name[i]));
decode_form(value[i], strlen(value[i]));
printf("%s ---> %s<BR>\n", name[i], value[i]);
sprintf(m_buf, "%s%s ---> %s\n", m_buf, name[i], value[i]); //メール用
}

puts("<BR>");
printf("Browser = %s<BR>\n", (char*)getenv("HTTP_USER_AGENT"));
printf("Address = %s<BR><BR>\n", (char*)getenv("REMOTE_ADDR"));

puts("#################################################################################<BR><BR>");
puts("フォームの内容をファイルに書き込みます。<BR><BR>");

if ((fp = fopen("data.txt", "a")) == NULL) //data.txtの所有者をapacheにしておく。
printf("警告 ファイルを開くことができません。\n");
else {
fprintf(fp, "###### %d年%d月%d日 %d時%d分%d秒 ######\n", local_t->tm_year + 1900, local_t->tm_mon + 1, local_t->tm_mday, local_t->tm_hour, local_t->tm_min, local_t->tm_sec);
for(i = 0; i < nfield; i++){
fprintf(fp, "%s ---> %s\n", name[i], value[i]);
}
fclose(fp);
printf("ファイルに書き込みました。<BR><BR>\n");
}

send_mail(m_buf);

printf("</BODY></HTML>\n");

free(LineBuffer);

}


●CGI(socketを用いたメール送信)  SELinuxをOFFにしてテストしてください。
フォームの内容を表示するとともにdata.txt(所有者をapacheにしておく)に書き込み、さらにメールで送信。

//postメソッド Shift_JISで保存(フォームのHTMLに合わせる)

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#define KOU (20) //フォームの数(nameとvalue配列の確保)
#define MAXLEN (4096) //フォーム全体の最大文字数(制限)
#define PORT 25

/*フォームを = と & で切り分けるルーチン。*/
int parse_form(char* line, char* name[], char* value[], int *p_nfld)
{
int i, cur_field;

*p_nfld = 0;
i = 0;
cur_field = 0;
if(line[0] == NULL) return(-1);
name[0] = line;
while((line[++i] != NULL) && (i < MAXLEN)){
if(line[i] == '='){
line[i] = NULL;
value[cur_field] = line + i + 1;
}
else if(line[i] == '&'){
line[i] = NULL;
cur_field ++;
name[cur_field] = line + i + 1;
}
}
*p_nfld = cur_field + 1;
return(0);
}

/*16進のコードをデコード。
文字列表現された「%16進数(2桁)」というところに当たると、ここの部分を通常のバイナリに置き換える。*/
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;}
//以下は'%'のとき(続く2文字の処理)
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);
}

/* メール送信 */
int send_mail(char* buf)
{
int sf; //ソケット
struct sockaddr_in addr; //ソケットのアドレス
char buff[BUFSIZ];

if((sf = socket(AF_INET, SOCK_STREAM, 0)) == -1) //クライアント用ソケットの作成
{
perror("client: socket");
return 1;
}

addr.sin_family = AF_INET;
addr.sin_addr.s_addr = inet_addr("127.0.0.1");
addr.sin_port = htons(PORT);


if(connect(sf, (struct sockaddr *)&addr, sizeof(addr)) == -1) //クライアントのソケットをサーバーのソケットに接続
{
perror("client: connect");
return 1;
}

printf("メールを送信します。<BR>\n");

write(sf, "HELO <kaisha.com>\r\n", strlen("HELO <kaisha.com>\r\n"));
read(sf, buff, BUFSIZ);
printf("HELO from server = %s<BR>\n", buff);
write(sf, "MAIL FROM:<form@kaisha.com>\r\n", strlen("MAIL FROM:<form@kaisha.com>\r\n"));
read(sf, buff, BUFSIZ);
printf("MAIL FROM from server = %s<BR>\n", buff);
write(sf, "RCPT TO:<info@kaisha.com>\r\n", strlen("RCPT TO:<info@kaisha.com>\r\n"));
read(sf, buff, BUFSIZ);
printf("RCPT TO from server = %s<BR>\n", buff);
write(sf, "DATA\r\n", strlen("DATA\r\n"));
read(sf, buff, BUFSIZ);
printf("DATA from server = %s<BR>\n", buff);
write(sf, buf, strlen(buf)+1);
write(sf, "\r\n.\r\n", strlen("\r\n.\r\n"));
read(sf, buff, BUFSIZ);
printf(". from server = %s<BR>\n", buff);
write(sf, "QUIT\r\n", strlen("QUIT\r\n"));
read(sf, buff, BUFSIZ);
printf("QUIT from server = %s<BR>\n", buff);

close(sf);

return 0;
}

main(int argc, char *argv[], char *envp[])
{
char *LineBuffer;
char m_buf[MAXLEN]; //メール用
int i;
char *name[KOU], *value[KOU];
long len;
int nfield;
FILE *fp;
time_t t;
struct tm *local_t;

time(&t);
local_t = localtime(&t);

len=atol((char*)getenv("CONTENT_LENGTH"));
if(len > MAXLEN) {printf("CONTENT_LENGTH > MAXLEN\n"); exit(0);}

LineBuffer=(char*)malloc(len+1);
scanf("%s",LineBuffer); //フォーム内容を取得

printf("Content-type: text/html\n\n");
puts("<HTML><HEAD><META http-equiv=\"Content-Type\" content=\"text/html; charset=Shift_JIS\"></HEAD><BODY>post3<BR><BR>");

puts(LineBuffer); //フォーム内容を生表示
puts("<BR><BR>");

for (i = 0; (char *)NULL != envp[i]; ++i)
printf("ENV[%d]=[%s]<BR>\n", i, envp[i]); //全環境変数を表示

puts("<BR>");
puts("#################################################################################<BR><BR>");

parse_form(LineBuffer, name, value, &nfield);

sprintf(m_buf, "%s", "");
for(i = 0; i <nfield; i++){
decode_form(name[i], strlen(name[i]));
decode_form(value[i], strlen(value[i]));
printf("%s ---> %s<BR>\n", name[i], value[i]);
sprintf(m_buf, "%s%s ---> %s\n", m_buf, name[i], value[i]); //メール用
}

puts("<BR>");
printf("Browser = %s<BR>\n", (char*)getenv("HTTP_USER_AGENT"));
printf("Address = %s<BR><BR>\n", (char*)getenv("REMOTE_ADDR"));

puts("#################################################################################<BR><BR>");
puts("フォームの内容をファイルに書き込みます。<BR><BR>");

if ((fp = fopen("data.txt", "a")) == NULL) //data.txtの所有者をapacheにしておく。
printf("警告 ファイルを開くことができません。\n");
else {
fprintf(fp, "###### %d年%d月%d日 %d時%d分%d秒 ######\n", local_t->tm_year + 1900, local_t->tm_mon + 1, local_t->tm_mday, local_t->tm_hour, local_t->tm_min, local_t->tm_sec);
for(i = 0; i < nfield; i++){
fprintf(fp, "%s ---> %s\n", name[i], value[i]);
}
fclose(fp);
printf("ファイルに書き込みました。<BR><BR>\n");
}

send_mail(m_buf);

printf("</BODY></HTML>\n");

free(LineBuffer);

}


TOPへ  Cプログラミング目次へ