什么是cgic
cgic是一个c语言库,用于基于CGI
标准规范的www
应用程序的创建。
cgic执行以下任务:
- 解析数据,校正有缺陷和不一致的浏览器
- 透明的接受由
GET
和POST
传递的表单数据 - 接受上传完毕的文件和普通的表单数据
- 提供设置和恢复
cookies
的函数 - 以一致的方式处理表单字段的换行符
- 提供字符串,整数,浮点数,单选,多选 函数来恢复表单数据
- 对于数字字段提供边界检查
- 加载CGI环境变量到C字符串,这些字符串总是不为空
- 在调试环境下,提供捕获CGI状态的方法,包括文件上传和cookies
cgic
在兼容CGI
的服务器环境下是可移植的,并且在posix/Unix/Linux/Windows
环境下,不用修改编译文件。
构建cgic:一个简单的应用
一个简单的例子“cgictest.c”, 包含在cgic库里面。这个CGI程序显示一个输入表单,
接收一个提交,然后显示提交的内容。在这个过程中,基本上所有的cgic的特性都被测试了。
在一个Unix系统上,可以通过输入make cgictest.cgi
来构建cgictest
。cgic.c
,cgictest.c
将会编译,
然后链接产生这个cgictest
应用程序。在非unix操作系统上,你需要创建一个工程包含cgic.c
和cgictest.c
,然后编译它。
怎样编写一个cgic应用程序
注:所有的cgic
应用程序必须连接到cgic.c
模块自身。怎样连接取决于你的操作系统,在unix下,
提供的Makefile
文件作为一个使用范例
因为所有的CGI应用程序必须执行确定的初始化任务,例如解析表单数据和检查环境变量,cgic
库提供了它自己的main()
函数。当你用cgic库来写应用时,你应该实现一个cgiMain()
函数来作为程序的开始,当初始化cgi的工作成功结束之后,
cgic
库将会调用cgiMain()
函数。你的程序必须包含 cgic.h
头文件。
重要:如果你写了你自己的main()函数,你的程序将不能顺利链接。你的代码应该从cgiMain()开始。cgic库为你提供了 main()。(想要不同行为的人可以修改cgic.c文件)
(对照cgictest.c
)
注意这个DEBUG #ifdef
。如果DEBUG被定义在编译时期,或者输入#define DEBUG 1
到程序里面,或者在Makefile
文件
中设置,或者其他的开发环境,那么LoadEnvironment()
函数将被调用,这个函数调用cgiReadEnvironment()
来恢复一个
被捕获的CGI环境用于调试的目的。另外可以看capture
程序的讨论,这个程序是用来在CGI调试的时候使用的。因为这是
一个测试程序,cgiFormSubmitClicked()
函数也被调用来检查按钮的点击状态,这个按钮用来重新载入被保存的CGI环境。
一个最终的CGI程序一般情况从不允许终端用户来做这个决定。
设置Cookies
下一步,一个cgiHeader
函数将被调用。这个特殊的程序演示了许多功能,包括cookies
的设置。如果程序员希望设置一个
cookie
,这个cookie
设置函数将被第一个调用,在其他的标题(Headers)
之前被输出。在cgictest.c
中调用CookieSet()
函数来完成。因为这是一个测试程序,cgiFormString()
函数被用来从用户之前填入的表单中获取名字和值。通常,
cookie
名字和值被用来满足程序员的需求并且提供一种之后标识相同用户的方法。
cgiHeaderCookiesSetString()
函数通过请求浏览器存储的内容来设置cookie
。这不能保证一定会成功。许多浏览器完全
拒绝cookies
,另外,浏览器不一定会保存他们直到服务器请求的到来,如果还在的话就完好无损的返回这些值。当使用
cookies
的时候代码总是要有防御性的。
这个cname
和cvalue
参数当然就是cookie
的名称和值,第三个参数是时间,以秒计数,表示浏览器应该存储的时间,之后
就过期。在这个例子中,被设置成86400
秒,表示一天。浏览器可能遵守也可能不遵守这个设置,正如cookie
的其他一切
一样。
第四个参数标识了在网站中的”路径“,对这个路径来说cookie
是有效的。一个cookie
要在每一次访问站点的时候以/路径
的形式被发送回去。在这个例子里,cookie
只跟CGI程序本身有关,所以cgiScriptName
被发送。类似的,一个cookie
被
认为跟一个单一站点相关或者整个域,例如www.boutell.com
或者整个.boutell.com
域。这个例子中,当前的这个程序
在它上面跑的这个站点是仅有的相关站点,所以cgiServerName
被当做域。
输出Content Type Header
下一步,cgiHeaderContentType()
函数被调用来标识输出文档的MIME
类型,这个例子中是text/html
。其他一些普通的MIME类型是image/gif
,image/jpeg
和 audio/wav
。
注意,cgiHeaderStatus()
或者cgiHeaderLocation()
被调用来输出一个错误代码或者重定向到一个不同的URL
。在程序的单次执行中,只能有一个cgiHeader
函数可以被调用。
重要:一个cgiHeader
函数,通常是cgiHeaderContentType()
,必须在输出任何其他应答之前调用。否则,结果将是一个不合法的文档,浏览器的行为不可预测。你当然也可以输出你自己的 ContentType
和其他的标题信息到cgiOut
如果你喜欢。cgiHeader
函数只是提供了一种便利。
处理表格提交
像许多CGI程序一样,cgitest
基于不同的提交按钮是否被按下来判断应该怎样处理。当testcgic
或者saveenvironment
按
钮被按下,cgitest
调用HandleSubmit
函数,它将调用其他的函数来处理表单的不同部分。
处理文本输入
cgitest中Name()
这个函数的目的是取回并且显示用户输入的名字。因为程序员决定名字被允许的最大长度是80个字符,
一个81字符缓冲区被声明(接收最后的null字符)。接着,cgiFormStringNoNewlines()
函数被调用来取出名字并且确保
回车不出现在这个名字中(忽略一些网页浏览器的错误行为)。第一个参数是表格中的输入字段的名称,第二个参数是
数据存储的buffe
r,第三个参数是buffer的长度
。cgic绝不会超出buffer大小,并且总是会提供一个null结尾的字符串作
为响应。如果buffer太小,字符串被截断。如果这个不可接受的,可以用cgiFormStringSapceNeeded()
函数来检测所需空
间量。cgiFormStringNoNewlines()
函数的返回值同样可以说明截断是否出现。参考详细的cgiFormStringNoNewlines()
描述。
处理输出
注意函数Name()
写他的HTML
到cgiOut
而不是stdout
。用户实际提交的名字可能包含对HTM
L来说有特殊意义的字符,特别是
<,>,&
字符。cgiHtmlEscape()
函数用来输出用户输入的名字,他会将名字中这些特殊字符的出现转换成<,>,&
。
重要:cgiOut
通常等价于stdout
,并且没有额外的执行开销。推荐使用cgiOut
作为输出是确保对”在改版过后的cgic
库对
于特殊环境下的每一个cgi连接不提供stdin
和stdout
“情况的可移植性。
注意,对于文本输入域,回车是被期望的,这时应该用cgiFormString()
函数替代。cgiFormString
确保换行符总是被一个
单回车符代表(ascii十进制13)。参考cgictest.c中的Address()
函数的源代码中例子。
处理复选框
考虑Hungry()
函数,它检查用户是否选中了hungry
复选框。
这个函数使用了cgiFormCheckboxSingle()
函数,它检查是否一个单复选框被勾选。cgiFormCheckboxSingle()
接受复选框的
名字属性作为它唯一的参数,选中返回cgiFormSuccess
,否则返回cgiFormNotFound
。如果多个复选框带有相同的名字,考
虑使用cgiFormCheckboxMultiple()
,cgiFormStringMultiple()
函数。
处理数字输入
现在看Temperature()
函数,它取回一个温度的度数(一个浮点数),并且确保位于一个特定的边界内。
cgiFormDoubleBounded()
函数用来取出温度,第一个参数是表格中温度字段名,第二个参数指向接受结果的变量地址,接
下来的两个参数是下限和上限,最后一个是默认值,当用户没有提交这个值时返回默认值。
这个函数总是返回一个区间内的合理值,值如果超出下限或者上限,则被限制在边界值。然而,cgiFormDoubleBounded
函数
的返回值可以确定用户的输入是否在区间,是否为空,等等。参考cgiFormDoubleBounded()
函数的详细描述。如果区间检查
没有必要,考虑使用cgiFormDouble()
代替。
注意,对于整数输入,可用函数cgiFormInteger
和cgiFormIntegerBounded
,他们的行为和上面的浮点数函数类似。
处理单选输入
HTML的<SELECT>
标签用来提供几个选项。单选按钮和复选框用于选择的数量相对较小的时候。看看cgictest.c
中的Color()
。
这个函数测试用户在表单中的<select>
列表中选择了哪个颜色。先声明一个颜色数组,然后调用cigFormSelectSingle()
函数测试哪个选项被选中。第一个参数表示表单中字段名,第二个参数指向颜色数组,第三个参数表示数组元素个数,第四
个参数指向一个下标变量,最后一个参数表示下标默认值,用于浏览器没有提交选择时。
cigFormSelectSingle()
总是标识一个合理的选项值。然而,如果程序员希望确认实际被提交的值是一个合法的值,等等,
可以咨询cgiFormSelectSingle()
的返回值。查看完整的cgiFormSelectSingle()
函数的描述获取更多信息。
注意单选按钮组合<SELECT>
列表都可以被这个函数处理,如果你处理单选按钮组,你可能更喜欢调用cgiFormRadio()
,这个
函数是一样的。
如果我在运行期间不知道可接受的选项呢?
如果可接受选项直到运行时还不知道,可以简单的从磁盘加载选项。但是如果可接受的选项不固定(考虑一个国家名字列表
新名字可能在任何时候加入表单并且直接改变代码或者分离列表是不方便的),简单的调用cgiFormStringNoNewlines()
代替
直接取出字符串。记住,如果你这样做,验证响应以确认它是安全的和合法的将会变成你的程序要解决的一个问题。
cgiFormSelectSingle()
函数的优点是不合法的响应不会返回。
对于处理多选<SELECT>
列表和带有相同名字的复选框组,请看下面马上讨论的cgitest.c
中NonExButtons()
函数。
处理多选输入
看看NonExButtons()
函数前半部分:
这个函数调用cgiFormCheckboxMultiple()
函数来识别一个或多个被选中的带有相同名字的复选按钮。这个函数与
cgiFormSelectMultiple()
有相同的功能。也就是说,处理<SELECT>
标签的MULTIPLE
属性类似于处理带有相同名字的
复选按钮选项组。
cgiFormCheckboxMultiple()
函数的第一个参数是复选框选项组字段的名称,第二个参数指向一个合法值得数组,这些
会对应到复选框的VALUE
属性(或者<SELECT>
列表中的OPTION
标签),第三个参数是合法值数组中值的个数。第四个参数
指向一个具有与合法值数组相同个数的整型数组,对于每一个值,如果复选框被选中,就设置成true
,否则false
。
最后一个参数指向一个整型值,这个被设置成提交的非法响应的个数。(不在合法响应值数组内)如果对这个值不感兴趣,
那么最后一个参数是null
指针。
注意cgiFormCheckboxMultiple()
的返回值可以用来检测所有选项都被选中。查看该函数的其他返回值完整描述请看。。。
如果我在运行期间不知道可接受的选项呢?
如果可接受选项直到运行时还不知道,可以简单的从磁盘加载选项。但是如果可接受的选项不固定(考虑一个冰淇淋口味 列表,新名字可能在任何时候加入表单并且直接改变代码或者分离列表是不方便的),需要一个动态的处理方法。
看看NonExButtons()
函数的后半部分:
这段代码摘录展示了一种可替代的方法来获取选项列表。函数cgiFormStringMultiple()
使用一个特定的输入字段名来获取
一个包含所有被提交选项的字符串数组,这种方法对<SELECT>
标签的MULTIPLE
属性和具有相同名字的复选按钮组同样适用。
cgiFormStringMultiple()
函数的第一个参数是输入字段名或者复选框选项组字段名。第二个参数是字符串数组的地址。
看看这个函数的简单调用:cgiFormStringMultiple("vote", &responses)
;
要怎么知道有多少个响应呢?
调用之后,这个字符串数组的最后一个值时null
。可以通过一个简单的循环计数来统计响应个数。
重要:cgiFormStringMultiple()
函数返回一个指向动态内存的指针。你不应该改变响应数组中的字符串或是数组本身,
如果有修改的需求,应该拷贝字符串。当你完成了对响应数组的使用,你要调用cgiStringArrayFree()
来释放内存。
否则找出内存泄露。不要调用free()
函数,如果你这样做,单个字符串不会被释放。
访问已上传文件
已上传文件作为表单提交的一部分,cgic提供了函数访问它。
重要:你必须设置form
标签的enctype
属性为multipart/form-data
以让这个功能可用。
作为一个例子,看cgictest.c中的ShowForm()
函数。
cgictest.c的File函数关心的是接收已上传的文件:
首先,函数检测用户提交的文件名。
非常重要:这个文件名不保证和用户电脑上真实的文件名有任何的关系,可能被恶意的目的故意操纵,
并且不应该被用于任何的用途除非你确定对于你刻意的使用来说他的内容是安全的,至少不要覆盖一个对你
来说重要的文件,特别是如果你想要使用它作为服务端上的一个文件名。
cgic库自身不会使用这个文件名作为临时存储。
如果cgiFormFileName()
函数不成功,表示没有文件上传。
下一步,cgiFormFileSize()
函数被调用来检测上传文件的大小,单位字节。
这个函数接着查询上传文件的内容类型。用户上传的文件有他们自己的内容类型信息,这可以用来检测一个文件是图片,
HTML文件档,word文档,还是其他的文件类型。然而,就像文件名一样,任何其他的由浏览器所做的声明信息,都不能盲目的
相信。浏览器可能上传一个名叫picture.jpg
,类型是image/jpeg
的文件,但是这不保证实际文件包含一个合法的能显示的
JPEG
图片。
浏览器提交的内容类型可以用cgiFormFileContentType()
函数来查询。
cigc提供访问已上传文件的方法。首先程序传递一个cgiFilePtr
对象的地址以调用cgiFormFileOpen
,如果函数调用成功,
这个cgiFilePtr
对象变成可用,可以被接下来的cgiFormFileRead
使用。注意,实际读取的字节数可能少于请求的字节数,
特别是在cgiFormFileRead
返回cgiFormEOF
之前的最后一次成功调用的时候。当cgiFormFileRead
不再返回cgiFormSuccess
,
程序调用cgiFOrmFileClose
释放cgiFilePtr
对象。
上传的文件可能包含任何的数据,包括二进制数据,null
字符,等等。这个例子使用cgiHtmlEscapeData
函数输出带有HTML
特殊字符的数据。大多数程序会保存上传信息到服务端文件或者数据库。
提取所有表单条目
有时,程序员预先不知道所有的表单字段。这种情况下,使用cgiFormEntries
函数是方便的。cgitest.c中的Entries
函数
演示了cgiFormEntries
的使用:
cgiFormEntries
函数获取表单字段名数组。这个数组是字符串指针数组,null指针标志列表的结尾。上面的代码演示了
一种循环遍历返回的字符串的方法。注意,最后cgiStringArrayFree
函数的调用,这是必需的用来释放存储字符串和
字符串数组内存。
取出Cookies
cgictest.c中Cookies()函数显示了跟随当前的表单一起提交的cookies和他们的值的列表。
非常重要:你的浏览器可能永远不提交cookies,无论你往测试表单填入什么值。
许多浏览器被配置成不接收或者发送cookies。有些配置成尽可能少的发送cookies,只需满足能进入热门网站就行。
用户经常会拒绝你的cookies,确保你的代码在这种情况下仍然可以工作。
上面的代码使用cgiCookies函数取出当前设置的cookies列表,这个列表是一个以null指针结尾的字符串数组。 cgiCookieString()函数用来获取每个cookie相关的值。这个函数的功能与之前讨论的cgiFormString很像。 注意,设置cookie作为当前表单提交处理的一部分,不会立即出现在这个列表上,因为它还没有被浏览器发回。 要使它出现在以后的提交中,这需要浏览器选择接受并重新发送回cookie。
显示一个提交给当前程序的表单
CGI程序员经常需要显示HTML页面作为CGI程序输出的一部分,这些HTML页面经常包含表单,这些页面要能够提交字段回到 发送他们的程序。服务器要易于配置,这可以通过方便的使用cgiScriptName 环境变量来实现,像下面显示的那样。这是 cgictest.c中SHowForm函数的源代码。
注意FORM标签中 enctype=”multipart/form-data”的使用。如果表单包含文件上传,这是绝对需要的,像上面的例子一样。 大多数浏览器,如果没有设置这个属性,甚至都不会尝试上传文件。
检验CGI 环境变量
CGI标准说明一系列由服务器设置的环境变量。然而,当环境变量没有设置的时候,服务器无法预测变量的值是null还是 指向空字符串。同时,为了允许程序员还原已保存的环境变量,cgic库需要有一种方法隔离程序员和实际的环境变量。
cgic总是使用环境变量的拷贝来代替getevn()函数获取环境变量的值,例如HTTP_USER_AGENT(使用的浏览器软件),这个 拷贝是一个合法的C字符串(绝不会是null,虽然他们可能指向一个空字符串)。例如,cgiUserAgent变量包含浏览器软件 的名字。cgiReferrer变量代表引用的URL。
cgic应用程序如何产生图像。
cgic可以结合gd图形库,来快速的生成GIF图像。
下面的简短例子暗示了可能行:
注意这个程序需要链接cgic.o和libgd.a。通常,这种类型的程序响应是一个cgiPathInfo值或者表单字段的集合,通过 返回的HTML页面内含图片的引用,轮流显示来产生一张GIF图片。
调试CGI程序,使用capture
调试CGI程序是一项痛苦的工作,因为CGI程序运行在网页服务器创建的特殊环境下,这导致在调试器里运行他们是困难的。 然而,cgic库提供了一种捕获当前有效的环境变量到文件的方法,同时提供了重新载入已保存环境变量的方法。
capture.c程序可以用来捕获CGI环境变量。只要修改capture.c文件中的cgiMain()函数的第一行,使其保存CGI环境变量到 你系统上的适当的文件中并且用make capture来编译它。然后放置capture程序到cgi目录下并且设置你要测试的表单动作 或者其他的链接指向它。当表单提交或者其他的链接生效的时候,capture程序会把那个时刻有效地CGI环境变量写到你代 码中指定的文件中去。然后可以调用cgiReadEnvironment()函数从相同的文件中(你在cigMain()函数开始处设置的)还原 捕获的环境变量。然后你可以在你选择的调试器中运行你的程序,并且他会像由真正的网页服务器启动的一样来执行,包括 文件上传,cookies 和所有其他cgic范围内的现象。
重要:确认你指定的是绝对路径,因为CGI脚本运行的当前工作目录可能跟你想得不一样。
更重要:如果你使用getenv()函数代替cgic中环境变量的拷贝,当使用已保存的CGI环境变量运行的时候,你将不会取得你 期望的值。所以,总是使用cgic变量代替getenv()函数。
cgic 函数手册
cgiFormResultType cgiFormString( char *name, char *result, int max)
cgiFormString
试图检索发送给指定字段的字符串。文本会被拷贝到指定的result buffer
中,最多但不超过max-1
个字节。null字
符标记字符串的结束。不管浏览器提交的换行格式是什么,cgiFormString
总是把每一个换行符都编码成一个单一 的换行符(ascii 十进制 10)。最终的字符串总是可能比调用cgiFormStringSpaceNeeded
的稍微短一点,但是不可能更长。 成功取出字符串,返回cgiFormSuccess
;
获取字符串但是为了适应buffer而被截断,返回cgiFormTruncated
;
取回的字符串是空字符串,返回cgiFormEmpty
;
没有相应的输入字段被提交,返回cgiFormNotFound
,并且一个空字符串被拷贝到result buffer
。cgiFormResultType cgiFormStringNoNewlines( char *name, char *result, int max)
这个函数几乎和cgiFormString()一样,除了出现在输入中的任何回车或者换行符将被去掉。建议使用这个函数在单行的文本 输入字段,因为有一些浏览器会提交回车和换行符当他们不该这样的时候。cgiFormResultType cgiFormStringSpaceNeeded( char *name, int *length)
cgiFormStringSpaceNeeded()
被用来确定输入文本缓冲区需要多少长度来接收指定输入字段的内容。这对于不管任意输入长度 都分配足够内存的程序员来说是有用的。由cgiFormString()
调用获取的字符串的实际长度可能稍微更短一点,但是不会更长。 如果成功,函数设置*length
,单位字节,包含null
结束符,返回cgiFormSuccess
。
如果指定的字段没有提交,该函数设置*length
的值为1,返回cgiFormNotFound
。1
被用来确保空字符串的空间(null结束符
), 如果cgiFormString
被调用,则忽略这个返回值。cgiFormResultType cgiFormStringMultiple( char *name, char ***ptrToStringArray)
cgiFormStringMultiple
在表单中有好几个输入元素具有相同的名字的特例情况非常有用,不管因为什么,程序员不想使用下面 提供的复选框,单选按钮和选项菜单等函数。这偶尔是需要的,如果程序员事先不知道在一个表单中什么值可能出现在一个多选 列表或者复选框组。结果是一个字符串数组,以null标记结尾。这个数组由cgic
库分配空间。
重要:完成这个数组的工作后,必须调用cgiStringArrayFree()
来释放数组。
如果这个名字至少出现一次,返回cgiFormSuccess
。
如果没有出现,返回cgiFormNotFound
。
如果没有足够的空间分配数组保存返回值,返回cgiFormMemory
。
除了最后一种情况,ptrToStringArray
指向一个以null
指针结尾的合法数组。在内存不足的情况下,ptrToStringArray
被设置成null
指针。cgiFormResultType cgiFormEntries( char ***ptrToStringArray)
当程序员事先不知道所有相关的表单字段名称的时候cgiFormEntries
是有用的。结果被设置成一个指向字符串的数组,最后一个值 是null
。这个数组由cgic
库分配。
重要:完成这个数组的工作后,必须调用cgiStringArrayFree()
来释放数组。这个函数返回cgiFormSuccess
,除非发生了 内存不足的错误事件。在成功的情况下,ptrToStringArray
设置成指向一个字符串的数组,null
指针结尾。在内存不足的情况ptrtoStringArray
设置成null指针,且返回cgiFormOutOfMemory
。void cgiStringArrayFree(char **stringArray)
cgiStringArrayFree()
用来释放由cgiFormStringMultiple()
,cgiFormEntries()
, orcgiFormCookies()
函数创建的字符串数组。cgiFormResultType cgiFormInteger( char *name, int *result, int defaultV)
cgiFormInterger()
尝试获取发送给指定输入字段的整型值。*result
设置为被提交的值。
成功获取值,返回cgiFormSuccess;
如果值是空字符串,返回cgiFormEmpty;
如果提交的不是整型值,返回cgiFormBadType;
如果没有该输入字段,返回cgiFormNotFound。
在后三种情况,*result设置为指定的默认值。cgiFormResultType cgiFormIntegerBounded( char *name, int *result, int min, int max, int defaultV)
cgiFormIntegerBounded()
尝试获取发送给指定输入字段的整型值,并且限定结果在指定的区间内。*result
被设置为提交的值。
如果成功获取到值,返回cgiFormSuccess;
如果值越界且被相应的调整,返回cgiFormConstrained;
如果提交的是一个空字符串,返回cgiFormEmpty;
如果提交的不是整型值,返回cgiFormBadType;
如果没有该输入字段,返回cgiFormNotFound。
在后三种情况,*result设置为指定的默认值。cgiFormResultType cgiFormDouble( char *name, double *result, double defaultV)
cgiFormDouble尝试获取发送给指定输入字段的浮点值,result设置为被提交的值。
成功获取值,返回cgiFormSuccess;
如果值是空字符串,返回cgiFormEmpty;
如果提交的不是数字,返回cgiFormBadType;
如果没有该输入字段,返回cgiFormNotFound。
在后三种情况,result设置为指定的默认值。cgiFormResultType cgiFormDoubleBounded( char *name, double *result, double min, double max, double defaultV)
cgiFormDoubleBounded()尝试获取发送给指定输入字段的浮点值,并且限定结果在指定的区间内。result被设置为提交的值。
如果成功获取到值,返回cgiFormSuccess;
如果值越界且被相应的调整,返回cgiFormConstrained;
如果提交的是一个空字符串,返回cgiFormEmpty;
如果提交的不是数字,返回cgiFormBadType;
如果没有该输入字段,返回cgiFormNotFound。
在后三种情况,result设置为指定的默认值。cgiFormResultType cgiFormSelectSingle( char *name, char **choicesText, int choicesTotal, int *result, int defaultV)
cgiFormSelectSingle()获取与cgiFormResultType cgiFormSelectMultiple( char *name, char **choicesText, int choicesTotal, int *result, int *invalid)
cgiFormSelectMultiple()获取与元素相关的选项号码,这个元素允许多选。name用来标识 元素的NAME属性, choicesText指向标识每个选项的字符串数组,choicesTotal表示选项的个数。result是个数和choicesText数组个数相同的 整数数组。对于每一个选项,选中设为1,否则为0.
如果至少有一个合法选项被成功获取,返回cgiFormSuccess;
如果没有合法的选项被提交,返回cgiFormNotFound。
*invalid设置为被提交的非法选项的个数,这个值应该为0,除非表单和选项数组不一致。giFormResultType cgiFormSubmitClicked( char *name)
通常需要知道一个指定的提交按钮是否被点击,不同的名字属性区分不同的提交按钮。cgiFormSubmitClicked函数可替换为 cgiFormCheckboxSingle函数,它适合用来测试一个特定的提交按钮是否被使用。cgiFormResultType cgiFormCheckboxSingle( char *name)
cgiFormCheckboxSingle用来确认一个指定名字的复选框是否被选中。
如果这个按钮被选中,返回cgiFormSuccess;
如果没有被选中,返回cgiFormNotFound。
cgiFormCheckboxSingle用于具有唯一名字的单复选框。下面的函数用于解决具有相同名字的多复选框和单选按钮。cgiFormResultType cgiFormCheckboxMultiple( char *name, char **valuesText, int valuesTotal, int *result, int *invalid)
cgiFormCheckboxMultiple()检测在一个具有相同名字的复选框组里面哪一个被选中。这跟单选按钮是不同的。valuesText指向 标识每个复选框的VALUE属性的字符串数组。ValuesTotal 是复选框的总数。result是个数和valuessText数组个数相同的 整数数组。对每一个选项,选中设为1,否则为0.
如果至少有一个合法复选框被选中,返回cgiFormSuccess。
如果没有合法的复选框被选中,返回cgiFormNotFound。
*invalid设置为被提交的非法选项的个数,这个值应该为0,除非表单和valuesText数组不一致。cgiFormResultType cgiFormRadio( char *name, char **valuesText, int valuesTotal, int *result, int defaultV)
如果有做选择的话,cgiFormRadio用于检测,带有相同名字的radio boxes组中的哪一个被选中。valuesText指向标识每个 radio box的VALUE属性的字符串数组,ValuesTotal 是radio boxes组中的radio box个数。
如果有做选择的话,*result设为valuesText数组中被选中的位置。
如果没有radio box被选中或者是一个非法的选项,那么设为默认值。
如果组里面有一个radio box被选中,返回cgiFormSuccess。
如果没有box被选中,返回cgiFormNotFound。
如果被提交的radio box不匹配valuesText中的任何一个,返回cgiFormNoSuchChoice。cgiFormResultType cgiFormFileName( char *name, char *fileName, int max)
cgiFormFileName尝试根据指定的带有file类型的表单输入字段,取出用户上传文件的名字。
如果在服务端直接使用,永远不要相信这个文件名是合理和安全的。
文本会被拷贝到fileName buffer中,最多但不超过max-1个字节,null结尾。
成功取出字符串并且不为空,返回cgiFormSuccess;
字符串被成功取回但为空,表示没有上传文件,返回cgiFormNoFileName。
获取字符串但是为了适应buffer而被截断,返回cgiFormTruncated;
如果没有相应的输入字段被提交,返回cgiFormNotFound,并且一个空字符串被拷贝到结果。cgiFormResultType cgiFormFileSize( char *name, int *sizeP)
cgiFormFileSize尝试根据name参数指定的具有file类型的输入字段,获取浏览器上传文件的大小,单位字节。
如果成功,大小存在*sizeP,并且函数返回cgiFormSuccess。
如果这个表单字段不存在,函数返回cgiFormNotFound。
如果表单字段存在但没有文件上传,返回cgiFormNotAFile。cgiFormResultType cgiFormFileContentType( char *name, char *contentType, int max)
cgiFormFileContentType尝试根据name参数指定的具有file类型的输入字段,获取浏览器上传文件的内容类型。不保证这个 内容类型是精确的。类型字符串拷贝到contentType buffer中,最多但不超过max-1个字节,null结尾。
成功取出字符串并且不为空,返回cgiFormSuccess;
字符串被成功取回但为空,表示没有上传文件,或者浏览器不认识内容类型,返回cgiFormNoContentType。
获取字符串但是为了适应buffer而被截断,返回cgiFormTruncated;
如果没有相应的输入字段被提交,返回cgiFormNotFound,并且一个空字符串被拷贝到结果。cgiFormResultType cgiFormFileOpen( char *name, cgiFilePtr *cfpp)
cgiFormFileOpen尝试根据指定的带有file类型的输入字段打开实际上传的文件。
如果成功,这个函数设置cfpp为一个有效地cgiFilePtr对象,返回cgiFormSuccess;
如果失败,设置cfpp为一个null指针,返回cgiFormNotFound,cgiFormNotAFile, cgiFormMemory or cgiFormIO中的合适值。cgiFormResultType cgiFormFileRead( cgiFilePtr cfp, char *buffer, int bufferSize, int *gotP)
cgiFormFileRead尝试从之前由cgiFormFileOpen产生的cgiFilePtr对象读取最多bufferSize字节数据。如果成功的读取到了数据, 拷贝到buffer,实际读取字节数保存到*gotP。
如果有任何数据被成功读取,返回cgiFormSuccess。
到达文件尾,函数返回cgiFormEOF。
如果发生I/O错误事件,返回cgiFormIO。
如果cfg是空指针,函数返回cgiFormOpenFailed。cgiFormResultType cgiFormFileClose( cgiFilePtr cfp)
cgiFormFileClose关闭一个cgiFilePtr对象,释放内存和其他系统资源。
这个函数返回cgiFormSuccess除非cfg是空指针。
在cfg为null的情况下,返回cgiFormOpenFailed。void cgiHeaderLocation(char *redirectUrl)
如果程序员想要重定向用户到不同的URL,可以调用cgiHeaderLocation(),在这种情况,没有额外的输出。
如果你想设置cookies,你必须在调用cgiHeaderLocation之前调用cgiHeaderCookieSetString()和 cgiHeaderCookieSetInteger()void cgiHeaderStatus(int status, char *statusMessage)
如果程序员想要输出一个HTTP错误状态码而不是一个文档,应该调用cgiHeaderStatus()。第一个参数是状态码,第二个参数是 状态信息用于显示给用户。
如果你想设置cookies,你必须在调用cgiHeaderStatus之前调用cgiHeaderCookieSetString()和 cgiHeaderCookieSetInteger()void cgiHeaderContentType(char *mimeType)
如果程序员希望输出一个新的文档作为用户请求的响应,需要调用cgiHeaderContentType()。这是普通情况。 这单独的参数是响应的MIME文档类型。典型值是“text/html”对应于HTML文档,“text/plain”对应于普通的ASCII文本没有 HTML标签,“image/gif” 对应于GIF图像,“audio/basic”对应于音频格式。
如果你想设置cookies,你必须在调用cgiHeaderContentType()之前调用cgiHeaderCookieSetString()和 cgiHeaderCookieSetInteger()void cgiHeaderCookieSetString(char *name, char *value, int secondsToLive, char *path, char *domain)
如果程序员希望存储一些信息在用户的客户端,以便于后续访问相关网站时这些信息再次传回服务器,就应该调用 cgiHeaderCookieSetString()。第一个参数是cookie的名字,为了在所有的浏览器都有好的结果,使用一个没有空格 和特殊标点符号的短名字。再次强调,为了最好的结果,使用一个短字符串。建议cookies用来存储一个唯一的标识信息, 这样在服务器端可以在数据库中查询更加详细的信息。试图在浏览器保存一些复杂的信息更可能失败。第三个参数是 cookie在浏览器端的保存时间,单位秒。86400是一天,365*86400 大概是一年。第四个参数是这个网站的部分URL,在 这里面cookie才是有意义的。如果想要每次访问整个网站,cookie都被发送回服务器,设置这个参数为/。最后的参数 是网站的名字或者是整个域,对于它们来说,cookie应该被提交。如果你选择对于整个域,cookie都要被传回,这个参数 必须以一个dot(.)开始,例如.boutell.com。cgic 变量cgiServerName对于第四和第五个参数是很方便的值。参考 cgiHeaderCookieSetInteger, cgiCookieString, cgiCookieInteger and cgiCookies.void cgiHeaderCookieSetInteger(char *name, int value, int secondsToLive, char *path, char *domain)
cgiHeaderCookieSetInteger
和cgiHeaderCookieSetString
是一样的,除了值是一个整数而不是字符串。参考cgiHeaderCookieSetString
获取更完整的信息。cgiFormResultType cgiCookieString( char *name, char *result, int max)
cgiCookieString
尝试获取发送给指定cookie
的值(浏览器端持续存储的),该文本被拷贝到result buffer
。最多但不 多于max-1个字节,null结尾。 成功获取到字符串值,返回cgiFormSuccess。 成功获取到值,但是为了适应buffer被截断,返回cgiFormTruncated; 字符串被取出但为空,返回cgiFormEmpty。 如果这样的cookie没有提交,返回cgiFromNotFound。在最后一种情况,一个空字符串拷贝到result。cgiFormResultType cgiCookieInteger( char *name, int *result, int defaultV)
cgiCookieInteger()尝试获取发送给指定cookie的整型值(浏览器端持续存储的),result设为被提交的值。 成功获取值,返回cgiFormSuccess; 如果值是空字符串,返回cgiFormEmpty; 如果提交的不是整型值,返回cgiFormBadType; 如果没有该输入字段,返回cgiFormNotFound。 在后三种情况,result设置为指定的默认值。cgiFormResultType cgiCookies( char *name, char ***ptrToStringArray)
当程序员事先不知道所有相关的cookies的时候,cgiCookies是有用的(浏览器端持续存在的字符串)。 结果指向的值是一个字符串数组的指针,最后一个值是null指针。这个数组由cgic库分配。 重要:当完成了该数组的工作后,你必须调用cgiStringArrayFree()释放该数组指针指向的内存。 函数返回cgiFormSuccess,除非发生了内存不足的错误事件。 如果成功,ptrToStringArray设为指向一个合法字符串数组的指针,最后一个是null。 在out-of-memory的情况下,ptrToStringArray设为null pointer,且返回cgiFormOutOfMemory.cgiFormResultType cgiHtmlEscape(char *s)
cgiHtmlEscape输出一个null结尾的字符串到cgiOut,转换任何 ‘<, &, >‘ 为正确的符号(<,>,&),以便于他们不会妨碍HTML标记。 返回cgiFormSuccess 或者如果发生了I/O错误事件,返回cgiFormIO。cgiFormResultType cgiHtmlEscapeData(char *data, int len)
cgiHtmlEscapeData和cgiHtmlEscape是一样的,除了数据不是null结尾的。这个版本的函数输出len长度字节数。cgiFormResultType cgiValueEscape(char *s)
cgiValueEscape()输出一个null结尾的字符串到cgiOut,转换 引号(”) 字符到正确的符号以便于不妨碍HTML属性值的 引用标记。当输出一个字符串作为一个输入标签的value属性的一部分的时候,或者一个link和表单标签的href属性的时候 它是有用的,函数返回cgiFormSuccess,或者cgiFormIO,如果发生I/O错误事件。cgiFormResultType cgiValueEscapeData(char *data, int len)
cgiValueEscapeData和cgiValueEscape是一样的,除了数据不是null结尾的。这个版本的函数输出len长度字节数。cgiEnvironmentResultType cgiWriteEnvironment(char *filename)
cgiWriteEnvironment()被用来写所有的CGI 环境变量包括表单数据,到指定的输出文件。然后cgiReadEnvironment() 用来从指定的输入文件还原环境变量用于调试。当然,只有你使用cgic 拷贝CGI环境变量并且使用cgiIn和cgiOut而不是 stdin和stdout,这些才会如你所期望的那样运行。这些函数用来捕获服务器运行期间真实的CGI状态,然后在调试环境下创建他们。 如果成功的话这两个函数都返回cgiEnvironmentSuccess; 如果发生IO错误返回cgiEnvironmentIO; 如果发生了内存不足的错误返回cgiEnvironmentMemory。cgiEnvironmentResultType cgiReadEnvironment(char *filename)
参考cgiWriteEnvironment。int cgiMain()
程序员必须实现这个函数,这个程序完成一个唯一的任务并且由cgic库中的真正的main()函数调用。cgiMain的返回值就是 程序的返回值。在这个函数里面会调用很多cgiForm函数。参考”如何写一个cgic应用程序“查看细节。
cgic 变量手册
这部分提供程序员可以利用的cgic库提供的全局变量的使用手册。为了cgic库的CGI调试功能的可移植性,应该使用这些变量代替stdin
,stdout
,getevn()
。这些变量的大部分与CGI环境变量相同。最大的不同是cgic环境字符串绝不会是空指针。如果没有内容,用空字符串代替。
char *cgiServerSoftware
: 指向服务器软件名,如果不知道,指向一个空字符串。char *cgiServerName
: 指向服务器名字,如果不知道,指向一个空字符串。char *cgiGatewayInterface
: 指向网关接口名字(通常是CGI/1.1
),如果不知道,指向一个空字符串。char *cgiServerProtocol
: 指向使用的协议(通常是HTTP/1.0
),如果不知道,指向一个空字符串。char *cgiServerPort
: 指向服务器监听HTTP连接的端口号(通常是80),如果不知道,指向一个空字符串。char *cgiRequestMethod
: 指向请求使用的方法(通常是GET 或 POST),如果不知道,指向一个空字符串(不该发生)。char *cgiPathInfo
: 指向请求URL中在CGI程序名前附加路径信息。char *cgiPathTranslated
: 指向附加路劲信息,这些信息被本地服务器转换成文件系统路径。char *cgiScriptName
: 指向被调用的脚本程序名字。char *cgiQueryString
: 包含GET
方法或者一个<ISINDEX>
标签提交的查询信息。没有必要直接去解析这个信息,除非使用的是标签。通常,cgic库会自动的解析,只需调用cgiForm家族的函数来获取相关表单输入字段的值就可以了。 char *cgiRemoteHost
: 指向浏览器的完全解析主机名,如果不知道,指向一个空字符串。char *cgiRemoteAddr
: 指向浏览器的点分十进制ip地址,如果不知道,指向一个空字符串。char *cgiAuthType
: 如果有的话,指向请求使用的身份验证类型,如果不知道,指向一个空字符串。char *cgiRemoteUser
: 指向已经通过身份验证的用户名。如果没有经过身份验证,为空字符串。确切的信息取决于使用的身份验证类型。char *cgiRemoteIdent
: 指向用户自愿给出的使用用户鉴别协议的用户名;如果不知道,为空字符串。这个信息不是安全的。鉴别进程可能被用户安装在不安全的系统,例如windows。char *cgiContentType
: 指向用户提交的信息的MIME类型;如果没有消息被提交,为空字符串。如果这个字符串是application/x-www-form-urlencoded
或者multipart/form-data
,cgic
库会自动的检查提交的表单数据。如果这个字符串是其他的非空字符串,代表被提交的数据是其他类型。目前这很少见,因为大部分浏览器仅仅提交表单和上传文件,这些cgic可以直接解析。char *cgiCookie
: 指向网页浏览器提交的原生cookie数据(浏览器端持续存储)。程序员应该使用cgiCookies,cgiCookieString,和cgiCookieInteger来检测数据。char *cgiAccept
: 指向浏览器可接受的以空格分隔的MIME内容类型列表,或者是一个空字符串。不幸的是,现在大多数的浏览器不在表单提供这个变量。char *cgiUserAgent
: 指向使用的浏览器的名字,如果这些信息不可用,指向一个空字符串。char *cgiReferrer
: 指向用户前一个访问页面的URL。这经常是表单的URL,把用户带到你的程序。报告这个信息完全取决于浏览器,它可能选择不这样做,或者选择不诚实的做。然而,这个变量通常是准确的。经常使用的另外一个拼写cgiReferer作为一个宏提供。int cgiContentLength
接收的表单的或者查询数据的字节数。注意,如果提交的是一个表单或者查询信息,cigc库会直接从cgiIn或者cgiQueryStirng读取并解析这些信息。 程序员不该这样做,并且这种情况下cgiIn会指向文件的末尾。FILE *cgiOut
: 指向CGI输出。cgiHeader函数,例如cgiHeaderContentType,要首先被调用来输出mime头。接着,HTML页面,GIF图像或者其他的网页文档被输出到cgiOut,使用标准CI/O
函数,如fprintf
和fwrite
。cgiOut
通常等价于stdout
。然而,考虑到对未来cgic的版本在特殊环境下的兼容性,建议使用cgiOut。FILE *cgiIn
: 在大多数情况下,cgic函数被设计成提供合理的结果,即使浏览器或者用户做了不合理的事情。然而,有时候精确的知道发生了什么不合理的事情是重要的,特别是当赋予一个默认值或者限制一个值不是很好的解决办法的时候。下面的返回码用于这种情况是有效的。
cgic返回码手册
cgiFormSuccess
: 表示函数成功执行了最少一个动作(或者在适用的情况下表示获取到至少一个值)cgiFormTruncated
: 表示截断了一个取出的字符串,以避免缓冲区越界。cgiFormBadType
: 表示用户提交的数字型值实际上不是合法值。cgiFormEmpty
: 表示一个取出的字段值没有数据。cgiFormNotFound
: 表示一个指定的字段没有被提交cgiFormConstrained
: 表示一个数字型值超过了指定的区间,被强制转换成上边界或者下边界。cgiFormNoSuchChoice
: 表示用户提交的一个单选字段的值不是可接受的值,这通常表示在表单和程序之间有矛盾。cgiFormEOF
: 表示cgiFormFileRead
函数调用的时候,cgiFilePtr
对象已经指向上传文件数据的末尾。cgiFormIO
: 当cgiFormFileRead
读取上传文件数据遇到一个I/O错误时返回这个错误。cgiFormNotAFile
: 试图使用一个文件相关的函数操作一个非上传文件表单字段时,返回这个错误作为响应。cgiFormNoContentType
: 试图获取上传文件的内容类型,浏览器没有指定内容类型时,返回这个错误作为响应。cgiFormNoFileName
: 试图获取上传文件的名字,浏览器没有指定名字时,返回这个错误作为响应cgiFormOpenFailed
: 试图从一个空的cgiFilePtr对象读数据,通常是程序员检测cgiFormFileOpen的调用失败。cgiEnvironmentMemory
: 表示试图从/向一个捕获文件读/写CGI环境变量时,发生了内存不足的错误。cgiEnvironmentSuccess
: 表示试图从/向一个捕获文件读/写CGI环境变量成功。cgiEnvironmentIO
: 表示试图从/向一个捕获文件读/写CGI环境变量时,发生了I/O的错误。cgiEnvironmentWrongVersion
: 表示试图读取由2.0以前的CGIC版本保存的用于调试的CGI环境变量。(版本不对)