-#!/usr/bin/env python
-import re, socket, sys
+import os
+from cStringIO import StringIO
+import urllib
+from allmydata.scripts.common_http import do_http, format_http_success, format_http_error
+from allmydata.scripts.common import get_alias, DEFAULT_ALIAS, escape_path, \
+ UnknownAliasError
+from allmydata.util.encodingutil import quote_output
-SERVERURL_RE=re.compile("http://([^:]*)(:([1-9][0-9]*))?")
-
-def put(nodeurl, vdrive, vdrive_fname, local_fname, verbosity):
+def put(options):
"""
@param verbosity: 0, 1, or 2, meaning quiet, verbose, or very verbose
@return: a Deferred which eventually fires with the exit code
"""
- if not isinstance(nodeurl, basestring):
- raise ValueError("nodeurl is required to be a string and look like \"http://HOSTNAMEORADDR:PORT\", not: %r" % (nodeurl,))
-
- mo = SERVERURL_RE.match(nodeurl)
- if not mo:
- raise ValueError("nodeurl is required to look like \"http://HOSTNAMEORADDR:PORT\", not: %r" % (nodeurl,))
- host = mo.group(1)
- port = int(mo.group(3))
-
- url = "/vdrive/" + vdrive + "/"
- if vdrive_fname:
- url += vdrive_fname
-
- if local_fname is None or local_fname == "-":
- infileobj = sys.stdin
+ nodeurl = options['node-url']
+ aliases = options.aliases
+ from_file = options.from_file
+ to_file = options.to_file
+ mutable = options['mutable']
+ mutable_type = options['mutable-type']
+ if options['quiet']:
+ verbosity = 0
else:
- infileobj = open(local_fname, "rb")
-
- so = socket.socket()
- so.connect((host, port,))
-
- CHUNKSIZE=2**16
- data = "PUT %s HTTP/1.1\r\nConnection: close\r\nHostname: %s\r\n\r\n" % (url, host,)
- while data:
- try:
- sent = so.send(data)
- except Exception, le:
- print "got socket error: %s" % (le,)
- return -1
-
- if sent == len(data):
- data = infileobj.read(CHUNKSIZE)
+ verbosity = 2
+ stdin = options.stdin
+ stdout = options.stdout
+ stderr = options.stderr
+
+ if nodeurl[-1] != "/":
+ nodeurl += "/"
+ if to_file:
+ # several possibilities for the TO_FILE argument.
+ # <none> : unlinked upload
+ # foo : TAHOE_ALIAS/foo
+ # subdir/foo : TAHOE_ALIAS/subdir/foo
+ # /oops/subdir/foo : DISALLOWED
+ # ALIAS:foo : aliases[ALIAS]/foo
+ # ALIAS:subdir/foo : aliases[ALIAS]/subdir/foo
+
+ # ALIAS:/oops/subdir/foo : DISALLOWED
+ # DIRCAP:./foo : DIRCAP/foo
+ # DIRCAP:./subdir/foo : DIRCAP/subdir/foo
+ # MUTABLE-FILE-WRITECAP : filecap
+
+ # FIXME: don't hardcode cap format.
+ if to_file.startswith("URI:MDMF:") or to_file.startswith("URI:SSK:"):
+ url = nodeurl + "uri/%s" % urllib.quote(to_file)
else:
- data = data[sent:]
-
- respbuf = []
- data = so.recv(CHUNKSIZE)
- while data:
- respbuf.append(data)
- data = so.recv(CHUNKSIZE)
-
- so.shutdown(socket.SHUT_WR)
-
- data = so.recv(CHUNKSIZE)
- while data:
- respbuf.append(data)
- data = so.recv(CHUNKSIZE)
+ try:
+ rootcap, path = get_alias(aliases, to_file, DEFAULT_ALIAS)
+ except UnknownAliasError, e:
+ e.display(stderr)
+ return 1
+ if path.startswith("/"):
+ suggestion = to_file.replace(u"/", u"", 1)
+ print >>stderr, "Error: The remote filename must not start with a slash"
+ print >>stderr, "Please try again, perhaps with %s" % quote_output(suggestion)
+ return 1
+ url = nodeurl + "uri/%s/" % urllib.quote(rootcap)
+ if path:
+ url += escape_path(path)
+ else:
+ # unlinked upload
+ url = nodeurl + "uri"
+ if mutable:
+ url += "?mutable=true"
+ if mutable_type:
+ assert mutable
+ url += "&mutable-type=%s" % mutable_type
+
+ if from_file:
+ infileobj = open(os.path.expanduser(from_file), "rb")
+ else:
+ # do_http() can't use stdin directly: for one thing, we need a
+ # Content-Length field. So we currently must copy it.
+ if verbosity > 0:
+ print >>stderr, "waiting for file data on stdin.."
+ data = stdin.read()
+ infileobj = StringIO(data)
- respstr = ''.join(respbuf)
+ resp = do_http("PUT", url, infileobj)
- headerend = respstr.find('\r\n\r\n')
- if headerend == -1:
- headerend = len(respstr)
- header = respstr[:headerend]
- RESP_RE=re.compile("^HTTP/[0-9]\.[0-9] ([0-9]*) *([A-Za-z_ ]*)") # This regex is soooo ad hoc... --Zooko 2007-08-16
- mo = RESP_RE.match(header)
- if mo:
- code = int(mo.group(1))
- word = mo.group(2)
+ if resp.status in (200, 201,):
+ print >>stderr, format_http_success(resp)
+ print >>stdout, quote_output(resp.read(), quotemarks=False)
+ return 0
- if code in (200, 201,):
- print "%s %s" % (code, word,)
- return 0
-
- print respstr[headerend:]
+ print >>stderr, format_http_error("Error", resp)
return 1
-
-def main():
- import optparse
- parser = optparse.OptionParser()
- parser.add_option("-d", "--vdrive", dest="vdrive", default="global")
- parser.add_option("-s", "--server", dest="server", default="http://tahoebs1.allmydata.com:8011")
-
- (options, args) = parser.parse_args()
-
- local_file = args[0]
- vdrive_file = None
- if len(args) > 1:
- vdrive_file = args[1]
-
- return put(options.server, options.vdrive, vdrive_file, local_file)
-
-if __name__ == '__main__':
- main()