问题:
几个问题:
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;
}