C言語CGIフレームワークのドラフト

未分類

スクラッチするのが面倒だけど、教材として必要になる…
というわけでChatGPTに作らせてみたら、狙った通りのアウトプットを提供してくれたので備忘録として残す…

セッションは、ファイル管理でオーダーしてみた。
休日にでもDB管理にカスタマイズオーダー出したらどうなるか試してみます。


📁 ディレクトリ構成(サンプルCGIフレームワーク)

/cgi-bin/
├── framework.h         // 共通ヘッダ(構造体・関数プロトタイプなど)
├── framework.c         // 共通処理+main
├── login_logic.c       // ログイン業務ロジック
├── dashboard_logic.c   // ログイン後の画面
├── logout_logic.c      // セッション削除
├── Makefile            // ビルド用

🔹 framework.h

#ifndef FRAMEWORK_H
#define FRAMEWORK_H

typedef struct NvPair {
    char* name;
    char* value;
    struct NvPair* next;
} NvPair;

NvPair* parse_post_data(void);
const char* get_value(NvPair* head, const char* key);
void free_nvpair(NvPair* head);

void print_header(void);
void redirect(const char* location);

void start_session(const char* username);
const char* get_session_user(void);
void destroy_session(void);

#endif

🔹 framework.c

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

#define SESSION_DIR "/tmp/sessions"
#define SESSION_COOKIE_NAME "SESSIONID"

extern void run_logic(NvPair*);  // 業務ロジック関数

// --- 中略:parse_post_data(), get_value(), free_nvpair() 定義(省略せず本番では書く) ---

void print_header(void) {
    printf("Content-Type: text/html\r\n\r\n");
}

void redirect(const char* location) {
    printf("Status: 302 Found\r\n");
    printf("Location: %s\r\n\r\n", location);
}

void start_session(const char* username) {
    char sid[33];
    snprintf(sid, sizeof(sid), "%08x%08x", rand(), rand());

    char path[256];
    snprintf(path, sizeof(path), "%s/%s", SESSION_DIR, sid);

    FILE* f = fopen(path, "w");
    if (f) {
        fprintf(f, "%s\n", username);
        fclose(f);
    }

    printf("Set-Cookie: %s=%s\r\n", SESSION_COOKIE_NAME, sid);
    print_header();
}

const char* get_session_user(void) {
    const char* cookie = getenv("HTTP_COOKIE");
    if (!cookie) return NULL;

    char* sid = strstr(cookie, SESSION_COOKIE_NAME "=");
    if (!sid) return NULL;
    sid += strlen(SESSION_COOKIE_NAME) + 1;

    char sid_val[65];
    sscanf(sid, "%64[^;]", sid_val);

    static char user[64];
    char path[256];
    snprintf(path, sizeof(path), "%s/%s", SESSION_DIR, sid_val);

    FILE* f = fopen(path, "r");
    if (!f) return NULL;
    fgets(user, sizeof(user), f);
    fclose(f);
    return user;
}

void destroy_session(void) {
    const char* cookie = getenv("HTTP_COOKIE");
    if (!cookie) return;

    char* sid = strstr(cookie, SESSION_COOKIE_NAME "=");
    if (!sid) return;
    sid += strlen(SESSION_COOKIE_NAME) + 1;

    char sid_val[65];
    sscanf(sid, "%64[^;]", sid_val);

    char path[256];
    snprintf(path, sizeof(path), "%s/%s", SESSION_DIR, sid_val);
    unlink(path);

    // クッキーを期限切れにする
    printf("Set-Cookie: %s=deleted; Max-Age=0\r\n", SESSION_COOKIE_NAME);
    print_header();
}

int main(void) {
    srand(time(NULL));

    NvPair* form_data = parse_post_data();
    run_logic(form_data);
    free_nvpair(form_data);
    return 0;
}

🔹 login_logic.c

#include <stdio.h>
#include <string.h>
#include "framework.h"

void run_logic(NvPair* data) {
    const char* user = get_value(data, "username");
    const char* pass = get_value(data, "password");

    if (user && pass && strcmp(user, "admin") == 0 && strcmp(pass, "1234") == 0) {
        start_session(user);
        redirect("/cgi-bin/dashboard.cgi");
    } else {
        print_header();
        printf("<h1>Login Failed</h1>");
        printf("<a href='/login.html'>Back</a>");
    }
}

🔹 dashboard_logic.c

#include <stdio.h>
#include "framework.h"

void run_logic(NvPair* _) {
    const char* user = get_session_user();
    if (!user) {
        redirect("/login.html");
        return;
    }

    print_header();
    printf("<h1>Welcome, %s!</h1>", user);
    printf("<a href='/cgi-bin/logout.cgi'>Logout</a>");
}

🔹 logout_logic.c

#include <stdio.h>
#include "framework.h"

void run_logic(NvPair* _) {
    destroy_session();
    redirect("/login.html");
}

🔹 Makefile

CC = gcc
CFLAGS = -Wall

all: login.cgi dashboard.cgi logout.cgi

login.cgi: login_logic.c framework.c
	$(CC) $(CFLAGS) -o $@ $^

dashboard.cgi: dashboard_logic.c framework.c
	$(CC) $(CFLAGS) -o $@ $^

logout.cgi: logout_logic.c framework.c
	$(CC) $(CFLAGS) -o $@ $^

clean:
	rm -f *.cgi

🔹 login.html(テスト用ログインフォーム)

<form method="POST" action="/cgi-bin/login.cgi">
  <input type="text" name="username" placeholder="Username" />
  <input type="password" name="password" placeholder="Password" />
  <button type="submit">Login</button>
</form>

✅ 補足

  • /tmp/sessions/www-data から書けるように chmod 777 推奨(教材用ならOK)。
  • クッキーがブラウザに保存されるので、再アクセス時にもセッション確認可。
  • セッションやパスワード管理は学習用途向けで、本番向けには要セキュア実装。

フレームワーク側にmain()関数が実装されていて、フレームワークを使う側でrun_logic()を実装すればよい構造にしているところがポイント(AIに説明するの苦労した…

休日にノンビリできそうだったら、手入れしてGitHubにUPするかもです。

コメント

タイトルとURLをコピーしました