cURL and cgi

I have a CGI application done in c++ that communicates with PayPal. I've had an issue where the application dies when I try to perform a cURL operation. Upon further inspection it seems that I can run cURL examples from the command line.

Upon even further inspection it seems that I can run cURL in the CGI if I don't set the URL option. Any other option seems to be ok. Of course this fails with an error from cURL saying that the URL was not set. But at least I get to the point of an error.

If I set the URL option it either displays a blank white screen at perform, assuming I print the necessary headers, or it fails with premature end of script headers.

So I'm not a unix guru, and I'm kind of in over my head here, but it is what it is. I'm thinking maybe there's a port blocked that's used by the cgi for outgoing requests that isn't used for the command line. I'm thinking maybe it's a permissions thing. The examples were run as root, and I set the .cgi file to be owned by root, but maybe that doesn't count.

I could really use some direction here. And keep in mind my unix knowledge is not phenominal. I can get around alright, but I need a little help.

I'm running red hat and apache.

Thanks,
Brandon

Could you please post your code?

The code is kind of a mess. A lot of things have been tried and commented out. I've tried it using cURLpp and using the standard cURL libraries. Same results.

//--------------------------------------------------------------------
// Includes
//--------------------------------------------------------------------
#include "verisign_io.h"
#include "file_io.h"
#include "error_lib.h"
#include "logging.h"
#include "toolkit.h"
#include "parm.h"

//using current time in seconds and credit card number for unique transaction id
#include <time.h>
#include <string>
#include <sstream>
#include <iostream>

//#include "curl/curl.h"
#include <curlpp/cURLpp.hpp>
#include <curlpp/Easy.hpp>
#include <curlpp/Options.hpp>
#include <curlpp/Exception.hpp>

#ifdef CGI_VERSION
        #include "cgic.h"
#endif

//*********************************************************************
// Global Functions
//*********************************************************************

//--------------------------------------------------------------------
// GetValueFromResponseString
//--------------------------------------------------------------------
bool GetValueFromResponseString (const char* key, 
                                                                TString& value, 
                                                                TString responseString)
{
        char*           startPtr        = NULL;
        char*           endPtr          = NULL;
        size_t          offset          = 0;
        size_t          length          = 0;

        value = "";

        startPtr = responseString.Find (key);
        if (startPtr)
        {
                startPtr += strlen(key);
                offset = responseString.PointerToOffset (startPtr);
                endPtr = responseString.Find ("&", true, offset);
                if (endPtr)
                        length = endPtr - startPtr;
                else
                        length = strlen(startPtr);

                value = responseString.SubString(offset,length);
        }

        return (startPtr != NULL);
}

