1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.ninjasoft.igallery.logic;
18
19 import java.util.*;
20 import java.net.*;
21 import java.io.*;
22
23 import org.ninjasoft.igallery.exceptions.*;
24 import org.ninjasoft.igallery.data.*;
25
26 /***
27 * Class to wrap a gallery connection
28 * @author enigma
29 */
30 public class GalleryConnection {
31 private StatusListener listener = null;
32 private Cookiejar cookiejar = new Cookiejar();
33 private URL url;
34 public static boolean DEBUG = false;
35
36 public GalleryConnection(String url) throws MalformedURLException {
37 this(url, null);
38 }
39
40 public GalleryConnection(String url, StatusListener listener) throws MalformedURLException {
41 this.listener = listener;
42 this.url = new URL(url);
43 if (!this.url.getProtocol().equals("http"))
44 throw new MalformedURLException("This application only supports standard http:// connections");
45 }
46
47 /***
48 * Login with the given username and password. The login information is then cached
49 * for the remainder of this object's usage. If successful, return true. If fail,
50 * throw exception. (This function actually never returns a false, only throws an
51 * exception.)
52 * @param username
53 * @param password
54 * @return
55 * @throws IOException
56 * @throws GalleryException
57 */
58 public boolean login(String username, String password) throws IOException, GalleryException {
59 Properties parameters = new Properties();
60 parameters.put("cmd", "login");
61 parameters.put("protocol_version", "2.0");
62 parameters.put("uname", username);
63 parameters.put("password", password);
64 Properties returnData = runCommand(parameters);
65 checkResponseCode(returnData);
66
67 return true;
68 }
69
70 /***
71 * Return a list of albums available to the user.
72 * @return Hashtable with name=String of album name, value=Album object
73 * @throws IOException
74 * @throws GalleryException
75 */
76 public Hashtable getAlbumList() throws IOException, GalleryException {
77 Properties parameters = new Properties();
78 parameters.put("cmd", "fetch-albums-prune");
79 parameters.put("protocol_version", "2.2");
80 parameters.put("check_writable", "no");
81 Properties returnData = runCommand(parameters);
82 checkResponseCode(returnData);
83 return parseAlbumList(returnData);
84 }
85
86 /***
87 * Given the server response for fetch-albums-prune, return a set of
88 * album beans. This is returned in a Hashtable with the name
89 * as the key and the bean as the object (this is so that the parent
90 * album of nested albums can be determined because the parent
91 * field is returned as a refnumber)
92 * @param data
93 * @return
94 */
95 private Hashtable parseAlbumList(Properties data) {
96 Hashtable result = new Hashtable();
97 int albumCount = 0;
98 try{
99 albumCount = Integer.parseInt(data.getProperty("album_count", "0"));
100 }catch(Exception e){}
101
102 for (int i=1; i<=albumCount; i++) {
103 GalleryAlbum album = new GalleryAlbum();
104 album.setRefNumber(i);
105 album.setName(data.getProperty("album.name." + i, ""));
106 album.setTitle(data.getProperty("album.title." + i, ""));
107 album.setSummary(data.getProperty("album.summary." + i, ""));
108 album.setParent(data.getProperty("album.parent." + i, ""));
109 result.put(album.getName(), album);
110 }
111
112 for (Iterator i = result.values().iterator(); i.hasNext(); ) {
113 GalleryAlbum thisAlbum = (GalleryAlbum) i.next();
114 String parent = "";
115 try{
116 parent = thisAlbum.getParent();
117 }catch(Exception e){}
118 if (parent != null)
119 thisAlbum.setParentAlbum((GalleryAlbum) result.get(parent));
120 }
121 return result;
122 }
123
124 public void uploadPhoto(GalleryAlbum album, File image, String name, String caption) throws GalleryException, IOException {
125 Properties parameters = new Properties();
126 parameters.put("cmd", "add-item");
127 parameters.put("protocol_version", "2.0");
128 parameters.put("set_albumName", album.getName());
129 parameters.put("userfile_name", name);
130 parameters.put("userfile", fileContent(image));
131 parameters.put("force_filename", name);
132 if ((caption != null) && (caption.length() > 0))
133 parameters.put("caption", caption);
134 Properties returnData = runCommand(parameters);
135 checkResponseCode(returnData);
136 }
137
138 /***
139 * Given a file, return the encoded file contents.
140 * @param f
141 * @return
142 * @throws IOException
143 */
144 public String encodeFileContent(File f) throws IOException {
145 StringBuffer result = new StringBuffer();
146 FileInputStream in = new FileInputStream(f);
147 int c;
148 while ((c = in.read()) != -1) {
149 char ch = (char) c;
150 if ( ((ch >= '0') && (ch <= '9')) ||
151 ((ch >= 'a') && (ch <= 'z')) ||
152 ((ch >= 'A') && (ch <= 'Z'))
153 ) {
154 result.append(ch);
155 } else {
156 result.append("%");
157 String code = Integer.toString(c, 16);
158 if (code.length() == 1)
159 result.append("0");
160 result.append(code);
161 }
162 }
163 in.close();
164 return result.toString();
165 }
166
167 /***
168 * Given a file, return the file contents.
169 * @param f
170 * @return
171 * @throws IOException
172 */
173 public byte[] fileContent(File f) throws IOException {
174 ByteArrayOutputStream result = new ByteArrayOutputStream();
175 FileInputStream in = new FileInputStream(f);
176 byte[] buff = new byte[1024];
177 int len;
178 while ((len = in.read(buff)) > 0)
179 result.write(buff, 0, len);
180 in.close();
181 result.close();
182 return result.toByteArray();
183 }
184
185 /***
186 * Given an album, return a list of pictures in that album.
187 * @param album
188 * @return Hashtable with name=String photo name, value=Image object
189 * @throws IOException
190 * @throws GalleryException
191 */
192 public Hashtable getPhotoList(GalleryAlbum album) throws IOException, GalleryException {
193 Properties parameters = new Properties();
194 parameters.put("cmd", "fetch-album-images");
195 parameters.put("protocol_version", "2.4");
196 parameters.put("set_albumName", album.getName());
197 parameters.put("albums_too", "no");
198 Properties returnData = runCommand(parameters);
199 checkResponseCode(returnData);
200 return parseImageList(returnData);
201 }
202
203 /***
204 * Given the server response for fetch-album-images, return a set of
205 * image beans. This is returned in a Hashtable with the name
206 * as the key and the bean as the object.
207 * @param data
208 * @return
209 */
210 private Hashtable parseImageList(Properties data) {
211 Hashtable result = new Hashtable();
212 int albumCount = 0;
213 try{
214 albumCount = Integer.parseInt(data.getProperty("image_count", "0"));
215 }catch(Exception e){}
216
217 for (int i=1; i<=albumCount; i++) {
218 GalleryPicture image= new GalleryPicture();
219 image.setName(data.getProperty("image.name." + i, ""));
220 image.setCaption(data.getProperty("image.caption." + i, ""));
221 result.put(image.getName(), image);
222 }
223 return result;
224 }
225
226 /***
227 * Ensure that the response code is present and equal to zero. Throw the proper
228 * exception if it is an error code.
229 * @param data
230 * @throws GalleryException
231 */
232 private void checkResponseCode(Properties data) throws GalleryException {
233 String codeStr = data.getProperty("status", "-1");
234 String message = data.getProperty("status_text", "");
235 int code = -1;
236 try{
237 code = Integer.parseInt(codeStr);
238 }catch(Exception e){}
239 if (DEBUG)
240 sendStatus("Got from server: " + code + " (" + message + ")");
241 if (code == -1)
242 throw new GalleryException(-1, "Could not find status in data");
243 if (code == 0)
244 return;
245 switch(code) {
246 case 101:
247 case 102:
248 case 103:
249 case 104:
250 throw new InvalidVersionException(code, message);
251 case 201:
252 case 202:
253 throw new InvalidLoginException(code, message);
254 case 301:
255 throw new UnknownCommandException(code, message);
256 case 401:
257 case 404:
258 case 501:
259 throw new InvalidPermissionException(code, message);
260 case 402:
261 throw new NoFilenameException(code, message);
262 case 403:
263 throw new UploadFailException(code, message);
264 case 502:
265 throw new CreateFailedException(code, message);
266 default:
267 throw new GalleryException(code, message);
268 }
269 }
270
271 /***
272 * Given a bunch of name/value pairs, send them to the server and get a set of
273 * name value pairs back.
274 * @param parameters
275 * @return
276 * @throws IOException
277 * @throws GalleryException
278 */
279 private Properties runCommand(Properties parameters) throws IOException, GalleryException {
280 byte[] queryString = null;
281 HttpURLConnection connection = (HttpURLConnection) this.url.openConnection();
282 if (!parameters.getProperty("cmd", "").equals("add-item")) {
283 queryString = buildQueryString(parameters).getBytes();
284 } else {
285 String boundary = "----iGallery-multipart-boundary----";
286 connection.addRequestProperty("Content-type", "multipart/form-data;.boundary=" + boundary);
287 queryString = buildMultipart(parameters, boundary);
288 }
289 int len = queryString.length;
290 connection.setDoOutput(true);
291 connection.setDoInput(true);
292 cookiejar.setCookies(connection);
293 connection.addRequestProperty("Content-length", Integer.toString(len));
294 OutputStream os = connection.getOutputStream();
295 os.write(queryString, 0, len);
296 os.close();
297 connection.connect();
298 if (connection.getResponseCode() != 200) {
299 throw new WebServerException(-1, "Invalid response from webserver: expected 200, got " +
300 connection.getResponseCode() + " (" +
301 connection.getResponseMessage() + ")");
302 }
303 InputStream in = connection.getInputStream();
304 Properties results = parseResults(in);
305 cookiejar.grabCookies(connection);
306 return results;
307 }
308
309 /***
310 * Given a server response, parse it into a Properties object
311 * @param in
312 * @return
313 * @throws IOException
314 */
315 private Properties parseResults(InputStream in) throws IOException {
316 Properties results = new Properties();
317
318 if (DEBUG) {
319 ByteArrayOutputStream out = new ByteArrayOutputStream();
320 byte buff[] = new byte[1024];
321 int len;
322 while ((len = in.read(buff)) > 0)
323 out.write(buff, 0, len);
324 out.close();
325 if (DEBUG) {
326 sendStatus("Got the following data from the server:");
327 sendStatus(out.toString());
328 }
329 in = new ByteArrayInputStream(out.toByteArray());
330 }
331
332 results.load(in);
333 return results;
334 }
335
336 /***
337 * Given a Properties object of data to send to the server, turn it into
338 * POST data.
339 * @param properties
340 * @return
341 */
342 private String buildQueryString(Properties properties) throws UnsupportedEncodingException {
343 StringBuffer result = new StringBuffer();
344 for (Iterator i = properties.keySet().iterator(); i.hasNext(); ) {
345 String key = (String) i.next();
346 String value = properties.getProperty(key, "");
347 result.append(URLEncoder.encode(key, "UTF-8"));
348 result.append("=");
349 result.append(URLEncoder.encode(value, "UTF-8"));
350 result.append("&");
351 }
352 return result.toString();
353 }
354
355 private byte[] buildMultipart(Properties properties, String boundary) throws IOException {
356 ByteArrayOutputStream result = new ByteArrayOutputStream();
357 for (Iterator i = properties.keySet().iterator(); i.hasNext(); ) {
358 String key = (String) i.next();
359 Object value = properties.get(key);
360 result.write("--".getBytes());
361 result.write(boundary.getBytes());
362 result.write("\r\n".getBytes());
363 result.write("Content-Disposition: form-data; name=\"".getBytes());
364 result.write(key.getBytes());
365 result.write("\"".getBytes());
366 if (value instanceof byte[]) {
367 String filename = properties.getProperty("force_filename", "");
368 result.write("; filename=\"".getBytes());
369 result.write(filename.getBytes());
370 result.write("\"".getBytes());
371
372
373
374
375
376
377
378
379 }
380 result.write("\r\n\r\n".getBytes());
381 if (value instanceof String)
382 result.write(((String) value).getBytes());
383 else if (value instanceof byte[])
384 result.write((byte[]) value);
385 result.write("\r\n".getBytes());
386 }
387 result.write("--".getBytes());
388 result.write(boundary.getBytes());
389 result.write("--\r\n".getBytes());
390 return result.toByteArray();
391 }
392
393 /***
394 * Simple unit test
395 * @param argv
396 * @throws Exception
397 */
398 public static void main(String[] argv) throws Exception {
399 GalleryConnection con = new GalleryConnection("http://localhost/gallerytest/gallery_remote2.php");
400 con.login("admin", "test");
401 Hashtable albums = con.getAlbumList();
402 for (Iterator i = albums.values().iterator(); i.hasNext(); ) {
403 GalleryAlbum thisAlbum = (GalleryAlbum) i.next();
404 System.out.println(thisAlbum);
405 }
406 System.out.println("Uploading first photo...");
407 con.uploadPhoto((GalleryAlbum) albums.get("nest2"), new File("/tmp/pics/IMG_1811.JPG"), "Pic1.jpg", "Caption 1");
408 System.out.println("Uploading second photo...");
409 con.uploadPhoto((GalleryAlbum) albums.get("nest2"), new File("/tmp/pics/IMG_1818.JPG"), "Pic2.jpg", "Caption 2");
410 }
411
412 public void setStatusListener(StatusListener l) {this.listener = l;}
413
414 private void sendStatus(String message) {
415 if (listener != null)
416 listener.setStatus(message);
417 }
418 private void sendError(String message) {
419 if (listener != null)
420 listener.setError(message);
421 }
422 }