/**************************************************************************** ** ** My own guestbook program ... in C, for Unix or Unix-like systems. ** ** Public Domain Software ** Disclaimer: No warranty, no support, no guarantee. If you use this ** software and it destroys your files or your systems, you are on your own. ** ** Keeping the above disclaimer in mind, feel free to do what you wish ** with this program. If you find bugs, I would appreciate if you would ** send me e-mail about it. ** Also, if you do use this, or modify it for your purposes, I would love ** to check out what you did with this; e-mail again appreciated. ** ** Written by: Avinash Chopde, Sep 1996 http://www.aczoom.com/ ** ** ** Aug 1998: added sendmail notification when an guestbook entry is made. ** Jun 2000: can send mail notification to multiple addresses in form ** Jul 2003: email error message text in case of failure ** send complete HTML text in email on success ** ** [Tabstop=8] ***************************************************************************** ** How to use: ** 0) Modify the source as needed (REMOVE my name from the "ERROR_CONTACT" ** #define in this file!) ** If you want to restrict access, define ONLY_REFERER in this file. ** ONLY_REFERER can be a comma- or space-separated list of multiple hostnames. ** and it will only allow pages at that site to access your executable. ** Otherwise, any host will be able to directly execute your script. ** This protecton may not really be necessary, but if you need it ... ** Once you have all the #defines the way you want them, ** make the cgi executable: cc -Wall -O -o guestbk.cgi guestbk.c [omit -Wall if not supported] ** This executable, when run, will modify two files in the current ** directory. You may need to allow write access to everyone for these ** two files in the WWW directory. ** The two files are the guestbook file: your-user-entry-file.html, and ** a temp file, same name as guestbook with a .tmp suffix. The temp file ** should be created as a zero-length file, with read/write permissions ** to all. ** ** 1) Create your-user-entry-file.html which will contain the form for the ** guestbook, and the automatically appended guest book entries. [You can ** keep the form and the user entries in separate files, just provide ** the right QUERY_STRING in the ACTION arg.] The name of this file must ** end in GBFNAMESUFFIX (see #define below). ** The html page containing the form specify the action :
The file name prefix-your-user-entry-file.html should contain its full path or its relative path from the directory guestbk.cgi is in, except for the trailing suffix GBFNAMESUFFIX. The suffix is added for security reasons. So, if the name of the file is avigb.html, and use this as the action line: ACTION="/cgi-bin/....../guestbk.cgi?avi" (leave out the trailing gb.html from the file name argument to guestbk.cgi) If email notification is needed, add a "notify" variable to the form, example: (and make sure NOTIFY_EMAIL is #defined) ** ** 2) Create the your-user-entry-file.html file. It must contain one ** line exactly like this (no extra space or indents anywhere): ** That is the point at which this CGI executable will add the user ** entries. ** Feel free to check out the Guestbook at my site (View Source from ** your browser) for a real working example of this file. ** Or, use the sample html code provided at the end of this file. ** ** Each user entry is added in this form ([] implies optional fields): ** user-name [affiliation] [e-mail addr] [home page] date, time, timezone user-comments ** The last line is stored in comments ** *****************************************************************************/ /* Portions copyright the National Center for Supercomputing */ /* Applications at the University of Illinois at Urbana-Champaign */ /* Information & additional resources available at: */ /* http://hoohoo.ncsa.uiuc.edu */ /* source file version identifier */ static char GB_VERSION[] = "@(#) GuestBook guestbk 0.99, 06Jul2003"; #define _BSD_COMPAT 1 /* for flock problem with C++, see man flock on SGI*/ #include #include #include #include #include #include #include /* strlen() */ #include /* malloc() */ #include /*-------------------------------------------------------------------------*/ /*-------- VARIABLES YOU MAY WANT TO CHANGE -------------------------------*/ /*-------- following VARIABLES MUST BE CHANGED! ---------------------------*/ /* modify this with your name -- used in the display_error function */ #define ERROR_CONTACT "Your Name\nYourName@YourHost " /*--------------*/ /* enable ONLYREFERER if are worried about people at other sites directly ** executing your .cgi script. define it with your host name, and all ** its aliases. example: #define ONLY_REFERER "www.host1 www.host2 www.host3 ...." */ /*--------------*/ /* following are to be used if the owner needs a sendmail notification * when a guestbook entry is made * If NOTIFY_EMAIL is not defined, no e-mail will be sent - even if the * "notify" input is present in the form. examples: #define NOTIFY_EMAIL "YourName@YourHost" example command: "/usr/lib/sendmail -t -f'YourName@YourHost'" */ #define SEND_EMAIL_COMMAND "/usr/lib/sendmail -t " /* if needed, define FROM_STR - normally, sendmail will add one itself #define FROM_STR "From: Your Name \n" */ /*--------------*/ /*-------- following VARIABLES MAY or MAY NOT BE CHANGED! --------------*/ /* string to look for in guestbook, place to add user entries */ #define USER_ENTRY_PLACE_HOLDER "" /*--------------*/ /* default guestbook file, if not provided in the QUERY_STRING ** may need file permision to be write for all: rw-rw-rw- ** if a name is provided in the QUERY_STRING, it is not used as is, but ** has GBFNAMESUFFIX appended to it ** this is done so malicious CGI crackers do not go around opening ** files they are not supposed to open, etc. */ #define GBFNAMESUFFIX "gb.html" /*--------------*/ /* whether to display the time field using UTC (GMT) or local time ** if you want your (server's) local time displayed, ** use #define USE_UTC 0 */ #define USE_UTC 1 /*--------------*/ /* temp file name -- may need to be pre-created, may need rw-rw-rw- ** is defined to be same as GBFNAMESUFFIX, plus a .tmp suffix */ #define GBTMPSUFFIX ".tmp" /*--------------*/ /* names of my fields in my form */ #define COMMENTS_STR "comments" #define NAME_STR "name" #define EMAIL_STR "email" #define HOMEPAGE_STR "homepage" #define COMPANY_STR "affiliation" #define CAMEFROM_STR "camefrom" #define NOTIFY_EMAIL_STR "notify" /*--------------*/ #define MAX_FIELDS 40 /* max fields to expect */ /* max size in bytes of user entry after it is converted to HTML ** note that 500-1000 chars of this will be HTML chars generated by ** this program, so make this be nice and hefty ** make_html_entry is the function that fills up the output string */ #define USER_ENTRY_HTML_SIZE 32768 /* linelen is used when copying files. ** must be > (size of USER_ENTRY_PLACE_HOLDER + 1) & >= USER_ENTRY_HTML_SIZE */ #define LINELEN_MAX 32768 /*-- end of VARIABLES YOU MAY WANT TO CHANGE -------------------------------*/ /*--------------------------------------------------------------------------*/ /* all the following source code probably can be left untouched...except ** to fix bugs!! */ /* names of env var fields -- may have no values in many cases! */ #define QUERY_STR "QUERY_STRING" #define RHOST_STR "REMOTE_HOST" #define RUSER_STR "REMOTE_USER" #define CONTENTLEN_STR "CONTENT_LENGTH" #define CONTENTTYPE_STR "CONTENT_TYPE" #define REFERER_STR "HTTP_REFERER" #define HTTPFROM_STR "HTTP_FROM" #define REQMETHOD_STR "REQUEST_METHOD" /*=========================== typedefs ===========================*/ typedef struct { /* structure to hold form post input */ char *name; char *val; } entries_t; /*=========================== globals ===========================*/ static char S_ErrorStr[1024]; /* set by display_error function */ /*=========================== prototypes ===========================*/ int get_form_input(entries_t entries[]); void show_all_entries(entries_t entries[]); int make_html_entry(entries_t entries[], char* outstr, int max_outstr_len); void show_invalid_input_page(entries_t e[], char* htmlstr, char* gbfname); int update_html_list(char *guest_entry); int add_to_book(char *htmlstr, char* gbfname); void display_error(char* s1, char* s2); int find_entry(entries_t entries[], char* str); int edit_comments(entries_t* centry); int send_email(char* message, char* to, char* replyto, char* subject, char* lastline); void blankout_html(char* htmlstr); /*=========================== include c-files ===========================*/ /* routines copied from ncsa CGI examples */ char *makeword(char *line, char stop); char *fmakeword(FILE *f, char stop, int *cl, int *alloc_len); char x2c(char *what); void unescape_url(char *url); void plustospace(char *str); void send_fd(FILE *f, FILE *fd); void output_html(void); void output_location(char *url); void html_header(const char *htitle); void html_footer(void); char *snr(char *instring, const char *search, const char *replace, int maxlen); int xtoe(char *str, int maxlen); int extract_host (const char url[], char* host, int host_len); /*=========================== main ===========================*/ int main(int argc, char* argv[]) { entries_t entries[MAX_FIELDS + 1]; int i, j; char* htmlstr; char* c; char gbfname[FILENAME_MAX]; int only_referer_ok; c = GB_VERSION; /* try to fool NOTUSED compiler warning */ /* check that we have received a POST message */ c = getenv(REQMETHOD_STR); if (!c || strcasecmp(c, "POST")) { /* for only referer status, run guestbk -h from command line */ if (argc > 1 && !strcmp(argv[1], "-h")) { printf("guestbk version: %s\n", GB_VERSION); #ifdef ONLY_REFERER printf("restricted referers to: %s\n", ONLY_REFERER); #else printf("no restriction on referers\n"); #endif printf("guest book name suffix \"%s\", tmp suffix \"%s\"\n", GBFNAMESUFFIX, GBTMPSUFFIX); printf("USE UTC is %d, max html chars %d\n", USE_UTC, USER_ENTRY_HTML_SIZE); printf("place holder is \"%s\"\n", USER_ENTRY_PLACE_HOLDER); } else { display_error("Sorry, REQUEST_METHOD was not POST, can't handle anything else.", ""); } exit(1); } /* read the form POST data from stdin */ /* do this before printing any error messages -- causes problems ** if all stdin is not read in */ get_form_input(entries); /* check that we have received a FORM data, in the correct format */ c = getenv(CONTENTTYPE_STR); if (!c || strcasecmp(c, "application/x-www-form-urlencoded")) { display_error("Sorry, CONTENT_TYPE was not form-urlencoded, can't handle anything else.", ""); exit(1); } /* first, validate host if list of hosts/aliases compiled in */ #ifdef ONLY_REFERER { char* s1; char rfname[FILENAME_MAX]; c = getenv(REFERER_STR); if (!c) { /* fail validation, server did not forward referer! */ display_error("Sorry, could not determine HTTP_REFERER, access denied", ""); exit(1); } i = extract_host(c, rfname, sizeof(rfname)); strncpy(gbfname, ONLY_REFERER, sizeof(gbfname) - 1); /*use gbfname as tmp*/ gbfname[sizeof(gbfname)-1] = '\0'; only_referer_ok = 0; s1 = strtok(gbfname, " , \n"); /* space comma tab newline */ while (s1) { if (!strcasecmp(rfname, s1)) { only_referer_ok = 1; break; } s1 = strtok(NULL, " , \n"); /* space comma tab newline */ } if (i <= 0 || !only_referer_ok) { fprintf(stderr, "guestbk.cgi: error: http_referer %s trying to execute guestbook script.\n", c); display_error("Sorry, your host is not yet configured for access..your host is:", rfname); exit(1); } } #endif /* the guest book html file -- is available in the ? QUERY STRING */ gbfname[0] = '\0'; c = getenv(QUERY_STR); if (c) { /* copy it only if it is not too long -- must have space for suffix */ if ((strlen(c) >= (sizeof(gbfname) - sizeof(GBFNAMESUFFIX) - 1)) ) { display_error("Sorry, Guestbook filename is too long:", c); exit(1); } strcpy(gbfname, c); c = NULL; } /* always append the filename suffix -- for CGI security reasons */ strncat(gbfname, GBFNAMESUFFIX, sizeof(gbfname) - strlen(gbfname)); gbfname[sizeof(gbfname)-1] = '\0'; /* edit the entries if required. ** the COMMENTS entry is modified: all ^M or ^M^J sequences are ** changed to
*/ j = find_entry(entries, COMMENTS_STR); if (j >= 0) { edit_comments(&entries[j]); } /* make the user entry: parse input from entries into htmlstr */ htmlstr = (char*) malloc( USER_ENTRY_HTML_SIZE + 1 ); if (!htmlstr) { display_error("Hmm...htmlstr malloc failed!", ""); exit(1); } i = make_html_entry(entries, htmlstr, USER_ENTRY_HTML_SIZE); if (i < 0) { show_invalid_input_page(entries, htmlstr, gbfname); exit(1); } /* validate user entry */ i = find_entry(entries, NAME_STR); j = find_entry(entries, COMMENTS_STR); if (i < 0 || j < 0) { /* user left out name or comments fields */ show_invalid_input_page(entries, htmlstr, gbfname); exit(1); } /* add entry to guest book */ i = add_to_book(htmlstr, gbfname); if (i < 0) { /* error in adding to book */ #ifdef NOTIFY_EMAIL strncat(S_ErrorStr,"\n",sizeof(S_ErrorStr) - strlen(S_ErrorStr)); strncat(S_ErrorStr,gbfname,sizeof(S_ErrorStr) - strlen(S_ErrorStr)); S_ErrorStr[sizeof(S_ErrorStr)-1] = '\0'; send_email(htmlstr, NOTIFY_EMAIL, NULL, "Error in adding guestbook entry", S_ErrorStr); #endif /* message is printed by add_to_book */ exit(1); } /* show feedback to the user -- display success, & the html entry */ output_html(); html_header("Added user entry!"); printf("

Successfully added your entry to the guest book!\n"); printf("

Here's what I got:


%s

\n", htmlstr); printf("

Back to Guestbook

\n", gbfname); printf("

(Note that you may have to reload the guestbook to see your entry there!)\n"); printf("

\n"); html_footer(); #ifdef NOTIFY_EMAIL { char tolist[512]; char replyto[512]; char subject[512]; int len; replyto[0] = tolist[0] = '\0'; sprintf(subject, "Guestbook entry added to %s", gbfname); strncpy(tolist, NOTIFY_EMAIL, sizeof(tolist)); tolist[sizeof(tolist)-1] = '\0'; i = find_entry(entries, NOTIFY_EMAIL_STR); /* this is optional */ if (i >= 0 && ( (len = strlen( (c=entries[i].val) ) ))){ /*found it*/ strncat(tolist, ",", sizeof(tolist)-strlen(tolist)); strncat(tolist, c, sizeof(tolist)-strlen(tolist)); /* no need to clean up the tolist, is not dangerous since * send_email passes To: names in stdin to sendmail, no * command line arguments are used. */ tolist[sizeof(tolist)-1] = '\0'; } /* poster's e-mail address */ i = find_entry(entries, EMAIL_STR); /* this is optional */ if (i >= 0 && ( (len = strlen( (c=entries[i].val) ) ))){ /*found it*/ strncpy(replyto, c, sizeof(replyto)); replyto[sizeof(replyto)-1] = '\0'; } /* clean up htmlstr - remove all html tags */ /*blankout_html(htmlstr); Jun '03: send all text*/ /* will modify htmlstr */ send_email(htmlstr, tolist, replyto, subject, subject); } #endif return 0; /* successful exit to shell */ } /*=========================== get_form_input ===========================*/ /* read in from stdin all the POSTed data ** will ignore names if their values are "" ** will ignore homepage entry if it is just "http://" ** all other entries are stored into the entries[] array */ int get_form_input(entries_t entries[]) { int data_size; /* size (in bytes) of POST input */ int index, alloc_len; char* val; char* name; name = getenv(CONTENTLEN_STR); if (name) data_size = atoi(name); else data_size = 0; for(index=0 ; (data_size > 0) && (!feof(stdin)) && (index < MAX_FIELDS) ; /* no index increment here */) { val = fmakeword(stdin,'&',&data_size, &alloc_len); if (!val) { display_error("Hmm...program error: fmakeword (or malloc) failed!\n", ""); continue; } plustospace(val); unescape_url(val); name = makeword(val,'='); if (!name) { display_error("Hmm...program error: makeword (or malloc) failed!\n", ""); continue; } /* change embedded < > " chars -- no HTML text allowed in input, ** so < is changed to < etc */ xtoe(val, alloc_len); /* may truncate input! */ /* skip empty fields */ if (strlen(val) <= 0) { continue; } /* skip empty fields -- homepage is empty if it has only "http://"*/ if (!strcmp(name, HOMEPAGE_STR) && !strcmp(val, "http://")) { continue; } /* got a non-null name=val pair, store it and increment index */ entries[index].name = name; entries[index].val = val; index++; } /* last one is null */ entries[index].name = NULL; entries[index].val = NULL; index++; return index; } /*=========================== show_all_entries ========================*/ void show_all_entries(entries_t entries[]) { int index; for (index = 0; index < MAX_FIELDS && entries[index].name ; index++) { printf("%s = %s\n
\n", entries[index].name, entries[index].val); } } /*=========================== display_error ===========================*/ void display_error(char* s1, char* s2) { output_html(); html_header("Guestbook Program Error"); S_ErrorStr[0] = '\0'; strncat(S_ErrorStr,"Error: guestbk.cgi: ",sizeof(S_ErrorStr) - strlen(S_ErrorStr)); strncat(S_ErrorStr,s1,sizeof(S_ErrorStr) - strlen(S_ErrorStr)); strncat(S_ErrorStr," ",sizeof(S_ErrorStr) - strlen(S_ErrorStr)); strncat(S_ErrorStr,s2,sizeof(S_ErrorStr) - strlen(S_ErrorStr)); S_ErrorStr[sizeof(S_ErrorStr)-1] = '\0'; printf("

%s\n", S_ErrorStr); printf("

Please report the above error to %s.\n", ERROR_CONTACT); printf("

Thanks!\n

"); html_footer(); } /*=========================== make_html_entry ===========================*/ /* in an ordered fashion, create the HTML text with the fields ** from the FORM. */ int make_html_entry(entries_t entries[], char* outstr, int max_outstr_len) { int i, len; char* val; char *ptr, *endptr; char *s1, *s2, *s3; time_t tt; struct tm* stm; char tmpstr[1024]; ptr = outstr; endptr = ptr + max_outstr_len - 1; ptr[0] = '\0'; /* first part of string is NAME **

val\n **/ s1 = "

"; s2 = "\n"; i = find_entry(entries, NAME_STR); if (i >= 0 && ( (len = strlen( (val=entries[i].val) ) ))){ /* found it */ len = strlen(s1) + strlen(s2) + len; if ( endptr < (ptr + len) ) { return -1; /* exceeded outstr len */ } strcat(ptr, s1); strcat(ptr, val); strcat(ptr, s2); ptr += len; } /* next part of string is COMPANY **  · val\n ** [#183 is same as · I think, but don't know how widely ** is middot accepted, so use #183 instead] **/ s1 = "  · "; s2 = "\n"; i = find_entry(entries, COMPANY_STR); if (i >= 0 && ( (len = strlen( (val=entries[i].val) ) ))){ /* found it */ len = strlen(s1) + strlen(s2) + len; if ( endptr < (ptr + len) ) { return -1; /* exceeded outstr len */ } strcat(ptr, s1); strcat(ptr, val); strcat(ptr, s2); ptr += len; } /* next part is e-mail addr ** <val>\n */ s1 = "  · <"; s3 = ">\n"; i = find_entry(entries, EMAIL_STR); if (i >= 0 && ( (len = strlen( (val=entries[i].val) ) ))){ /* found it */ len = strlen(s1) + strlen(s2) + strlen(s3) + 2*len; if (endptr < (ptr + len) ) { return -1; /* exceeded outstr len */ } strcat(ptr, s1); strcat(ptr, val); strcat(ptr, s2); strcat(ptr, val); strcat(ptr, s3); ptr += len; } /* next part of string is HOMEPAGE **  · homepage\n **/ s1 = "  · home page\n"; i = find_entry(entries, HOMEPAGE_STR); if (i >= 0 && ( (len = strlen( (val=entries[i].val) ) ))){ /* found it */ /* if string is just http://, ignore it */ if (strcmp(val, "http://")) { /* is not http:// */ len = strlen(s1) + strlen(s2) + len; if ( endptr < (ptr + len) ) { return -1; /* exceeded outstr len */ } strcat(ptr, s1); strcat(ptr, val); strcat(ptr, s2); ptr += len; } } /* next part of string needs to have current time recorded **

\n date

\n ** div align does not look good ... writes in in the next line, ** I want to use the same line as name for date too ... ** so use **  ·· date timezone **/ tt = time(NULL); #if USE_UTC stm = gmtime(&tt); len = strftime(tmpstr, sizeof(tmpstr), "%e-%b-%y, %a %H:%M UTC", stm); #else stm = localtime(&tt); len = strftime(tmpstr, sizeof(tmpstr), "%e-%b-%y, %a %H:%M %Z", stm); #endif /* USE_UTC */ s1 = "  ·· "; if (len > 0) s2 = tmpstr; else s2 = ""; s3 = "\n"; len = strlen(s1) + strlen(s2) + strlen(s3); if ( endptr < (ptr + len) ) { return -1; /* exceeded outstr len */ } strcat(ptr, s1); strcat(ptr, s2); strcat(ptr, s3); ptr += len; /* next part of string is COMMENTS **

\nval\n
\n **/ s1 = "

\n"; s2 = "\n
\n"; i = find_entry(entries, COMMENTS_STR); if (i >= 0 && ( (len = strlen( (val=entries[i].val) ) ))){ /* found it */ len = strlen(s1) + strlen(s2) + len; if ( endptr < (ptr + len) ) { return -1; /* exceeded outstr len */ } strcat(ptr, s1); strcat(ptr, val); strcat(ptr, s2); ptr += len; } /* store as comments: CAMEFROM ** \n **/ i = find_entry(entries, CAMEFROM_STR); if (i >= 0 && ( (len = strlen( (val=entries[i].val) ) ))){ /* found it */ /* XXX: remove -- chars from val */ s1 = "\n"; val = entries[i].val; len = strlen(s1) + strlen(s2) + len; if ( endptr < (ptr + len) ) { return -1; /* exceeded outstr len */ } strcat(ptr, s1); strcat(ptr, val); strcat(ptr, s2); ptr += len; } /* final part of string is a hrule **
\n **/ s1 = "
\n\n"; len = strlen(s1); if ( endptr < (ptr + len) ) { return -1; /* exceeded outstr len */ } strcat(ptr, s1); ptr += len; return (ptr - outstr); } /*=========================== add_to_book ===========================*/ int add_to_book(char *htmlstr, char* gbfname) { char tmpfname[FILENAME_MAX]; char line[LINELEN_MAX]; FILE* tmpf = NULL; FILE* realf = NULL; int i; int ret_val = 1; /* default - success */ int markerlen = 0; if (strlen(gbfname) >= (sizeof(tmpfname) - strlen(GBTMPSUFFIX) - 1)) { display_error("Sorry, Guestbook file name is too long, cannot append .tmp:", gbfname); return -1; /* failure */ } markerlen = strlen(USER_ENTRY_PLACE_HOLDER); if ((markerlen + 3) > sizeof(line)) { display_error("Program error:USER_ENTRY_PLACE_HOLDER is too long:", USER_ENTRY_PLACE_HOLDER); return -1; /* failure */ } strcpy(tmpfname, gbfname); strcat(tmpfname, GBTMPSUFFIX); /* need to do locking to ensure correctness here ! ** I lock the GuestBook file, and then do all the operations on it. ** This should guarantee correctness when another "add_to_book" is ** being executed at the same time (in some other process). */ realf = fopen(gbfname, "r+"); if (!realf) { display_error(gbfname, strerror(errno)); return -1; /* failure */ } i = flock(fileno(realf), LOCK_EX); /* LOCK *****************************/ /* open existing temp file -- should be created beforehand, so ** random users do not need write access in your WWW directory ** for an existing temp file, "r+" is ok, or else use "w+" tmpf = fopen(tmpfname, "w+"); */ tmpf = fopen(tmpfname, "r+"); if (!tmpf) { display_error(tmpfname, strerror(errno)); ret_val = -1; /* failure */ goto exit_func; } ftruncate(fileno(tmpf), 0); /* keep reading lines until you locate the place holder line */ i = 0; while (fgets(line, sizeof(line), realf) == line) { /* read a line */ fputs(line, tmpf); if (!strncmp(line, USER_ENTRY_PLACE_HOLDER, markerlen)) { fputs(htmlstr, tmpf); i = 1; break; /* inserted user entry, do normal file copy now */ } } if (i != 1) { display_error("Could not locate place holder in guestbook:", USER_ENTRY_PLACE_HOLDER); ret_val = -1; /* failure */ goto exit_func; } /* copy rest of real file to temp file */ send_fd(realf, tmpf); /*** closing file will release lock, so can't close file. ** don't know if closing is needed for rename to work, so just ** being safe and doing a manual copy of the temp file to the real file... i = rename(tmpfname, gbfname); fclose(realf); fclose(tmpf); ***/ /* copy temp file to real file */ rewind(realf); rewind(tmpf); send_fd(tmpf, realf); exit_func: /* throw away all additions to temp file so fclose does not have to ** touch the disk at all */ if (tmpf) ftruncate(fileno(tmpf), 0); if (tmpf) i = fclose(tmpf); /* ** no need to unlock, just closing the file will release the lock */ #if 0 i = flock(fileno(realf), LOCK_UN); /* UN-LOCK **************************/ #endif i = fclose(realf); if ((i != 0) && (ret_val == 1)) { display_error("Could not close the guestbook file, perror:", strerror(errno)); return -1; /* failure */ } /* don't remove temp file, in case random user is to be prevented ** from getting write access to your WWW directory unlink(tmpfname); */ return ret_val; /* success */ } /*=========================== find_entry ===========================*/ int find_entry(entries_t entries[], char* str) { int index; for (index = 0; index < MAX_FIELDS && entries[index].name ; index++) { if (!strcmp(entries[index].name, str)) { return index; } } return -1; } /*======================== edit_comments =====================*/ /* edit_comments goes thru the comment string entered by the user, ** and changes all occurences of a sequence of newline characters ** to
*/ int edit_comments(entries_t* centry) { char *c, *c1, *c2; int i; c2 = c1 = (char *) malloc(sizeof(char) * USER_ENTRY_HTML_SIZE ); if (!c1) { display_error("Hmm...edit c1 malloc failed!", ""); return -1; } c = centry->val; /* copy c into c1, change newlines to
*/ for (i = 0; c && (*c) && (i < (USER_ENTRY_HTML_SIZE - 6)); i++ ) { if (*c == '\r' || *c == '\n') { /* change a newline char to
\n */ *c1++ = '<'; *c1++ = 'B'; *c1++ = 'R'; *c1++ = '>'; *c1++ = '\n'; i+= 4; /* added 4+1 chars in this if, normally just add 1 */ /* skip over any multiple newline characters */ do { c++; } while (*c && (*c == '\n' || *c == '\r')); } else { /* regular character */ *c1++ = *c; c++; } } *c1 = '\0'; /* don't free centry->val, mem leak is preferable to worrying ** about how fmakeword/makeword actually work! */ centry->val = c2; return strlen(c2); } /*======================== show_invalid_input_page =====================*/ void show_invalid_input_page(entries_t entries[], char* htmlstr, char* gbfname) { entries = entries; /*NOTUSED*/ output_html(); html_header("Error in processing entry"); printf("

Sorry, could not add your entry.\n"); printf("

Either a required field -- either your name, or your comment, is missing,\n"); printf("Or, some input text was too long for this guestbook to handle!\n"); printf("

Please re-enter the info and re-submit your entry.
Thanks!
\n"); printf("

Here's what I got:


%s

\n", htmlstr); printf("

Back to Guestbook

\n", gbfname); html_footer(); } /*============================ extract_host ==============================*/ int extract_host (const char url[], char* host, int host_len) { const char *c; char *o, *oo; int i; o = host; oo = host + host_len - 1; *o = '\0'; /* skip to // */ c = url; while ((*c != '/') && (*c != '\0')) { c++; } if (*c == '/') c += 2; /* skip over both // */ /* copy host, until a / or a :, copy max host_len-1 chars */ i = 1; while ((*c != '/') && (*c != ':') && (*c != '\0') && (o < oo) && (i < host_len)) { *o++ = *c++; i++; } *o = '\0'; return (o - host); /* length of string */ } /*============================= send_email ===============================*/ int send_email(char* message, char* to, char* replyto, char* subject, char* lastline) { FILE* sendmailfp = NULL; if (!to || !subject || !message) { /* args needed */ return -1; /* failure */ } sendmailfp = popen(SEND_EMAIL_COMMAND, "w"); if (!sendmailfp) { fprintf(stderr, "guestbk.cgi: error: could not popen(sendmail).\n"); return -1; /* failure */ } #ifdef FROM_STR fputs(FROM_STR, sendmailfp); #endif fprintf(sendmailfp, "To: %s\n", to); if (replyto && *replyto) fprintf(sendmailfp, "Reply-To: %s\n", replyto); fprintf(sendmailfp, "Subject: %s\n", subject); fputs("\n", sendmailfp); /* end of headers (this is 2nd \n) */ fputs(message, sendmailfp); /* end of message */ fputs("\n", sendmailfp); if (lastline && lastline[0]) { fprintf(sendmailfp, "%s\n", lastline); } pclose(sendmailfp); return 1; /* success */ } /*=======================================================================*/ /* following portions from ncsa */ /* Portions copyright the National Center for Supercomputing */ /* Applications at the University of Illinois at Urbana-Champaign */ /* Information & additional resources available at: */ /* http://hoohoo.ncsa.uiuc.edu */ #define LF 10 #define CR 13 /*------------------------------------------------------------------------*/ char *makeword(char *line, char stop) { int x = 0,y; char *word = (char *) malloc(sizeof(char) * (strlen(line) + 1)); if (!word) return NULL; for(x=0;((line[x]) && (line[x] != stop));x++) word[x] = line[x]; word[x] = '\0'; if(line[x]) ++x; y=0; while( (line[y++] = line[x++]) ); return word; } /*------------------------------------------------------------------------*/ char *fmakeword(FILE *f, char stop, int *cl, int *alloc_len) { int wsize; char *word; int ll; wsize = 102400; wsize = (wsize < *cl) ? wsize : *cl; ll=0; *alloc_len = (wsize + 1); word = (char *) malloc(sizeof(char) * (*alloc_len)); if (!word) return NULL; while(1) { word[ll] = (char)fgetc(f); if(ll==wsize) { word[ll+1] = '\0'; wsize+=1024; (*alloc_len) = wsize + 1; word = (char *)realloc(word,sizeof(char)*(*alloc_len)); } --(*cl); if((word[ll] == stop) || (feof(f)) || (!(*cl))) { if(word[ll] != stop) ll++; word[ll] = '\0'; return word; } ++ll; } /*NOTREACHED*/ } /*------------------------------------------------------------------------*/ char x2c(char *what) { register char digit; digit = (what[0] >= 'A' ? ((what[0] & 0xdf) - 'A')+10 : (what[0] - '0')); digit *= 16; digit += (what[1] >= 'A' ? ((what[1] & 0xdf) - 'A')+10 : (what[1] - '0')); return(digit); } /*------------------------------------------------------------------------*/ void unescape_url(char *url) { register int x,y; for(x=0,y=0;url[y];++x,++y) { if((url[x] = url[y]) == '%') { url[x] = x2c(&url[y+1]); y+=2; } } url[x] = '\0'; } /*------------------------------------------------------------------------*/ void plustospace(char *str) { register int x; for(x=0;str[x];x++) if(str[x] == '+') str[x] = ' '; } /*------------------------------------------------------------------------*/ void send_fd(FILE *f, FILE *fd) { int c; while (1) { c = fgetc(f); if(feof(f) || (c < 0)) return; fputc(c,fd); } } /*------------------------------------------------------------------------*/ void output_html(void) { /* outputs MIME html header */ /* printf("Pragma: no-cache\n"); */ printf("Content-type: text/html%c%c",10,10); } /*------------------------------------------------------------------------*/ void output_location(char *url) { /* outputs MIME html header */ printf("Location: %s%c%c",url,10,10); } /*------------------------------------------------------------------------*/ void html_header(const char *htitle) /* outputs a typical html header section */ { printf("%s\n",htitle); printf("\n"); return; } /*------------------------------------------------------------------------*/ /* convert all characters between < and > (inclusive) to ' ' * also convert all characters between & ; to ' ' [ex: ·   ] * allow "& " to stay unchanged */ void blankout_html(char* htmlstr) { int i, len, state; char c, cn; if (!htmlstr) return; i = 0; state = 0; len = strlen(htmlstr); for (i = 0; i < len; i ++) { c = htmlstr[i]; switch (state) { case 0: /* outside html */ if (c == '<') state = 1; if (c == '&') { /* if next char is ' ' or \n \t, let it go */ cn = htmlstr[i+1]; if (cn && !isspace(cn)) state = 3; } break; case 1: /* inside html comment */ if (c == '>') state = 2; /* saw end of text to suppress */ break; case 2: /* seen ending > or ; [delay by one character] */ if (c == '<') state = 1; else if (c == '&') state = 3; else state = 0; break; case 3: /* just seen & */ if (c == ';') state = 2; else if (isspace(c)) state = 0; else if (c == '<') state = 1; break; } if (state != 0) htmlstr[i] = ' '; /* blank out unwanted stuff */ } } /*------------------------------------------------------------------------*/ void html_footer(void) /* outputs a typical html footer section */ { printf("\n"); return; } /*------------------------------------------------------------------------*/ char *snr(char *instring, const char *search, const char *replace, int maxlen) { /* A multipurpose search & replace string routine; can also be used to erase selected substrings from a string by specifying an empty string as the replace string; dynamically allocates temporary string space to hold max possible s&r permutations. snr returns NULL if unable to allocate memory for operation. NOTE: No boundary checking is made for instring; its length must be at least strlen(instring)*strlen(replace) in order to avoid potential memory overwrites. */ char *ptr, *ptr2, *newstring; /* allocate temp string */ if ((newstring=(char *)malloc(strlen(instring)*(strlen(replace)+1)+1))==NULL) return(NULL); newstring[0]='\0'; ptr2=instring; while ((ptr=strstr(ptr2,search))!=NULL) { strncat(newstring,ptr2,(ptr-ptr2)); strcat(newstring,replace); ptr2=(ptr+strlen(search)); } strcat(newstring,ptr2); strncpy(instring,newstring,maxlen); instring[maxlen-1] = '\0'; free(newstring); return(instring); } /*------------------------------------------------------------------------*/ #define NUM_TAGS 3 const char *tags[NUM_TAGS][2]={ {"\x22", """}, /* == " */ {"<" , "<"}, /* == < */ {">" , ">"}, /* == > */ }; int xtoe(char *str, int maxlen) { /* Process character string for use as embedded form value string returns nz if successful; the main reason for this conversion is to eliminate characters such as ">" or quotes which can cause the browser to mis-interpret the field's contents. */ register int x; for(x=0;x Guestbook for ...

enter your page title here ...

Your Name:
Comments:

Additional info (optional):
Your E-mail:
Your Home Page:
School/Company:
How did you find us?



** end of sample html code **/ /*======================================================================*/