Using libcurl to login to fetch sql table data

Started by
9 comments, last by Dirge 16 years, 9 months ago
So, yeah, like the subject says, I want to allow my server programs to take a user name and password, login to a core stats site (for instance), and modify or read mysql table data. So right now I'm trying to get the first part working, logging in. Using libcurl, I'm attempting to login using a form in a php-nuke site that looks like this:

<br>
<table width="100%" border="0" cellspacing="1" cellpadding="0" bgcolor="#0026FD"><tr><td>
<table width="100%" border="0" cellspacing="1" cellpadding="8" background="themes/Solaris/forums/images/bg_space_one_layer.jpg"><tr><td>
<form action="modules.php?name=Your_Account" method="post">
<b>User Login</b><br><br>
<table border="0"><tr><td>
Nickname:</td><td><input type="text" name="username" size="15" maxlength="25"></td></tr>
<tr><td>Password:</td><td><input type="password" name="user_password" size="15" maxlength="20"></td></tr>
</table><input type="hidden" name="redirect" value=>
<input type="hidden" name="mode" value=>
<input type="hidden" name="f" value=>
<input type="hidden" name="t" value=>
<input type="hidden" name="op" value="login">
<input type="submit" value="Login"></form><br>

So to test this out, for my curl post string I use this: curl -d "username=MyUserName&user_password=MyUserPassword" http://www.MySite.com/modules.php?name=Your_Account username and user_password are pretty obvious, but I'm not quite sure how to submit the form. I've tried a number of things without any luck. Using that string for the return value I get the "unknown user/password" page, which means something is wrong here, but I can't spot what (other than the submit). When I try to submit the form with op=Login, I get no result at all. Can anyone tell me what I'm missing here? Also, as far as retrieving and modifying the table data, is this really the best way to go about doing this. In the end what I want to have is a number of servers that sporadically update the central database with user statistics (think Battlefield 2). Does anyone have any experience on something like this that would be willing to share some advice? Thanks for any insights!
"Artificial Intelligence: the art of making computers that behave like the ones in movies."www.CodeFortress.com
Advertisement
Change the form method to GET and pass the user name/password as part of the URL. I don't think your -d data is properly formatted as a form post document.
enum Bool { True, False, FileNotFound };
No, I definitely want to use POST. If you read here you'll see what I mean: http://curl.haxx.se/docs/httpscripting.html.

Specifically this part:
Quote:
4.2 POST

The GET method makes all input field names get displayed in the URL field of
your browser. That's generally a good thing when you want to be able to
bookmark that page with your given data, but it is an obvious disadvantage
if you entered secret information in one of the fields or if there are a
large amount of fields creating a very long and unreadable URL.

The HTTP protocol then offers the POST method. This way the client sends the
data separated from the URL and thus you won't see any of it in the URL
address field.

The form would look very similar to the previous one:

<form method="POST" action="junk.cgi">
<input type=text name="birthyear">
<input type=submit name=press value=" OK ">
</form>

And to use curl to post this form with the same data filled in as before, we
could do it like:

curl -d "birthyear=1905&press=%20OK%20" www.hotmail.com/when/junk.cgi

This kind of POST will use the Content-Type
application/x-www-form-urlencoded and is the most widely used POST kind.

The data you send to the server MUST already be properly encoded, curl will
not do that for you. For example, if you want the data to contain a space,
you need to replace that space with %20 etc. Failing to comply with this
will most likely cause your data to be received wrongly and messed up.


I think you're right though, my post is definitely not formatted properly, but I'm not sure why. Anyone?
"Artificial Intelligence: the art of making computers that behave like the ones in movies."www.CodeFortress.com
Okay, its 3.20am here in NZ, and I was up watching america's cup, what does this have to do you with you? nothing, appart from me not having the concentration to look at your problem in detail.