//--------------------------------------------------------------------
// CallVerisign
//--------------------------------------------------------------------
bool CallVerisign (const TDBSplitReport* dbObjPtr,
                                         TString creditCardNumber,
                                         TString expirationDate,
                                         float amount,
                                         TString customerZipCode,
                                         TString customerAddress,
                                         TString authorizationCode,
                                         TString transactionType,
                                         TString comment,
                                         TString& answerText,
                                         TString& referenceID)
{
        bool                    success = false;
        bool                    isTest = false;
        //int                           verisignContext;
        int                             resultCode;
        TString                 localAmount;
        TString                 verisignArgs;
        TString                 responseMessage;
        //TString                       referenceID;
        TString                 tmpString;
        TString                 responseString;
        TString                 logEntry;
        //char                  *responseBuffer;

AddLogEntry(dbObjPtr, "INFO\tVerisign.cc\tAbout to setup environment");

        // Setup the environment so it can find the certificate
        setenv("PFPRO_CERT_PATH", "/usr/local/verisign/payflowpro/linux/certs", 1);

        // Nullify the answer text
        answerText = "";
        // Adjust some arguments
        creditCardNumber.NumbersOnly();
        expirationDate.NumbersOnly();
        customerZipCode.NumbersOnly();
        if (customerZipCode.IsEmpty())
                customerZipCode = "99999";
        customerZipCode = customerZipCode.SubString(0,5);
        if (comment.IsEmpty())
                comment = "---";
        comment = comment.SubString(0,18);
        localAmount.CopyFrom(amount,2);
        // convert expiration from YYYY to MMYY format
        tmpString = expirationDate;
        expirationDate = tmpString.SubString(4,2);
        expirationDate += tmpString.SubString(2,2);

        // setup the log entry
        logEntry = "Verisign Transaction\t";
        logEntry += transactionType;
        logEntry += "\t";
        logEntry += comment;
        logEntry += "\t";
        logEntry += localAmount;
        logEntry += "\t";
        tmpString = creditCardNumber.SubString(0,1);
        tmpString += "-";
        tmpString += creditCardNumber.SubString(0,4,true);
        logEntry += tmpString;
        logEntry += "\t";
        logEntry += expirationDate;
        logEntry += "\t";

AddLogEntry(dbObjPtr, "INFO\tVerisign.cc\tEntering Try");

try{
        if (strcasecmp(creditCardNumber,kTestCreditCardNumber) == 0)
        {
                isTest = true;
                //answerText = kApprovedPrefixString;
                answerText += "987654321";
        }
        else
        {
AddLogEntry(dbObjPtr, "INFO\tVerisign.cc\tNot A Test");
                // Build up the application's piped arguments
                verisignArgs += "TRXTYPE=";
                verisignArgs += transactionType;
                verisignArgs += "&TENDER=C";
                verisignArgs += "&PARTNER=";
                verisignArgs += kVerisignPartner;
                verisignArgs += "&VENDOR=";
                verisignArgs += kVerisignVendor;
                verisignArgs += "&USER=";
                verisignArgs += kVerisignUser;
                verisignArgs += "&PWD=";
                verisignArgs += kVerisignPassword;
                verisignArgs += "&ACCT=";
                verisignArgs += creditCardNumber;
                verisignArgs += "&EXPDATE=";
                verisignArgs += expirationDate;
                verisignArgs += "&AMT=";
                verisignArgs += localAmount;
                verisignArgs += "&COMMENT1[";
                verisignArgs += comment.GetLength();
                verisignArgs += "]=";
                verisignArgs += comment;
                verisignArgs += "&STREET[";
                verisignArgs += customerAddress.GetLength();
                verisignArgs += "]=";
                verisignArgs += customerAddress;
                verisignArgs += "&ZIP=";
                verisignArgs += customerZipCode;
                verisignArgs += "\r\n";
AddLogEntry(dbObjPtr, "INFO\tVerisign.cc\tArgs Set Up");

                //Using credit card number and time in seconds to create unique id
                char uniqueID[64];
                strcpy(uniqueID, creditCardNumber);
                sprintf(uniqueID, "%s%ld", uniqueID, time(NULL));
AddLogEntry(dbObjPtr, "INFO\tVerisign.cc\tBuilt Unique ID");

                //set the headers.
                std::list<std::string> headers;
                headers.push_back("Content-Type: text/namevalue");
                headers.push_back("Content-Length: " + verisignArgs.GetLength());
                headers.push_back("X-VPS-Timeout: 45");
                headers.push_back("X-VPS-Request_ID:" + std::string(uniqueID));

AddLogEntry(dbObjPtr, "INFO\tVerisign.cc\tRest Of Headers Set");

AddLogEntry(dbObjPtr, "INFO\tVerisign.cc\tInit Curl");
                cURLpp::Cleanup myCleanup;
                cURLpp::Easy myRequest;
                myRequest.setOpt(new cURLpp::Options::Url(kVerisignHost));
                myRequest.setOpt(new cURLpp::Options::Port(kVerisignPort));
                myRequest.setOpt(new cURLpp::Options::Timeout(kVerisignTimeout));
                myRequest.setOpt(new cURLpp::Options::Header(1));
                myRequest.setOpt(new cURLpp::Options::FollowLocation(0));
                myRequest.setOpt(new cURLpp::Options::SslVerifyPeer(0));
                myRequest.setOpt(new cURLpp::Options::SslVerifyHost(2));
                myRequest.setOpt(new cURLpp::Options::ForbidReuse(true));
                myRequest.setOpt(new cURLpp::Options::Post(1));
                myRequest.setOpt(new cURLpp::Options::HttpHeader(headers));
                myRequest.setOpt(new cURLpp::Options::PostFields(std::string(verisignArgs)));
                myRequest.setOpt(new cURLpp::Options::UserAgent("Mozilla/5.0 (Windows; U; Windows NT 5.1; en-GB; rv:1.8.1.6) Gecko/20070725 Firefox/2.0.0.6"));
                //myRequest.setOpt(new cURLpp::Options::Verbose(true));

                std::ostringstream responseBuffer;
//AddLogEntry(dbObjPtr, "INFO\tVerisign.cc\tSet up output stream.  About to perform shorthand version");
                //responseBuffer << myRequest;
                        
                cURLpp::Options::WriteStream ws(&responseBuffer);
                myRequest.setOpt(ws);
AddLogEntry(dbObjPtr, "INFO\tVerisign.cc\tSet up output stream.  About to perform");
//                cout << "Content-type: text/plain" << endl << endl; 
                myRequest.perform();

AddLogEntry(dbObjPtr, "INFO\tVerisign.cc\tPerformed request");
                const char* temp = responseBuffer.str().c_str();
                responseString.CopyFrom(temp);
AddLogEntry(dbObjPtr, "INFO\tVerisign.cc\tSet Response String");

                                if (! responseString.IsEmpty())
                                {
                                        // get the reponse code
                                        if ( GetValueFromResponseString(kVSResponseFieldResult,tmpString, responseString) )
                                        {
                                                resultCode = tmpString.AsSInt();
                                                GetValueFromResponseString(kVSResponseFieldRespMsg,responseMessage, responseString);
                                                GetValueFromResponseString(kVSResponseFieldPnref,referenceID, responseString);

                                                if (resultCode == 0)
                                                {
                                                        // approved
                                                        GetValueFromResponseString(kVSResponseFieldAuthCode, answerText, responseString);
                                                        success = true;
                                                        logEntry += "\tAuthorized";
                                                        logEntry += "\t";
                                                        logEntry += answerText;
                                                        logEntry += "\t";
                                                        logEntry += referenceID;
                                                }
                                                else if (resultCode > 0)
                                                {
                                                        // declined
                                                        answerText += resultCode;
                                                        answerText += ": ";
                                                        answerText += responseMessage;

                                                        logEntry += "\tDeclined";
                                                        logEntry += "\t";
                                                        logEntry += resultCode;
                                                        logEntry += "\t";
                                                        logEntry += referenceID;
                                                        logEntry += "\t";
                                                        logEntry += responseMessage;
                                                }
                                                else
                                                {
                                                        // errror 
                                                        answerText += resultCode;
                                                        answerText += ": ";
                                                        answerText += responseMessage;

                                                        logEntry += "\tDeclined";
                                                        logEntry += "\t";
                                                        logEntry += resultCode;
                                                        logEntry += "\t";
                                                        logEntry += referenceID;
                                                        logEntry += "\t";
                                                        logEntry += responseMessage;
                                                }
                                        }
                                }
                                else
                                {
                                        answerText += "Received empty response from Verisign client.";

                                        logEntry += "\tError";
                                        logEntry += "\t";
                                        logEntry += answerText;
                                }
        }
}
catch( cURLpp::RuntimeError &e){
        logEntry += "\tError\t";
        logEntry += e.what();
        logEntry += "\t";
        AddLogEntry(dbObjPtr, "Runtime Error");
}
catch( cURLpp::LogicError &e ){
        logEntry += "\tError\t";
        logEntry += e.what();
        logEntry += "\t";
 AddLogEntry(dbObjPtr, "Logic Error");
}
catch(...){
        AddLogEntry(dbObjPtr, "General Error");
}

        AddLogEntry(dbObjPtr,logEntry);
        return success;
}

