/********************************************************************* 簡単な掲示板プログラム webbbs.c 松阪大学 奥村晴彦 インストールは, gcc -Wall -O webbbs.c -o webbbs.cgi とします。たとえば次の形で使います。 例:

お名前:

メールアドレス:

タイトル:

メッセージ:

*********************************************************************/ /* 書き換えを要するところ(ディレクトリの絶対パス) */ #define COUNTFILE "count" /* 以下は書き換え不要 */ #include #include #include #include #include #include #include #include #include /* htonl, ntohl */ #define MYEOF (-1) #define MYAMP (-2) #define MYEQU (-3) #define iseuc(c) (((c)>=0xa1 && (c)<=0xfe)) #define issjis1(c) (((c)>=0x81 && (c)<=0x9f) || ((c)>=0xe0 && (c)<=0xef)) #define issjis2(c) ((c)>=0x40 && (c)<=0xfc && (c)!=0x7f) #define ishankana(c) ((c)>=0xa0 && (c)<=0xdf) int length; char email[128]; char name[128]; char subject[128]; /** エラーメッセージを表示して終了 */ void error(char *format, ...) { va_list argptr; va_start(argptr, format); printf("Content-type: text/html\n\n"); printf("\n"); printf("\n"); printf("\n"); printf("エラー\n"); printf("\n"); printf("\n"); printf("

エラー!

\n

"); vprintf(format, argptr); va_end(argptr); printf("\n


\n

戻る\n"); printf("\n"); exit(0); } /** カウントを COUNTFILE から読み, 一つ増やした値を COUNTFILE に入れ,新しいカウント値を返す。 */ unsigned long incr_count(void) { int fd; unsigned long ncount, hcount; struct flock lock; if ((fd = open(COUNTFILE, O_RDWR | O_CREAT, 0666)) < 0) return 0; lock.l_type = F_WRLCK; lock.l_start = 0; lock.l_whence = SEEK_SET; lock.l_len = 0; /* ロック対象はファイル全体 */ /* ↓ 他のプログラムがロックしている間待つ */ if (fcntl(fd, F_SETLKW, &lock) < 0) return 0; if (read(fd, &ncount, sizeof(ncount)) == sizeof(ncount)) { hcount = ntohl(ncount); } else { hcount = 0; } if (++hcount == 0) hcount = 1; ncount = htonl(hcount); lseek(fd, 0, SEEK_SET); write(fd, &ncount, sizeof(ncount)); close(fd); /* close() するとロックは自動解除される */ return hcount; } /** 標準入力(この場合は Web の POST)から文字を取得する。 POST に特有の書式を通常の文字に変換する。 < > & の文字も < > & に変換する。 */ int mygetchar(void) { int c, d, e; static char *p = ""; if (*p != '\0') return *p++; if (--length < 0) return MYEOF; if ((c = getchar()) == EOF) { return MYEOF; /* shouldn't happen */ } else if (c == '&') { return MYAMP; /* record separator */ } else if (c == '=') { return MYEQU; /* field separator */ } else if (c == '+') { return ' '; } else if (c == '%') { if ((length -= 2) < 0) return MYEOF; d = getchar(); e = getchar(); d = (d >= 'A') ? (d & 0xdf) - 'A' + 10 : d - '0'; e = (e >= 'A') ? (e & 0xdf) - 'A' + 10 : e - '0'; if (d >= 0 && d < 16 && e >= 0 && e < 16) c = 16 * d + e; } if (c == '<') { p = "<"; return *p++; } if (c == '>') { p = ">"; return *p++; } if (c == '&') { p = "&"; return *p++; } return c; } /** シフトJISをJISに変換する */ #ifdef SJIS static void sjis_to_jis(int *ph, int *pl) { if (*ph <= 0x9f) { if (*pl < 0x9f) *ph = (*ph << 1) - 0xe1; else *ph = (*ph << 1) - 0xe0; } else { if (*pl < 0x9f) *ph = (*ph << 1) - 0x161; else *ph = (*ph << 1) - 0x160; } if (*pl < 0x7f) *pl -= 0x1f; else if (*pl < 0x9f) *pl -= 0x20; else *pl -= 0x7e; } #endif /** 標準入力(この場合は Web の POST)をスキャンし, ファイル出力する */ int scan_and_write(FILE *f) { int c, d; enum {LHS, SUBJ, NAME, EMAIL, MSG} state = LHS; char lhs[16]; int i = 0; int msglen = 0; while ((c = mygetchar()) != MYEOF) { d = 0; #ifdef SJIS if (issjis1(c)) { d = mygetchar(); if (issjis2(d)) { sjis_to_jis(&c, &d); c |= 0x80; d |= 0x80; } else { c = d = 0; } } else if (ishankana(c)) { d = c; c = 0x8e; } #else if (iseuc(c)) { d = mygetchar(); if (! iseuc(d)) { c = d = 0; } } else if (c == 0x8e) { d = mygetchar(); if (! ishankana(d)) { c = d = 0; } } #endif else if (c == MYEQU) { if (state == LHS) { lhs[i] = '\0'; i = 0; if (strcmp(lhs, "email") == 0) { fputs("メールアドレス: ", f); state = EMAIL; } else if (strcmp(lhs, "name") == 0) { fputs("名前: ", f); state = NAME; } else if (strcmp(lhs, "subject") == 0) { fputs("タイトル: ", f); state = SUBJ; } else if (strcmp(lhs, "message") == 0) { putc('\n', f); state = MSG; } } else { c = '='; /* shouldn't happen */ } } else if (c == MYAMP) { putc('\n', f); if (state == EMAIL) email[i] = '\0'; if (state == NAME) name[i] = '\0'; if (state == SUBJ) subject[i] = '\0'; state = LHS; i = 0; } else if (c == '\n') { putc('\n', f); c = 0; } else if (c == '\t') { fputs(" ", f); c = 0; } else if (c < ' ') { c = 0; } if (c <= 0) { /* do nothing */ } else if (state == LHS) { if (c < 0x80 && i < sizeof(lhs) - 1) lhs[i++] = c; } else { putc(c, f); if (d != 0) putc(d, f); if (state == EMAIL) { if (c < 0x80 && i < sizeof(email) - 1) email[i++] = c; } else if (state == NAME) { if (i < sizeof(name) - 2) { name[i++] = c; if (d != 0) name[i++] = d; } } else if (state == SUBJ) { if (i < sizeof(subject) - 2) { subject[i++] = c; if (d != 0) subject[i++] = d; } } else if (state == MSG) { if (c > ' ' && !(c == 0xa1 && d == 0xa1)) msglen++; } } } return msglen; } int invisible(unsigned char *s) { while (*s) { if (*s <= ' ') s++; else if (*s == 0xa1 && *(s+1) == 0xa1) s += 2; else break; } return (*s == '\0'); } /** メイン */ int main() { int fd; char *p; time_t t; struct tm *lt; char timestr[20]; char filename[128]; char line[1024]; unsigned long count; FILE *outfile; if ((p = getenv("CONTENT_LENGTH")) == NULL) error("CONTENT_LENGTH が得られません。"); if ((length = atoi(p)) <= 0) error("CONTENT_LENGTH の値 %d が変です。", length); if (length > 50000) error("メッセージ(%d バイト)が長すぎます。", length); if ((count = incr_count()) == 0) error("%s が読み書きできません。", filename); sprintf(filename, "%lu.html", count); t = time(NULL); lt = localtime(&t); strftime(timestr, 20, "%Y-%m-%d %X", lt); if ((outfile = fopen(filename, "w")) == NULL) error("ファイル %s が開けません。", filename); fprintf(outfile, "\n"); fprintf(outfile, "\n"); fprintf(outfile, "

