devserver: support for querying statically staged file size and hashes

This adds a new 'api/fileinfo' URL, which returns a JSON encoded
dictionary containing the size, SHA1 and SHA256 hash values of a given
file under the devserver's static directory.

* Migrated size/hash methods from autoupdate.Autoupdate to common_util.
  This makes more sense as they are applicable to any file and are not
  necessarily AU related. Also renamed and reorganized their code.

* Added a new CherryPy-exposed method implementing the new
  functionality.

BUG=chromium-os:33762
TEST=New method returns desired values; unit tests pass.

Change-Id: Iff7e0af2c8962de4976da7c6deaa9892d5b106a5
Reviewed-on: https://gerrit.chromium.org/gerrit/34426
Commit-Ready: Gilad Arnold <garnold@chromium.org>
Reviewed-by: Gilad Arnold <garnold@chromium.org>
Tested-by: Gilad Arnold <garnold@chromium.org>
diff --git a/devserver.py b/devserver.py
index 2f63f5d..5e45236 100755
--- a/devserver.py
+++ b/devserver.py
@@ -6,6 +6,7 @@
 
 """A CherryPy-based webserver to host images and build packages."""
 
+import json
 import logging
 import optparse
 import os
@@ -279,6 +280,32 @@
     raise cherrypy.HTTPError(400, 'No label provided.')
 
 
+  @cherrypy.expose
+  def fileinfo(self, *path_args):
+    """Returns information about a given staged file.
+
+    Args:
+      path_args: path to the file inside the server's static staging directory
+    Returns:
+      A JSON encoded dictionary with information about the said file, which may
+      contain the following keys/values:
+        size:   the file size in bytes (int)
+        sha1:   a base64 encoded SHA1 hash (string)
+        sha256: a base64 encoded SHA256 hash (string)
+    """
+    file_path = os.path.join(updater.static_dir, *path_args)
+    if not os.path.exists(file_path):
+      raise DevServerError('file not found: %s' % file_path)
+    try:
+      file_size = os.path.getsize(file_path)
+      file_sha1 = common_util.GetFileSha1(file_path)
+      file_sha256 = common_util.GetFileSha256(file_path)
+    except os.error, e:
+      raise DevServerError('failed to get info for file %s: %s' %
+                           (file_path, str(e)))
+    return json.dumps(
+        {'size': file_size, 'sha1': file_sha1, 'sha256': file_sha256})
+
 class DevServerRoot(object):
   """The Root Class for the Dev Server.