DeepSeek+半天实现一个lighttpd服务程序

问题:

几个问题:

1、lighttpd作为服务器,如何实现CGI程序对开发板的IP、子网掩码等进行配置。

2、CGI登录怎么实现。

3、模版文件如何实现。通过CGI程序读取html文件后,动态替换其中的模板参数。


交叉编译:

./configure CC=arm-rockchip830-linux-uclibcgnueabihf-gcc 
--host=arm-openwrt-linux-muslgnueabi target=arm-linux 
--enable-utf8 
--enable-unicode-properties 
--prefix=$(pwd)/install 
--without-pcre2 --without-zlib


修改modules.conf配置CGI

##
## plain old CGI (mod_cgi)
##
include conf_dir + "/conf.d/cgi.conf"

修改/conf.d/cgi.conf

cgi.assign                 = ( ".pl"  => "/usr/bin/perl",
                               ".rb"  => "/usr/bin/ruby",
                               ".cgi" => "",
                               ".erb" => "/usr/bin/eruby",
                               ".py"  => "/usr/bin/python" )

网页保存路径:lighttpd.conf 注释掉用户名配置

#server.username  = "lighttpd"
#server.groupname = "lighttpd"

##
## Document root
##
server.document-root = server_root + "/htdocs"

启动:将install目录修改为lighttpd

/lighttpd/sbin/lighttpd -f /lighttpd/config/lighttpd.conf -m /lighttpd/lib


CGI程序保存路径,这个问题其实也好理解,http服务的网页根路径下,存放一个cgi-bin的路径,然后将cgi程序拷贝的这个路径下。


问题2:登录如何实现。

网页侧:

    <div class="layui-container" style="margin-top: 20px;">
        <form class="layui-form" action="/cgi-bin/login.cgi"  method="POST">
            <div class="layui-form-item">
                <label class="layui-form-label">用户名</label>
                <div class="layui-input-block">
                    <input type="text" name="username" required lay-verify="required" placeholder="请输入用户名" autocomplete="off" class="layui-input">
                </div>
            </div>

            <div class="layui-form-item">
                <label class="layui-form-label">密码</label>
                <div class="layui-input-block">
                    <input type="password" name="password" id="password" required lay-verify="required" placeholder="请输入密码" autocomplete="off" class="layui-input">
                </div>
            </div> 

            <div class="layui-form-item">
                <div class="layui-input-block">
                    <button class="layui-btn" lay-submit lay-filter="formDemo">登录</button> 
                </div>
            </div>
        </form>
    </div>

CGI侧:login.c 编译命令:arm-openwrt-linux-muslgnueabi-gcc -o login.cgi login.c

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

// 函数:解码 URL 编码的字符串
void url_decode(char *str) {
    char *src = str;
    char *dst = str;
    while (*src) {
        if (*src == '+') {
            *dst = ' ';
        } else if (*src == '%' && src[1] && src[2]) {
            *dst = (char) strtol(src + 1, NULL, 16);
            src += 2;
        } else {
            *dst = *src;
        }
        src++;
        dst++;
    }
    *dst = '\0';
}

// 解析 URL 编码的表单数据
void parse_form_data(char *data, char *username, char *password) {
    char *token;
    char *key, *value;
		  
#if 0
	printf("<h1>Received Data:</h1>\n");

	printf("<p>%s</p>\n", data);
	printf("<ul>\n");

	printf("</ul>\n"); 
#endif 
	char *saveptr; 
	token = strtok_r(data, "&", &saveptr);
	while (token) {
		key = strtok(token, "=");
		value = strtok(NULL, "=");

		if (key && value) {
			url_decode(key);
			url_decode(value);
			//printf("<li><strong>%s:</strong> %s</li>\n", key, value);
			if (strcmp(key, "username") == 0) {
				strncpy(username, value, 50);
			}
			if (strcmp(key, "password") == 0) {
				strncpy(password, value, 50);
			}
			 
		}

		token = strtok_r(NULL, "&", &saveptr);
	}
}

static char* readFileContents(const char* filename, size_t* fileSize) {
    if (filename == NULL){
	return NULL;
    }
    // 打开文件
    FILE* file = fopen(filename, "rb");
    if (file == NULL) {
        perror("Failed to open file");
        return NULL;
    }

    // 移动文件指针到末尾,获取文件大小
    fseek(file, 0, SEEK_END);
    long size = ftell(file);
    if (size < 0) {
        perror("Failed to determine file size");
        fclose(file);
        return NULL;
    }

    // 重置文件指针到文件开头
    rewind(file);

    // 分配内存缓冲区
    char* buffer = (char*)malloc(size + 128); // +1 是为了存储结尾的 '\0'
    if (buffer == NULL) {
        perror("Failed to allocate memory");
        fclose(file);
        return NULL;
    }

    // 读取文件内容到缓冲区
    size_t bytesRead = fread(buffer, 1, size, file);
    if (bytesRead != size) {
        perror("Failed to read the complete file");
        free(buffer);
        fclose(file);
        return NULL;
    }

    // 添加字符串结束符(对于二进制文件可选)
    buffer[size] = '\0';
    
	if (buffer[size -1] == '\n' || buffer[size -2] == '\n'){
		if (buffer[size -2] == '\n'){
		    buffer[size -2] = '\0';
		}else{
		    buffer[size -1] = '\0'; 
		} 
	}

    // 关闭文件
    fclose(file);

    // 返回文件大小和内容
    if (fileSize != NULL) {
        *fileSize = size;
    }
    return buffer;
}