\n");
    fprintf(outfile, "日時: %s\n", timestr);
    if ((p = getenv("REMOTE_ADDR")) != NULL)
        fprintf(outfile, "IPアドレス: %s\n", p);
    if (scan_and_write(outfile) == 0) {
        fclose(outfile);
        unlink(filename);
        error("本文がありません");
    }
    fprintf(outfile, "
\n"); fclose(outfile); #ifdef OKUMURA /* send mail to okumura */ sprintf(line, "/bin/mail okumura <%s", filename); system(line); #endif if (invisible(subject)) strcpy(subject, "(無題)"); fd = open("index.html", O_WRONLY | O_APPEND, 0644); if (fd < 0) { fd = open("index.shtml", O_WRONLY | O_APPEND, 0644); if (fd < 0) { fd = open("index.htm", O_WRONLY | O_APPEND, 0644); if (fd < 0) fd = open("index.html", O_WRONLY | O_CREAT | O_APPEND, 0644); } } if (fd > 0) { /* timestr[10] = '\0'; */ sprintf(line, "%lu: %s %s %s
\n", count, timestr, count, subject, name); write(fd, line, strlen(line)); close(fd); } printf("Content-type: text/html\n\n"); printf("\n"); printf("\n"); printf("\n"); printf("Thank You\n"); printf("\n"); printf("\n"); printf("

ありがとうございます

"); printf("

メッセージ番号 %lu を書き込みました。\n", count); if (fd <= 0) printf("

メッセージ一覧が更新できませんでした。" "管理人にご連絡下さい。\n"); printf("


\n"); printf("

戻る" "……戻ってから再読み込み(更新)が必要かもしれません\n"); printf("\n"); return 0; }