HOWTO: Twitpic and OAuth


I am no longer confused! Here is a quick tutorial in how to post images to Twitpic and Twitter when using OAuth. I'm indebted to Steve Corona of Twitpic, for his help with this.

You can see the full code on Dabr's Google Code page.

First of all, you'll need to have enabled OAuth for your Twitter client. I use Abraham's excellent OAuth libraries for PHP.

This tutorial assumes you already have OAuth working. I'll attempt to explain what I'm doing as I go along - but the code should be pretty readable.

Start by reading the Twitpic API documentation. You will also need to register for an API key - this only takes a few seconds.

We start by setting CURL's headers to those required by Twitpic

//Has the user submitted an image and message?
if ($_POST['message']) 
{
	$twitpicURL = 'http://api.twitpic.com/2/upload.json';
	
	//Set the initial headers
	$header = array(
							'X-Auth-Service-Provider: https://api.twitter.com/1/account/verify_credentials.json', 
							'X-Verify-Credentials-Authorization: OAuth realm="http://api.twitter.com/"'
						);

Next, we create the OAuth credentials that we need. Essentially, we're signing a URL request. We then pass that on to Twitpic and they verify it with Twitter. We never pass our OAUTH_CONSUMER_SECRET - so Twitpic can't impersonate us.

	
	//Using Abraham's OAuth library
	require_once('OAuth.php');

	// instantiating OAuth customer 
	$consumer = new OAuthConsumer(OAUTH_CONSUMER_KEY, OAUTH_CONSUMER_SECRET);

	// instantiating signer
	$sha1_method = new OAuthSignatureMethod_HMAC_SHA1();

	// user's token
	list($oauth_token, $oauth_token_secret) = explode('|', $GLOBALS['user']['password']);
	$token = new OAuthConsumer($oauth_token, $oauth_token_secret);

	// Generate all the OAuth parameters needed
	$signingURL = 'https://api.twitter.com/1/account/verify_credentials.json';

	$request = OAuthRequest::from_consumer_and_token($consumer, $token, 'GET', $signingURL, array());

	$request->sign_request($sha1_method, $consumer, $token);

We add these generated credentials into the header.

	$header[1] .= ", oauth_consumer_key="" . $request->get_parameter('oauth_consumer_key') .""";
	$header[1] .= ", oauth_signature_method="" . $request->get_parameter('oauth_signature_method') .""";
	$header[1] .= ", oauth_token="" . $request->get_parameter('oauth_token') .""";
	$header[1] .= ", oauth_timestamp="" . $request->get_parameter('oauth_timestamp') .""";
	$header[1] .= ", oauth_nonce="" . $request->get_parameter('oauth_nonce') .""";
	$header[1] .= ", oauth_version="" . $request->get_parameter('oauth_version') .""";
	$header[1] .= ", oauth_signature="" . urlencode($request->get_parameter('oauth_signature')) .""";

Add everything into CURL

	//open connection
	$ch = curl_init();
	
	//Set paramaters
	curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
	curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
	curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);

	//set the url, number of POST vars, POST data
	curl_setopt($ch,CURLOPT_URL,$twitpicURL);

The data we send to Twitpic (message text, image and key) have to go via POST.

	
	//TwitPic requires the data to be sent as POST
	$media_data = array(
							'media' => '@'.$_FILES['media']['tmp_name'],
					      'message' => ' ' . stripslashes($_POST['message']), //A space is needed because twitpic b0rks if first char is an @
					      'key'=>TWITPIC_API_KEY
						);

	curl_setopt($ch, CURLOPT_POST, true);
	curl_setopt($ch,CURLOPT_POSTFIELDS,$media_data);

All done, we now send the data to Twitpic.

	//execute post
	$result = curl_exec($ch);
	$response_info=curl_getinfo($ch);

	//close connection
	curl_close($ch);

