Upload Images using PHP to Gallery (sockets, gallery_remote)

chuckg

Joined: 2003-04-21
Posts: 3
Posted: Tue, 2004-02-24 07:57

[Edit] I didn't make it clear enough that I have indeed run into an error in using the gallery_remote2.php "api", as it were. This whole post breaks down the entire process, in hopes that people can help me optimize the code or point out the breakdown along any step of the process.

First, I think it's necessary for me to state my mission. It is my foremost wish to integrate image uploads when posting in Wordpress to uploading images in Gallery. For those of you who aren't familiar with WordPress, it is a blog management program.

The goal is to be able to simply login to my Wordpress blog as normal, write my current entry, and if I like add an image (from wordpress) to a specified album in Gallery. Naturally, I would not want a fullsize image posted in my blog, so I am hoping to utilize the automatic thumbnails created in gallery in my blog then link to the fullsize image later on.

In order to acomplish all of the things that I wished without having to login to the Gallery separately and add the images manually then return to my wordpress, I turned to the gallery_remote2.php and it's functionality. I am fully aware that it does not allow you to connect to it directly through a browser and only accepts incoming socket connections, so that is exactly the approach I took using PHP.

Naturally, I was going to need some variables for use later on when dealing with gallery_remote2.php and the usernames/passwords it would require. I would also later need to know the webserver, path to gallery and gallery_remote2.php. These were all neessary so I could pass the correct HTTP headers. I set these up in the following manner:

$gallery_host = "cirkeljerk.blindmuse.com"; // Name of your website
$gallery_path = "/gallery";
$gallery_remote_file = $gallery_path ."/gallery_remote2.php"; // Path to your gallery_remote2.php (this is the default)
$gallery_user = "blog";	// Username to login to the gallery
$gallery_password = "test"; // Password used
$gallery_protocol = '2.1'; // Don't touch this please.

$max_file_size = "1000";

//Where you temporarily want to store 
$tmp_dir = "/tmp";

// Used for no real purpose....
$referer = "www.blindmuse.com";

// The default album you want to add the pictures to.
$gallery_album = "blog";

// Allowed files types, using jpg for example in this script.
$allowed_types[0] = "jpg";

The first step is getting the file on the server for handling later. What I thought the easiest way to handle this was to have the file upload first to a temporary location through the usual PHP form submission, ie: /tmp on a linux server that is writable by every user. Once it is there, I can then handle it with the gallery remote "API" and have it moved to the gallery itself. I accomplished in the usual fashion (I've commented everything so that we can read what's going on):

