// NB // This program must run as a set user id program // (Either use set-uid mechanism or SU-Exec options for Apache // server - it has to run under user-id of student because // will need to write to a datafile created by student). // setuid or SU-Exec? It depends on your systems administrators // who will chose how they want things set up. If the sys admin // choses setuid style, the directories must honor setuid tags and // students must use chmod to set the setuid bit. If sys admins // chose SU-Exec style, you can bascially forget the issue; SU-Exec handles // everything. // Place in (subdirectory) of cgi-bin along with // DataLog a file for recording all pizza orders #include #include #include #include #include #include #include #include #include #include #include "CGI.h" /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ /* Pizza data structure for holding order details etc Note potential for maintenance problems --- Javascript on HTML page has one set of data like option choices and costs Here we have hopefully the same data repeated But easy to make inconsistent changes! Part of the fun of Internet computing. */ class FredsPizza { public: FredsPizza(); ~FredsPizza(); void SetSize(const char* sizeinfo); void AddTopping(const char* toptype); void AddExtra(const char* extra); void SetDelivery(const char* deliver); void SetCustomer(const char* customer); void SetAddress(const char* address); void SetEmail(const char* email); void SetPhone(const char* phone); void Report(ostream& out); void Log(ostream& out); const char* Email() { return fEmail; } const char* Delivery() { return fDelivery; } private: double Cost(); char* catenate(const char* old, const char* more); int fSize; int fNumToppings; double fPizzaCost; double fExtrasCost; double fDeliveryCost; char* fTops; char* fXtras; char* fDelivery; char* fCustomer; char* fAddress; char* fEmail; char* fPhone; }; FredsPizza::FredsPizza() { fSize = 1; fNumToppings = 0; fExtrasCost = 0.0; fTops = fXtras = fDelivery = fCustomer = fAddress = fEmail = fPhone = NULL; } FredsPizza::~FredsPizza() { delete fTops; delete fXtras; delete fDelivery; delete fCustomer; delete fAddress; delete fEmail; delete fPhone; } void FredsPizza::SetSize(const char* sizeinfo) { fSize = 1; if(0 == strcmp(sizeinfo, "fam")) fSize = 2; if(0 == strcmp(sizeinfo, "pop")) fSize = 3; } void FredsPizza::AddTopping(const char* toptype) { fNumToppings++; char *temp = catenate(fTops, toptype); delete [] fTops; fTops = temp; } void FredsPizza::AddExtra(const char* extra) { char *temp = catenate(fXtras, extra); delete [] fXtras; fXtras = temp; if(0 == strcmp("Coke",extra)) fExtrasCost += 1.70; if(0 == strcmp("Lemonade",extra)) fExtrasCost += 1.70; if(0 == strcmp("Ice cream",extra)) fExtrasCost += 3.5; if(0 == strcmp("Salad",extra)) fExtrasCost += 4.50; } void FredsPizza::SetDelivery(const char* delivery) { fDelivery = new char[strlen(delivery) + 1]; strcpy(fDelivery, delivery); if(0 == strcmp(delivery,"Express")) fDeliveryCost = 1.50; if(0 == strcmp(delivery,"Gorrila")) fDeliveryCost = 15.0; if(0 == strcmp(delivery,"Fat lady who sings")) fDeliveryCost = 20.0; if(0 == strcmp(delivery,"Female stripper")) fDeliveryCost = 50.0; if(0 == strcmp(delivery,"Male stripper")) fDeliveryCost = 40.0; if(0 == strcmp(delivery,"Coco the clown")) fDeliveryCost = 25.0; if(0 == strcmp(delivery,"Bozo the programmer")) fDeliveryCost = 1.65; if(0 == strcmp(delivery,"Fred the cook")) fDeliveryCost = 101.50; } void FredsPizza::SetCustomer(const char* customer) { fCustomer = new char[strlen(customer) + 1]; strcpy(fCustomer, customer); } void FredsPizza::SetAddress(const char* address) { fAddress = new char[strlen(address) + 1]; strcpy(fAddress, address); } void FredsPizza::SetPhone(const char* phone) { fPhone = new char[strlen(phone) + 1]; strcpy(fPhone, phone); } void FredsPizza::SetEmail(const char* email) { fEmail = new char[strlen(email) + 1]; strcpy(fEmail, email); } void FredsPizza::Report(ostream& out) { out << "Your "; if(fSize==1) out << "regular "; if(fSize==2) out << "Family "; if(fSize==3) out << "POPULAR "; out << "sized pizza"; if(fNumToppings>0) out << ", with " << fTops << "," ; out << " is now being prepared." << endl; out << "Your pizza "; if(fXtras != NULL) out << ", and the following extras : " << fXtras << "," << endl; out << "will be delivered soon by our " << fDelivery << " service." << endl; out << "The cost will be $" << Cost() << ". Please have money ready to pay delivery person." << endl; } void FredsPizza::Log(ostream& out) { out << "Customer: " << fCustomer << endl; out << "Address: " << fAddress << endl; out << "Phone: " << fPhone << endl; out << "Cost: $" << Cost() << endl; out << "Size: " << fSize << endl; out << "Tops: " << fTops << endl; out << "Extras: " << fXtras << endl; out << "Delivery: " << fDelivery << endl; } double FredsPizza::Cost() { double cost = 0.0; if(fSize == 1) cost = 7.0; if(fSize == 2) cost = 11.0; if(fSize == 3) cost = 15.0; cost += fSize*fNumToppings*0.50; cost += fExtrasCost; cost += fDeliveryCost; return cost; } char* FredsPizza::catenate(const char* old, const char* more) { char *temp = NULL; if(old == NULL) { temp = new char[strlen(more) + 1]; strcpy(temp, more); } else { int len = strlen(old) + strlen(more) + 3; temp = new char[len]; strcpy(temp, old); strcat(temp, ", "); strcat(temp, more); } return temp; } /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ /* Here have chosen to do everything in a specialised subclass of CGI_Helper An alternative choice would be to have an object of some other class to handle the work and use a CGI_Helper subclass where only real change is ProcessToken() function that calls other object to do processing. Processing: automatically lock the records file read and process tokens to build a pizza with order details use this to generate HTML of reply and entry in log file (move to end of log file before writing) automatically unlock the records file The code is missing safety checks --- like did we open the records file successfully? */ class FredsCGI : public CGI_Helper { public: FredsCGI(); ~FredsCGI(); protected: virtual void StartTokens() ; virtual void ProcessToken(Token *tok); virtual void EndTokens(); private: fstream fRecords; time_t fArrivalTime; FredsPizza fPizza; int fFiledescriptor; }; FredsCGI::FredsCGI() { fRecords.open("DataLog",ios::in | ios::out); fRecords.seekp(0, ios::end); fFiledescriptor = fRecords.rdbuf()->fd(); lockf(fFiledescriptor, F_LOCK, 0); } FredsCGI::~FredsCGI() { fRecords.close(); lockf(fFiledescriptor, F_ULOCK, 0); } void FredsCGI::StartTokens() { (void) time(&fArrivalTime); fRecords << "Order received at " << ctime(&fArrivalTime); cout << "

Fred's CyberPizza Parlor

"; cout << "Thank you for your order.
"; } void FredsCGI::EndTokens() { fPizza.Report(cout); fPizza.Log(fRecords); fRecords << "----" << endl; } void FredsCGI::ProcessToken(Token *tok) { if(0==strcmp(tok->Name(), "phone")) fPizza.SetPhone(tok->Value()); if(0==strcmp(tok->Name(), "email")) fPizza.SetEmail(tok->Value()); if(0==strcmp(tok->Name(), "address")) fPizza.SetAddress(tok->Value()); if(0==strcmp(tok->Name(), "customer")) fPizza.SetCustomer(tok->Value()); if(0==strcmp(tok->Name(), "deliv")) fPizza.SetDelivery(tok->Value()); if(0==strcmp(tok->Name(), "xtra")) fPizza.AddExtra(tok->Value()); if(0==strcmp(tok->Name(), "tops")) fPizza.AddTopping(tok->Value()); if(0==strcmp(tok->Name(), "p_size")) fPizza.SetSize(tok->Value()); } int main() { FredsCGI x; x.HTML_Header("Re: Your pizza order"); x.Handle_Request(); x.HTML_Trailer(); return 0; }