/// <summary>
	/// Validate the token and get informations.
	/// </summary>
	/// <param name="accessToken">Access token.</param>
	/// <returns>TokenInfoResponse or Exception for error</returns>
	static IEnumerator ValidateToken(string accessToken)
	{
		var request = new UnityWebRequest(
			"https://www.googleapis.com/oauth2/v1/tokeninfo?access_token=" + accessToken);

		var response = request.GetResponse();
		while (!response.isDone)
			yield return null;

		if (response.error != null)
		{
			yield return response.error;
			yield break;
		}

		JsonReader reader = new JsonReader(response.text);
		var json = reader.Deserialize<Dictionary<string, object>>();

		if (json == null)
		{
			yield return new Exception(-1, "TokenInfo response parsing failed.");
			yield break;
		}

		yield return new TokenInfoResponse(json);
	}
	/// <summary>
	/// Revoke a access token.
	/// </summary>
	/// <param name="token">Access token.</param>
	/// <returns>RevokeResponse or Exception for error.</returns>
	static IEnumerator RevokeToken(string token)
	{
		var request = new UnityWebRequest(
			"https://accounts.google.com/o/oauth2/revoke?token=" + token);

		var response = request.GetResponse();
		while (!response.isDone)
			yield return null;

		if (response.error != null)
		{
			yield return response.error;
			yield break;
		}

		JsonReader reader = new JsonReader(response.text);
		var json = reader.Deserialize<Dictionary<string, object>>();

		if (json == null) // no response is success.
			yield return new RevokeResponse(); // error is null.
		else
			yield return new RevokeResponse(json);
	}
		public UnityWebResponse(UnityWebRequest request)
		{
			ThreadPool.QueueUserWorkItem((arg) =>
			{
				try
				{
					int redirection = 0;

					TcpClient client = new TcpClient();
					Uri uri = request.uri;
					client.Connect(uri.Host, uri.Port);
					bool newConnection = true;
					Stream stream = null;

					do
					{
						#region Get stream
						if (newConnection)
						{
							client.SendTimeout = TIMEOUT;
							client.ReceiveTimeout = TIMEOUT;

							stream = client.GetStream();

							if (uri.Scheme == "https")
							{
								stream = new SslStream(stream, false,
									new RemoteCertificateValidationCallback((sender, cert, chain, error) => true));
								(stream as SslStream).AuthenticateAsClient(uri.Host);
							}
						}
						#endregion

						#region Request
						{
							BinaryWriter writer = new BinaryWriter(stream);
							writer.Write(Encoding.UTF8.GetBytes(request.method + " " +
								uri.PathAndQuery + " " + request.protocol + "\r\n"));

							request.headers["Host"] = uri.Host;
							
							foreach (DictionaryEntry kv in request.headers)
							{
								if (kv.Key is string && kv.Value is string)
								{
									writer.Write(Encoding.UTF8.GetBytes(kv.Key + ": " +
											kv.Value + "\r\n"));
								}
								else if (kv.Key is string && kv.Value is string[])
								{
									for (int i = 0; i < (kv.Value as string[]).Length; i++)
									{
										writer.Write(Encoding.UTF8.GetBytes(kv.Key + ": " +
												(kv.Value as string[])[i] + "\r\n"));
									}
								}
							}

							if (request.body != null)
							{
								writer.Write(Encoding.UTF8.GetBytes("Content-Length:" +
									request.body.Length + "\r\n\r\n"));
								writer.Write(request.body);
							}
							else
							{
								writer.Write(Encoding.UTF8.GetBytes("\r\n"));
							}
						}
						#endregion

						#region Response
						{
							BufferedStream bufferedStream = new BufferedStream(stream);

							#region Read headers
							{
								List<string> lines = new List<string>();

								do
								{
									string line = ReadLine(bufferedStream);

									if (line.Length == 0)
										break;

									lines.Add(line);
								} while (true);

								string[] statusLine = lines[0].Split(' ');
								this.httpVersion = statusLine[0];
								this.statusCode = int.Parse(statusLine[1]);
								this.reasonPhrase = string.Join(" ", statusLine, 2, statusLine.Length - 2);

								this.headers = new Hashtable();

								for (int i = 1; i < lines.Count; i++)
								{
									string k = lines[i].Substring(0, lines[i].IndexOf(':')).Trim();
									string v = lines[i].Substring(lines[i].IndexOf(':') + 1).Trim();

									if (!this.headers.ContainsKey(k))
									{
										this.headers.Add(k, v);
									}
									else
									{
										if (this.headers[k] is string)
										{
											string a = this.headers[k] as string;
											string[] b = { a, v };

											this.headers[k] = b;
										}
										else if (this.headers[k] is string[])
										{
											string[] a = this.headers[k] as string[];
											string[] b = new string[a.Length + 1];
											
											a.CopyTo(b, 0);
											b[a.Length - 1] = v;

											this.headers[k] = b;
										}
									}
								}
							}
							#endregion

							//UnityEngine.Debug.Log(DumpHeaders() +
							//    "\r\n" +
							//    "----");

							#region Read body
							{
								int contentLength = -1;
								if (this.headers.ContainsKey("Content-Length"))
									contentLength = int.Parse(this.headers["Content-Length"] as string);

								string transferEncoding = null;
								if (this.headers.ContainsKey("Transfer-Encoding"))
									transferEncoding = (this.headers["Transfer-Encoding"] as string).ToLower();

								if (contentLength >= 0)
								{
									this.bytes = new byte[contentLength];
									int bytesReceived = 0;

									while (bytesReceived < contentLength)
									{
										bytesReceived += bufferedStream.Read(this.bytes,
											bytesReceived, this.bytes.Length - bytesReceived);
									}
								}
								else if (transferEncoding == "chunked")
								{
									MemoryStream ms = new MemoryStream(4096);
									byte[] buffer = new byte[4096];
									
									do
									{
										string chunkSizeString = ReadLine(bufferedStream);
										
										if (chunkSizeString.Length == 0)
											break;

										int chunkSize = Convert.ToInt32(chunkSizeString, 16);

										if (chunkSize == 0)
											break;

										int bytesReceived = 0;

										while (bytesReceived < chunkSize)
										{
											int read = bufferedStream.Read(buffer, 0, 
												chunkSize - bytesReceived < buffer.Length ? 
												chunkSize - bytesReceived : buffer.Length);

											ms.Write(buffer, 0, read);
											bytesReceived += read;
										}

										bufferedStream.ReadByte(); // \r
										bufferedStream.ReadByte(); // \n
									} while (true);

									this.bytes = ms.ToArray();
									ms.Dispose();
								}
								else
								{
									MemoryStream ms = new MemoryStream(4096);
									byte[] buffer = new byte[4096];
									int read = 0;

									do
									{
										read = bufferedStream.Read(buffer, 0, buffer.Length);
										ms.Write(buffer, 0, read);
									} while (read > 0);

									this.bytes = ms.ToArray();
									ms.Dispose();
								}

								cachedText = null;
							}
							#endregion

							#region Redirection
							if ((this.statusCode == 301 ||
								this.statusCode == 302 ||
								this.statusCode == 303 ||
								this.statusCode == 307) &&
								this.headers.ContainsKey("Location") &&
								redirection < MAX_REDIRECTION)
							{
								string oldHost = uri.Host;

								string location = this.headers["Location"] as string;
								uri = new Uri(uri, location);

								if (oldHost != uri.Host)
								{
									stream.Close();
									client.Close();

									client = new TcpClient();
									client.Connect(uri.Host, uri.Port);

									newConnection = true;
								}
								else
									newConnection = false;

								redirection++;
								continue;
							}
							#endregion

							#region Decoding
							if (this.headers.ContainsKey("Content-Encoding"))
							{
								bytes = Decompress((this.headers["Content-Encoding"] as string).ToLower(), bytes);
							}
							#endregion

#if UNITY_EDITOR
							// test---
							UnityEngine.Debug.LogWarning(request.DumpHeaders() +
								(request.body == null ? "" : "Content-Length: " + request.body.Length + "\r\n") +
								"\r\n" +
								(request.body == null ? "" : Encoding.UTF8.GetString(request.body)));
							UnityEngine.Debug.LogWarning(DumpHeaders() +
								"\r\n" +
								this.text);
#endif
						}
						#endregion

						stream.Close();
						client.Close();
						break;
					} while (redirection < MAX_REDIRECTION);
				}
				catch (Exception e)
				{
					error = e;
				}
				finally
				{
					isDone = true;
				}
			});
		}
	/// <summary>
	/// Upload a file.
	/// </summary>
	/// <param name="file">File metadata.</param>
	/// <param name="data">Data.</param>
	/// <returns>AsyncSuccess with File or Exception for error.</returns>
	/// <example>
	/// Upload a file to the root folder.
	/// <code>
	/// var bytes = Encoding.UTF8.GetBytes("world!");
	/// 
	/// var file = new GoogleDrive.File(new Dictionary<string, object>
	///	{
	///		{ "title", "hello.txt" },
	///		{ "mimeType", "text/plain" },
	///	});
	///	
	/// StartCoroutine(drive.UploadFile(file, bytes));
	/// </code>
	/// Update the file content.
	/// <code>
	/// var listFiles = drive.ListFilesByQueary("title = 'a.txt'");
	/// yield return StartCoroutine(listFiles);
	/// 
	/// var files = GoogleDrive.GetResult<List<GoogleDrive.File>>(listFiles);
	/// if (files != null && files.Count > 0)
	/// {
	///		var bytes = Encoding.UTF8.GetBytes("new content.");
	///		StartCoroutine(drive.UploadFile(files[0], bytes));
	/// }
	/// </code>
	/// </example>
	public IEnumerator UploadFile(File file, byte[] data)
	{
		#region Check the access token is expired
		var check = CheckExpiration();
		while (check.MoveNext())
			yield return null;

		if (check.Current is Exception)
		{
			yield return check.Current;
			yield break;
		}
		#endregion

		string uploadUrl = null;

		// Start a resumable session.
		if (file.ID == null || file.ID == string.Empty)
		{
			var request = new UnityWebRequest(
				"https://www.googleapis.com/upload/drive/v2/files?uploadType=resumable");
			request.method = "POST";
			request.headers["Authorization"] = "Bearer " + AccessToken;
			request.headers["Content-Type"] = "application/json";
			request.headers["X-Upload-Content-Type"] = file.MimeType;
			request.headers["X-Upload-Content-Length"] = data.Length;

			string metadata = JsonWriter.Serialize(file.ToJSON());
			request.body = Encoding.UTF8.GetBytes(metadata);

			var response = new UnityWebResponse(request);
			while (!response.isDone)
				yield return null;

			if (response.statusCode != 200)
			{
				JsonReader reader = new JsonReader(response.text);
				var json = reader.Deserialize<Dictionary<string, object>>();

				if (json == null)
				{
					yield return new Exception(-1, "UploadFile response parsing failed.");
					yield break;
				}
				else if (json.ContainsKey("error"))
				{
					yield return GetError(json);
					yield break;
				}
			}

			// Save the resumable session URI.
			uploadUrl = response.headers["Location"] as string;
		}
		else
		{
			uploadUrl = "https://www.googleapis.com/upload/drive/v2/files/" + file.ID;
		}

		// Upload the file.
		{
			var request = new UnityWebRequest(uploadUrl);
			request.method = "PUT";
			request.headers["Authorization"] = "Bearer " + AccessToken;
			request.headers["Content-Type"] = "application/octet-stream"; // file.MimeType;
			request.body = data;

			var response = new UnityWebResponse(request);
			while (!response.isDone)
				yield return null;

			JsonReader reader = new JsonReader(response.text);
			var json = reader.Deserialize<Dictionary<string, object>>();

			if (json == null)
			{
				yield return new Exception(-1, "UploadFile response parsing failed.");
				yield break;
			}
			else if (json.ContainsKey("error"))
			{
				yield return GetError(json);
				yield break;
			}

			yield return new AsyncSuccess(new File(json));
		}
	}
	/// <summary>
	/// Download a file content.
	/// </summary>
	/// <param name="file">Download URL.</param>
	/// <returns>AsyncSuccess with byte[] or Exception for error.</returns>
	/// <example>
	/// Download the thumbnail image.
	/// <code>
	/// if (file.ThumbnailLink != null)
	/// {
	///		var download = drive.DownloadFile(file.ThumbnailLink);
	///		yield return StartCoroutine(drive);
	///		
	///		var data = GoogleDrive.GetResult<byte[]>(download);
	///		if (data != null)
	///			someTexture.LoadImage(data);
	///	}
	/// </code>
	/// </example>
	public IEnumerator DownloadFile(string url)
	{
		#region Check the access token is expired
		var check = CheckExpiration();
		while (check.MoveNext())
			yield return null;

		if (check.Current is Exception)
		{
			yield return check.Current;
			yield break;
		}
		#endregion

		var request = new UnityWebRequest(url);
		request.headers["Authorization"] = "Bearer " + AccessToken;

		var response = new UnityWebResponse(request);
		while (!response.isDone)
			yield return null;

		yield return new AsyncSuccess(response.bytes);
	}
	/// <summary>
	/// Duplicate a file.
	/// </summary>
	/// <param name="file">File to duplicate.</param>
	/// <param name="newFile">New file data.</param>
	/// <returns>AsyncSuccess with File or Exception for error.</returns>
	/// <example>
	/// Copy 'someFile' to 'newFile'.
	/// <code>
	/// var newFile = new GoogleDrive.File(new Dictionary<string, object>
	///	{
	///		{ "title", someFile.Title + "(2)" },
	///		{ "mimeType", someFile.MimeType },
	///	});
	///	newFile.Parents = new List<string> { newParentFolder.ID };
	///	
	/// StartCoroutine(drive.DuplicateFile(someFile, newFile));
	/// </code>
	/// </example>
	IEnumerator DuplicateFile(File file, File newFile)
	{
		#region Check the access token is expired
		var check = CheckExpiration();
		while (check.MoveNext())
			yield return null;

		if (check.Current is Exception)
		{
			yield return check.Current;
			yield break;
		}
		#endregion

		var request = new UnityWebRequest("https://www.googleapis.com/drive/v2/files/" + 
			file.ID + "/copy");
		request.method = "POST";
		request.headers["Authorization"] = "Bearer " + AccessToken;
		request.headers["Content-Type"] = "application/json";

		string metadata = JsonWriter.Serialize(newFile.ToJSON());
		request.body = Encoding.UTF8.GetBytes(metadata);

		var response = new UnityWebResponse(request);
		while (!response.isDone)
			yield return null;

		JsonReader reader = new JsonReader(response.text);
		var json = reader.Deserialize<Dictionary<string, object>>();

		if (json == null)
		{
			yield return new Exception(-1, "DuplicateFile response parsing failed.");
			yield break;
		}
		else if (json.ContainsKey("error"))
		{
			yield return GetError(json);
			yield break;
		}

		yield return new AsyncSuccess(new File(json));
	}
	/// <summary>
	/// Touch a file(or folder).
	/// </summary>
	/// <param name="file">File to touch.</param>
	/// <returns>AsyncSuccess with File or Exception for error.</returns>
	/// <example>
	/// <code>
	/// StartCoroutine(drive.TouchFile(someFile));
	/// </code>
	/// </example>
	public IEnumerator TouchFile(File file)
	{
		#region Check the access token is expired
		var check = CheckExpiration();
		while (check.MoveNext())
			yield return null;

		if (check.Current is Exception)
		{
			yield return check.Current;
			yield break;
		}
		#endregion

		var request = new UnityWebRequest("https://www.googleapis.com/drive/v2/files/" + 
			file.ID + "/touch");
		request.method = "POST";
		request.headers["Authorization"] = "Bearer " + AccessToken;
		request.body = new byte[0]; // with no data

		var response = new UnityWebResponse(request);
		while (!response.isDone)
			yield return null;

		JsonReader reader = new JsonReader(response.text);
		var json = reader.Deserialize<Dictionary<string, object>>();

		if (json == null)
		{
			yield return new Exception(-1, "TouchFile response parsing failed.");
			yield break;
		}
		else if (json.ContainsKey("error"))
		{
			yield return GetError(json);
			yield break;
		}

		yield return new AsyncSuccess(new File(json));
	}
	/// <summary>
	/// Delete a file(or folder).
	/// </summary>
	/// <param name="file">File.</param>
	/// <returns>AsyncSuccess or Exception for error.</returns>
	/// <example>
	/// Delete all files.
	/// <code>
	/// var listFiles = drive.ListAllFiles();
	/// yield return StartCoroutine(listFiles);
	/// var files = GoogleDrive.GetResult<List<GoogleDrive.File>>(listFiles);
	/// 
	/// if (files != null)
	/// {
	///		for (int i = 0; i < files.Count; i++)
	///			yield return StartCoroutine(drive.DeleteFile(files[i]));
	/// }
	/// </code>
	/// </example>
	public IEnumerator DeleteFile(File file)
	{
		#region Check the access token is expired
		var check = CheckExpiration();
		while (check.MoveNext())
			yield return null;

		if (check.Current is Exception)
		{
			yield return check.Current;
			yield break;
		}
		#endregion

		var request = new UnityWebRequest("https://www.googleapis.com/drive/v2/files/" + file.ID);
		request.method = "DELETE";
		request.headers["Authorization"] = "Bearer " + AccessToken;

		var response = new UnityWebResponse(request);
		while (!response.isDone)
			yield return null;

		// If successful, empty response.
		JsonReader reader = new JsonReader(response.text);
		var json = reader.Deserialize<Dictionary<string, object>>();
		
		if (json != null && json.ContainsKey("error"))
		{
			yield return GetError(json);
			yield break;
		}

		yield return new AsyncSuccess();
	}
	public IEnumerator ListFilesByQueary(string query)
	{
		#region Check the access token is expired
		var check = CheckExpiration();
		while (check.MoveNext())
			yield return null;

		if (check.Current is Exception)
		{
			yield return check.Current;
			yield break;
		}
		#endregion

		var request = new UnityWebRequest(
			new Uri("https://www.googleapis.com/drive/v2/files?q=" + query));
		request.headers["Authorization"] = "Bearer " + AccessToken;

		var response = new UnityWebResponse(request);
		while (!response.isDone)
			yield return null;

		JsonReader reader = new JsonReader(response.text);
		var json = reader.Deserialize<Dictionary<string, object>>();

		if (json == null)
		{
			yield return new Exception(-1, "ListFiles response parsing failed.");
			yield break;
		}
		else if (json.ContainsKey("error"))
		{
			yield return GetError(json);
			yield break;
		}

		// parsing
		var results = new List<File>();

		if (json.ContainsKey("items") &&
			json["items"] is Dictionary<string, object>[])
		{
			var items = json["items"] as Dictionary<string, object>[];
			foreach (var item in items)
			{
				results.Add(new File(item));
			}
		}

		yield return new AsyncSuccess(results);
	}
	/// <summary>
	/// Get the access code by the refresh token.
	/// </summary>
	/// <param name="refreshToken">Refresh token.</param>
	/// <returns>TokenResponse or Exception for error.</returns>
	IEnumerator GetAccessTokenByRefreshToken(string refreshToken)
	{
		var request = new UnityWebRequest("https://accounts.google.com/o/oauth2/token");

		request.method = "POST";
		request.headers["Content-Type"] = "application/x-www-form-urlencoded";
		request.body = Encoding.UTF8.GetBytes(string.Format(
			"client_id={0}&" +
			"client_secret={1}&" +
			"refresh_token={2}&" +
			"grant_type=refresh_token",
			ClientID, ClientSecret, refreshToken));

		var response = request.GetResponse();
		while (!response.isDone)
			yield return null;

		if (response.error != null)
		{
			yield return response.error;
			yield break;
		}

		JsonReader reader = new JsonReader(response.text);
		var json = reader.Deserialize<Dictionary<string, object>>();

		if (json == null)
		{
			yield return new Exception(-1, "RefreshToken response parsing failed.");
			yield break;
		}

		yield return new TokenResponse(json);
	}
