顯示具有 C - Lib - cgic 標籤的文章。 顯示所有文章
顯示具有 C - Lib - cgic 標籤的文章。 顯示所有文章

2022年2月27日 星期日

lighttpd & CGI note


CGIC提供了簡單的API, 存取那一些CGIC已經幫你parse好的資料,比如: cgiRequestMethod, cgiRemoteAddr, cgiScriptName, 以及POST/GET資料等等, header的資料可以透過environ讀取
可以透過cgiFormEntries(char ***result)抓key, 再透過cgiFormString(char *name, char *result, int max)取得資料,
#include <stdio.h>
#include <cgic.h>
extern char **environ;

int cgiMain() {
  char **array, **arrayStep, val[64];
  fprintf(cgiOut, "cgiRequestMethod:%s\n", cgiRequestMethod);
  fprintf(cgiOut, "cgiRemoteAddr:%s\n", cgiRemoteAddr);
  fprintf(cgiOut, "cgiScriptName:%s\n", cgiScriptName);
  fprintf(cgiOut, "cgiContentType:%s\n", cgiContentType);
  fprintf(cgiOut, "used for GET, cgiQueryString:%s\n", cgiQueryString);

  if (cgiFormEntries(&array) == cgiFormSuccess) {
      for (arrayStep = array; *arrayStep; arrayStep++) {
          fprintf(cgiOut, "get post by cgiFormEntries():%s\n", *arrayStep);
          cgiFormString(*arrayStep, val, sizeof(val));
          fprintf(cgiOut, "val:%s\n", val);
      }
  }
  for (array = environ; *array; array++) {
      fprintf(cgiOut, "get from env:%s\n", *array);
  }

  return 0;
}


GET

[brook@:/var/www/html]$ curl -H "xhead: 123" --noproxy 127.0.0.1 http://127.0.0.1/cgic.cgi?xquery=123
cgiRequestMethod:GET
cgiRemoteAddr:127.0.0.1
cgiScriptName:/cgic.cgi
cgiContentType:
used for GET, cgiQueryString:xquery=123
get post by cgiFormEntries():xquery
val:123
get from env:CONTENT_LENGTH=0
get from env:QUERY_STRING=xquery=123
get from env:REQUEST_URI=/cgic.cgi?xquery=123
get from env:REDIRECT_STATUS=200
get from env:SCRIPT_NAME=/cgic.cgi
get from env:SCRIPT_FILENAME=/var/www/html/cgic.cgi
get from env:DOCUMENT_ROOT=/var/www/html
get from env:REQUEST_METHOD=GET
get from env:SERVER_PROTOCOL=HTTP/1.1
get from env:SERVER_SOFTWARE=lighttpd/1.4.64
get from env:GATEWAY_INTERFACE=CGI/1.1
get from env:REQUEST_SCHEME=http
get from env:SERVER_PORT=80
get from env:SERVER_ADDR=127.0.0.1
get from env:SERVER_NAME=127.0.0.1
get from env:REMOTE_ADDR=127.0.0.1
get from env:REMOTE_PORT=52894
get from env:HTTP_HOST=127.0.0.1
get from env:HTTP_USER_AGENT=curl/7.47.0
get from env:HTTP_ACCEPT=*/*
get from env:HTTP_XHEAD=123



POST

[brook@:/var/www/html]$ curl -H "xhead: 123" --noproxy 127.0.0.1 -X POST -d 'post1=p1&post2=p2' http://127.0.0.1/cgic.cgi?xquery=123
cgiRequestMethod:POST
cgiRemoteAddr:127.0.0.1
cgiScriptName:/cgic.cgi
cgiContentType:application/x-www-form-urlencoded
used for GET, cgiQueryString:xquery=123
get post by cgiFormEntries():post1
val:p1
get post by cgiFormEntries():post2
val:p2
get from env:CONTENT_LENGTH=17
get from env:QUERY_STRING=xquery=123
get from env:REQUEST_URI=/cgic.cgi?xquery=123
get from env:REDIRECT_STATUS=200
get from env:SCRIPT_NAME=/cgic.cgi
get from env:SCRIPT_FILENAME=/var/www/html/cgic.cgi
get from env:DOCUMENT_ROOT=/var/www/html
get from env:REQUEST_METHOD=POST
get from env:SERVER_PROTOCOL=HTTP/1.1
get from env:SERVER_SOFTWARE=lighttpd/1.4.64
get from env:GATEWAY_INTERFACE=CGI/1.1
get from env:REQUEST_SCHEME=http
get from env:SERVER_PORT=80
get from env:SERVER_ADDR=127.0.0.1
get from env:SERVER_NAME=127.0.0.1
get from env:REMOTE_ADDR=127.0.0.1
get from env:REMOTE_PORT=52904
get from env:HTTP_HOST=127.0.0.1
get from env:HTTP_USER_AGENT=curl/7.47.0
get from env:HTTP_ACCEPT=*/*
get from env:HTTP_XHEAD=123
get from env:HTTP_CONTENT_LENGTH=17
get from env:CONTENT_TYPE=application/x-www-form-urlencoded



header的部分會被轉成HTTP_VAR=val方式存在environment中, 相關的code如下,
http_cgi_encode_varname()
{
  if (is_http_header) {
    memcpy(p, "HTTP_", 5);
    j = 5; /* "HTTP_" */
  }
  /* uppercase alpha */
  ...
}

http_cgi_headers()
{
  ...
  for (n = 0; n < r->rqst_headers.used; n++) {
    http_cgi_encode_varname(tb, BUF_PTR_LEN(&ds->key), 1);
  }
  ...
}