---------- Post updated at 09:10 PM ---------- Previous update was at 09:09 PM ----------

kverisignhost is the paypal payflow link. I've also tried google.com. Doesn't matter what the url is, if I set that option the program dies with no error and no explanation whatsoever.

---------- Post updated 08-17-09 at 10:31 AM ---------- Previous update was 08-16-09 at 09:10 PM ----------

I dumped the ldd info for both testing.cgi and example06. And here's what I got. Definitely something up with the libraries, but I don't know how to fix them.

testing.cgi

libmysqlclient.so.10 => /usr/lib/mysql/libmysqlclient.so.10 (0x4001e000)
libcrypt.so.1 => /lib/libcrypt.so.1 (0x40055000)
libpfpro.so => /usr/local/verisign/payflowpro/linux/lib/libpfpro.so (0x40082000)
libcurlpp.so.0 => /usr/lib/libcurlpp.so.0 (0x40128000)
libstdc++.so.5 => /usr/lib/libstdc++.so.5 (0x40152000)
libidn.so.11 => /usr/lib/libidn.so.11 (0x40205000)
libssh2.so.1 => /usr/lib/libssh2.so.1 (0x40236000)
libssl.so.0.9.7 => /usr/lib/libssl.so.0.9.7 (0x40257000)
libcrypto.so.0.9.7 => /usr/lib/libcrypto.so.0.9.7 (0x4028a000)
libldap.so.2 => /usr/lib/libldap.so.2 (0x4038f000)
libgssapi_krb5.so.2 => /usr/kerberos/lib/libgssapi_krb5.so.2 (0x403c1000)
libkrb5.so.3 => /usr/kerberos/lib/libkrb5.so.3 (0x403d4000)
libk5crypto.so.3 => /usr/kerberos/lib/libk5crypto.so.3 (0x40433000)
libz.so.1 => /usr/lib/libz.so.1 (0x40443000)
libm.so.6 => /lib/tls/libm.so.6 (0x40451000)
libgcc_s.so.1 => /lib/libgcc_s.so.1 (0x40473000)
libc.so.6 => /lib/tls/libc.so.6 (0x42000000)
libpthread.so.0 => /lib/tls/libpthread.so.0 (0x4047b000)
libcurl.so.4 => /usr/lib/libcurl.so.4 (0x4048a000)
libnsl.so.1 => /lib/libnsl.so.1 (0x4051c000)
libssl.so.4 => /lib/libssl.so.4 (0x40531000)
libcom_err.so.3 => /usr/kerberos/lib/libcom_err.so.3 (0x40566000)
libresolv.so.2 => /lib/libresolv.so.2 (0x40568000)
libdl.so.2 => /lib/libdl.so.2 (0x4057b000)
liblber.so.2 => /usr/lib/liblber.so.2 (0x4057f000)
libsasl2.so.2 => /usr/lib/libsasl2.so.2 (0x4058b000)
/lib/ld-linux.so.2 => /lib/ld-linux.so.2 (0x40000000)

