5
* Thomas Van Machelen <thomas.vanmachelen@gmail.com>
7
* Copyright (C) 2006 Thomas Van Machelen
8
* This is free software. See COPYING for details.
13
using System.Collections.Generic;
17
using System.Text.RegularExpressions;
19
using System.Collections.Specialized;
23
public struct Credentials
25
public string session_id;
27
public string password_hash;
29
public string SessionID {
30
get { return session_id; }
31
set { session_id = value; }
35
get { return user_id; }
38
public string PasswordHash {
39
get { return password_hash; }
42
public Credentials(string session_id, int user_id, string password_hash)
44
this.session_id = session_id;
45
this.user_id = user_id;
46
this.password_hash = password_hash;
50
public struct Category
52
public Category( string title, int id)
55
this.category_id = id;
58
private int category_id;
61
get { return category_id; }
62
set { category_id = value; }
69
set { title = value; }
75
public Album(string title, int id)
84
get { return album_id; }
85
set { album_id = value; }
92
set { title = value; }
96
public class SmugMugApi
98
private string username = String.Empty;
99
private string password = String.Empty;
100
private bool connected = false;
102
private Credentials credentials;
103
private const string VERSION = "1.1.1";
104
private Category[] categories;
106
public bool Connected
108
get { return connected; }
111
public SmugMugApi (string email_address, string password)
113
this.username = email_address;
114
this.password = password;
119
if (this.username.Length == 0 | this.password.Length == 0)
121
throw new SmugMugException("There is no username or password.");
124
if (this.connected == false && this.credentials.UserID == 0)
128
this.credentials = SmugMugProxy.LoginWithPassword (this.username, this.password);
129
this.connected = true;
144
private void LoginWithHash ()
147
string session_id = SmugMugProxy.LoginWithHash (this.credentials.UserID, this.credentials.PasswordHash);
149
if (session_id != null && session_id.Length > 0)
151
this.credentials.SessionID = session_id;
155
throw new SmugMugException ("SessionID was empty");
158
catch (Exception ex) {
159
throw new SmugMugException ("A login error occured, SessionID may be invalid.", ex.InnerException);
163
public void Logout ()
168
if (this.credentials.SessionID == null && this.credentials.SessionID.Length == 0)
171
SmugMugProxy.Logout (this.credentials.SessionID);
173
this.credentials = new Credentials (null, 0, null);
176
public Category[] GetCategories ()
178
if (this.categories == null)
181
this.categories = SmugMugProxy.GetCategories (credentials.SessionID);
183
catch (Exception ex) {
184
throw new SmugMugException ("Could not retrieve Categories", ex.InnerException);
187
return this.categories;
190
public Album CreateAlbum (string title, int category_id, bool is_public)
193
return SmugMugProxy.CreateAlbum (title, category_id, credentials.SessionID, is_public);
195
catch (Exception ex) {
196
throw new SmugMugException ("Could not create album", ex.InnerException);
200
public Album[] GetAlbums ()
203
return SmugMugProxy.GetAlbums(credentials.SessionID);
205
catch (Exception ex) {
206
throw new SmugMugException ("Could not get albums", ex.InnerException);
210
public Uri GetAlbumUrl (int image_id)
213
return SmugMugProxy.GetAlbumUrl (image_id, credentials.SessionID);
215
catch (Exception ex) {
216
throw new SmugMugException ("Could not get album url", ex.InnerException);
220
public int Upload (string path, int album_id)
223
return SmugMugProxy.Upload (path, album_id, credentials.SessionID);
225
catch (Exception ex) {
226
throw new SmugMugException ("Could not upload file", ex.InnerException);
231
public class SmugMugProxy
233
// FIXME: this getting should be done over https
234
private const string GET_URL = "https://api.SmugMug.com/hack/rest/";
235
private const string POST_URL = "https://upload.SmugMug.com/hack/rest/";
237
private const string APIKEY = "umtr0zB2wzwTZDhF2BySidg0hY0le3K6";
238
private const string VERSION = "1.1.1";
241
private const string LOGIN_WITHPASS_METHOD = "smugmug.login.withPassword";
242
private const string LOGIN_WITHHASH_METHOD = "smugmug.login.withHash";
243
private const string LOGOUT_METHOD = "smugmug.logout";
244
private const string ALBUMS_CREATE_METHOD = "smugmug.albums.create";
245
private const string ALBUMS_GET_URLS_METHOD = "smugmug.images.getURLs";
246
private const string ALBUMS_GET_METHOD = "smugmug.albums.get";
247
private const string CATEGORIES_GET_METHOD = "smugmug.categories.get";
249
// parameter constants
250
private const string EMAIL = "EmailAddress";
251
private const string PASSWORD = "Password";
252
private const string USER_ID = "UserID";
253
private const string PASSWORD_HASH = "PasswordHash";
254
private const string SESSION_ID = "SessionID";
255
private const string CATEGORY_ID = "CategoryID";
256
private const string IMAGE_ID = "ImageID";
257
private const string TITLE = "Title";
258
private const string ID = "id";
260
public static Credentials LoginWithPassword (string username, string password)
262
string url = FormatGetUrl (LOGIN_WITHPASS_METHOD, new SmugMugParam (EMAIL, username), new SmugMugParam (PASSWORD, password));
263
XmlDocument doc = GetResponseXml (url);
265
string sessionId = doc.SelectSingleNode ("/rsp/Login/SessionID").InnerText;
266
int userId = int.Parse (doc.SelectSingleNode ("/rsp/Login/UserID").InnerText);
267
string passwordHash = doc.SelectSingleNode ("/rsp/Login/PasswordHash").InnerText;
269
return new Credentials (sessionId, userId, passwordHash);
272
public static string LoginWithHash (int user_id, string password_hash)
274
string url = FormatGetUrl (LOGIN_WITHHASH_METHOD, new SmugMugParam (USER_ID, user_id), new SmugMugParam (PASSWORD_HASH, password_hash));
275
XmlDocument doc = GetResponseXml(url);
277
return doc.SelectSingleNode ("/rsp/Login/SessionID").InnerText;
280
public static void Logout (string session_id)
282
string url = FormatGetUrl (LOGOUT_METHOD, new SmugMugParam (SESSION_ID, session_id));
283
GetResponseXml (url);
286
public static Album[] GetAlbums (string session_id)
288
string url = FormatGetUrl (ALBUMS_GET_METHOD, new SmugMugParam(SESSION_ID, session_id));
289
XmlDocument doc = GetResponseXml (url);
290
XmlNodeList albumNodes = doc.SelectNodes ("/rsp/Albums/Album");
292
Album[] albums = new Album[albumNodes.Count];
294
for (int i = 0; i < albumNodes.Count; i++)
296
XmlNode current = albumNodes[i];
297
albums[i] = new Album (current.SelectSingleNode (TITLE).InnerText, int.Parse (current.Attributes[ID].Value));
302
public static Uri GetAlbumUrl (int image_id, string session_id)
304
string url = FormatGetUrl(ALBUMS_GET_URLS_METHOD, new SmugMugParam(IMAGE_ID, image_id), new SmugMugParam(SESSION_ID, session_id));
305
XmlDocument doc = GetResponseXml(url);
307
string album_url = doc.SelectSingleNode("/rsp/ImageURLs/Image/AlbumURL").InnerText;
309
return new Uri(album_url);
312
public static Category[] GetCategories (string session_id)
314
string url = FormatGetUrl(CATEGORIES_GET_METHOD, new SmugMugParam (SESSION_ID, session_id));
315
XmlDocument doc = GetResponseXml (url);
317
XmlNodeList categoryNodes = doc.SelectNodes ("/rsp/Categories/Category");
318
Category[] categories = new Category[categoryNodes.Count];
320
for (int i = 0; i < categoryNodes.Count; i++)
322
XmlNode current = categoryNodes[i];
323
categories[i] = new Category (current.SelectSingleNode (TITLE).InnerText, int.Parse (current.Attributes[ID].Value));
328
public static Album CreateAlbum (string title, int category_id, string session_id)
330
return CreateAlbum (title, category_id, session_id, true);
333
public static Album CreateAlbum (string title, int category_id, string session_id, bool is_public)
335
int public_int = is_public ? 1 : 0;
336
string url = FormatGetUrl (ALBUMS_CREATE_METHOD, new SmugMugParam (TITLE, title), new SmugMugParam (CATEGORY_ID, category_id), new SmugMugParam (SESSION_ID, session_id), new SmugMugParam ("Public", public_int));
337
XmlDocument doc = GetResponseXml (url);
339
int id = int.Parse(doc.SelectSingleNode("/rsp/Create/Album").Attributes[ID].Value);
341
return new Album(title, id);
344
public static int Upload (string path, int album_id, string session_id)
346
FileInfo file = new FileInfo(path);
349
throw new ArgumentException("Image does not exist: " + file.FullName);
353
WebClient client = new WebClient ();
354
client.BaseAddress = "http://upload.smugmug.com";
355
client.Headers.Add ("Cookie:SMSESS=" + session_id);
357
NameValueCollection queryStringCollection = new NameValueCollection ();
358
queryStringCollection.Add ("AlbumID", album_id.ToString());
359
// Temporarily disabled because rest doesn't seem to return the ImageID anymore
360
// queryStringCollection.Add ("ResponseType", "REST");
361
// luckily JSON still holds it
362
queryStringCollection.Add ("ResponseType", "JSON");
363
client.QueryString = queryStringCollection;
365
byte[] responseArray = client.UploadFile ("http://upload.smugmug.com/photos/xmladd.mg", "POST", file.FullName);
366
string response = Encoding.ASCII.GetString (responseArray);
369
Regex id_regex = new Regex ("\\\"id\\\":( )?(?<image_id>\\d+),");
370
Match m = id_regex.Match (response);
375
id = int.Parse (m.Groups["image_id"].Value);
379
// REST approach, disabled for now
380
//XmlDocument doc = new XmlDocument ();
381
//doc.LoadXml (response);
382
// return int.Parse (doc.SelectSingleNode ("/rsp/ImageID").InnerText);
387
throw new SmugMugUploadException ("Error uploading image: " + file.FullName, ex.InnerException);
391
private static string FormatGetUrl(string method_name, params SmugMugParam[] parameters)
393
StringBuilder builder = new StringBuilder (string.Format ("{0}{1}/?method={2}", GET_URL, VERSION, method_name));
395
foreach (SmugMugParam param in parameters)
396
builder.Append (param.ToString ());
398
builder.Append (new SmugMugParam ("APIKey", APIKEY));
399
return builder.ToString();
402
private static XmlDocument GetResponseXml (string url)
404
HttpWebRequest request = HttpWebRequest.Create (url) as HttpWebRequest;
405
request.Credentials = CredentialCache.DefaultCredentials;
406
WebResponse response = request.GetResponse ();
408
XmlDocument doc = new XmlDocument ();
409
doc.LoadXml (new StreamReader (response.GetResponseStream ()).ReadToEnd ());
410
CheckResponseXml (doc);
416
private static void CheckResponseXml (XmlDocument doc)
418
if (doc.SelectSingleNode("/rsp").Attributes["stat"].Value == "ok")
421
string message = doc.SelectSingleNode ("/rsp/err").Attributes["msg"].Value;
422
throw new SmugMugException (message);
425
private class SmugMugParam
430
public SmugMugParam (string name, object value)
446
public override string ToString()
448
return string.Format("&{0}={1}", Name, Value);
453
public class SmugMugException : ApplicationException
455
public SmugMugException(string message) : base (message)
457
Console.WriteLine (message);
460
public SmugMugException (string message, Exception innerException) : base (message, innerException)
462
Console.WriteLine (message, innerException.ToString());
466
public sealed class SmugMugUploadException : ApplicationException
468
public SmugMugUploadException (string message, Exception innerException) : base (message, innerException)
470
Console.WriteLine (message, innerException.ToString ());