int main() {
    // 获取请求方法
    char *request_method = getenv("REQUEST_METHOD");
    if (!request_method) {
        printf("Content-Type: text/html\r\n\r\n");
        printf("<h1>Error: No request method.</h1>\n");
        return 1;
    }

    char username[50] = {0};
    char password[50] = {0};

    if (strcmp(request_method, "GET") == 0) {
        // 处理 GET 请求
        char *query_string = getenv("QUERY_STRING");
        if (!query_string) {
            printf("Content-Type: text/html\r\n\r\n");
            printf("<h1>Error: No form data received.</h1>\n");
            return 1;
        }
        parse_form_data(query_string, username, password);
    } else if (strcmp(request_method, "POST") == 0) {
        // 处理 POST 请求
        char *content_length_str = getenv("CONTENT_LENGTH");
        if (!content_length_str) {
            printf("Content-Type: text/html\r\n\r\n");
            printf("<h1>Error: No content length.</h1>\n");
            return 1;
        }
        int content_length = atoi(content_length_str);
        char *data = malloc(content_length + 1);
        fread(data, 1, content_length, stdin);
        data[content_length] = '\0';

        parse_form_data(data, username, password); 
        free(data);
		 
    } else {
        printf("Content-Type: text/html\r\n\r\n");
        printf("<h1>Error: Unsupported request method.</h1>\n");
        return 1;
    }

    // 简单的用户验证
    if (strcmp(username, "admin") == 0 && strcmp(password, "admin") == 0) {
        // 登录成功,跳转到欢迎页面
        //printf("Location: /home.html\r\n\r\n");
		//回显home.html界面
		char ip[128] = "\0";
		char netmask[128] = "\0";
		char gateway[128] = "\0";
			
		size_t fileSize = 0;
		char *fileContent = readFileContents("/lighttpd/htdocs/cgi-bin/ip.config", &fileSize);
		const char *delimiter = "#";
		if (fileContent != NULL){ 
	       //sscanf(fileContent, "%s#%s#%s", ip, netmask, gateway);
			// 使用 strtok 分割字符串
			char *token = strtok(fileContent, delimiter);

			// 输出分割后的字符串
			int count = 0;
			int i=0;
			while (token != NULL) {
				//printf("Part %d: %s\n", ++count, token);
				if (i==0){
					sprintf(ip, "%s", token);
				}else if (i==1){
					sprintf(netmask, "%s", token);
					
				}else if (i==2){
					sprintf(gateway, "%s", token);
					
				}else{
				}
				
				i++;
				token = strtok(NULL, delimiter);
			}

		} 
		printf("Location: /home.html?ip=%s&netmask=%s&gateway=%s\r\n\r\n", 
				ip, netmask, gateway
				);
	    if (fileContent!= NULL){
		    free(fileContent);
		}
		if (sipFileContent!= NULL){
		    free(sipFileContent);
		}
    } else {  
        // 登录失败,显示错误信息
        printf("Content-Type: text/html\r\n\r\n");
        printf("<h1>Login Failed!</h1>\n");
        printf("<p>Invalid %s or %s.</p>\n",username,password);
        printf("<p><a href='/index.html'>Try again</a></p>\n");
    }

    return 0;
}


问题3、模版文件如何实现。CGI程序读取html文件后,动态替换其中的模板参数。

html文件侧:

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>CGI Form Example</title>
</head>
<body>
    <h1>Enter Your Details</h1>
    <form action="/cgi-bin/echo.cgi" method="post">
        <label for="username">Username:</label>
        <input type="text" id="username" name="username" required><br><br>
        <label for="email">Email:</label>
        <input type="email" id="email" name="email" required><br><br>
        <input type="submit" value="Submit">
    </form>
</body>
</html>


CGI程序侧:

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

// 替换字符串中的占位符
void replace_placeholder(char *str, const char *placeholder, const char *value) {
    char buffer[4096]; // 更大的缓冲区,防止溢出
    char *pos;
    int placeholder_len = strlen(placeholder);
    int value_len = strlen(value);

    while ((pos = strstr(str, placeholder)) != NULL) {
        // 复制占位符之前的部分
        strncpy(buffer, str, pos - str);
        buffer[pos - str] = '\0';

        // 添加替换值
        strcat(buffer, value);

        // 添加占位符之后的部分
        strcat(buffer, pos + placeholder_len);

        // 将结果复制回原字符串
        strcpy(str, buffer);
    }
}

int main() {
    // 设置 HTTP 响应头
    printf("Content-Type: text/html\r\n\r\n");

    // 获取查询字符串(GET 参数)
    char *query_string = getenv("QUERY_STRING");
    if (!query_string) {
        printf("<h1>Error: No query string.</h1>\n");
        return 1;
    }

    // 解析查询字符串
    char username[50] = {0};
    char email[50] = {0};
    char *token;

    token = strtok(query_string, "&");
    while (token != NULL) {
        if (strstr(token, "username=") == token) {
            strncpy(username, token + 9, 50);
        } else if (strstr(token, "email=") == token) {
            strncpy(email, token + 6, 50);
        }
        token = strtok(NULL, "&");
    }

    // 读取 HTML 模板文件
    FILE *file = fopen("/lighttpd/htdocs/home.html", "r");
    if (!file) {
        printf("<h1>Error: Unable to open template file.</h1>\n");
        return 1;
    }

    // 读取文件内容
    char template[4096] = {0};
    fread(template, 1, sizeof(template), file);
    fclose(file);

    // 替换占位符
    replace_placeholder(template, "{{username}}", username);
    replace_placeholder(template, "{{email}}", email);

    // 输出动态生成的 HTML
    printf("%s", template);

    return 0;
}



呱牛笔记

-------------------广告线---------------
项目、合作,欢迎勾搭,邮箱:promall@qq.com


本文为呱牛笔记原创文章,转载无需和我联系,但请注明来自呱牛笔记 ,it3q.com

请先登录后发表评论
  • 最新评论
  • 总共0条评论