example06

libstdc++.so.5 => /usr/lib/libstdc++.so.5 (0x4001e000)
libcurl.so.3 => /usr/local/lib/libcurl.so.3 (0x400d1000)
libssl.so.0.9.7 => /usr/lib/libssl.so.0.9.7 (0x400fe000)
libcrypto.so.0.9.7 => /usr/lib/libcrypto.so.0.9.7 (0x40131000)
libdl.so.2 => /lib/libdl.so.2 (0x40236000)
libz.so.1 => /usr/lib/libz.so.1 (0x4023a000)
libm.so.6 => /lib/tls/libm.so.6 (0x40249000)
libgcc_s.so.1 => /lib/libgcc_s.so.1 (0x4026b000)
libc.so.6 => /lib/tls/libc.so.6 (0x42000000)
/lib/ld-linux.so.2 => /lib/ld-linux.so.2 (0x40000000)

Thanks

Maybe it would be easier to debug if you would add some debug hooks and use a debugger to find out what line of the code is giving problems and why?

I've been dumping log info and it's curl perform() that is causing it to die. Presumably from a linking error. Other than that I'm stuck in command line red hat and don't know how to apply a debugger. I'm kind of out of my element here.

compile it with -ggdb and use the gdb commandline debugging tool to run it. There is some information on gdb here.

I've run the application with strace, and don't see any point where it's failing. But then again, it's a large file and I'm not really sure what I'm looking for.

Attached is the current GDB manual in PDF.

I'm running this from a web browser. How would I use gdb to do that?