2
// Mono.Tabblo.Connection
5
// Wojciech Dzierzanowski (wojciech.dzierzanowski@gmail.com)
7
// (C) Copyright 2008 Wojciech Dzierzanowski
10
// Permission is hereby granted, free of charge, to any person obtaining
11
// a copy of this software and associated documentation files (the
12
// "Software"), to deal in the Software without restriction, including
13
// without limitation the rights to use, copy, modify, merge, publish,
14
// distribute, sublicense, and/or sell copies of the Software, and to
15
// permit persons to whom the Software is furnished to do so, subject to
16
// the following conditions:
18
// The above copyright notice and this permission notice shall be
19
// included in all copies or substantial portions of the Software.
21
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
22
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
23
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
24
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
25
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
26
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
27
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
33
using System.Diagnostics;
40
namespace Mono.Tabblo {
42
public class Connection {
44
private const string LoginUrl =
45
"https://store.tabblo.com:443/studio/authtoken";
46
private const string AuthorizeUrl = "https://store.tabblo.com"
47
+ ":443/studio/upload/getposturl";
48
private const string RedirUrl = "http://www.tabblo.com/studio"
49
+ "/token/{0}/?url=/studio"
50
+ "/report_upload_session";
52
private readonly IPreferences preferences;
54
private string auth_token = null;
55
private string session_upload_url = null;
57
private CookieCollection cookies;
60
public Connection (IPreferences preferences)
62
if (null == preferences) {
63
throw new ArgumentNullException ("preferences");
65
this.preferences = preferences;
66
this.cookies = new CookieCollection ();
70
public void UploadFile (string name, Stream data_stream,
71
string mime_type, string [,] arguments)
73
if (!IsAuthenticated ()) {
77
Log.DebugFormat ("Uploading " + mime_type + " file " + name);
78
DoUploadFile (name, data_stream, mime_type, arguments);
82
private void DoUploadFile (string name, Stream data_stream,
86
string upload_url = GetUploadUrl (arguments);
87
HttpWebRequest http_request = CreateHttpRequest (
88
upload_url, "POST", true);
89
MultipartRequest request =
90
new MultipartRequest (http_request);
92
MemoryStream mem_stream = null;
93
if (null != UploadProgressHandler) {
94
// "Manual buffering" using a MemoryStream.
95
request.Request.AllowWriteStreamBuffering =
97
mem_stream = new MemoryStream ();
98
request.OutputStream = mem_stream;
101
request.BeginPart (true);
102
request.AddHeader ("Content-Disposition",
103
"form-data; name=\"filename0\"; "
104
+ "filename=\"" + name
107
request.AddHeader ("Content-Type", mime_type, true);
109
byte [] data_buffer = new byte [8192];
111
while ((read_count = data_stream.Read (
112
data_buffer, 0, data_buffer.Length))
114
request.WritePartialContent (
115
data_buffer, 0, read_count);
117
request.EndPartialContent ();
118
request.EndPart (true);
120
if (null != UploadProgressHandler) {
122
int total = (int) request.OutputStream.Length;
123
request.Request.ContentLength = total;
125
string progress_title = String.Format (
126
Catalog.GetString ("Uploading "
131
using (Stream request_stream = request.Request
132
.GetRequestStream ()) {
134
mem_stream.GetBuffer ();
136
for (int offset = 0; offset < total;
137
offset += write_count) {
141
write_count = System.Math.Min (
144
request_stream.Write (buffer,
148
FireUploadProgress (progress_title,
153
SendRequest ("upload", request.Request, true);
157
public event UploadProgressEventHandler UploadProgressHandler;
159
private void FireUploadProgress (string title, int sent,
162
if (null != UploadProgressHandler) {
163
UploadProgressEventArgs args =
164
new UploadProgressEventArgs (
167
UploadProgressHandler (this, args);
172
private bool IsAuthenticated ()
174
return null != auth_token;
178
private void Login ()
180
FireUploadProgress (Catalog.GetString (
181
"Logging into Tabblo"),
186
HttpWebRequest request = CreateHttpRequest (
188
request.ContentType =
189
"application/x-www-form-urlencoded";
191
string [,] arguments = {
192
{"username", preferences.Username},
193
{"password", preferences.Password}
197
WriteRequestContent (request,
198
FormatRequestArguments (
200
string response = SendRequest (
202
if ("BAD".Equals (response)) {
203
Log.DebugFormat ("Invalid username or password");
204
throw new TabbloException (
205
"Login failed: Invalid username"
209
auth_token = response;
211
} catch (TabbloException e) {
212
// Here's us trying to produce a more
213
// descriptive message when we have... trust
214
// issues. This doesn't work, though, at least
215
// as long as Mono bug #346635 is not fixed.
217
// TODO: When it _starts_ to work, we should
218
// think about doing the same for
220
WebException we = e.InnerException
223
Log.DebugFormat ("Caught a WebException, status=" + we.Status);
225
&& WebExceptionStatus.TrustFailure
227
throw new TabbloException (
228
"Trust failure", we);
233
if (null != auth_token)
234
Log.DebugFormat ("Login successful. Token: " + auth_token);
238
private string GetUploadUrl (string [,] arguments)
240
FireUploadProgress (Catalog.GetString (
241
"Obtaining URL for upload"),
244
if (! IsAuthenticated ())
245
Log.DebugFormat ("Not authenticated");
247
if (null == session_upload_url) {
249
string [,] auth_arguments =
250
{ {"auth_token", auth_token} };
251
string url = AuthorizeUrl + "/?"
252
+ FormatRequestArguments (
255
HttpWebRequest request =
256
CreateHttpRequest (url, "GET");
258
string response = SendRequest (
259
"getposturl", request);
261
if (response.StartsWith ("@")) {
263
response.Substring (1);
265
throw new TabbloException (
266
"Session upload URL "
267
+ "retrieval failed");
271
string upload_url = session_upload_url;
272
upload_url += "&redir=" + String.Format (
273
RedirUrl, auth_token);
274
if (null != arguments && arguments.GetLength (0) > 0) {
275
upload_url += '&' + FormatRequestArguments (
279
Log.DebugFormat ("Upload URL: " + upload_url);
284
private HttpWebRequest CreateHttpRequest (string url,
287
return CreateHttpRequest (url, method, false);
290
private HttpWebRequest CreateHttpRequest (string url,
294
HttpWebRequest request = (HttpWebRequest)
295
WebRequest.Create (url);
296
// For some reason, POST requests are _really_ slow with
298
request.ProtocolVersion = HttpVersion.Version10;
299
request.Method = method;
301
HandleRequestCookies (request);
307
private void HandleRequestCookies (HttpWebRequest request)
309
request.CookieContainer = new CookieContainer ();
310
// Instead of just doing a
311
// `request.CookieContainer.Add(cookies)', add cookies
312
// mannually to work around the fact that some cookies
313
// are not properly formatted as they are received from
315
foreach (Cookie c in cookies) {
316
Cookie new_cookie = new Cookie (c.Name, c.Value,
318
request.CookieContainer.Add (new_cookie);
321
string cookie_header = request.CookieContainer
322
.GetCookieHeader (request.RequestUri);
323
if (cookie_header.Length > 0)
324
Log.DebugFormat ("Cookie: " + cookie_header);
328
private static void WriteRequestContent (HttpWebRequest request,
331
byte [] content_bytes =
332
Encoding.UTF8.GetBytes (content);
334
request.ContentLength = content_bytes.Length;
337
using (Stream request_stream =
338
request.GetRequestStream ()) {
339
request_stream.Write (content_bytes, 0,
340
content_bytes.Length);
342
} catch (WebException e) {
344
throw new TabbloException (
345
"HTTP request failure: "
350
char [] content_chars = new char [content_bytes.Length];
351
content_bytes.CopyTo (content_chars, 0);
352
Log.DebugFormat ("Request content: " + new string (content_chars));
356
private static string FormatRequestArguments (
357
string [,] arguments)
359
StringBuilder content = new StringBuilder ();
361
for (int i = 0; i < arguments.GetLength (0); ++i) {
362
content.AppendFormat( "{0}={1}&",
367
if (content.Length > 0) {
368
content.Remove (content.Length - 1, 1);
371
byte [] content_bytes = Encoding.UTF8.GetBytes (
372
content.ToString ());
373
char [] content_chars = new char [content_bytes.Length];
374
content_bytes.CopyTo (content_chars, 0);
376
return new string (content_chars);
380
private string SendRequest (string description,
381
HttpWebRequest request)
383
return SendRequest (description, request, false);
387
/// Sends an HTTP request.
389
/// <param name="description"></param>
390
/// <param name="request"></param>
391
/// <returns>the HTTP response as string</returns>
392
private string SendRequest (string description,
393
HttpWebRequest request,
396
Log.DebugFormat ("Sending " + description + ' ' + request.Method + " request to " + request.Address);
398
HttpWebResponse response = null;
400
response = (HttpWebResponse)
401
request.GetResponse ();
403
cookies.Add (response.Cookies);
404
Log.DebugFormat (response.Cookies.Count + " cookie(s)");
405
foreach (Cookie c in response.Cookies) {
406
Log.DebugFormat ("Set-Cookie: " + c.Name + '=' + c.Value + "; Domain=" + c.Domain + "; expires=" + c.Expires);
409
return GetResponseAsString (response);
410
} catch (WebException e) {
412
HttpWebResponse error_response =
413
e.Response as HttpWebResponse;
414
string response_string = null != error_response
415
? GetResponseAsString (
418
throw new TabbloException (description
423
if (null != response) {
430
private static string GetResponseAsString (HttpWebResponse response)
432
Log.DebugFormat ("Response: ");
434
Encoding encoding = Encoding.UTF8;
435
if (response.ContentEncoding.Length > 0) {
437
encoding = Encoding.GetEncoding (
440
} catch (ArgumentException) {
441
// Swallow invalid encoding exception
442
// and use the default one.
446
string response_string = null;
448
using (Stream stream = response.GetResponseStream ()) {
449
StreamReader reader = new StreamReader (
451
response_string = reader.ReadToEnd ();
455
if (null != response_string)
457
Log.DebugFormat (response_string);
458
} catch (System.FormatException e) {
459
Log.DebugFormat ("Unable to print respose string: not in correct format");
461
return response_string;