Hope this helps (it's MFC but should be easy to translate):
struct HttpRequestObj{	CURL *sessionHandle;		std::vector<char> tempWriteBuffer;	size_t numBytesWritten;	HttpRequestObj() : sessionHandle(NULL), numBytesWritten(0)	{}	void resetWriteBuffer()	{		tempWriteBuffer.resize(0);		numBytesWritten = 0;	}	static size_t writeDataCB(void *buffer, size_t size, size_t nmemb, void *userp)	{		if( userp == NULL )			return 0;		HttpRequestObj *self = static_cast<HttpRequestObj*> (userp);		size_t oldSize = self->tempWriteBuffer.size();		char *p = NULL;		self->tempWriteBuffer.resize( self->tempWriteBuffer.size() + (size * nmemb) );		p = &self->tempWriteBuffer[0] + oldSize;		memcpy( p, buffer, (size * nmemb) );		self->numBytesWritten += (size * nmemb);		return size * nmemb;	}} httpReq;void HttpRequestInit() {	if( curl_global_init( CURL_GLOBAL_ALL ) != 0 )	{		AfxMessageBox("Error initializing HTTP library", MB_ICONERROR|MB_OK);		return;	}}CString HttpRequestPost(CString url, CString pd, CStatusDlg& st){	if( httpReq.sessionHandle == NULL )	{		httpReq.sessionHandle = curl_easy_init();		if( httpReq.sessionHandle == NULL ) 		{			AfxMessageBox("Error initializing HTTP session handle", MB_ICONERROR|MB_OK);			return "";		}	}	curl_slist *headers = NULL;	headers = curl_slist_append( headers, "Content-Type: application/x-www-form-urlencoded");	int len = pd.GetLength();	curl_easy_setopt( httpReq.sessionHandle, CURLOPT_POSTFIELDS, pd );	curl_easy_setopt( httpReq.sessionHandle, CURLOPT_HTTPHEADER, headers );	curl_easy_setopt( httpReq.sessionHandle, CURLOPT_URL, url ); 	curl_easy_setopt( httpReq.sessionHandle, CURLOPT_WRITEFUNCTION, httpReq.writeDataCB ); 	curl_easy_setopt( httpReq.sessionHandle, CURLOPT_WRITEDATA, (void*) &httpReq );		httpReq.resetWriteBuffer();	CURLcode r = curl_easy_perform( httpReq.sessionHandle ); 	curl_slist_free_all( headers );	if( r != 0 ) 	{		CString msg;		msg.Format("Error performing HTTP request: Code %i", (int)r );		AfxMessageBox( msg, MB_ICONERROR|MB_OK );		return "";	}	CString response( (LPCSTR) &httpReq.tempWriteBuffer[0] );	return response;}


where pd is a string like "msg=hello%20world&blah=bleh"

FYI > it definitely works, but it probably won't compile.
"I am a donut! Ask not how many tris/batch, but rather how many batches/frame!" -- Matthias Wloka & Richard Huddy, (GDC, DirectX 9 Performance)

http://www.silvermace.com/ -- My personal website
Quote:it is an obvious disadvantage if you entered secret information


While that may be true for browsers that store previous URL lines, I believe libcurl doesn't integrate with your browser history (and if it does, it should be re-educated :-)

On the wire, there is no security difference between GET and POST. However, if the destination requires POST, and you can't change the destination, then that's a good reason to want to POST.
enum Bool { True, False, FileNotFound };
Quote:Original post by hplus0603
Change the form method to GET and pass the user name/password as part of the URL. I don't think your -d data is properly formatted as a form post document.


Big no-no, if I understand the intentions of the OP right.

GET is designated for idempotent operations. It must never mutate the state, or very weird things can happen (see thedailywtf.com for examples). While it may work for the login itself, it really shouldn't be used for updating the stats.

Random link:
http://www.cs.tut.fi/~jkorpela/forms/methods.html

Note that as always, YMMV, but I feel that problems associated with this method are dangerous enough to be worth mentioning.
Quote:GET is designated for idempotent operations. It must never mutate the state


The result of a GET is the login data, in this case. That doesn't mutate the state -- the state mutates of itself. Or are you saying that if I have a web page that displays the time when the page was generated, or the number of page views, cannot be served by a GET request? After all, the page view counter goes up by each GET request.

In practice, there are some web caches that will cache GET requests (but you cannot cache POST requests) -- however, with the appropriate cache validators (or de-validators) in the headers, GET is fine for this purpose. Again, assuming the target application will actually accept a GET.

enum Bool { True, False, FileNotFound };
Quote:Original post by hplus0603
Quote:GET is designated for idempotent operations. It must never mutate the state


The result of a GET is the login data, in this case. That doesn't mutate the state -- the state mutates of itself. Or are you saying that if I have a web page that displays the time when the page was generated, or the number of page views, cannot be served by a GET request? After all, the page view counter goes up by each GET request.

In practice, there are some web caches that will cache GET requests (but you cannot cache POST requests) -- however, with the appropriate cache validators (or de-validators) in the headers, GET is fine for this purpose. Again, assuming the target application will actually accept a GET.


The most dangerous situation comes from bots, who may, by following links, affect the internal state.

The idempotent here refers to the server's internal state, not the generated page. The counter itself is prone to abuse due to not following the rule - hence some counters restrict itself to unique IPs when determining the visitors.

The basic guideline is, that issuing a GET once or one million times should have no ill-effect on the server (it would however taint the counter). Not so in the case of updating a database using POST, where it's expected for some internal state to be affected by design.

This example isn't directly limited to GET vs. POST (involves cookies), but it does demonstrate the dangers of allowing GET requests to mutate the state:
http://worsethanfailure.com/Comments/The_Spider_of_Doom.aspx
silvermace: That code works great, and I definitely get a response back from the server, but it's still not the one I'm expecting.

Can anyone tell me how I can actually submit the form data. '&op=Login' doesn't work, and a field name for the 'submit' element doesn't exist, only the 'Login' value. How do you submit to a form with an anonymous submit element. If this is more of an html question I apologize, but I figure someone around here must know.

BTW, thanks everyone for your feedback, I appreciate it.
"Artificial Intelligence: the art of making computers that behave like the ones in movies."www.CodeFortress.com
Quote:Spider of Doom


Right. Don't build a site that's broken. I think we all agree on that :-)

Quote:How do you submit to a form with an anonymous submit element.


The anonymous submit element is not by itself a problem.

Try this form in your browser, and see what the URL comes out as. Yes, it uses GET, just so you can easily see the URL!

<html><head><title>Test Form</title></head><body><form method='get'><input type='text' name='foo'/><input type='submit' value='Button'/></form></body></html>


(And, no, I didn't run it through the validator, and it doesn't have a doctype, etc -- it's just an illustration, that will happen to work)
enum Bool { True, False, FileNotFound };

This topic is closed to new replies.

Advertisement