cgi_create_env()
{
  /* create environment */
  http_cgi_headers(r, &opts, cgi_env_add, env);
  ...
  pid_t pid = (dfd >= 0) ? fdevent_fork_execve(args[0], args, envp,
    to_cgi_fds[0], from_cgi_fds[1], serrh_fd, dfd): -1;
  ...
}

mod_cgi_handle_subrequest()
{
  ...
  cgi_create_env();
  ...
}


2009年7月27日 星期一

在cgic中印出form中所有的data


在cgic中印出form中所有的data是我覺得是非常重要的功能之一,因為這可以幫助你debug,印出cgi到底收到哪些from,以及其內容(value)為何。
首先利用cgiFormEntries()抓取form中所有欄位名稱(field name),接著在利用cgiFormStringNoNewlines()抓取內容(value)。
#include <stdio.h>
#include <string.h>
#include "cgic.h"

int cgiMain(int argc, char *argv[])
{
    int ret;
    char **array, **arrayStep, value[64];

    cgiHeaderContentType("text/html");
    if (cgiFormEntries(&array) != cgiFormSuccess) {
        return -1;
    }

    for (arrayStep = array; *arrayStep; arrayStep++) {
        snprintf(value, sizeof(value), "%s=", *arrayStep);
        if ((ret =
             cgiFormStringNoNewlines(*arrayStep, value + strlen(value),
                                     sizeof(value) - strlen(value)))
            != cgiFormSuccess) {
            fprintf(cgiOut, "<p>%s(null). failed</p>\n", value);
        } else {
            fprintf(cgiOut, "<p>%s</p>\n", value);
        }
    }
    return 0;
}


利用cgic上傳檔案


CGIC提供一些上傳檔案的API,讓您可以輕鬆的利用這些API處理上傳檔案。
注意,您的form必須將enctype設定為multipart/form-data。
<html>
    <head><title>upload test</title></head>
    <body>
        <form method="post" enctype="multipart/form-data"
                action="upload.cgi">
            <input type="file" name="fileName" />
            <input type="submit" />
        </form>
    </body>
</html>

接著就是處理CGI的部份了。
1. 首先就是利用cgiFormFileName()檢查檔案是否被提交。
2. 接著利用cgiFormFileOpen()取得cgiFilePtr的object。
3. 再利用while(cgiFormFileRead(cgiFilePtr) == cgiFormSuccess)讀取資料。
4. 利用cgiFormFileClose(cgiFilePtr)釋放不再需要的cgiFilePtr。

#include <stdio.h>
#include <cgic.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>

#define F_FILENAME      "fileName"
#define FILENAME_LEN    1024
#define BUF_LEN         1024

int cgiMain(int argc, char *argv[])
{
    cgiFilePtr cfp;
    char rfile[FILENAME_LEN] /*remote file */ ,
        lfile[FILENAME_LEN] /* local file */ , buf[BUF_LEN];
    int ret, fd, fsize, got;

    cgiHeaderContentType("text/html");
    if ((ret = cgiFormFileName(F_FILENAME, rfile, sizeof(rfile))) !=
        cgiFormSuccess) {
        fprintf(cgiOut, "<p>No file was uploaded.(%d)</p>", ret);
        return -1;
    }
    cgiFormFileSize(F_FILENAME, &fsize);

    if ((ret = cgiFormFileOpen(F_FILENAME, &cfp)) != cgiFormSuccess) {
        fprintf(cgiOut, "<p>open cgi file failed.(%d)</p>", ret);
        return -1;
    }
    snprintf(lfile, sizeof(lfile), "/tmp/%s", rfile);
    fprintf(cgiOut, "write to %s", lfile);
    if ((fd =
         open(lfile, O_CREAT | O_WRONLY,
              S_IRWXU | S_IRWXG | S_IRWXO)) < 0) {
        fprintf(cgiOut, "<p>create file on server failed.(%s)</p>",
                strerror(errno));
        return -1;
    }
    while (cgiFormFileRead(cfp, buf, sizeof(buf), &got) == cgiFormSuccess) {
        if ((ret = write(fd, buf, got)) < 0) {
            fprintf(cgiOut, "<p> write failed(%d/%s)</p>\n", errno,
                    strerror(errno));
        }
        fprintf(cgiOut, "<p>got(%d)</p>\n", got);
    }
    close(fd);
    cgiFormFileClose(cfp);
    fprintf(cgiOut, "<p> end </p>\n");
    return 0;
}

基本上,I/O讀取上利用buffered I/O是比較有效率的,不過範例沒有用buffered I/O,留給大家去修改一下吧。
參考資料: http://www.boutell.com/cgic/


2009年7月10日 星期五

cgic簡介


CGIC是一套用C寫成的CGI Library,主要幫您parse form data:

安裝
您可以到他的官方網站下載 wget http://www.boutell.com/cgic/cgic205.tar.gz 並且編譯
tar zxvf cgic205.tar.gz
cd cgic205 && make
編譯完會產生libcgic.a,或者您可以修改Makefile產生libcgic.so。每個cgi其實就等同於一個獨立的可執行檔,您可以在shell底下直接執行,因此每個CGI就必須要有main(),而cgic一開始就會先幫user處理一些data,您可以在cgic.c裡面可以看到,而在main()後面會呼叫cgiMain(),這也是我們cgi的進入點。
#include <stdio.h>
#include <cgic.h>

int cgiMain() {
  cgiHeaderContentType("text/html");
  fprintf(cgiOut, " <h1>hello world</h1>\n");
  return 0;
}

brook@debian:~/cgic/cgic205$ gcc -o hello.cgi hello.c libcgic.a -I.
brook@debian:~/cgic/cgic205$ ./hello.cgi
Content-type: text/html

<h1>hello world</h1>


熱門文章