aboutsummaryrefslogtreecommitdiffstats
path: root/tools/xen/lib/xend/encode.py
blob: 38c9351db739e84146adcb2ad0a471e23e605531 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
# Copyright (C) 2004 Mike Wray <mike.wray@hp.com>
"""Encoding for arguments to HTTP calls.
   Uses the url-encoding with MIME type 'application/x-www-form-urlencoded'
   if the data does not include files. Otherwise it uses the encoding with
   MIME type 'multipart/form-data'. See the HTML4 spec for details.

   """
import sys
import types
from StringIO import StringIO

import urllib
import httplib
import random
import md5

# Extract from HTML4 spec.
## The following example illustrates "multipart/form-data"
## encoding. Suppose we have the following form:

##  <FORM action="http://server.com/cgi/handle"
##        enctype="multipart/form-data"
##        method="post">
##    <P>
##    What is your name? <INPUT type="text" name="submit-name"><BR>
##    What files are you sending? <INPUT type="file" name="files"><BR>
##    <INPUT type="submit" value="Send"> <INPUT type="reset">
##  </FORM>

## If the user enters "Larry" in the text input, and selects the text
## file "file1.txt", the user agent might send back the following data:

##    Content-Type: multipart/form-data; boundary=AaB03x

##    --AaB03x
##    Content-Disposition: form-data; name="submit-name"

##    Larry
##    --AaB03x
##    Content-Disposition: form-data; name="files"; filename="file1.txt"
##    Content-Type: text/plain

##    ... contents of file1.txt ...
##    --AaB03x--

## If the user selected a second (image) file "file2.gif", the user agent
## might construct the parts as follows:

##    Content-Type: multipart/form-data; boundary=AaB03x

##    --AaB03x
##    Content-Disposition: form-data; name="submit-name"

##    Larry
##    --AaB03x
##    Content-Disposition: form-data; name="files"
##    Content-Type: multipart/mixed; boundary=BbC04y

##    --BbC04y
##    Content-Disposition: file; filename="file1.txt"
##    Content-Type: text/plain

##    ... contents of file1.txt ...
##    --BbC04y
##    Content-Disposition: file; filename="file2.gif"
##    Content-Type: image/gif
##    Content-Transfer-Encoding: binary

##    ...contents of file2.gif...
##    --BbC04y--
##    --AaB03x--

__all__ = ['encode_data', 'encode_multipart', 'encode_form', 'mime_boundary' ]

def data_values(d):
    if isinstance(d, types.DictType):
        return d.items()
    else:
        return d

def encode_data(d):
    """Encode some data for HTTP transport.
    The encoding used is stored in 'Content-Type' in the headers.

    d	data - sequence of tuples or dictionary
    returns a 2-tuple of the headers and the encoded data
    """
    val = ({}, None)
    if d is None: return val
    multipart = 0
    for (k, v) in data_values(d):
        if encode_isfile(v):
            multipart = 1
            break
    if multipart:
        val = encode_multipart(d)
    else:
        val = encode_form(d)
    return val

def encode_isfile(v):
    if isinstance(v, types.FileType):
        return 1
    if hasattr(v, 'readlines'):
        return 1
    return 0

def encode_multipart(d):
    boundary = mime_boundary()
    hdr = { 'Content-Type': 'multipart/form-data; boundary=' + boundary }
    out = StringIO()
    for (k,v) in data_values(d):
        out.write('--')
        out.write(boundary)
        out.write('\r\n')
        if encode_isfile(v):
            out.write('Content-Disposition: form-data; name="')
            out.write(k)
            if hasattr(v, 'name'):
                out.write('"; filename="')
                out.write(v.name)
            out.write('"\r\n')
            out.write('Content-Type: application/octet-stream\r\n')
            out.write('\r\n')
            for l in v.readlines():
               out.write(l)  
        else:
            out.write('Content-Disposition: form-data; name="')
            out.write(k)
            out.write('"\r\n')
            out.write('\r\n')
            out.write(str(v))
            out.write('\r\n')
    out.write('--')
    out.write(boundary)
    out.write('--')
    out.write('\r\n')
    return (hdr, out.getvalue())

def mime_boundary():
    random.seed()
    m = md5.new()
    for i in range(0, 10):
        c = chr(random.randint(1, 255))
        m.update(c)
    b = m.hexdigest()
    return b[0:16]

def encode_form(d):
    hdr = { 'Content-Type': 'application/x-www-form-urlencoded' }
    val = urllib.urlencode(d)
    return (hdr, val)

def main():
    #d = {'a': 1, 'b': 'x y', 'c': file('conf.sxp') }
    #d = {'a': 1, 'b': 'x y' }
    d = [ ('a', 1), ('b', 'x y'), ('c', file('conf.sxp')) ]
    #d = [ ('a', 1), ('b', 'x y')]
    v = encode_data(d)
    print v[0]
    sys.stdout.write(v[1])
    print

if __name__ == "__main__":
    main()