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 *
14 dotted_ints
= '[0-9]+(.[0-9]+)*'
15 version_regexp
= '[^a-zA-Z0-9](%s)(-(pre|rc|post|)%s)*' % (dotted_ints
, dotted_ints
)
17 def get_combo_value(combo
):
18 i
= combo
.get_active()
22 watch
= gtk
.gdk
.Cursor(gtk
.gdk
.WATCH
)
25 def __init__(self
, feed_editor
, local_archive
= None):
26 self
.feed_editor
= feed_editor
29 widgets
= gtk
.glade
.XML(main
.gladefile
, 'add_archive')
31 tree
= widgets
.get_widget('extract_list')
32 model
= g
.TreeStore(str)
34 selection
= tree
.get_selection()
35 selection
.set_mode(g
.SELECTION_BROWSE
)
37 cell
= g
.CellRendererText()
38 col
= g
.TreeViewColumn('Extract', cell
)
39 col
.add_attribute(cell
, 'text', 0)
40 tree
.append_column(col
)
42 dialog
= widgets
.get_widget('add_archive')
44 widgets
.get_widget('mime_type').set_active(0)
46 def local_archive_changed(chooser
):
48 path
= chooser
.get_filename()
49 widgets
.get_widget('subdirectory_frame').set_sensitive(False)
53 self
.tmpdir
= tempfile
.mkdtemp('-0publish-gui')
54 url
= widgets
.get_widget('archive_url').get_text()
56 dialog
.window
.set_cursor(watch
)
59 unpack
.unpack_archive(url
, file(path
), self
.tmpdir
)
61 dialog
.window
.set_cursor(None)
63 chooser
.unselect_filename(path
)
66 iter = model
.append(None, ['Everything'])
67 items
= os
.listdir(self
.tmpdir
)
69 model
.append(iter, [f
])
71 # Choose a sensible default
72 iter = model
.get_iter_root()
73 if len(items
) == 1 and \
74 os
.path
.isdir(os
.path
.join(self
.tmpdir
, items
[0])) and \
75 items
[0] not in ('usr', 'opt', 'bin', 'etc', 'sbin', 'doc', 'var'):
76 iter = model
.iter_children(iter)
77 selection
.select_iter(iter)
79 widgets
.get_widget('subdirectory_frame').set_sensitive(True)
81 local_archive_button
= widgets
.get_widget('local_archive')
82 local_archive_button
.connect('selection-changed', local_archive_changed
)
83 widgets
.get_widget('subdirectory_frame').set_sensitive(False)
86 url
= widgets
.get_widget('archive_url').get_text()
88 raise Exception("Enter a URL to download from!")
90 chooser
= g
.FileChooserDialog('Save archive as...', dialog
, g
.FILE_CHOOSER_ACTION_SAVE
)
91 chooser
.set_current_name(os
.path
.basename(url
))
92 chooser
.add_button(g
.STOCK_CANCEL
, g
.RESPONSE_CANCEL
)
93 chooser
.add_button(g
.STOCK_SAVE
, g
.RESPONSE_OK
)
94 chooser
.set_default_response(g
.RESPONSE_OK
)
96 filename
= chooser
.get_filename()
98 if resp
!= g
.RESPONSE_OK
:
101 DownloadBox(url
, filename
, local_archive_button
)
103 widgets
.get_widget('download').connect('clicked', download
)
106 if r
== g
.RESPONSE_OK
:
107 url
= widgets
.get_widget('archive_url').get_text()
108 if urlparse
.urlparse(url
)[1] == '':
109 raise Exception('Missing host name in URL "%s"' % url
)
110 if urlparse
.urlparse(url
)[2] == '':
111 raise Exception('Missing resource part in URL "%s"' % url
)
112 local_archive
= widgets
.get_widget('local_archive').get_filename()
113 if not local_archive
:
114 raise Exception('Please select a local file')
115 mime_type
= get_combo_value(widgets
.get_widget('mime_type'))
116 if selection
.iter_is_selected(model
.get_iter_root()):
120 _
, iter = selection
.get_selected()
121 extract
= model
[iter][0]
122 root
= os
.path
.join(self
.tmpdir
, extract
)
124 size
= os
.path
.getsize(local_archive
)
125 self
.create_archive_element(url
, mime_type
, root
, extract
, size
)
129 dialog
.connect('response', resp
)
132 local_archive_button
.set_filename(local_archive
)
133 initial_url
= 'http://SITE/' + os
.path
.basename(local_archive
)
134 widgets
.get_widget('archive_url').set_text(initial_url
)
136 def destroy_tmp(self
):
138 shutil
.rmtree(self
.tmpdir
)
141 def create_archive_element(self
, url
, mime_type
, root
, extract
, size
):
142 alg
= manifest
.get_algorithm('sha1new')
143 digest
= alg
.new_digest()
144 for line
in alg
.generate_manifest(root
):
145 digest
.update(line
+ '\n')
146 id = alg
.getID(digest
)
148 # Add it to the cache if missing
149 # Helps with setting 'main' attribute later
151 main
.stores
.lookup(id)
153 main
.stores
.add_dir_to_cache(id, root
)
155 # Do we already have an implementation with this digest?
156 impl_element
= self
.feed_editor
.find_implementation(id)
158 if impl_element
is None:
159 # No. Create a new implementation. Guess the details...
161 leaf
= url
.split('/')[-1]
163 for m
in re
.finditer(version_regexp
, leaf
):
164 match
= m
.group()[1:]
165 if version
is None or len(best
) < len(match
):
168 impl_element
= create_element(self
.feed_editor
.doc
.documentElement
, 'implementation')
169 impl_element
.setAttribute('id', id)
170 impl_element
.setAttribute('released', time
.strftime('%Y-%m-%d'))
171 if version
: impl_element
.setAttribute('version', version
)
176 archive_element
= create_element(impl_element
, 'archive')
177 archive_element
.setAttribute('size', str(size
))
178 archive_element
.setAttribute('href', url
)
179 if extract
: archive_element
.setAttribute('extract', extract
)
181 self
.feed_editor
.update_version_model()
184 self
.feed_editor
.edit_properties(element
= impl_element
)
187 def __init__(self
, url
, path
, archive_button
):
188 widgets
= gtk
.glade
.XML(main
.gladefile
, 'download')
191 output
= file(path
, 'w')
193 dialog
= widgets
.get_widget('download')
194 progress
= widgets
.get_widget('progress')
196 cancelled
= tasks
.Blocker()
199 dialog
.connect('response', resp
)
212 # (urllib2 is buggy; no fileno)
213 stream
= urllib
.urlopen(url
)
214 size
= float(stream
.info().get('Content-Length', None))
218 yield signing
.InputBlocker(stream
), cancelled
219 if cancelled
.happened
:
220 raise Exception("Download cancelled at user's request")
221 data
= os
.read(stream
.fileno(), 1024)
227 progress
.set_fraction(got
/ size
)
231 # No finally in python 2.4
236 archive_button
.set_filename(path
)
238 tasks
.Task(download())