if (!empty($HTTP_POST_VARS)) { // The user just posted the form, lets try to handle it
      $img1_name = $HTTP_POST_FILES['img1']['name'];
	$img1_type = $HTTP_POST_FILES['img1']['type'];
	$imgdesc = str_replace('"', '"', $HTTP_POST_VARS['imgdesc']);

	$imgtype = explode(".",$img1_name);	// Find out what the extension of the file is.
	$imgtype = $imgtype[count($imgtype)-1];

	if (in_array($imgtype, $allowed_types) == false) {	// Is the file extension within out set $allowed_types (an array of gif, jpg, etc)
	    die("File $img1_name of type $imgtype is not allowed.");
	}

	$pathtofile = $tmp_dir."/".$img1_name;	// Where we want to move the image
	$img1 = $HTTP_POST_FILES['img1']['tmp_name'];	// The temporary name php used when it was uploaded
	
	@$moved = move_uploaded_file($img1, $pathtofile);  // Move the file to our $pathtofile, which uses global variables set in a "conf section"
	// move_uploaded_file() can fail if open_basedir in PHP.INI doesn't
	// include your tmp directory. Try copy instead?
	if(!moved) {	// Was the file moved? If not, try copy
		$moved = copy($img1, $pathtofile);
	}
	// Still couldn't get it. Give up.
	if (!moved) {  	// The file couldn't get moved, something is up w/ the $tmp_dir and our copying
		die("Couldn't Upload Your File to $pathtofile.");
	} else {
		@unlink($img1);
	}

         // The socket stuff comes after this

This was quite simple, it was simple PHP uploading files and verifying the file was copied correctly. Naturally, the $img1 was the name I gave the file stream in the form before it was submitted. I hope everyone is still with me.

The next step was begin a connection with the host so that I could later pass HTTP requests to gallery_remote2.php. The only possible way to do this was to use sockets, so I began the process by simply making a connection with the host system:
$socket = fsockopen($gallery_host, 80, $errno, $errstr, 120); // Open our socket.

With my connection open with the server, I then had to create HTTP headers to pass to the socket under the specified $gallery_remote_file location, I accomplished this using functions to keep my code from becoming cluttered. There is one function which actually creates the header itself based on an array of information it is passed. The $postValue is an array that contains any number of values. This is necessary when sending commands to gallery_remote2.php in the post format and is the core of how logging in and adding files works (as far as my understanding goes). Here is the function I used to create such headers:

function setup_request($postValue, $getValues = "", $method = "POST") {
	global $gallery_host, $gallery_remote_file;
	
	$request  = "$method $gallery_remote_file$getValues HTTP/1.1\r\n";
	$request .= "Host: $gallery_host\r\n";
	$request .= "Keep-Alive: 300\r\n";
	$request .= "Connection: keep-alive\r\n";
	
	foreach( $postValue AS $name => $value ){
       $postStr .= urlencode( $name ) . "=" . urlencode( $value ) . '&';
   	}
    $postStr = substr( $postStr, 0, -1 );
	
	if ( $method == "POST" ) {
	   $lenght = strlen( $postStr );
	   $request .= "Content-Type: application/x-www-form-urlencoded\r\n";
	   $request .= "Content-Length: $lenght\r\n";
	   $request .= "\r\n";
	   $request .= $postStr;
	}
	
	return $request;
}

To login and add the image, it was necessary for me to pass a number of post values to gallery_remote2.php and the "setup_request()". I did this with functions again, to keep my code from getting cluttered. Again, they simple call global variables and setup an array that is returned when called; they do nothing special. The two functions are as follows:

function setup_login () {
	global $gallery_protocol, $gallery_user, $gallery_password, $HTTP_VARS;
	
	$post = array ( 'protocol_version' => $gallery_protocol,
					'cmd' => 'login',
					'uname' => $gallery_user,
	                		'password' => $gallery_password
					);
	
	return $post;
}

function setup_upload ($pathtofile, $filename, $caption) {
	global $gallery_protocol, $gallery_album;
	$post = array (	'PHPSESSID' => session_id(),
					'protocol_version' => $gallery_protocol,
					'cmd' => 'add-item',
					'set_AlbumName' => $gallery_album,
					'userfile' => $pathtofile,
					'userfile_name' => $filename,
					'caption' => $caption
					);
	
	return $post;
}

The last function that I'll be using is "socket_post()", which simply put, sends the header information all the other functions generate. It is nothing special, here it is:

function socket_post ($socket, $request) {
	fputs( $socket, $request );
}

Now that you understand how I setup all of my post values and requests to the socket, I can go about explaining the process logging in and adding the image to gallery and where in fact the problem arises. So, remember the last thing we'd done in our code was saved the uploaded image to a temporary location for use later. We still can't do anything with it since we haven't connected to gallery yet, so lets go ahead and do that:

$socket = fsockopen($gallery_host, 80, $errno, $errstr, 120);	// Open our socket to the $gallery_host
$setLogin = setup_login();		// Create an array of variables we're going to want to pass
$login = setup_request($setLogin);	// Put the variables into HTTP1.1 format 

socket_post($socket, $login);		// Do the login by posting to our socket @ location $gallery_host.$gallery_remote_file

// I can't read till EOF since it in essence, kills the connection inadvertantly,
// I read in the first 26 lines (which I know contain the success or failure code of login)
$loginResponse = array();
for ($i = 0 ; $i < 26; $i++)  {
	$currLine = "$i = " . fgets ($socket, 1024) . nl;
	$loginResponse[$i] = $currLine;
}

It's important to note that between the step of logging in and sending another command using PHP sockets, the session ID is natively lost. I deal with it in the setup_upload() function passing it the current session_id() and tells gallery that I'm still the same user and not another logging in trying file upload without logging in beforehand. This still, isn't my problem. Lets continue the process:

// Now I check to see if the "successful" text is within the first socket return from gallery_remote2.php ... lets assume it is
// Check to see if the login was successful...
if (eregi("(login successful)", $loginResponse[22])) {
	// Find our login PHPSESSID
	eregi("([0-9a-z]{32})", $loginResponse[4], $temp);
	$sessid = $temp[0];
	
	session_id($sessid);	// Set the session_id() the same used when logging in
	
	// Do our upload 
	$setUpload = setup_upload($pathtofile, $img1_name, $imgdesc);	// Setup the headers that are necessary.
	$upload = setup_request($setUpload); 	// Create the header.

	socket_post($socket, $upload);	// Send header to socket
	
	// **************************** Here lies our error ***********************************

	$uploadResponse = array();		// Take in our response
	for ($i = 0 ; $i < 26; $i++)  {					
		$currLine = "$i = " . fgets ($socket, 1024) . nl;
		$uploadResponse[$i] = $currLine;
	}
} else {
	die ("	Unable to login to gallery_remote.  Check your host and path to the gallery_remote2.php.  It is currently: $gallery_host$gallery_file <br />
			Another possiblity are the username and password you supplied, please double check these as well.");
}

The second socket_post() to add the image is where the problem lies. I receive an error "Fatal error: Call to a member function on a non-object in /var/www/gallery/gallery_remote2.php on line 826". The line that it is specifying is:
foreach ($gallery->album->getExtraFields() as $field) {

I understand this to mean that the $gallery->album class does not yet exist, and therefore I'm going to have a problem adding the image. Yes, all well and said. But why don't I have this class instantiated? We have to look at our gallery/init.php for the answer to this question. The answer to our question is that the class is not being instantiated on line 286:

if (!empty($gallery->session->albumName)) {
	$gallery->album = new Album;
	$ret = $gallery->album->load($gallery->session->albumName);
	if (!$ret) {
		$gallery->session->albumName = "";
	} else {
		if ($gallery->album->versionOutOfDate()) {
			include($GALLERY_BASEDIR . "upgrade_album.php");
			exit;
		}
	}
}

The session variable for albumName is not being passed or set somewhere in my process, and I've yet to figure out yet how to pass it along. This is where I hope those of you who know how to work with gallery_remote2.php and sockets can lend me some suggestions. If you would like a copy of the full file for your own testing, it's available at the following location: http://www.blindmuse.com/wp-gallery/upload-gallery.phps. I didn't bother to cleanup the code, so sorry about that; it is still in the development process, afterall :)

Thanks for taking the time to read through this guys, I appreciate any and all suggestions. Till then, I'll not sleep :)

 
paour
paour's picture

Joined: 2002-08-14
Posts: 1479
Posted: Tue, 2004-02-24 14:37

chuckg, that's a very detailed post, congrats.

Two things you can try:

1. close the socket and open a new one between requests; this makes sure that your implementation doesn't leave the server hanging.

2. it's strange that you have to re-implement an HTTP client yourself; isn't there such support in PHP? Even if there is not, I'm sure there is an open-source PHP HTTP implementation you can use. Your implementation may be flawed.

In any case, you can use a proxy that prints out HTTP requests and responses (or a network analyser) to look at the differences in traffic between GR and your client. There is a simple Java implementation of this called TCPMon (part of Apache).

 
chuckg

Joined: 2003-04-21
Posts: 3
Posted: Wed, 2004-02-25 00:04

paour:

I appreciate the suggestions. I did, in fact, take up your advise in using a prebuilt HTTP class in PHP. I found two things:

1. I actually was passing the headers correctly.
2. The pre-built clients were having the same problems I was passing the same session from request to request.

Those two things aside, it did make my code a bit easier to read, and for that, I thank you. Onto the actual errors I was receiving, in that the class was unsubstantiated ... I figured out why. While reading through some of the code of a perl application (galleryadd.pl), I noticed that it called "fetch-albums" first. This seemed pointless to me, since I didn't really care what albums were on the server. But this is where the gallery_remote substantiates the $gallery->albums class, and therefore was the result of my error.

I'm cleaning up the code now and plan on posting this little gem later. Thanks!