View Full Version : CGI High Score Program
davecom
2003.11.21, 03:51 PM
Hi,
I'm trying to write a CGI program that will receive a score and name, compare it to the list of names already in a text file, and then ouput the name and score combination to the text file if it is high enough.
I've been working with the C standard libraries. I have the whole getting the information down pat, but my C input output skills are not anymore up to par. Anybody have some good resources/tips?
jfaller
2003.11.21, 08:53 PM
Having written a bunch of CGIs myself, I'd recommend that you REALLY DON'T use C -- rather C++ or (PREFERABLY) PERL.
Here is a simple example in C++ that does what you want:
#include <iostream>
#include <fstream>
#include <string>
#include <algorithm>
#define kNumScores (10)
using namespace std;
struct score_t
{
string name;
unsigned long score;
bool operator<(const score_t &inVal) const
{ return score > inVal.score; } // this line is confusing
};
int main()
{
ifstream inFile("my_scores.dat");
score_t scores[kNumScores + 1];
// Read the scores
for(unsigned i = 0; i < kNumScores; ++i)
{
string score;
getline(inFile, scores[i].name);
getline(inFile, score);
scores[i].score = atoi(score.c_str());
}
inFile.close();
// Add the new score
scores[kNumScores].name = "New Name";
scores[kNumScores].score = 150;
// Sort the scores
sort(scores, scores + kNumScores + 1);
ofstream outFile("my_scores.dat");
for(unsigned i = 0; i < kNumScores; ++i)
{
outFile << scores[i].name << endl;
outFile << scores[i].score << endl;
}
outFile.close();
return 0;
}
There are some fun bugs to try and find in the above code that have to do with concurrency (and multiple people entering data at the EXACT same time). This can be fixed with some configuration changes within Apache (causing it to run only a single instantiation of a CGI at a time).
Now, having given you all this, you really don't want to use C/C++, but rather PERL. The reason has to do with the fact that C/C++ can have exploited buffer overflows quite easily, and PERL is much friendlier at ignoring exploit attempts.
(If you choose not to user PERL, use C++ and syntax like the above. [The code presented is actually reasonably good at ignoring buffer overflow problems, etc.])
Good luck with whatever your solution.
davecom
2003.11.23, 11:30 PM
Wow, thanks for all the code. But I can't find anywhere how to access environment variables in C++. Is it necessary to use the C library?
OneSadCookie
2003.11.24, 12:06 AM
#include <stdlib.h> and getenv() (C/ObjC/C++); or
#include <cstdlib> and std::getenv() (C++)
davecom
2003.11.24, 05:50 PM
current version just prints out any score sent to it but now I'm getting type 500 errors. Anybody know what causes type 500 errors?
Thanks,
David
ps email notification doesn't appear to be working
jfaller
2003.11.25, 01:09 AM
Originally posted by davecom
current version just prints out any score sent to it but now I'm getting type 500 errors. Anybody know what causes type 500 errors?
I dunno, but the code I wrote assumes a file of at least 10 scores.... Maybe this is part of the problem?
:bored:
Fenris
2003.11.25, 07:22 AM
Isn't 500 access error? I assume you've run chmod on it?
Steven
2003.11.25, 10:55 AM
Read the server error log.
davecom
2003.11.29, 01:33 AM
Thanks for the replies. Where can I find the server error log? Is it the apache one I'm looking for?
Also, I'll have to look into the file errors you referred to jfaller. However I believe it worked on my local machine. Is the problem that I'm attempting to process a get request and then doing file access. Does this REQUIRE a post request?
I did chmod 777 the file it rw to.
Here's the current code - just a few small changes:
#include <iostream>
#include <fstream>
#include <string>
#include <algorithm>
#include <cstdlib>
#define kNumScores (10)
using namespace std;
struct score_t
{
string name;
unsigned long score;
bool operator<(const score_t &inVal) const
{ return score > inVal.score; } // this line is confusing
};
int main()
{
int i,l;
string d,n;
ifstream inFile("highscores.txt");
score_t scores[kNumScores + 1];
// Read the scores
for(i = 0; i < kNumScores; ++i)
{
string score;
getline(inFile, scores[i].name);
getline(inFile, score);
scores[i].score = atoi(score.c_str());
}
inFile.close();
// Add the new score - maybe?
char * descr = getenv("QUERY_STRING");
d = string(descr);
for(i = 0; i<d.size();i++)
{
if(d[i] = ':')
{
n = string(d.substr(0,i+1)); //name
l = atoi((d.substr(i+1,d.size()-i)).c_str()); //level
break;
}
}
scores[kNumScores].name = n;
scores[kNumScores].score = l;
// Sort the scores
sort(scores, scores + kNumScores + 1);
ofstream outFile("highscores.txt");
for(unsigned i = 0; i < kNumScores; ++i)
{
outFile << scores[i].name << endl;
outFile << scores[i].score << endl;
}
outFile.close();
return 0;
}
jfaller
2003.11.29, 02:50 AM
Originally posted by davecom
Thanks for the replies. Where can I find the server error log? Is it the apache one I'm looking for?
Also, I'll have to look into the file errors you referred to jfaller. However I believe it worked on my local machine. Is the problem that I'm attempting to process a get request and then doing file access. Does this REQUIRE a post request?
I did chmod 777 the file it rw to.
I think the apache server error log is located in /var/log/httpd/error_log, and an access log is there abouts too. Something clever is try "tail -f /var/log/httpd/error_log" in Terminal and then try to run the cgi from Safari. That should get it debugged quickly.
Additionally, I think this might have something to do with 500 errors... From locahost (http://localhost/manual/howto/cgi.html#writingacgiprogram)
File permissions
Remember that the server does not run as you. That is, when the server starts up, it is running with the permissions of an unprivileged user - usually ``nobody'', or ``www'' - and so it will need extra permissions to execute files that are owned by you. Usually, the way to give a file sufficient permissions to be executed by ``nobody'' is to give everyone execute permission on the file:
chmod a+x first.pl
Also, if your program reads from, or writes to, any other files, those files will need to have the correct permissions to permit this.
The exception to this is when the server is configured to use suexec. This program allows CGI programs to be run under different user permissions, depending on which virtual host or user home directory they are located in. Suexec has very strict permission checking, and any failure in that checking will result in your CGI programs failing with an "Internal Server Error". In this case, you will need to check the suexec log file to see what specific security check is failing.
As stated above, you will have to adjust the permissions on the high score file accordingly.
If memory serves (and it has been YEARS), the only real differences between a POST and a GET are whether or not the data is visible on the http: request line, and whether apache sends the data to the CGI via stdio or environment variables. You're really probing the old brains cells that cider killed long ago...:rolleyes:
The online docs for apache are really quite good. Keep probing there, and if you have questions, post 'em here too.
Good luck
Fenris
2003.11.29, 02:52 AM
I will need to look into this post in a few months, so Carlos, if you prune this, I'll prune you. ;) Please respect my obsessive need to control people. :D
davecom
2003.11.29, 03:04 AM
Okay here's the Apache error. But what does it really mean?
[Fri Nov 28 21:25:50 2003] [error] [client 68.194.30.217] Premature end of script headers: /home/web/david/kopecsoft.com/cgi-bin/a.cgi
Oh and as I said I did chmod the highscores.txt .
jfaller
2003.11.29, 10:08 AM
Originally posted by davecom
Okay here's the Apache error. But what does it really mean?
[Fri Nov 28 21:25:50 2003] [error] [client 68.194.30.217] Premature end of script headers: /home/web/david/kopecsoft.com/cgi-bin/a.cgi
Oh and as I said I did chmod the highscores.txt .
Welcome to cgi debugging. :???:
I think this is caused because apache (when it runs your cgi) wants you to send the response headers -- or the HTML responses down the pipe. I think the following should do be put at the end of main(), and you'll be good:
{
cout << "HTTP/1.1 200 OK" << endl;
cout << "Content-Type: text/html\n" << endl;
}
Check that I got the commands right, it has been a while.
Again, I hope this helps.
davecom
2003.11.29, 03:07 PM
Yes, that did help. Using the second line of your header returning code stopped Apache from complaining and everything is working now.
Thanks a lot, everyone!
vBulletin® v3.6.8, Copyright ©2000-2008, Jelsoft Enterprises Ltd.