Documentation & Manual
Index
2.1.1.1 CGI independent environment Vars
2.1.1.2.2 the simple
POST Method
2.1.1.2.3 the
multipart/form-data POST Method
3.1.3 cgiParam() – the main function
3.1.5 Compatibility to version <= 0.5
3.2.2 The filename and the Content-Type
3.3 Accessing Elements without the knowing the
name
3.3.2 Access to Elements using cgiGetFirstName() /
cgiGetNextName()
3.3.3 Access to Elements using cgiPosNext()
3.4.2 „usual“ c file functions and memory files
3.4.3 additional memory file functions
3.4.3.2 mfFileToMFile()/mfFileToMFileN()
3.5 Saving/Loading the environment for simple
debugging
4.3 ecgi-internal.h – not for the user!
A2 Multi Form Demo / All Data access Demo /
Template Demo:
eCgiLib
(= easy CGI Libary) is a simple to use c cgi libary based on cgic. It
transparently supports the CGI Method GET and POST and also multipart/form-data
file uploads. The user interface is designed as easy as possible and maintains
full compatibility to cgic 0.5, from which it is derived
It also
contains a libary independend introduction to CGI programming with C, a .html
to .h HTML template preprozessor and fast block allocating „memory files“
It was
written after needing file upload, searching the web, and finding NO usable c
cgi libary, which support all mentioned things AND a easy interface. Course i
used cgic 0.5 before, and knew Todor Prokopov <koprok@newmail.net>
stopped developing it further, i used his work - and his multipart tests, which
he send me. Thanks man!
*****************
* NOW TESTED ON *
*****************
- linux glibc 2.1 gcc 2.95.2
- solaris7 gcc
2.95.2
***********************
* SOURCE CODE
CHANGES *
***********************
0.6.2
-----
- added comments headers to files - juhuu!
- copied gpl to doc/COPYING
- fixed bug in html2h - input files where also parsed in comments
13.02.2001
- there was only ONE untouched function from cgic 0.5 left in my ecgi lib,
and this bastard function had an segfault in it (which occured only
VERY rarely) - thanks to "Bao C. Ha" <bao@hacom.net> for showing me.
(who also maintains a ecgi debian package now ...)
- fixed a bug in memfile.h, where i used a "va_list *ap" instead of a
"va_list ap" - how silly ...
thanks to Ramon Poca <ramon@tau.uab.es> for telling me
- ecgitk uses time.h - added an "#include <time.h>", so you dont have to
include it in your programm
0.6.1
------
08.12.2000
- compiled now on solaris - fixed some segfaulting typecasts.
- added: html2h preprozessor now finds HTML input tags and adds them to
the .h header - so you allways know, what names to ask for with cgiParam(),
when getting input from a template
- "ANSI-CLEANED" the sources by changing some things after invoking gcc
with -ansi and -pedantic - changed all // to /* .. */ - ANSI-PROVED now :)
- cleaned up Makefile - ie: if the "gcc --shared" fails now, this happens
as the last thing - should not make any real problems, since libecgi.a
should be generated then
- replaced setenv() with putenv() in loadDebugData(), because setenv is not
supported on solaris (why ever)
- read basics about "autoconf" and decided NOT to do this myself, since my
focus is c-programming!
0.6 Initial Release
-------------------
December 2000
- rewrote complete source from Todor Prokopov - only 1 untouched function
in the sources - added new functions for file handling, for accessing all
objects without the name and saving/loading the whole environment for
debugging.
06.12.2000
- added mfFileToMFileN()
- cgiLoadDebugData() and cgiSaveDebugData() functions work now
- mfprintf() added to memory files
- added "make install" to the Makefile (it creates a .a ar
archive and a .so lib now)
- html2h preprozessor added
- added first functions to ecgitk - eCgiToolkit
*************************
* Documentation Changes *
*************************
13.02.2000
- added example to cgiInit() documentation, since i got an email with a
segfault while calling the cgi from bash
06.12.2000
- documented mfFileToMFileN() - see 3.4.3.2
- updated cgiLoadDebugData() and cgiSaveDebugData() documentation (3.5.1 and 3.5.2)
- added mfprintf() without further documentation to 3.4.2
- updated chapter 3.6 ecgi Toolkit
- added chapter 3.7 html2h HTML preprozessor
December 2000
- wrote first version
Thats me!
Sven Dawitz – Sven@Dawitz.de. The web adress for ecgi is
http://www.Global-OWL.de/ecgi
If you
want to contact me, feel free to do so!
This Software is under GPL. Blah Blah Blah ...
You have
the sources. Just do with it, what you want.
It would
be nice, if you drop me an email, if you use it. Just to see, who finds it
useful. Perhaps i will add a „Used by“ section later.
Put this documentation to professional english.
Copy Dahl promised me to do so, if he is available for it. So currently,
remeber that i am no natural english speaking guy and have my problems with
putting it really professional – but the information itself should be ok ...
If someone else, who is naturally speaking
english, read this – perhaps you could edit it and fix up my language.
If you try file uploading, but it fails: check
your proxy settings – squid for example only allows upload till 1MB at all – to
find out, my program doesnt segfault, but isnt even executed took me about 6
hours of work – upside: i rewrote much code for it and made it (hopefully)
segfault secure.
- check all malloc/realloc calls for returning
NULL – memory at end
- really USE maxfilesize and maxsize –
currently dont do anything
- implement the
cgiSaveDebugData()/cgiLoadDebugData() – currently they write an error to stdout
and exit the program ... not good ...
- check error defines - some are obsolete /
others must be added
- think about defining max name size due
security (a 150mb name would be fun)
- integrate mad sql libary ...
- integrate ecgitk lib
- test if you can use fread/fseek on stdin to
speed up reading
doesn
work with popen files, so i guess, wont work with stdin, too
but
have to check! Block reads would be really nice!
The user
submits Data via the browser to the web server, who calls the program. There
are two main kinds of input to our programm:
The web server
sets environment vars, which contain misc information about the client, like
"REMOTE_ADDR" for the remote ip adress, "REMOTE_HOST" for
the remote hostname, "SCRIPT" for the script name and others. You can
get these environment variables with getenv(). Dump the whole environment variables
with a little test programm to see more. These are not part of ecgi. (Or at
least, most of them). And they are extremly server and browser dependend.
This
describes the ways, the CGI programm can get input from the user - There are
three ways for that.
Ecgilib
handles all transparent for you – you dont need to know how that works. This is
just info about the things behind ecgilib.
The data
is send in the URL - you might have seen this before:
http://foo.bar/whatever.cgi?field1=this+is+test+data&field2=any%20other%20data
This is a
simple Method - and the only method, you can use with links - for the others,
you need a real form, with datafields and at least one submit button. So you
will need GET when having many links on your page, which all do something else.
The web
server puts this data (after the ?) to the environment variable
"QUERY_STRING" where ecgilib reads and parses it.
The GET
Method is detected by the environment variable REQUEST_METHOD which is set to
"GET".
As far as
i know, this method has a maximum length somewhere - but i am neither sure, nor
do i know, where this limit is. If you know, email me.
The data
is encrypted the same way than in the GET Method:
field1=this+is+test+data&field2=any%20other%20data
But the
Web Server writes it to STDIN instead of writing it to a environment variable.
You need a real form with <FORM ACTION="script.cgi"
METHOD="POST"> and a submit button for this method.
The POST
Method is detected by the environment variable REQUEST_METHOD set to
"POST" and CONTENT_TYPE set to
"application/x-www-form-urlencoded".
With
using stdin, this method has no limits i am aware of.
This
method was created, when realizing, that the usual GET/POST methods were not
able to send binary data. So multipart/form-data was born, which uses MIME
encryption like:
Content-tpye: mulitpart/form-data; boundary=xyz4711
--xyz4711
content-disposition: form-data; name=“username“
Joe Blow
--xyz4711
content-disposition: form-data; name=“file-upload“; filename=“c:\command.com“
Content-Type: unknown/unknown
Binary data here ff 00 xx yy ...
--xyz4711
content-disposition: form-data; name=“multiline-field“
This is
The multiline
Field test
--xyz4711--
For
Input, STDIN is used again. if we have a <FORM
ACTION="script.cgi" METHOD="POST" ENCTYPE="multipart/form-data"> we can use a <INPUT
TYPE="FILE" NAME="file"> inside this form to upload any files (text
files AND binary files)
The
method is detected by the environment variable REQUEST_METHOD set to
"POST" and CONTENT_TYPE starting with
"multipart/form-data".
The
result from the program is simply sent to stdout – in two parts, which are
seperated by an empty line
Without
an Header, the webserver will produce an internal server error. The Header has
to be at least „Content-Type: text/html“ followed by two returns.
In the
header, you can set some more items, like telling the browser/proxies not to
cache the site, redirecting the browser to another url by a „Location: http://new.url.com“, setting cookies and other stuff.
The data
submitted in the header is not shown to the user.
Read the
HTTP documentation for more info on this.
This is
simply the HTML Page itself, which is rendered by the browser and displayed to
the user. Use simple HTML syntax here.
This
Chapter shortly gives tips for creating larger cgi programs. It is only a short
list of items which can be used. It mainly focuses on the fact, that when
having two (or more) pages (ie first page like login with username and
password, second page enter some data), the programm is called once for any
pages submitted – so you have to exchange data between these two pages. If you
dont use any technics here, your programm wont know about the username on the
second page.
Make sure
to read the demo programms which are in the appendix.
This is
an „old“ method – mainly used by javascript programms, which run on the client.
You can
use cookies to save data on the client. This is protected by the second domain
name, so „www.domain.com“ and „shop.domain.com“ can access the same cookie,
which is marked as „domain.com“. They can not access cookies from
„other-domain.com“.
The data
saved is limited to 40k (as far as i remeber).
Since
many security problems with cookies were found, the new browsers can be set to
ask you for any cookie before accepting it. Some firewalls completly disallow
cookies.
So dont
use this method if you are not absolutly sure what you are doing.
Remark:
Cookies are not yet supported by ecgi. Perhaps a later version will support it
in the toolkit.
A
commonly used technic to save data from one form to the next is using hidden
input fields like with <input type=“hidden“ name=“username“ value=“john
doe“> or <input type=“hidden“ name=“password“ value=“secret“>. So the
data entered on one form will be printed on the other form. You will need
hidden input fields for the following methods. There is not disadvantage of
using this. Only remeber that a „bad user“ might create his own form, which he
sends to your server. So check all input.
If you
have one programm, which generates various pages, you must verify, which action
is called now. Take a hidden input field like <input type=“hidden“
name=“action“ value“3“> for it. If you are in the main function, you can
read the action variable, if it is empty, display the enter page, if not, call
the function associated with this value. Read the demo in appendix for an
simple example.
This is
one of the best technics i know of. When the user enters, you assign a unique
session id to him. To make it unique, take the time, the remote address and the
remote port - this combination must be unique according to tcp/ip.
Format it
as characters, so it is a little crypted.
From now
on, ALLWAYS keep the session key as a hidden field. So you can fill shoppers,
save any other data of this session without cookies. You can also add an expire
data, when the session times out – and an expire idle date, when the session
times out when the user not longer accesses you (you must update a last access
field for this of course – use databases!)
If you
use frames/layers generated by your cgi programm, you should redirect him, if
he enters. Else, if he hits reload, you could loose his session id.
Not much
to say about it, just: use them! You can use databases extremly well for all
kind of cgi. Else, you have to write to tmp files on your own.
I usually
use html templates to write resulting pages. So you dont have html in your
sources, where it probably shouldnt be, but only in external html includes. If
you wanna change the layout, you dont have to change the sources. Read the demo
for an example.
Caution:
if you use tables with lists of data, you have to split the html include in
something like „head“ / „row“ / „foot“ – or even more. I am currently working
on a preprozessor, so you keep your old html files and only add comments for
the preprozessor – which generates your .h file from the .html file. This way,
you can allways edit your html file with a html editor and dont have to care
about the output.
Advanced
tip: For big programs, think about putting the templates in a database, too. So
you can change the layout without recompiling the source. You can even add an
option to let the user change layout while using the program (if you have more
than one layout in your database)
Here i will put the basic command to ecgi. If
you dont use File Upload, this is probably all you need – only 3 functions:
cgiInit()/cgiDone() at start/end of programm and cgiParam() for accessing the
data. Cgic <=0.5 ONLY had these functions. Thats why it was intended by
Todor to call it EASY cgi.
int cgiInit();
Call this function before accessing anything
else. It will parse the input from the server to the program and create an
internal list of elements. Only call this function once.
The only functions which may be called before
cgiInit are cgiSetMaxFileSize() and cgiSetMaxSize() to set the maximum limit of
memory used by ecgiLib (see below)
If the init fails for any reson, the function
returns false and sets cgi_errno. You can call cgiStrError() then to get the
error.
Call it this way. Else, you might get
segfaults, when calling the program from the shell directly!
If(cgiInit()!=ECGI_SUCCESS){
printf(“ERROR: %s\n”, cgiStrError(cgi_errno));
exit(cgi_errno);
}
void cgiDone();
Call this function before exiting the programm.
Will free all memory occupied by cgiInit().
Note: since most unix systems free all used
memory of a program, when this program exits, you must not call cgiDone.
Depending on the specific unix system, which you use, it might be faster just
to exit and let the system free the memory.
const char *cgiParam(const char *name);
This will probably be your most called function.
I take the explation of Todor Prokopov here, which is very good:
Use the `cgiParam' function to grab the HTML
form entry value, with a
given name, for example:
char *color;
...
color = cgiParam("color");
This function returns NULL, if there isn't an
entry with the given name,
passed to the program, so after calling it, you
should check that:
if (color == NULL)
{
/* There isn't an entry called "color", passed to us. */
...
}
In instances of multiple values, associated
with the same entry name, repeated
use of `cgiParam' with the same entry name
cycles through all it's values,
associated with the given name. The return of a
null pointer indicates the end
of the cycle. If you need to retrieve the first
value instance again, you need
to reset the function. Do this by passing a
null pointer, as a parameter to it.
For example, let's assume we have the following
entries:
fruit=apple
fruit=pear
The
first call `cgiParam("fruit")' returns "apple", the second
returns
"pear", the third, fourth and so on
returns NULL. If you wish to retrieve the
first value ("apple") again, first
reset the function by calling
`cgiParam(NULL)', and then the call
`cgiParam("fruit")' returns "apple"
again.
The `cgiParam()' function is reset also by
invoking it with another entry
name, not the same as that, you have asked the
last time. For example if you
have the following entries, passed:
fruit=apple
fruit=pear
vegetables=potato
Invoking `cgiParam("fruit")' for the first time gives
"apple", the second
call `cgiParam("vegetables")' returns
"potato" and if you call it again like
this `cgiParam("fruit")', you get
"apple" again, not "pear"! If you call
`cgiParam("fruit")' one more time,
then you get "pear".
NOTE: You should not modify the string returned
by `cgiParam'. If you have
to modify it, first copy it to another piece of
memory, using `strdup' or
something similar.
const char* cgiStrError(int errnum);
call this, if cgiInit() returns false, which indicates
an error:
if(cgiInit()==false){
printf(„An Error occured: %s“, cgiStrError(cgi_errno));
exit(cgi_errno);
}
You might have noticed, that the function names
changed. For example, cgi_init() now is called cgiInit(). This naming system is
used for all functions now. To stay compatible with the old names, there are
some precompiler macros. So the old sources still run with the new lib.
Its just, i personally prefer the case
separtion for names than the underscore stuff.
The basic concept for fileuploads it NOT to
write the files to any temporary directory, but to the memory. This is done,
because writing to a directory will probably bring up security problems.
Overwriting existing files, files readable for everyone etc. Reading in memory
is a problem, too (you could send REAL big files for DOS attacks). Thats, why i
implemented a limit (by default unlimited)
Remember once again: If you do a file upload,
the memory is free’d as soon as the result is sent to the user again and NOT
during the whole session. So memory usage should be no problem. Its in the
hands of the programmer (YES, thats you!). eCgi only provides the tools.
Perhaps i will add an option to write them
directly to a directory – even i dont see any need for this now.
const char *cgiGetCTyp(const char *name);
the filename can be obtained by a simple
cgiParam() call – it is treated like a usual value. The content type can be
obtained by a cgiGetCType() call.
Note that both – the file name and the content
type – are extremly browser dependend. For example an upload of „/etc/passwd“
with netscape for linux will result in filename „passwd“ content type
„text/plain“. The upload of „C:\autoexec.bat“ with Microsoft Internet Explorer
for Windows will result in filename „C:\autoexec.bat“ with content-type
„dont-remeber-gotta-testJ“
With the cgiGetKind() function, you can get the
kind of a cgi element by the name. There are four kinds.
#define CgiKindValue 1
Usual Name/Value Pair send with normal HTML
datafields. Just use cgiParam for those.
#define CgiKindFileEmpty 3
An Empty file has been sent.
This can have two reasons:
- No File selected by the user.
- The selected file was only 0bytes long
You can probably check this, when checking if a
filename is set. If filename is NOT set, there was no file selected. if it is
set, the file was 0byte.
Note: this is browser depended – some browsers
perhaps wont send a filename in some cases - so you can NOT check, if it is
empty or not selected anymore
#define CgiKindFileToBig 4
the file uploaded has exceeded one of the
limits set with the limit functions (see below).
#define CgiKindFile 2
Here we finally have a real file.
You can get the Memory File with cgiGetMFile()
(see below), or you can directly dump the file to a real file with
cgiMFileToFile()
MFILE* cgiGetMFile(const char *name);
Calling this function with the name as
attribute (see cgiParam()) returns a Pointer to the Memory File MFILE* (see
below for handling of memoryfiles)
int cgiMFileToFile(const char *name, const char *fname, const char *fopenmode);
This function writes the file from memory to
your local hard disk. Call it with the
fname to write to, and the open mode (see „man fopen“ for details of openmode)
Note: memory is NOT free’d by this. You must
call mfclose to do so.
As mentioned above, you can set limits to the
memory used for file uploads to prevent DOS attacks.
Once one of the Limits is overrun, the kind is
set to cgiKindFileToBig. The other cgi elements are processed as usual.
void cgiSetMaxFileSize(int size);
This functions sets the maximum amount of
memory used for a single file.
void cgiSetMaxSize(int size);
Since a form can have more than one file upload
fields, this functions sets the maximum amount of memory which can be used for
all file uploads.
Some might say: „You allways know the name,
course you are the programmer“ – right, but there are some cases, you do NOT
know them.
Imagine a tool for sending mails (like
formmail.pl does) – you can set a hidden filed „needed“ which has the names of
the fields which may not be empty. If all fields are filled, you send an email
to the adress in „mailto“. If you can access all elements, you could easily
write such a variable tool. Thats only ONE point where i needed these functions
in the past.
There are two general ways of accessing the
internal list of elements.
const char* cgiGetFirstName();
const char* cgiGetNextName();
cgiGetFirstName returns the name of the first
element – or NULL if no element found –
and sets an internal pointer to the last returned name.
CgiGetNextName returns the next – or NULL if
there is no next name.
Since you can access all cgi functions by the
name, this is one way of getting all elements.
Example:
const char *val;
printf("%s\n", cgiGetFirstName());
while((val=cgiGetNextName())!=NULL)
printf("%s\n", val);
typedef void CgiPos;
CgiPos* cgiPosNext(CgiPos *lastpos);
const char* cgiPosParam(CgiPos *where);
const char* cgiPosName(CgiPos *where);
int cgiPosGetKind(CgiPos *where);
const char* cgiPosGetCTyp(CgiPos *where);
MFILE* cgiPosGetMFile(CgiPos *where);
int cgiPosMFileToFile(CgiPos *where, const char *fname, const char *fopenmode);
The function cgiPosNext() returns a pointer to
the first element if called with parameter NULL, or to the next if you give the
last return value.
With the CgiPos *, you can access all the cgi
functions like accessing them by name. (In fact, the name functions internally
use these functions. So using the CgiPos* pointer might be slightly faster)
An example should show the usage:
CgiPos *p=NULL;
while((p=cgiPosNext(p))!=NULL){
printf("%s : ", cgiPosName(p));
while((val=cgiPosParam(p))!=NULL)
printf("%s ", val);
if(cgiPosGetKind(p)==cgiKindFile)
printf(“This is an file!”);
printf("\n");
}
const char* cgiNameByValue(char *value);
This function returns the name of an element by
give the value. Might be usefull in some cases. If called multiple, it acts
like cgiParam() and returns all names containing this value, returning NULL
when not finding any more.
Example:
while((name=cgiNameByValue("test"))!=NULL)
printf("%s\n", name);
The memoryfiles are not only the representative
of the uploaded files, but heavily used internal for dynamic allocation. Since
they dont call realloc every added byte, but allocate blocks of bytes (is
defined in memfile.h – default=4k Blocks), they are the fast way to dynamic
memory.
You can use this files for everything, which
requires much allocation like:
char *buf=NULL;
FILE *f=fopen(„whatever“, „r“);
int c, size=0;
While((c=fgetc(f))!=EOF){
buf=realloc(buf, size+1);
buf[size++]=c;
}
In such a case, you should use the memfiles –
and their feature of block allocation. This should be faster, since malloc/realloc
is commonly slow.
FILE *f=fopen(“whatever”, “r”);
MFILE *mf=mfopen();
int c;
while((c=fgetc(f))!=EOF)
mfputc(c, mf);
Now you can access the data with all the
mf-functions.
MFILE* mfopen();
void mfclose(MFILE *mf);
int mfseek(MFILE *mf, int offset, int whence);
int mftell(MFILE *mf);
int mfeof(MFILE *mf);
int mfread(void *data, int times, int length, MFILE *mf);
int mfwrite(void *data, int times, int length, MFILE *mf);
int mfgetc(MFILE
*mf);
int mfputc(unsigned char c, MFILE *mf);
int mfprintf(MFILE *mf, const char *format, ...);
If you are a c-programmer (hope you are, why
else you read this ???), you know these functions. They are exactly what the
functions without the leading m are – like mfopen() is the pedant to fopen(),
mfseek() is the pedant to fseek(). If you dont know them, just read the c
documentation (like „man fseek“)
Differences:
mfopen() doesnt get any filename,
openmode – i think, its obvious why J
mfclose() free’s all memory used by
the file – dont access this pointer after mfclose()
Since this are not REAL Files, we have some
more (usefull?) functions.
int mfMFileToFile(MFILE *mf, FILE *f);
Appends a MFile to a given File Handle.
Use cgiMFileToFile() for simple dumping.
Note: it APPENDS it to the current position of
the cursor in the file. (see „man fopen“, „man fseek“)
int mfFileToMFile(FILE *f, MFILE *mf);
int mfFileToMFileN(FILE *f, MFILE *mf, int count);
Appends a File to a given MFile.
Use the mfFileToMFileN(), if you dont want the
whole file to be appended.
Note: it APPENDS it to the current position of
the cursor in the MFile (see mfseek)
const char* mfGetData(MFILE *mf);
Returns the data of the file. The data is
terminated by 0 at the end of the file, so you can use
printf(„%s“, mfGetData(myfile));
to dump the data. Remeber: if it is a binary
file, it might look very ugly.
const char* mfGetDataAt(MFILE *mf, int start);
same as mfGetData(), but only returns data from
start byte. String also NULL Terminated.
int mfSetLength(MFILE *mf, int length);
For resetting a file to 0 bytes, or any other
length. This does not free the used memory, if you set it to 0 bytes. Use
mfclose()/mfopen() to free memory. If you set the length bigger than old
filesize, data is reallocated.
int mfGetLength(MFILE *mf);
returns the length of the file – so you dont
have to get it with
mfseek(mf, 0, SEEK_SET);
size=mftell(mf);
int cgiSaveDebugData(char *fname, char *fopenmode);
Call cgiSaveDebugData() after cgiInit(). This
will dump the whole internal list of data plus all the environment variables to
a file, which can be later loaded.
int cgiLoadDebugData(char *fname);
Call cgiLoadDebugData() instead of cgiInit() –
after calling this, all environment variables and all cgi elements are restored
to the state, they have been with cgiInit() when saving the data.
You can now debug your program from the console
instead of using a webbrowser.
I will add a cgi Toolkit in „ecgitk.h“ which is
independend from ecgi.h, but includes commonly used functions which you need
when programming cgi programms – like creating a unique session key or
reading/saving cookies. I will put here functions, which I personally need, or
things users request.
int ctkRedirect(const char *format, ...);
This functions redirects the browser to another
url. To make this work, you may NOT have printed anything to stdout yet,
because this is done with the header.
If you redirect does not start with
"http:“, the server name is added to the redirect target to make sure the
browser propperly shows the new url, since some browsers have problems with
this when getting a relativ url.
Example:
ctkRedirekt("%s?action=%d&sessionid=%s“, getenv("SCRIPT_NAME“), ActShowList, cgiParam("sessionid“));
const char *ctkGetSessionID();
Returns a unique session id build from the
time, the remote ip adress and the remote port. Data put to characters to crypt
data a little.
Returned value must be free’d with free()
const char *ctkTimeToHDate(time_t time);
Constructs a asci date from the given time.
Currently only in DD.MM.YYYY format.
Returned value must be free’d with free()
time_t ctkHDateToTime(const char *hdate);
vice versa of ctkTimeToHDate() – returns a
time_t record constructed from a input in format DD.MM.YYYY
Retruns 0, if the given date was in wrong format.
In the html2h/ subdir, you will find a binary
called html2h, which is the first quick version of a html preprozessor. This is
my first way of transparently use the html files as .h templates in a c
programm AND still having the old .html file, which can be edited in a program
like dreamweaver. The problem was, you have to split the html file in various
substrings, delete some rows, you need for the layout, but not for the template
and adding quick links to the script. Make sure, you read and understand my concept
of html templates (2.2.6) and read the example at Appendix A2, which shows the
usage of templates.
html2h parses the tags for it – starting with
<!--# -, replaces " with \“ and replaces % with %%. You need this as
input for printf().
Since it is complicate, to explain what html2h
does, without seeing it, here an simple example (which is also included in the
html2h/ directory)
Input: demo-simple.html
<!--#Section htmlSimpleHead 'Header of Simple demo file'-->
<html>
<head>
<title>Untitled Document</title>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
</head>
<body bgcolor="#FFFFFF" text="#000000">
<form name="form1" method="post" action="<!--#param s script 'Script Name'-->">
<p>Hello <!--#param s name 'Name of logged in User'-->! </p>
<p>Your Phonenumber:
<input type="text" name="phone" value="<!--#param s phone 'Phone Number'-->>
<br>
Select Area:
<select name="areaselect">
<!--#Section htmlSimpleSelectRow 'The select box items - one row for every possible'-->
<option <!--#Param s selected 'Write SELECTED here, if selected'-->><!--#Param 30s katname 'Area Name - bound to right'--></option>
<!--#/*-->
<option>hong kong</option>
<option>japan</option>
<option>home</option>
<!--#*/-->
<!--#Section htmlSimpleSelectBody-->
</select>
</p>
<p>Current Numbers:</p>
<table width="400" border="0" cellpadding="4">
<tr>
<td>Number</td>
<td>Area</td>
<td>
<div align="center">Delete</div>
</td>
<td>
<div align="center">Edit</div>
</td>
</tr>
<!--#Section htmlSimplePhoneRow 'This lists all existing entries'-->
<tr>
<td><!--#param s number 'Phone Number as string'--></td>
<td><!--#param s area 'Area of number'--></td>
<td>
<div align="center"><a href="<!--#SLink 'Link to Delete Entry' s skey 'Session ID' d eid 'ID of the specific entry'-->">->X<-</a></div>
</td>
<td>
<div align="center"><a href="<!--#SLink 'Link to Edit this Entry' s skey 'Session ID' d eid-->->X<-</a></div>
</td>
</tr>
<!--#Section htmlSimpleFoot-->
<!--#/*-->
<tr>
<td>234233</td>
<td>Home</td>
<td>
<div align="center"><a href="x">->X<-</a></div>
</td>
<td>
<div align="center"><a href="x">->X<-</a></div>
</td>
</tr>
<tr>
<td>342523</td>
<td>japan</td>
<td>
<div align="center"><a href="x">->X<-</a></div>
</td>
<td>
<div align="center"><a href="x">->X<-</a></div>
</td>
</tr>
<!--#*/-->
</table>
<p>
<input type="submit" name="Submit" value="Submit">
<input type="reset" name="Submit2" value="Reset">
</p>
</form>
</body>
</html>
Output: demo-simple.h
/****************************************************
* Header file generated with html2h 0.1 *
* (c) 2000 by Sven@Dawitz.de *
****************************************************
----------------------------------------
htmlSimpleHead "Header of Simple demo file"
----------------------------------------
FORMAT NAME COMMENT
----------------------------------------
s script "Script Name"
s name "Name of logged in User"
s phone "Phone Number"
----------------------------------------
htmlSimpleSelectRow "The select box items - one row for every possible"
----------------------------------------
FORMAT NAME COMMENT
----------------------------------------
s selected "Write SELECTED here, if selected"
30s katname "Area Name - bound to right"
----------------------------------------
htmlSimpleSelectBody
----------------------------------------
FORMAT NAME COMMENT
----------------------------------------
----------------------------------------
htmlSimplePhoneRow "This lists all existing entries"
----------------------------------------
FORMAT NAME COMMENT
----------------------------------------
s number "Phone Number as string"
s area "Area of number"
***SCRIPT LINK*** "Link to Delete Entry"
s script "Script Name"
s skey "Session ID"
d eid "ID of the specific entry"
***SCRIPT LINK*** "Link to Edit this Entry"
s script "Script Name"
s skey "Session ID"
d eid
----------------------------------------
htmlSimpleFoot
----------------------------------------
FORMAT NAME COMMENT
----------------------------------------
****************************************************/
const char htmlSimpleHead[]="
<html>
<head>
<title>Untitled Document</title>
<meta http-equiv=\"Content-Type\" content=\"text/html; charset=iso-8859-1\">
</head>
<body bgcolor=\"#FFFFFF\" text=\"#000000\">
<form name=\"form1\" method=\"post\" action=\"%s\">
<p>Hello %s! </p>
<p>Your Phonenumber:
<input type=\"text\" name=\"phone\" value=\"%s>
<br>
Select Area:
<select name=\"areaselect\">
";
const char htmlSimpleSelectRow[]="
<option %s>%30s</option>
";
const char htmlSimpleSelectBody[]="
</select>
</p>
<p>Current Numbers:</p>
<table width=\"400\" border=\"0\" cellpadding=\"4\">
<tr>
<td>Number</td>
<td>Area</td>
<td>
<div align=\"center\">Delete</div>
</td>
<td>
<div align=\"center\">Edit</div>
</td>
</tr>
";
const char htmlSimplePhoneRow[]="
<tr>
<td>%s</td>
<td>%s</td>
<td>
<div align=\"center\"><a href=\"%s?skey=%s&eid=%d\">->X<-</a></div>
</td>
<td>
<div align=\"center\"><a href=\"%s?skey=%s&eid=%d->X<-</a></div>
</td>
</tr>
";
const char htmlSimpleFoot[]="
</table>
<p>
<input type=\"submit\" name=\"Submit\" value=\"Submit\">
<input type=\"reset\" name=\"Submit2\" value=\"Reset\">
</p>
</form>
</body>
</html>
";
<!--#section name [‘comment’]-->
The section tag will create a const char name[]
entry for every section generated. You need a section before any html data. The
comment is optional.
<!--#section htmlHead 'this is just a demo’-->
html stuff here
<!--#section htmlRow 'this is the next row’-->
would result in
const char htmlHead[]=“
html stuff here
„;
const char htmlRow[]=“...
<!--#param format name [‘comment’]-->
format is a printf format.
<!--#param
10.3f floatvar-->
would result in
%10.3f
<!--#slink format name1 [‘comment’] format name2 [‘comment’] ... format name3 [‘comment’] -->
Use this, if you link to the script with varous
parameters.
<!--#slink s skey 'session ID’ d action 'ActShowStats’ s user-->
would result in
%s?skey=%s&action=%d&user=%s
<!--#/*--> not printed in .h file <!--#*/-->
These are the comment tags – link in c. html2h
supports multi nested comments.
Here are the header files of ecgi lib. The
Usage should be obvious after reading chapter 3.
The only file you need to include from your
programm. Auto includes memfile.h, course you need it, too.
#define CgiKindValue 1
#define CgiKindFile 2
#define CgiKindFileEmpty 3
typedef void CgiPos;
/****************************************
* OLD FUNCTION NAMES FOR COMPATIBILITY *
****************************************/
#define cgi_init() cgiInit()
#define cgi_param(name) cgiParam(name)
#define cgi_done() cgiDone()
#define cgi_strerror(no) cgiStrError(no)
/*****************************************
* TRADITIONAL FUNCTIONS OF OLD CGIC 0.5 *
*****************************************/
int cgiInit();
const char* cgiParam(const char *name);
void cgiDone();
const char* cgiStrError(int errnum);
/*******************************
* NEW FILE DEPENDED FUNCTIONS *
*******************************/
void cgiSetMaxFileSize(int size);
void cgiSetMaxSize(int size);
int cgiGetKind(const char *name);
const char* cgiGetCTyp(const char *name);
MFILE* cgiGetMFile(const char *name);
int cgiMFileToFile(const char *name, const char *fname, const char *fopenmode);
/****************************
* MISC OTHER NEW FUNCTIONS *
****************************/
const char* cgiNameByValue(char *value);
/**********************************************************
* FUNCTION FOR GETTING ALL DATA WITHOUT KNOWING THE NAME *
**********************************************************/
const char* cgiGetFirstName();
const char* cgiGetNextName();
CgiPos* cgiPosNext(CgiPos *lastpos);
const char* cgiPosParam(CgiPos *where);
const char* cgiPosName(CgiPos *where);
int cgiPosGetKind(CgiPos *where);
const char* cgiPosGetCTyp(CgiPos *where);
MFILE* cgiPosGetMFile(CgiPos *where);
int cgiPosMFileToFile(CgiPos *where, const char *fname, const char *fopenmode);
/*****************************************************************
* FUNCTIONS FOR SAVING/LOADING ENVIRONMENT - GOOD FOR DEBUGGING *
*****************************************************************/
int cgiSaveDebugData(char *fname, char *fopenmode);
int cgiLoadDebugData(char *fname);
/* the error number */
extern int cgi_errno;
This is the interface for the memory files.
Again: should be compatible with the usual f-functions.
#define MFILE_BLOCK 4096 // in what block should mem be increased
#ifndef true
#define true (1==1)
#define false (0==1)
#endif
typedef struct S_MFILE{
void *data;
int blocks;
int eof;
int used;
int
pos;
}MFILE;
/**************************************************************
* FUNCTIONS 100% COMPATIBLE WITH USUAL F* FUNCTIONS - I HOPE *
**************************************************************/
MFILE* mfopen();
void mfclose(MFILE *mf);
int mfseek(MFILE *mf, int offset, int whence);
int mftell(MFILE *mf);
int mfeof(MFILE *mf);
int mfread(void *data, int times, int length, MFILE *mf);
int mfwrite(void *data, int times, int length, MFILE *mf);
int mfgetc(MFILE
*mf);
int mfputc(unsigned char c, MFILE *mf);
/************************
* ADDITIONAL FUNCTIONS *
************************/
int mfMFileToFile(MFILE *mf, FILE *f);
int mfFileToMFile(FILE *f, MFILE *mf);
const char* mfGetData(MFILE *mf);
const char* mfGetDataAt(MFILE *mf, int start);
int mfSetLength(MFILE *mf, int length);
int mfGetLength(MFILE *mf);
you should not use this file – this is the part
which is used internal, but „hidden“ to the user – if you have any reason to
include this file, i might have forgotten something.
Note: this file and the internal list of cgi
elements might be changed later. The user interface of ecgi.h will not be
changed. So you are compatible to following versions with ecgi.h – with
ecgi-internal.h you might be not!
/***********************************************
* THE STRUCTS/LIST WE STORE THE WHOLE SHIT IN *
***********************************************/
typedef struct _CgiValue{
char *value;
struct _CgiValue *next;
}CgiValue;
typedef struct _CgiElement {
int type;
char *name;
char *ctyp;
CgiValue *values;
MFILE *mf;
struct _CgiElement *next;
}CgiElement;
typedef struct _CGI{
CgiElement *list;
CgiElement *lastasked;
CgiValue *lastret;
CgiElement *lastvalasked;
CgiElement *lastnameasked;
}Cgi;
/********************************
* INTERNAL FUNCTION PROTOTYPES *
********************************/
// List Stuff
int listAddData(int type, const char *name, const char *value, const char *ctyp, MFILE *mf);
CgiElement *listGetByName(const char *name);
CgiElement *listAppendElement(int type, const char *name, const char *ctyp, MFILE *mf);
CgiValue *listAppendValue(CgiElement *where, const char *value);
int listHasValue(CgiElement *check, char *value);
void listFreeAll();
void listDump(); // for test usage only ... dumps all vals to stdout ...
// Init/Parse Stuff
int initGet();
int initPost();
int initMultiPart(const char *cont_type);
int parseMultiPart(char *boundary);
int parseMultiHead(char **name, char **fname, char **ctyp);
int parseQueryString(const char *str, int length); // str==NULL - read from stdin
// Misc Help Functions
int miscStringDecode(char *s);
int miscFReadLn(FILE *f, MFILE *mf);
int miscStringToUInt(const char *str, unsigned int *res);
char *miscStringDelCrLf(char *str);
/**********************
* OTHER USELESS SHIT *
**********************/
/* external vars - here for real */
int cgi_errno=0;
/* global vars */
Cgi *c=NULL;
int maxfilesize=-1;
int maxsize=-1;
int init_called=false;
int init_complete=false;
#define NUMERRS 25
static const char *errmsgs[NUMERRS] = {
"Success",
"Unknown request method",
"Repeated initialization attempt",
"Null query string",
"Unknown content type",
"Invalid content length",
"Unexpected end of input stream",
"Input stream read error",
"Maximum entry name length exceeded",
"Maximum entry value length exceeded",
"Invalid URL-encoded data",
"Maximum number of entries exceeded",
"Memory allocation error",
"Maximum memory limit exceeded",
"Invalid boundary string",
"Null file upload directory",
"Pathname length limit exceeded",
"Extremely long line encountered",
"Missing boundary string",
"Error opening file for writing",
"Error writing to file",
"Error closing file",
"Error changing file permissions",
"Missing initial boundary string",
"Error parsing content disposition"
};
#define CGIERR_UREQM 1 /* Unknown request method */
#define CGIERR_REINIT 2 /* Repeated initialization attempt */
#define CGIERR_NULQSTR 3 /* Null query string */
#define CGIERR_UCONTT 4 /* Unknown content type */
#define CGIERR_ICONTLEN 5 /* Invalid content length */
#define CGIERR_UEOINP 6 /* Unexpected end of input stream */
#define CGIERR_INPREAD 7 /* Input stream read error */
#define CGIERR_MAXNAMEE 8 /* Maximum entry name length exceeded */
#define CGIERR_MAXVALE 9 /* Maximum entry value length exceeded */
#define CGIERR_IURLENC 10 /* Invalid URL-encoded data */
#define CGIERR_MAXENTRSE 11 /* Maximum number of entries exceeded */
#define CGIERR_MALLOC 12 /* Memory allocation error */
#define CGIERR_MAXMEME 13 /* Maximum memory limit exceeded */
#define CGIERR_IBSTR 14 /* Invalid boundary string */
#define CGIERR_NULUPLD 15 /* Null file upload directory */
#define CGIERR_PATHMAXE 16 /* Pathname length limit exceeded */
#define CGIERR_LONGLN 17 /* Extremely long line encountered */
#define CGIERR_MBSTR 18 /* Missing boundary string */
#define CGIERR_FOPEN 19 /* Error opening file for writing */
#define CGIERR_FWRITE 20 /* Error writing to file */
#define CGIERR_FCLOSE 21 /* Error closing file */
#define CGIERR_CHMOD 22 /* Error changing file permissions */
#define CGIERR_MBBSTR 23 /* Missing initial boundary string */
#define CGIERR_DISPPARS 24 /* Error parsing
content disposition */
These are
two very simple, but working programms which should show the basic use of
ecgilib.
<html>
<body><pre>
<form action="demos.cgi" method="post">
Name: <input type=text name="name">
Password: <input type=password name="passwd">
<input type=submit>
</form>
</html>
#include <stdio.h>
#include <ecgi.h>
int main()
{
cgiInit();
printf("Content-Type: text/html\n\n");
printf("Your name: %s\n", cgiParam("name"));
printf("Your password: %s\n", cgiParam("passwd"));
cgiDone();
return(0);
}
Thats it!
const char htmlEnter[]="
<html><body><pre>
<form script=\"%s\">
<input type=hidden name=action value=%d>
Your Name: <input type=text name=name>
Your Password: <input type=text name=passwd>
<input type=submit>
</form>
</body></html>
";
const char htmlPageTwo[]="
<html><body><pre>
<form script=\"%s\">
<input type=hidden name=action value=%d>
<input type=hidden name=\"name\" value=\"%s\">
<input type=hidden name=\"passwd\" value=\"%s\">
Hello %s!
Please Select persons, you wanna kill: <select name=\"to kill\" multiple>
<option>Wife / Girlfriend</option>
<option>Mum and Dad</option>
<option>KILL ALL HUMANS</option>
</select>
Please Enter the amount of Bullets for each person: <input type=text name=bulcount>
<input type=submit value=\"kill em\">
</form>
</body></html>
";
const char htmlPageThreeHead[]=“
<html><head><title>Result of your inputs</title></head>
<body>
This is the data send from you:<p>
„;
const char htmlPageThreeRowHead[]=“
Name: %s <br> Values: <br>
„;
const char htmlPageThreeRow[]=“
%s <br>
„;
const char htmlPageThreeRowFoot[]=“
<hr width=90%%>
„;
const char htmlPageThreeFoot[]=“
<br>Your commands will be executed now!</body></html>“;
#include
<stdio.h>
#include <stdlib.h>
#include <ecgi.h>
#include „html/enter.h“
#include „html/pageTwo.h“
#include „html/pageThree.h“
#define ActEnter 0
#define ActPageTwo 1
#define ActPageThree 2
void
mainEnter();
void
mainPageTwo();
void mainPageThree();
const char *script;
int main()
{
int action=0;
const char *saction;
cgi_init();
script=getenv("SCRIPT_NAME");
saction=cgiParam("action");
if(saction!=NULL) action=atoi(saction);
printf("Content-Type:
text/html\n\n");
switch(action){
case ActPageTwo: mainPageTwo(); break;
case ActPageThree: mainPageThree(); break;
default: mainEnter(); break;
}
cgiDone();
return(0);
}
void mainEnter()
{
printf(htmlEnter, script, ActPageTwo);
}
void mainPageTwo()
{
printf(htmlPageTwo, script, ActPageThree, cgiParam("name"), cgiParam("passwd"), cgiParam("name"));
}
void mainPageThree()
{
CgiPos *p=NULL;
const char *val;
cgiParam(NULL);
// notice, no need to know any of the names of the fields from above
printf(htmlPageThreeHead);
while((p=cgiPosNext(p))!=NULL){
printf(htmlPageThreeRowHead, cgiPosName(p));
while((val=cgiPosParam(p))!=NULL)
printf(htmlPageThreeRow, val);
printf(htmlPageThreeRowFoot);
}
printf(htmlPageThreeFoot);
}