// cgiex.cpp : Defines the entry point for the console application. // /* sample linux compile: gcc -o cgiex cgiex.cpp -L/usr/local/lib/gd2/ -I/usr/local/include/gd2/ -lgd -ljpeg -lpng -lfreetype -lstdc++ */ //#include "stdafx.h" //make sure to ignore msvcrt.lib #include "gd.h" #include "gdfonts.h" //tiny font #include #include #include #include //this includes the necessary low level file/function headers for //win32 to enable binary mode in stdout #if defined(_WIN32) || defined(WIN32) #include #include //extern int setmode(int filenum, int mode); #endif #define MAX_QUERY_LEN 4096 //the maximum query length //#define DEBUG //turn debugging on or off struct s_query_items { char *name; //name of the item char *value; //value of the item s_query_items *next; //pointer to the next struct }**query_url; //array of pointers to s_query_items structs struct s_rgb { unsigned char r; //red unsigned char g; //green unsigned char b; //blue }; struct s_button { int b_height; //button height int b_width; //button width s_rgb back_color; //button color s_rgb font_color; //button font color char *caption; //button caption }; FILE *fp; //global logfile for debugging and error reporting void clean_up(int code){ #ifdef DEBUG if((code == 0) && (fp != NULL)){ fprintf(fp, "SUCCESS\n"); } #endif fclose(fp); //free memory here//////// ////////////////////////// exit(code); } char char_htoi(char *c){ char d = (char)toupper(*c); //this might cause problems on other platforms //(some freak out if we toupper() an already uppercase char if( (d >= '0') && (d <= '9') ){ return(atoi(&d)); } switch(d){ case 'A': return(0xA); break; case 'B': return(0xB); break; case 'C': return(0xC); break; case 'D': return(0xD); break; case 'E': return(0xE); break; case 'F': return(0xF); break; default: //just return 0, we dont' care about any other characters //and nobody is going to check if this function returns an error return(0); break; } //just return 0, we dont' care about any other characters //and nobody is going to check if this function returns an error return(0); } int split_qurl(char *s, //pointer to the query string from getenv() s_query_items **q_url //pointer to the malloc'd (at least partially malloc'd) //query url structure ){ int done = 0; int n = 0; int p = 0; int in = 0; //index for query url structure /*************************** NOTE ************************************ We will probably have to loop this loop once before hand so we can dynamically malloc memory for the name and value strings. (see the process_qurl() function comments) (ok, we are looping and malloc'ing before hand) I'll leave these comments in here though in case anybody wonders why that's going on. there is also something funny about relying on in++ to increment our query_url structure when we are 100% not sure there is one there. we should plop a list pointer in the main struct that we use to store the next pointer if there is one, then we can check it at run time to make sure there is a pointer available to another structure, before we just blindly index it. (ok I changed the in++ thing) I'll leave these comments in here though in case anybody wonders why that's going on. **********************************************************************/ //allocate memory while( (s[n] != '\0') && (done != 1) ){ p = 0; while( s[n] != '=' ){ n++; p++; } //make sure p is never zero, that means the item name was empty //and we can't have that if(p == 0){ fprintf(fp, "SPLIT_QURL[allocator]: Empty field in query URL.\n"); return(-1); } //we should be safe to malloc here based on p //everyone says not to get sizeof(char) because it's always 1. //If you don't like it, change it q_url[in]->name = (char *)malloc((sizeof(char) * p) + 1); if(q_url[in]->name == NULL){ fprintf(fp,"SPLIT_QURL[allocator]: Couldn't allocate memory for q_url[%d]->name. FATAL ERROR.\n",in); return(-1); } if(s[n] != '\0') n++; //skip past the equals sign p = 0; //reset p while( (s[n] != '&') && (s[n] != '\0') ){ n++; p++; if(s[n] == '\n') done = 1; } //make sure p is never zero, that means the item value was empty //and we can't have that if(p == 0){ fprintf(fp, "[SPLIT_QURL]: Empty field in query URL.\n"); return(-1); } //we should be safe to malloc here based on p //everyone says not to get sizeof(char) because it's always 1. //If you don't like it, change it q_url[in]->value = (char *)malloc((sizeof(char) * p) + 1); if(q_url[in]->value == NULL){ fprintf(fp, "SPLIT_QURL[allocator]: Couldn't allocate memory for q_url[%d]->value. FATAL ERROR.\n",in); return(-1); } //skip past the ampersand and //increment to the next query url structure(so long as we are not at the end) if(s[n] != '\0'){ //make sure we HAVE another query_url struct if(q_url[in]->next == NULL){ //that's bad, barf and exit fprintf(fp, "SPLIT_QURL[allocator]: Ran out of pointers to q_url structs. FATAL ERROR.\n"); return(-1); } n++; in++; } } //reset everything done = 0; n = 0; p = 0; in = 0; //copy the stuff over while( (s[n] != '\0') && (done != 1) ){ p = 0; while( s[n] != '=' ){ q_url[in]->name[p] = s[n]; n++; p++; } //make sure p is never zero, that means the item name was empty //and we can't have that if(p == 0){ fprintf(fp, "SPLIT_QURL[copier]: Empty field in query URL.\n"); return(-1); } q_url[in]->name[p] = '\0'; //terminate it if(s[n] != '\0') n++; //skip past the equals sign p = 0; //reset p while( (s[n] != '&') && (s[n] != '\0') ){ q_url[in]->value[p] = s[n]; n++; p++; if(s[n] == '\n') done = 1; } //make sure p is never zero, that means the item value was empty //and we can't have that if(p == 0){ fprintf(fp, "SPLIT_QURL[copier]: Empty field in query URL.\n"); return(-1); } q_url[in]->value[p] = '\0'; //skip past the ampersand and //increment to the next query url structure(so long as we are not at the end) if(s[n] != '\0'){ //make sure we HAVE another query_url struct if(q_url[in]->next == NULL){ //that's bad, barf and exit fprintf(fp, "SPLIT_QURL: Ran out of pointers to q_url structs. FATAL ERROR.\n"); return(-1); } n++; in++; } } return n; } int process_qurl_prop(char *s, //pointer to the query string from getenv int *len, //pointer to the integer storing the length of the qurl int *num_items) { //pointer to the integer storing the number of ampersands int n = 0; //it has to equal one at the start *num_items = 1; while( (*(s + n)) != '\0'){ //check for null if((*(s + n)) == '&'){ (*(num_items))++; //increment the number of items } /*********************************************************/ //we should UCASE everything here!!!!!!!!!!!!!!!!!!!! /*********************************************************/ (*(len))++; //increment the length of the string n++; //increment n index if(n >= MAX_QUERY_LEN){ //prevent looping forever fprintf(fp, "PROCESS_QURL: query URL exceeded maximum length allowance\n"); return -1; break; } } return 0; //success } int process_qurl(){ int i; //good old i //this bit is here to help debug without a browser //it creates its own query string and sets the //QUERY_STRING environment variable #ifdef DEBUG char *qurl_str = "QUERY_STRING=btncaption=Search&btnwidth=50&btnheight=15&btncolor=33CCFF&btnfontcolor=FFFFFF"; putenv(qurl_str); #else char *qurl_str; //query string #endif int qurl_len; //query string length int qurl_num; //query string num items if((qurl_str = getenv("QUERY_STRING")) == NULL){ //try and get a pointer to the query string fprintf(fp, "PROCESS_QURL[getenv()]: Couldn't get query string or query string empty.\n"); return(-1); } //process the url to get some basic information like length, num items etc. if(process_qurl_prop(qurl_str, &qurl_len, &qurl_num) != 0){ fprintf(fp, "PROCESS_QURL[process_qurl_prop()]: Error processing qurl properties.\n"); return(-1); } //malloc pointers for the item structs query_url = (s_query_items **)malloc(sizeof(s_query_items **) * qurl_num); if(query_url == NULL){ fprintf(fp, "PROCESS_QURL: Couldn't malloc memory for query_url structure\n"); return(-1); } //malloc the structures for(i = 0; i<= (qurl_num - 1); i++){ query_url[i] = (s_query_items *)malloc(sizeof(s_query_items)); if(query_url[i] == NULL){ fprintf(fp, "PROCESS_QURL: Couldn't malloc memory for query url member\n"); return(-1); } /*********************NOTE**************************************** just remember this is where we used to allocate mem for the string members too. *********************************************************************/ } /************************************************************************* OK, this mini mess stores a pointer in the "next" member of the query_url struct. I am doing this to reduce the chance of stepping over the bounds of the global query_url structure when we split it (split_qurl()) **************************************************************************/ //null the "next" pointer reference in the very last query_url structure query_url[(qurl_num - 1)]->next = NULL; i = (qurl_num - 1); //reassign i s_query_items *last; //temporary storage for our pointer while(i != 0){ last = query_url[i]; //the pointer to this one query_url[i -1]->next = last; //put it in the "next" member //of the previous structure i--; //decrement to the previous query_url } #ifdef DEBUG for(i = 0; i<= (qurl_num -1); i++){ fprintf(fp, "ME[%d]:0x%x NEXT[%d]:0x%x\n",i,query_url[i],i, query_url[i]->next); } #endif /******************************************************************************/ if(split_qurl(qurl_str, query_url) < 0){ fprintf(fp, "PROCESS_QURL[slit_qurl()]: Couldn't split string.\n"); return(-1); } #ifdef DEBUG fprintf(fp, "QUERY_STRING:%s\n", qurl_str); for(i = 0; i<= (qurl_num -1); i++){ fprintf(fp, "ITEM:%s||VALUE:%s\n", query_url[i]->name, query_url[i]->value); } #endif return(qurl_num); //number of items in the query url } int process_btncolorstr(char *c, s_rgb *clr){ char buf; //storage for one char int n = 5; //pointer index (start from the end) unsigned char tmp = 0x00; //nobody talks to this function with a string less than 6 chars long if(strlen(c) < 6){ fprintf(fp, "PROCESS_BTNCOLORSTR: colorstring is not long enough:%d\n",strlen(c)); return -1; } //do the last color (blue) buf = c[n]; //get the first char buf = char_htoi(&buf); //char_htoi it tmp = buf; //copy it over to tmp n--; //decrement n buf = c[n]; //get the next char buf = char_htoi(&buf); //char_htoi it tmp += ((buf) << 4); //shift the color bits over, and add it to tmp clr->b = tmp; //copy it over to the color structure n--; //decrement n //do the middle color (green) buf = c[n]; //get the first char buf = char_htoi(&buf); //char_htoi it tmp = buf; //copy it over to tmp n--; //decrement n buf = c[n]; //get the next char buf = char_htoi(&buf); //char_htoi it tmp += ((buf) << 4); //shift the color bits over, and add it to tmp clr->g = tmp; //copy it over to the color structure n--; //decrement n //do the first color (red) buf = c[n]; //get the first char buf = char_htoi(&buf); //char_htoi it tmp = buf; //copy it over to tmp n--; //decrement n buf = c[n]; //get the next char buf = char_htoi(&buf); //char_htoi it tmp += ((buf) << 4); //shift the color bits over, and add it to tmp clr->r = tmp; //copy it over to the color structure return 0; } int get_button_properties(s_button *btn //reference to the button structure in the calling function ){ int i; //good old i int f,g; //reference for whether or not we found our "thing" in the url int qurl_num = 0; //process the whole mess if((qurl_num = process_qurl()) <= 0){ fprintf(fp,"GET_BUTTON_PROPERTIES[process_qurl()]: Failed to process query URL.\n"); return(-1); } //initialize our button with handy stuff btn->b_height = 0; btn->b_width = 0; btn->back_color.r = 0x00; btn->back_color.g = 0x00; btn->back_color.b = 0x00; btn->font_color.r = 0xFF; btn->font_color.g = 0xFF; btn->font_color.b = 0xFF; btn->caption = NULL; //got the query URL, step through the global query_url structure until we //find the various items we are looking for f = 0; //init f to zero (not found) for(i = 0; i<= (qurl_num -1); i++){ //look for the caption if((g = strcmp(query_url[i]->name, "btncaption")) == 0){ //everyone says not to get sizeof(char) because it's always 1. //If you don't like it, change it btn->caption = (char *)malloc((sizeof(char) * strlen(query_url[i]->value) + 1)); if(btn->caption == NULL){ fprintf(fp, "GET_BUTTON_PROPERTIES: Error allocating memory for button caption.\n"); return(-1); } strcpy(btn->caption, query_url[i]->value); //copy over the caption f = 1; } } if(f == 0){ //we did not find the button caption, this is not allowed fprintf(fp, "GET_BUTTON_PROPERTIES: Could not find the button caption value\n"); return(-1); } //init f to 0 (not found) f = 0; //look for the button width for(i = 0; i<= (qurl_num -1); i++){ if((g = strcmp(query_url[i]->name, "btnwidth")) == 0){ btn->b_width = atoi(query_url[i]->value); f = 1; } } if(f == 0){ //we did not find the button width, this is not allowed fprintf(fp, "GET_BUTTON_PROPERTIES: Could not find the button width value.\n"); return(-1); } f = 0; //look for the button width for(i = 0; i<= (qurl_num -1); i++){ if((g = strcmp(query_url[i]->name, "btnheight")) == 0){ btn->b_height = atoi(query_url[i]->value); f = 1; } } if(f == 0){ //we did not find the button width, this is not allowed fprintf(fp, "GET_BUTTON_PROPERTIES: Could not find the button width value.\n"); return(-1); } f = 0; //look for the button color for(i = 0; i<= (qurl_num -1); i++){ if((g = strcmp(query_url[i]->name, "btncolor")) == 0){ if(process_btncolorstr(query_url[i]->value, &(btn->back_color)) == -1){ fprintf(fp, "GET_BUTTON_PROPERTIES[process_btncolorstr()]: Error processing button color.\n"); return(-1); } f = 1; } } if(f == 0){ //we did not find the button width, this is not allowed fprintf(fp, "GET_BUTTON_PROPERTIES: Could not find the button color value.\n"); return(-1); } f = 0; //look for the button color for(i = 0; i<= (qurl_num -1); i++){ if((g = strcmp(query_url[i]->name, "btnfontcolor")) == 0){ if(process_btncolorstr(query_url[i]->value, &(btn->font_color)) == -1){ fprintf(fp, "GET_BUTTON_PROPERTIES[process_btncolorstr()]: Error processing button font color.\n"); return(-1); } f = 1; } } if(f == 0){ //we did not find the button width, this is not allowed fprintf(fp, "GET_BUTTON_PROPERTIES: Could not find the font color value.\n"); return(-1); } return 0; } int make3D(gdImagePtr ptImage, int intW, //button width int intH, //button height s_rgb *clr) { int intThickness = 4; int ret, black; //intH -= intThickness - 4; //intW -= intThickness - 4; double r = (double)clr->r; double g = (double)clr->g; double b = (double)clr->b; r *= 0.75; g *= 0.75; b *= 0.75; clr->r = (unsigned char)r; clr->g = (unsigned char)g; clr->b = (unsigned char)b; /** unsigned char... never mind if(clr->r < 0) clr->r = 0; if(clr->g < 0) clr->g = 0; if(clr->b < 0) clr->b = 0; **/ ret = gdImageColorAllocate(ptImage, clr->r, clr->g, clr->b); black = gdImageColorAllocate(ptImage, 0, 0, 0); gdImageSetThickness(ptImage, intThickness); //void gdImageLine(gdImagePtr im, int x1, int y1, int x2, int y2, int color); gdImageLine(ptImage, 0, 0 + (intThickness/2), intW, 0 + (intThickness/2), ret); gdImageLine(ptImage, (intW - (intThickness/2)), 0, (intW - (intThickness/2)), intH, black); gdImageLine(ptImage, 0 + (intThickness/2), intH, 0 + (intThickness/2),0, ret); gdImageLine(ptImage, intW, (intH - (intThickness/2)), 0, (intH - (intThickness/2)), black); gdImageSetThickness(ptImage, 1); return 0; } int draw_button(s_button *btn){ gdImagePtr im; //create an image pointer int btn_color, fnt_color; //gd colors im = gdImageCreate(btn->b_width, btn->b_height); //create an image surface if(im == NULL){ fprintf(fp, "DRAW_BUTTON[gdImageCreate()]: Couldn't create gd image.\n"); return(-1); } //allocate some colors btn_color = gdImageColorAllocate(im, btn->back_color.r, btn->back_color.g, btn->back_color.b); fnt_color = gdImageColorAllocate(im, btn->font_color.r, btn->font_color.g, btn->font_color.b); make3D(im, btn->b_width, btn->b_height, &(btn->back_color)); gdImageString(im, gdFontSmall, btn->b_width / 2 - (strlen(btn->caption) * gdFontSmall->w / 2), btn->b_height / 2 - gdFontSmall->h / 2, (unsigned char *)btn->caption, fnt_color); fprintf(stdout, "Content-type: image/png\n\n"); //print the content type header #ifdef DEBUG FILE *image = fopen("image.png","wb"); if(image == NULL){ fprintf(fp, "DRAW_BUTTON[fopen()]: Couldn't open image.png.\n"); return(-1); } gdImagePng(im, image); fclose(image); #endif #if defined(_WIN32) || defined(WIN32) _setmode(fileno(stdout), O_BINARY); //set stdout to binary mode #ifdef DEBUG fprintf(fp,"DRAW_BUTTON: switching stdout to binary.\n"); #endif #endif gdImagePng(im, stdout); //send it to the browser gdImageDestroy(im); //destroy the image pointer //should never get here return 0; } int main(int argc, char* argv[]) { s_button button; //the button structure fp = fopen("log.txt", "w+"); //open the global log file if(fp == NULL){ //print out the basic content-type header printf("Content-type: text/plain\n\n"); //print out to the browser printf("Couldn't open logfile for write.\n"); //print to stderr fprintf(stderr, "Couldn't open logfile for write.\n"); return -1; } //try and get the button properties if(get_button_properties(&button) != 0){ fprintf(fp, "Error getting buton properties.\n"); clean_up(-1); } #ifdef DEBUG //print the variables we have found into the log fprintf(fp, "BTNCAPTION:%s\n", button.caption); //the caption fprintf(fp, "BTNWIDTH:%d\n", button.b_width); //the width fprintf(fp, "BTNHEIGHT:%d\n", button.b_height); //the height fprintf(fp, "BACKCOLOR:0x%x%x%x\n", button.back_color.r, button.back_color.g, button.back_color.b); //the backcolor fprintf(fp, "FONTCOLOR:0x%x%x%x\n", button.font_color.r, button.font_color.g, #endif button.font_color.b); //the font color //its all good, draw the button if(draw_button(&button) < 0){ fprintf(fp, "MAIN[draw_button()]: Error creating button.\n"); clean_up(-1); } clean_up(0); return 0; }