Example #11
-1
	/// <summary>
	/// Insert a folder to otehr folder.
	/// </summary>
	/// <param name="parentFolder">Parent folder.</param>
	/// <returns>AsyncSuccess with File or Exception for error.</returns>
	/// <example>
	/// <code>
	/// var insert = drive.InsertFolder("new_folder_in_appdata", drive.AppData);
	/// yield return StartCoroutine(insert);
	/// </code>
	/// </example>
	public IEnumerator InsertFolder(string title, File parentFolder)
	{
		#region Check the access token is expired
		var check = CheckExpiration();
		while (check.MoveNext())
			yield return null;

		if (check.Current is Exception)
		{
			yield return check.Current;
			yield break;
		}
		#endregion

		var request = new UnityWebRequest("https://www.googleapis.com/drive/v2/files");
		request.method = "POST";
		request.headers["Authorization"] = "Bearer " + AccessToken;
		request.headers["Content-Type"] = "application/json";
		
		Dictionary<string, object> data = new Dictionary<string, object>();
		data["title"] = title;
		data["mimeType"] = "application/vnd.google-apps.folder";
		if (parentFolder != null)
		{
			data["parents"] = new List<Dictionary<string, string>>
			{
				new Dictionary<string, string> 
				{
					{ "id", parentFolder.ID }
				},
			};
		}
		request.body = Encoding.UTF8.GetBytes(JsonWriter.Serialize(data));

		var response = new UnityWebResponse(request);
		while (!response.isDone)
			yield return null;

		JsonReader reader = new JsonReader(response.text);
		var json = reader.Deserialize<Dictionary<string, object>>();

		if (json == null)
		{
			yield return new Exception(-1, "InsertFolder response parsing failed.");
			yield break;
		}
		else if (json.ContainsKey("error"))
		{
			yield return GetError(json);
			yield break;
		}

		yield return new AsyncSuccess(new File(json));
	}