1 from xml
.dom
import Node
, minidom
4 import rox
, os
, sys
, urlparse
, tempfile
, shutil
, time
, urllib
5 from rox
import g
, tasks
8 from zeroinstall
.zerostore
import unpack
, manifest
, NotStored
11 from xmltools
import *
15 RESPONSE_SAVE_AND_TEST
= 1
17 dotted_ints
= '[0-9]+(.[0-9]+)*'
18 version_regexp
= '[^a-zA-Z0-9](%s)(-(pre|rc|post|)%s)*' % (dotted_ints
, dotted_ints
)
20 def get_combo_value(combo
):
21 i
= combo
.get_active()
25 watch
= gtk
.gdk
.Cursor(gtk
.gdk
.WATCH
)
28 def __init__(self
, feed_editor
, local_archive
= None):
29 self
.feed_editor
= feed_editor
32 widgets
= gtk
.glade
.XML(main
.gladefile
, 'add_archive')
34 tree
= widgets
.get_widget('extract_list')
35 model
= g
.TreeStore(str)
37 selection
= tree
.get_selection()
38 selection
.set_mode(g
.SELECTION_BROWSE
)
40 cell
= g
.CellRendererText()
41 col
= g
.TreeViewColumn('Extract', cell
)
42 col
.add_attribute(cell
, 'text', 0)
43 tree
.append_column(col
)
45 dialog
= widgets
.get_widget('add_archive')
47 widgets
.get_widget('mime_type').set_active(0)
49 def local_archive_changed(chooser
):
51 path
= chooser
.get_filename()
52 widgets
.get_widget('subdirectory_frame').set_sensitive(False)
56 self
.tmpdir
= tempfile
.mkdtemp('-0publish-gui')
57 url
= widgets
.get_widget('archive_url').get_text()
59 dialog
.window
.set_cursor(watch
)
62 unpack
.unpack_archive(url
, file(path
), self
.tmpdir
)
64 dialog
.window
.set_cursor(None)
66 chooser
.unselect_filename(path
)
69 iter = model
.append(None, ['Everything'])
70 items
= os
.listdir(self
.tmpdir
)
72 model
.append(iter, [f
])
74 # Choose a sensible default
75 iter = model
.get_iter_root()
76 if len(items
) == 1 and \
77 os
.path
.isdir(os
.path
.join(self
.tmpdir
, items
[0])) and \
78 items
[0] not in ('usr', 'opt', 'bin', 'etc', 'sbin', 'doc', 'var'):
79 iter = model
.iter_children(iter)
80 selection
.select_iter(iter)
82 widgets
.get_widget('subdirectory_frame').set_sensitive(True)
84 local_archive_button
= widgets
.get_widget('local_archive')
85 local_archive_button
.connect('selection-changed', local_archive_changed
)
86 widgets
.get_widget('subdirectory_frame').set_sensitive(False)
89 url
= widgets
.get_widget('archive_url').get_text()
91 raise Exception("Enter a URL to download from!")
93 chooser
= g
.FileChooserDialog('Save archive as...', dialog
, g
.FILE_CHOOSER_ACTION_SAVE
)
94 chooser
.set_current_name(os
.path
.basename(url
))
95 chooser
.add_button(g
.STOCK_CANCEL
, g
.RESPONSE_CANCEL
)
96 chooser
.add_button(g
.STOCK_SAVE
, g
.RESPONSE_OK
)
97 chooser
.set_default_response(g
.RESPONSE_OK
)
99 filename
= chooser
.get_filename()
101 if resp
!= g
.RESPONSE_OK
:
104 DownloadBox(url
, filename
, local_archive_button
)
106 widgets
.get_widget('download').connect('clicked', download
)
109 if r
== g
.RESPONSE_OK
:
110 url
= widgets
.get_widget('archive_url').get_text()
111 if urlparse
.urlparse(url
)[1] == '':
112 raise Exception('Missing host name in URL "%s"' % url
)
113 if urlparse
.urlparse(url
)[2] == '':
114 raise Exception('Missing resource part in URL "%s"' % url
)
115 local_archive
= widgets
.get_widget('local_archive').get_filename()
116 if not local_archive
:
117 raise Exception('Please select a local file')
118 mime_type
= get_combo_value(widgets
.get_widget('mime_type'))
119 if selection
.iter_is_selected(model
.get_iter_root()):
123 _
, iter = selection
.get_selected()
124 extract
= model
[iter][0]
125 root
= os
.path
.join(self
.tmpdir
, extract
)
127 size
= os
.path
.getsize(local_archive
)
128 self
.create_archive_element(url
, mime_type
, root
, extract
, size
)
132 dialog
.connect('response', resp
)
135 local_archive_button
.set_filename(local_archive
)
136 initial_url
= 'http://SITE/' + os
.path
.basename(local_archive
)
137 widgets
.get_widget('archive_url').set_text(initial_url
)
139 def destroy_tmp(self
):
141 shutil
.rmtree(self
.tmpdir
)
144 def create_archive_element(self
, url
, mime_type
, root
, extract
, size
):
145 alg
= manifest
.get_algorithm('sha1new')
146 digest
= alg
.new_digest()
147 for line
in alg
.generate_manifest(root
):
148 digest
.update(line
+ '\n')
149 id = alg
.getID(digest
)
151 # Add it to the cache if missing
152 # Helps with setting 'main' attribute later
154 main
.stores
.lookup(id)
156 main
.stores
.add_dir_to_cache(id, root
)
158 # Do we already have an implementation with this digest?
159 impl_element
= self
.feed_editor
.find_implementation(id)
161 if impl_element
is None:
162 # No. Create a new implementation. Guess the details...
164 leaf
= url
.split('/')[-1]
166 for m
in re
.finditer(version_regexp
, leaf
):
167 match
= m
.group()[1:]
168 if version
is None or len(best
) < len(match
):
171 impl_element
= create_element(self
.feed_editor
.doc
.documentElement
, 'implementation')
172 impl_element
.setAttribute('id', id)
173 impl_element
.setAttribute('released', time
.strftime('%Y-%m-%d'))
174 if version
: impl_element
.setAttribute('version', version
)
179 archive_element
= create_element(impl_element
, 'archive')
180 archive_element
.setAttribute('size', str(size
))
181 archive_element
.setAttribute('href', url
)
182 if extract
: archive_element
.setAttribute('extract', extract
)
184 self
.feed_editor
.update_version_model()
187 self
.feed_editor
.edit_properties(element
= impl_element
)
190 def __init__(self
, url
, path
, archive_button
):
191 widgets
= gtk
.glade
.XML(main
.gladefile
, 'download')
194 output
= file(path
, 'w')
196 dialog
= widgets
.get_widget('download')
197 progress
= widgets
.get_widget('progress')
199 cancelled
= tasks
.Blocker()
202 dialog
.connect('response', resp
)
215 # (urllib2 is buggy; no fileno)
216 stream
= urllib
.urlopen(url
)
217 size
= float(stream
.info().get('Content-Length', None))
221 yield signing
.InputBlocker(stream
), cancelled
222 if cancelled
.happened
:
223 raise Exception("Download cancelled at user's request")
224 data
= os
.read(stream
.fileno(), 1024)
230 progress
.set_fraction(got
/ size
)
234 # No finally in python 2.4
239 archive_button
.set_filename(path
)
241 tasks
.Task(download())