If this has worked, Twitpic will pass back the URL of the image we posted. We then need to post the entire message to Twitter ourselves (Twitpic can't do it for us).

	if ($response_info['http_code'] == 200) //Success
	{
		//Decode the response
		$json = json_decode($result);
		$id = $json->id;
		$twitpicURL = $json->url;
		$text = $json->text;
		$message = trim($text) . " " . $twitpicURL;

This next part is specific to Dabr - your client may post things differently.

		//Send the user's message to twitter
		$request = API_URL.'statuses/update.json';
		
		$post_data = array('source' => 'dabr', 'status' => $message);
		$status = twitter_process($request, $post_data);

		//Back to the timeline
		twitter_refresh("twitpic/confirm/$id");
	} 

If it didn't work, Twitpic will tell us why.

	else 
	{
		$content = "

Twitpic upload failed. No idea why!

"; $json = json_decode($result); $content .= "
message " . urlencode($_POST['message']); $content .= "
json " . print_r($json); $content .= "
Response " . print_r($response_info); $content .= "
header " . print_r($header); $content .= "
media_data " . print_r($media_data); $content .= "
URL was " . $twitpicURL; $content .= "
File uploaded was " . $_FILES['media']['tmp_name']; } }

25 thoughts on “HOWTO: Twitpic and OAuth

  1. hi,
    I am doing everything as it is mentioned, but I am getting this error,
    {"errors":[{"code":400,"message":"Bad Request. Media not included in POST payload."}]}

    can you tell me how can I fix this error ?

    1. Hi Shakoor,

      The error message is saying that you're not sending an image to Twitpic. You need to POST the image from a form, like this....
      <form method='post' action='twitpic' enctype='multipart/form-data'>
      <input type='file' name='media' />
      Message (optional):<br />
      <textarea name='message' style='width:100%; max-width: 400px;' rows='3' id='message'>
      </textarea>
      <input type='submit' value='Send'>
      </form>

      HTH

      T

  2. Hi,

    thanks for your reply, there were some small errors in my code, I fixed them but now I am getting this error and I have tried alot and I am doing exactly what you said, but I can't resolve this error:

    "Could not authenticate you (header rejected by twitter)"

    so please tell me how can I resolve this issue.

  3. I'm sure I'm not the first to find this a really *really* useful post, but it looks like I'm the first to say it (maybe?).

    So thank you! I too was confused as to how to ping twitpic, especially seeing as their documentation is fairly sparse.

    (I've ported this to codeigniter, if you're interested I can send you the code?)

    Ta,

    R

  4. I'm getting the same error from shakoor.
    Could not authenticate you (header rejected by twitter).

  5. hello! thanks for share =]

    question: Could we re-use a token and a secret? I'm working on application which posts on twitter using token+secret that were stored on database (these are stored when user use it for the first time). I want to post on twitpic too. I tried to do what you said using the stored token and secret in place of this line
    list($oauth_token, $oauth_token_secret) = explode('|', $GLOBALS['user']['password']);
    but I could not be authenticated by this way.
    Is possible to use the stored token+secret? Any idea ?

  6. after follow your code...

    the message tell "Twitter timed out
    Dabr gave up on waiting for Twitter to respond. They're probably overloaded right now, try again in a minute."

    but when i check the twitpic.com the picture was succeed to upload.
    the timeline also not update..

    any suggestion?

    thanks

        1. I agree with adhi again, I upload the source code to my own server, but it turns out like that T_T

          Now, there is no image upload service in my dabr server. Do you happen to have any image upload alternative else than twitpic? I can't find dabr yfrog image upload codes..

          PS: thank you for replying Terence! ^_^

  7. I'm trying to change the twitpic information with my image hosting service.
    The response from my api is not JSON but XML I quess this is the problem?
    Can somebody help me out?

    function twitter_twitrp_page($query) {
    if (user_type() == 'oauth') {
    //V2 of the Twitpic API allows for OAuth
    //http://dev.twitpic.com/docs/2/upload/

    //Has the user submitted an image and message?
    if ($_POST['message']) {
    $twitrpURL = 'http://twitrp.com/drippic2/upload';

    //Set the initial headers
    $header = array(
    'X-Auth-Service-Provider: https://api.twitter.com/1/account/verify_credentials.json',
    'X-Verify-Credentials-Authorization: OAuth realm="http://api.twitter.com/"'
    );

    //Using Abraham's OAuth library
    require_once('OAuth.php');

    // instantiating OAuth customer
    $consumer = new OAuthConsumer(OAUTH_CONSUMER_KEY, OAUTH_CONSUMER_SECRET);

    // instantiating signer
    $sha1_method = new OAuthSignatureMethod_HMAC_SHA1();

    // user's token
    list($oauth_token, $oauth_token_secret) = explode('|', $GLOBALS['user']['password']);
    $token = new OAuthConsumer($oauth_token, $oauth_token_secret);

    // Generate all the OAuth parameters needed
    $signingURL = 'https://api.twitter.com/1/account/verify_credentials.json';
    $request = OAuthRequest::from_consumer_and_token($consumer, $token, 'GET', $signingURL, array());
    $request->sign_request($sha1_method, $consumer, $token);

    $header[1] .= ", oauth_consumer_key="" . $request->get_parameter('oauth_consumer_key') .""";
    $header[1] .= ", oauth_signature_method="" . $request->get_parameter('oauth_signature_method') .""";
    $header[1] .= ", oauth_token="" . $request->get_parameter('oauth_token') .""";
    $header[1] .= ", oauth_timestamp="" . $request->get_parameter('oauth_timestamp') .""";
    $header[1] .= ", oauth_nonce="" . $request->get_parameter('oauth_nonce') .""";
    $header[1] .= ", oauth_version="" . $request->get_parameter('oauth_version') .""";
    $header[1] .= ", oauth_signature="" . urlencode($request->get_parameter('oauth_signature')) .""";

    //open connection
    $ch = curl_init();

    //Set paramaters
    curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);

    //set the url, number of POST vars, POST data
    curl_setopt($ch,CURLOPT_URL,$twitrpURL);

    //TwitPic requires the data to be sent as POST
    $media_data = array(
    'media' => '@'.$_FILES['media']['tmp_name'],
    'message' => ' ' . stripslashes($_POST['message']), //A space is needed because twitpic b0rks if first char is an @
    //'key'=>TWITPIC_API_KEY
    );

    curl_setopt($ch, CURLOPT_POST, true);
    curl_setopt($ch,CURLOPT_POSTFIELDS,$media_data);

    //execute post
    $result = curl_exec($ch);
    $response_info=curl_getinfo($ch);

    //close connection
    curl_close($ch);

    if ($response_info['http_code'] == 200) { //Success
    //Decode the response
    $xml = json_decode($result);
    $id = $xml->id;
    $twitrpURL = $xml->url;
    $text = $xml->text;
    $message = trim($text) . " " . $twitrpURL;

    //Send the user's message to twitter
    $request = API_URL.'statuses/update.json';

    $post_data = array('source' => 'dabr', 'status' => $message);
    $status = twitter_process($request, $post_data);

    //Back to the timeline
    twitter_refresh("twitrp/confirm/$id");
    }
    else {
    $content = "Twitrp upload failed. No idea why!";
    $content .= "";
    $xml = json_decode($result);
    $content .= "message " . urlencode($_POST['message']);
    $content .= "xml " . print_r($xml);
    $content .= "Response " . print_r($response_info);
    $content .= "header " . print_r($header);
    $content .= "media_data " . print_r($media_data);
    $content .= "URL was " . $twitrpURL;
    $content .= "File uploaded was " . $_FILES['media']['tmp_name'];
    $content .= "";
    }
    }
    elseif ($query[1] == 'confirm') {
    $content = "Upload success. Image posted to Twitter.";
    }
    else {
    $content = "Image Message (optional):110";
    $content .= js_counter("message", "110");
    }

    return theme('page', 'Twitrp Upload', $content);
    }
    }

  8. This is what I'm getting:

    Array ( [url] => http://twitrp.com/drippic2/upload [content_type] => text/xml [http_code] => 200 [header_size] => 758 [request_size] => 652 [filetime] => -1 [ssl_verify_result] => 0 [redirect_count] => 0 [total_time] => 1.853142 [namelookup_time] => 0.004088 [connect_time] => 0.004235 [pretransfer_time] => 0.004239 [size_upload] => 88922 [size_download] => 129 [speed_download] => 69 [speed_upload] => 47984 [download_content_length] => 0 [upload_content_length] => 88922 [starttransfer_time] => 0.007135 [redirect_time] => 0 ) Array ( [0] => X-Auth-Service-Provider: https://api.twitter.com/1/account/verify_credentials.json [1] => X-Verify-Credentials-Authorization: OAuth realm="http://api.twitter.com/", oauth_consumer_key="oYZMHa63wTaotuDlWA7A", oauth_signature_method="HMAC-SHA1", oauth_token="34570110-ae12Ki4vGKDog7kfsKJFSUitPb4lVxzLqlxobeKc", oauth_timestamp="1301565933", oauth_nonce="68c3b2306886d8e5c44e4f9e28da55df", oauth_version="1.0", oauth_signature="vDciGS4MpaNMT%2BQBqcbky1AH%2B8I%3D" ) Array ( [media] => @/var/www/clients/client1/web55/tmp/phpAliKL0 [message] => Hond )
    Warning: Cannot modify header information - headers already sent by (output started at /var/www/clients/client1/web55/web/common/twitter.php:439) in /var/www/clients/client1/web55/web/common/theme.php on line 143
    rudolfpi | home | replies | directs | search | favourites | followers | friends | twitpic | twitrp | trends | lists | settings | about | logout
    Twitrp upload failed. No idea why!

    message Hond
    xml 1
    Response 1
    header 1
    media_data 1
    URL was http://twitrp.com/drippic2/upload
    File uploaded was /var/www/clients/client1/web55/tmp/phpAliKL0

  9. I'm getting the error:

    {“errors”:[{"code":400,"message":"Bad Request. Media not included in POST payload."}]}

    Are there any limitations on matching up keys between Twitter and TwitPic, or on what tokens and token secrets can be used on behalf of the user?

  10. Thanks very much for this Terence, it works.

    However, when I use the uploadAndPost.Json , the image is uploaded but no tweet is generated towards Twitter.

    Do you have any experience with this method ?

  11. Here's the code for twic.li

    function twitter_uploadpicture_page($query) {
    if (user_type() == 'oauth') {
    //Has the user submitted an image and message?
    if ($_POST['message']) {
    $messages = stripslashes($_POST['message']);
    $serviceURL = 'http://twic.li/photos/upload.json';

    //Set the initial headers
    $header = array(
    'X-Auth-Service-Provider: https://api.twitter.com/1/account/verify_credentials.json',
    'X-Verify-Credentials-Authorization: OAuth realm="http://api.twitter.com/"'
    );

    //Using Abraham's OAuth library
    require_once('OAuth.php');

    // instantiating OAuth customer
    $consumer = new OAuthConsumer(OAUTH_CONSUMER_KEY, OAUTH_CONSUMER_SECRET);

    // instantiating signer
    $sha1_method = new OAuthSignatureMethod_HMAC_SHA1();

    // user's token
    list($oauth_token, $oauth_token_secret) = explode('|', $GLOBALS['user']['password']);
    $token = new OAuthConsumer($oauth_token, $oauth_token_secret);

    // Generate all the OAuth parameters needed
    $signingURL = 'https://api.twitter.com/1/account/verify_credentials.json';
    $request = OAuthRequest::from_consumer_and_token($consumer, $token, 'GET', $signingURL, array());
    $request->sign_request($sha1_method, $consumer, $token);

    $header[1] .= ", oauth_consumer_key="" . $request->get_parameter('oauth_consumer_key') .""";
    $header[1] .= ", oauth_signature_method="" . $request->get_parameter('oauth_signature_method') .""";
    $header[1] .= ", oauth_token="" . $request->get_parameter('oauth_token') .""";
    $header[1] .= ", oauth_timestamp="" . $request->get_parameter('oauth_timestamp') .""";
    $header[1] .= ", oauth_nonce="" . $request->get_parameter('oauth_nonce') .""";
    $header[1] .= ", oauth_version="" . $request->get_parameter('oauth_version') .""";
    $header[1] .= ", oauth_signature="" . urlencode($request->get_parameter('oauth_signature')) .""";

    //open connection
    $ch = curl_init();

    //Set paramaters
    curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);

    //set the url, number of POST vars, POST data
    curl_setopt($ch,CURLOPT_URL,$serviceURL);

    $media_data = array(
    'media' => '@'.$_FILES['media']['tmp_name'],
    'message' => ' ' . $messages, //A space is needed because twitpic b0rks if first char is an @
    'key'=>TWICLI_API_KEY
    );

    curl_setopt($ch, CURLOPT_POST, true);
    curl_setopt($ch,CURLOPT_POSTFIELDS,$media_data);

    //execute post
    $result = curl_exec($ch);
    $response_info=curl_getinfo($ch);

    //close connection
    curl_close($ch);

    if ($response_info['http_code'] == 200) { //Success
    //Decode the response
    $json = json_decode($result);
    $id = $json->slug;
    $imgurl = $json->mediaurl;
    $message = trim($messages) . " " . $imgurl;
    //Send the user's message to twitter
    $request = API_URL.'statuses/update.json';

    $post_data = array('source' => 'Rupi', 'status' => $message);
    $status = twitter_process($request, $post_data);

    //Back to the timeline
    twitter_refresh("upload picture/confirm/$id");
    }
    else {
    $content = "twic.li upload failed. No idea why!";
    $content .= "";
    $json = json_decode($result);
    $content .= "message " . urlencode($_POST['message']);
    $content .= "json " . print_r($json);
    $content .= "Response " . print_r($response_info);
    $content .= "header " . print_r($header);
    $content .= "media_data " . print_r($media_data);
    $content .= "URL was " . $serviceURL;
    $content .= "File uploaded was " . $_FILES['media']['tmp_name'];
    $content .= "";
    }
    }
    elseif ($query[1] == 'confirm') {
    // because the thumbnail is big enough, I think better using the Sencha.io again. :)
    $content = "Upload success. Image posted to Twitter.
    ";
    }
    else {
    $content = "Image Message (optional):110";
    $content .= js_counter("message", "110");
    }

    return theme('page', 'Twic.li Upload', $content);
    }
    }

Leave a Reply

Your email address will not be published. Required fields are marked *

%d bloggers like this: