[api] Don't cleanup empty user home project after submit request accept.
[opensuse:build-service.git] / src / api / app / controllers / request_controller.rb
1
2 include ProductHelper
3
4 class RequestController < ApplicationController
5   #TODO: request schema validation
6
7   # the simple writing action.type instead of action.data.attributes['type'] can not be used, since it is a rails function
8
9   # GET /request
10   alias_method :index, :pass_to_backend
11
12   # POST /request?cmd=create
13   alias_method :create, :dispatch_command
14
15   # GET /request/:id
16   def show
17     valid_http_methods :get
18     # ACL(show) TODO: check this leaks no information that is prevented by ACL
19     # parse and rewrite the request to latest format
20
21     data = Suse::Backend.get("/request/#{URI.escape params[:id]}").body
22     req = BsRequest.new(data)
23
24     send_data(req.dump_xml, :type => "text/xml")
25   end
26
27   # POST /request/:id? :cmd :newstate
28   alias_method :command, :dispatch_command
29
30   # PUT /request/:id
31   def update
32     # ACL(update) TODO: check this leaks no information that is prevented by ACL
33     params[:user] = @http_user.login if @http_user
34     
35     #TODO: allow PUT for non-admins
36     unless @http_user.is_admin?
37       render_error :status => 403, :errorcode => 'put_request_no_permission',
38         :message => "PUT on requests currently requires admin privileges"
39       return
40     end
41
42     path = request.path
43     path << build_query_from_hash(params, [:user])
44     pass_to_backend path
45   end
46
47   # DELETE /request/:id
48   #def destroy
49   # Do we want to allow to delete requests at all ?
50   #end
51
52   private
53
54   #
55   # find default reviewers of a project/package via role
56   # 
57   def find_reviewers(obj)
58     # obj can be a project or package object
59     reviewers = Array.new(0)
60     prj = nil
61
62     # check for reviewers in a package first
63     if obj.class == DbProject
64       prj = obj
65     elsif obj.class == DbPackage
66       if defined? obj.package_user_role_relationships
67         obj.package_user_role_relationships.find(:all, :conditions => ["role_id = ?", Role.find_by_title("reviewer").id] ).each do |r|
68           reviewers << User.find_by_id(r.bs_user_id)
69         end
70       end
71       prj = obj.db_project
72     else
73     end
74
75     # add reviewers of project in any case
76     if defined? prj.project_user_role_relationships
77       prj.project_user_role_relationships.find(:all, :conditions => ["role_id = ?", Role.find_by_title("reviewer").id] ).each do |r|
78         reviewers << User.find_by_id(r.bs_user_id)
79       end
80     end
81     return reviewers
82   end
83
84   def find_review_groups(obj)
85     # obj can be a project or package object
86     review_groups = Array.new(0)
87     prj = nil
88     # check for reviewers in a package first
89     if obj.class == DbProject
90       prj = obj
91     elsif obj.class == DbPackage
92       if defined? obj.package_group_role_relationships
93         obj.package_group_role_relationships.find(:all, :conditions => ["role_id = ?", Role.find_by_title("reviewer").id] ).each do |r|
94           review_groups << Group.find_by_id(r.bs_group_id)
95         end
96       end
97       prj = obj.db_project
98     else
99     end
100
101     # add reviewers of project in any case
102     if defined? prj.project_group_role_relationships
103       prj.project_group_role_relationships.find(:all, :conditions => ["role_id = ?", Role.find_by_title("reviewer").id] ).each do |r|
104         review_groups << Group.find_by_id(r.bs_group_id)
105       end
106     end
107     return review_groups
108   end
109
110   # POST /request?cmd=create
111   def create_create
112     # ACL(create_create) TODO: check this leaks no information that is prevented by ACL
113     # ACL(create_create) TODO: how to handle if permissions in source and target project are different
114     req = BsRequest.new(request.body.read)
115
116     req.each_action do |action|
117       # find objects if specified or report error
118       role=nil
119       sprj=nil
120       spkg=nil
121       tprj=nil
122       tpkg=nil
123       if action.has_element? 'person'
124         unless User.find_by_login(action.person.name)
125           render_error :status => 404, :errorcode => 'unknown_person',
126             :message => "Unknown person  #{action.person.data.attributes["name"]}"
127           return
128         end
129         role = action.person.role if action.person.has_attribute? 'role'
130       end
131       if action.has_element? 'group'
132         unless Group.find_by_title(action.group.data.attributes["name"])
133           render_error :status => 404, :errorcode => 'unknown_group',
134             :message => "Unknown group  #{action.group.data.attributes["name"]}"
135           return
136         end
137         role = action.group.role if action.group.has_attribute? 'role'
138       end
139       if role
140         unless Role.find_by_title(role)
141           render_error :status => 404, :errorcode => 'unknown_role',
142             :message => "Unknown role  #{role}"
143           return
144         end
145       end
146       if action.has_element? 'source'
147         if action.source.has_attribute? 'project'
148           sprj = DbProject.find_by_name action.source.project
149           unless sprj
150             render_error :status => 404, :errorcode => 'unknown_project',
151               :message => "Unknown source project #{action.source.project}"
152             return
153           end
154         end
155         if action.source.has_attribute? 'package'
156           spkg = sprj.db_packages.find_by_name action.source.package
157           unless spkg
158             render_error :status => 404, :errorcode => 'unknown_package',
159               :message => "Unknown source package #{action.source.package} in project #{action.source.project}"
160             return
161           end
162         end
163       end
164
165       if action.has_element? 'target'
166         if action.target.has_attribute? 'project'
167           tprj = DbProject.find_by_name action.target.project
168           unless tprj
169             render_error :status => 404, :errorcode => 'unknown_project',
170               :message => "Unknown target project #{action.target.project}"
171             return
172           end
173         end
174         if action.target.has_attribute? 'package' and action.data.attributes["type"] != "submit"
175           tpkg = tprj.db_packages.find_by_name action.target.package
176           unless tpkg
177             render_error :status => 404, :errorcode => 'unknown_package',
178               :message => "Unknown target package #{action.target.package} in project #{action.target.project}"
179             return
180           end
181         end
182       end
183
184       # Type specific checks
185       if action.data.attributes["type"] == "delete" or action.data.attributes["type"] == "add_role" or action.data.attributes["type"] == "set_bugowner"
186         #check existence of target
187         unless tprj
188           if DbProject.find_remote_project(action.target.project)
189             render_error :status => 404, :errorcode => 'unknown_package',
190               :message => "Project is on remote instance, #{action.data.attributes["type"]} not possible  #{action.target.project}"
191             return
192           end
193           render_error :status => 404, :errorcode => 'unknown_project',
194             :message => "No target project specified"
195           return
196         end
197         if action.data.attributes["type"] == "add_role"
198           unless role
199             render_error :status => 404, :errorcode => 'unknown_role',
200               :message => "No role specified"
201             return
202           end
203         end
204       elsif action.data.attributes["type"] == "submit" or action.data.attributes["type"] == "change_devel"
205         #check existence of source
206         unless sprj
207           # no support for remote projects yet, it needs special support during accept as well
208           render_error :status => 404, :errorcode => 'unknown_project',
209             :message => "No source project specified"
210           return
211         end
212
213         if action.data.attributes["type"] == "submit"
214           # source package is required for submit, but optional for change_devel
215           unless spkg
216             render_error :status => 404, :errorcode => 'unknown_package',
217               :message => "No source package specified"
218             return
219           end
220         end
221
222         # source update checks
223         if action.data.attributes["type"] == "submit"
224           sourceupdate = nil
225           if action.has_element? 'options' and action.options.has_element? 'sourceupdate'
226              sourceupdate = action.options.sourceupdate.text
227           end
228           # cleanup implicit home branches, should be done in client with 2.0
229           if not sourceupdate and action.has_element? :target
230              if "home:#{@http_user.login}:branches:#{action.target.project}" == action.source.project
231                if not action.has_element? 'options'
232                  action.add_element 'options'
233                end
234                sourceupdate = 'cleanup'
235                e = action.options.add_element 'sourceupdate'
236                e.text = sourceupdate
237              end
238           end
239           # allow cleanup only, if no devel package reference
240           if sourceupdate == 'cleanup'
241             unless spkg.develpackages.empty?
242               msg = "Unable to delete package #{spkg.name}; following packages use this package as devel package: "
243               msg += spkg.develpackages.map {|dp| dp.db_project.name+"/"+dp.name}.join(", ")
244               render_error :status => 400, :errorcode => 'develpackage_dependency',
245                 :message => msg
246               return
247             end
248           end
249         end
250
251         if action.data.attributes["type"] == "change_devel"
252           unless tpkg
253             render_error :status => 404, :errorcode => 'unknown_package',
254               :message => "No target package specified"
255             return
256           end
257         end
258
259         # We only allow submit/change_devel requests from projects where people have write access
260         # to avoid that random people can submit versions without talking to the maintainers 
261         if spkg
262           unless @http_user.can_modify_package? spkg
263             render_error :status => 403, :errorcode => "create_request_no_permission",
264               :message => "No permission to create request for package '#{spkg.name}' in project '#{sprj.name}', only maintainers can create requests."
265             return
266           end
267         else
268           unless @http_user.can_modify_project? sprj
269             render_error :status => 403, :errorcode => "create_request_no_permission",
270               :message => "No permission to create request based on project '#{sprj.name}', only maintainers can create requests."
271             return
272           end
273         end
274
275       else
276         render_error :status => 403, :errorcode => "create_unknown_request",
277           :message => "Request type is unknown '#{action.data.attributes["type"]}'"
278         return
279       end
280     end
281
282     #
283     # Find out about defined reviewers in target
284     #
285     # check targets for defined default reviewers
286     reviewers = []
287     review_groups = []
288
289     req.each_action do |action|
290       tprj = nil
291       tpkg = nil
292       if action.has_element? 'target'
293         tprj = DbProject.find_by_name action.target.project
294         if action.target.has_attribute? 'package'
295           tpkg = tprj.db_packages.find_by_name action.target.package
296         elsif action.has_element? 'source' and action.source.has_attribute? 'package'
297           tpkg = tprj.db_packages.find_by_name action.source.package
298         end
299       elsif action.has_element? 'source'
300         # find target via linkinfo or fail
301         data = REXML::Document.new( backend_get("/source/#{CGI.escape(action.source.project)}/#{CGI.escape(action.source.package)}") )
302         data.elements.each("directory/linkinfo") do |e|
303           tprj = DbProject.find_by_name e.attributes["project"]
304           tpkg = tprj.db_packages.find_by_name e.attributes["package"]
305         end
306       end
307
308       # find reviewers in target package
309       if tpkg
310         reviewers += find_reviewers(tpkg)
311         review_groups += find_review_groups(tpkg)
312       end
313       # project reviewers get added additionaly
314       if tprj
315         reviewers += find_reviewers(tprj)
316         review_groups += find_review_groups(tprj)
317       end
318     end
319
320     # apply reviewers
321     reviewers.uniq!
322     if reviewers.length > 0
323       reviewers.each do |r|
324         e = req.add_element "review"
325         e.data.attributes["by_user"] = r.login
326         e.data.attributes["state"] = "new"
327       end
328     end
329     review_groups.uniq!
330     if review_groups.length > 0
331       review_groups.each do |g|
332         e = req.add_element "review"
333         e.data.attributes["by_group"] = g.title
334         e.data.attributes["state"] = "new"
335       end
336     end
337
338     #
339     # create the actual request
340     #
341     params[:user] = @http_user.login if @http_user
342     path = request.path
343     path << build_query_from_hash(params, [:cmd, :user, :comment])
344     begin
345       response = backend_post( path, req.dump_xml )
346     rescue ActiveXML::Transport::Error => e
347       render_error :status => 400, :errorcode => "backend_error",
348         :message => e.message
349       return
350     end
351     send_data( response, :disposition => "inline" )
352   end
353
354   def command_diff
355     valid_http_methods :post
356
357     data = Suse::Backend.get("/request/#{URI.escape params[:id]}").body
358     req = BsRequest.new(data)
359
360     diff_text = ""
361
362     req.each_action do |action|
363       if action.data.attributes["type"] == "submit" and action.target.project and action.target.package
364         transport = ActiveXML::Config::transport_for(:request)
365         path = nil
366
367         if action.has_element? :acceptinfo
368           # OBS 2.1 adds acceptinfo on request accept
369           path = "/source/%s/%s?cmd=diff" %
370                [CGI.escape(action.target.project), CGI.escape(action.target.package)]
371           if action.acceptinfo.data.attributes["xsrcmd5"]
372             path += "&rev=" + action.acceptinfo.data.attributes["xsrcmd5"]
373           else
374             path += "&rev=" + action.acceptinfo.data.attributes["srcmd5"]
375           end
376           if action.acceptinfo.data.attributes["oxsrcmd5"]
377             path += "&orev=" + action.acceptinfo.data.attributes["oxsrcmd5"]
378           elsif action.acceptinfo.data.attributes["osrcmd5"]
379             path += "&orev=" + action.acceptinfo.data.attributes["osrcmd5"]
380           else
381             # md5sum of empty package
382             path += "&orev=d41d8cd98f00b204e9800998ecf8427e"
383           end
384         else
385           spkg = DbPackage.find_by_project_and_name( action.source.project, action.source.package )
386           tpkg = DbPackage.find_by_project_and_name( action.target.project, action.target.package )
387           unless spkg
388               render_error :status => 404, :errorcode => "unknown_package",
389                  :message => "Source package #{action.source.package}, project #{action.source.project} does not exist"
390               return
391           end
392           # ACL: show diff only if user has either read access rights in source or has maintainer rights in target
393           if spkg.disabled_for?('sourceaccess', nil, nil) and not @http_user.can_source_access?(spkg)
394             isTargetMaintainer = false
395             if @http_user
396               if tpkg
397                 isTargetMaintainer = true if @http_user.can_modify_package?(tpkg)
398               else
399                 tprj = DbProject.find_by_name( action.target.project ) unless tpkg
400                 if tprj and @http_user.can_create_package_in?(tprj)
401                   isTargetMaintainer = true
402                 end
403               end
404             end
405             if @http_user.nil? or not isTargetMaintainer
406               render_error :status => 403, :errorcode => "source_access_no_permission",
407                  :message => "user #{@http_user.login} has no read access to package #{action.source.package}, project #{action.source.project} and is not a maintainer of package #{action.target.package}, project #{action.target.project}"
408               return
409             end
410           end
411
412           # for requests not yet accepted or accepted with OBS 2.0 and before
413           if tpkg
414             path = "/source/%s/%s?oproject=%s&opackage=%s&cmd=diff&expand=1" %
415                    [CGI.escape(action.source.project), CGI.escape(action.source.package), CGI.escape(action.target.project), CGI.escape(action.target.package)]
416             if action.source.data['rev']
417               path += "&rev=#{action.source.rev}"
418             end
419           else
420             diff_text = "New package: " + action.target.project + "/" + action.target.package + "\n" + diff_text
421           end
422         end
423
424         begin
425           diff_text += Suse::Backend.post(path, nil).body if path
426         rescue ActiveXML::Transport::Error => e
427           render_error :status => 404, :errorcode => 'diff_failure',
428                        :message => "The diff call for #{path} failed"
429           return
430         end
431
432       end
433     end
434
435     send_data(diff_text, :type => "text/plain")
436   end
437
438   def command_addreview
439      command_changestate# :cmd => "addreview",
440                        # :by_user => params[:by_user], :by_group => params[:by_group]
441   end
442   def command_changereviewstate
443      command_changestate # :cmd => "changereviewstate", :newstate => params[:newstate], :comment => params[:comment],
444                         #:by_user => params[:by_user], :by_group => params[:by_group]
445   end
446   def command_changestate
447     if params[:id].nil? or params[:id].to_i == 0
448       render_error :status => 404, :message => "Request ID is not a number", :errorcode => "no_such_request"
449       return
450     end
451     req = BsRequest.find params[:id]
452     if req.nil?
453       render_error :status => 404, :message => "No such request", :errorcode => "no_such_request"
454       return
455     end
456     if not @http_user or not @http_user.login
457       render_error :status => 403, :errorcode => "post_request_no_permission",
458                :message => "Action requires authentifacted user."
459       return
460     end
461     params[:user] = @http_user.login
462
463     # transform request body into query parameter 'comment'
464     # the query parameter is preferred if both are set
465     if params[:comment].blank? and request.body
466       params[:comment] = request.body.read
467     end
468
469     if req.has_element? 'submit' and req.has_attribute? 'type'
470       # old style, convert to new style on the fly
471       node = req.submit
472       node.data.name = 'action'
473       node.data.attributes['type'] = 'submit'
474       req.delete_attribute('type')
475     end
476     path = request.path + build_query_from_hash(params, [:cmd, :user, :newstate, :by_user, :by_group, :superseded_by, :comment])
477
478     # do not allow direct switches from accept to decline or vice versa or double actions
479     if params[:newstate] == "accepted" or params[:newstate] == "declined" or params[:newstate] == "superseded" or params[:newstate] == "revoked"
480        if req.state.name == "accepted" or req.state.name == "declined" or req.state.name == "superseded" or req.state.name == "revoked"
481           render_error :status => 403, :errorcode => "post_request_no_permission",
482             :message => "set state to #{params[:newstate]} from accepted, superseded or declined is not allowed."
483           return
484        end
485     end
486     # Do not accept to skip the review, except force argument is given
487     if params[:newstate] == "accepted"
488        if params[:cmd] == "changestate" and req.state.name == "review" and not params[:force]
489           render_error :status => 403, :errorcode => "post_request_no_permission",
490             :message => "Request is in review state."
491           return
492        end
493     end
494
495     # valid users and groups ?
496     if params[:by_user] and User.find_by_login(params[:by_user]).nil?
497        render_error :status => 404, :errorcode => "unknown_user",
498                 :message => "User #{params[:by_user]} is unkown"
499        return
500     end
501     if params[:by_group] and Group.find_by_title(params[:by_group]).nil?
502        render_error :status => 404, :errorcode => "unknown_group",
503                 :message => "Group #{params[:by_group]} is unkown"
504        return
505     end
506
507     # generic permission check
508     permission_granted = false
509     if @http_user.is_admin?
510       permission_granted = true
511     elsif params[:newstate] == "deleted"
512       render_error :status => 403, :errorcode => "post_request_no_permission",
513                :message => "Deletion of a request is only permitted for administrators. Please revoke the request instead."
514       return
515     elsif params[:newstate] == "superseded" and not params[:superseded_by]
516       render_error :status => 403, :errorcode => "post_request_missing_parameter",
517                :message => "Supersed a request requires a 'superseded_by' parameter with the request id."
518       return
519     elsif params[:cmd] == "addreview" and (req.creator == @http_user.login or req.is_reviewer? @http_user)
520       # allow request creator to add further reviewers
521       permission_granted = true
522     elsif (params[:cmd] == "changereviewstate" and @http_user.is_in_group?(params[:by_group]))
523       permission_granted = true
524     elsif (params[:cmd] == "changereviewstate" and params[:by_user] == @http_user.login)
525       permission_granted = true
526     elsif (req.state.name == "new" or req.state.name == "review") and (params[:newstate] == "superseded" or params[:newstate] == "revoked") and req.creator == @http_user.login
527       # allow new -> revoked state change to creators of request
528       permission_granted = true
529     end
530
531     # permission and validation check for each request inside
532     req.each_action do |action|
533       if action.data.attributes["type"] == "submit" or action.data.attributes["type"] == "change_devel"
534         source_project = DbProject.find_by_name(action.source.project)
535         target_project = DbProject.find_by_name(action.target.project)
536         if params[:newstate] != "declined" and params[:newstate] != "revoked"
537           if target_project.nil?
538             render_error :status => 403, :errorcode => "post_request_no_permission",
539               :message => "Target project is missing for request #{req.id} (type #{action.data.attributes['type']})"
540             return
541           end
542           if action.target.package.nil? and action.data.attributes["type"] == "change_devel"
543             render_error :status => 403, :errorcode => "post_request_no_permission",
544               :message => "Target package is missing in request #{req.id} (type #{action.data.attributes['type']})"
545             return
546           end
547           if source_project.nil?
548             render_error :status => 403, :errorcode => "post_request_no_permission",
549               :message => "Source project is missing for request #{req.id} (type #{action.data.attributes['type']})"
550             return
551           else
552             source_package = source_project.db_packages.find_by_name(action.source.package)
553           end
554           if source_package.nil? and params[:newstate] != "revoked"
555             render_error :status => 403, :errorcode => "post_request_no_permission",
556               :message => "Source package is missing for request #{req.id} (type #{action.data.attributes['type']})"
557             return
558           end
559         end
560         if target_project
561           if action.target.has_attribute? :package
562             target_package = target_project.db_packages.find_by_name(action.target.package)
563           else
564             target_package = target_project.db_packages.find_by_name(action.source.package)
565           end
566         end
567         if ( target_package and @http_user.can_modify_package? target_package ) or
568            ( not target_package and target_project and @http_user.can_modify_project? target_project )
569            permission_granted = true
570         elsif source_project and req.state.name == "new" and params[:newstate] == "revoked" 
571            # source project owners should be able to revoke submit requests as well
572            source_package = source_project.db_packages.find_by_name(action.source.package)
573            if ( source_package and @http_user.can_modify_package? source_package ) or
574               ( not source_package and @http_user.can_modify_project? source_project )
575              permission_granted = true
576            elsif permission_granted != true
577              render_error :status => 403, :errorcode => "post_request_no_permission",
578                :message => "No permission to revoke request #{req.id} (type #{action.data.attributes['type']})"
579              return
580            end
581         else
582           if permission_granted != true
583             render_error :status => 403, :errorcode => "post_request_no_permission",
584               :message => "No permission to change state of request #{req.id} to #{params[:newstate]} (type #{action.data.attributes['type']})"
585             return
586           end
587         end
588     
589       elsif action.data.attributes["type"] == "delete" or action.data.attributes["type"] == "add_role" or action.data.attributes["type"] == "set_bugowner"
590         # check permissions for delete
591         project = DbProject.find_by_name(action.target.project)
592         if not project and params[:newstate] == "accepted"
593           msg = "Unable to delete project #{action.target.project}; it does not exist."
594           render_error :status => 400, :errorcode => 'not_existing_target',
595             :message => msg
596           return
597         end
598         package = nil
599         if action.target.has_attribute? :package
600            package = project.db_packages.find_by_name(action.target.package)
601            if not package and params[:newstate] == "accepted"
602              msg = "Unable to delete package #{action.target.project}/#{action.target.package}; it does not exist."
603              render_error :status => 400, :errorcode => 'not_existing_target',
604                :message => msg
605              return
606            end
607            if package and @http_user.can_modify_package? package
608               permission_granted = true
609            end
610         end
611         if not permission_granted and project and @http_user.can_modify_project? project
612            permission_granted = true
613         end
614         unless permission_granted == true
615           render_error :status => 403, :errorcode => "post_request_no_permission",
616             :message => "No permission to change state of request #{req.id} (type #{action.data.attributes['type']})"
617           return
618         end
619       else
620         render_error :status => 403, :errorcode => "post_request_no_permission",
621           :message => "Unknown request type #{params[:newstate]} of request #{req.id} (type #{action.data.attributes['type']})"
622         return
623       end
624     end
625
626     # at this point permissions should be granted, but let's double check
627     unless permission_granted == true
628       render_error :status => 403, :errorcode => "post_request_no_permission",
629         :message => "No permission to change state of request #{req.id} (INTERNAL ERROR, PLEASE REPORT ! )"
630       return
631     end
632
633     # All commands are process by the backend. Just the request accept is controlled by the api.
634     unless params[:cmd] == "changestate" and params[:newstate] == "accepted"
635       pass_to_backend path
636       return
637     end
638
639     # We have permission to change all requests inside, now execute
640     req.each_action do |action|
641       if action.data.attributes["type"] == "set_bugowner"
642           object = DbProject.find_by_name(action.target.project)
643           bugowner = Role.find_by_title("bugowner")
644           if action.target.has_attribute? 'package'
645              object = object.db_packages.find_by_name(action.target.package)
646              PackageUserRoleRelationship.find(:all, :conditions => ["db_package_id = ? AND role_id = ?", object, bugowner]).each do |r|
647                 r.destroy
648              end
649           else
650              ProjectUserRoleRelationship.find(:all, :conditions => ["db_project_id = ? AND role_id = ?", object, bugowner]).each do |r|
651                 r.destroy
652              end
653           end
654           object.add_user( action.person.name, bugowner )
655           object.store
656       elsif action.data.attributes["type"] == "add_role"
657           object = DbProject.find_by_name(action.target.project)
658           if action.target.has_attribute? 'package'
659              object = object.db_packages.find_by_name(action.target.package)
660           end
661           if action.has_element? 'person'
662              role = Role.find_by_title(action.person.role)
663              object.add_user( action.person.name, role )
664           end
665           if action.has_element? 'group'
666              role = Role.find_by_title(action.group.role)
667              object.add_group( action.group.name, role )
668           end
669           object.store
670       elsif action.data.attributes["type"] == "change_devel"
671           target_project = DbProject.find_by_name(action.target.project)
672           target_package = target_project.db_packages.find_by_name(action.target.package)
673           target_package.develpackage = DbPackage.find_by_project_and_name(action.source.project, action.source.package)
674           begin
675             target_package.resolve_devel_package
676             target_package.store
677           rescue DbPackage::CycleError => e
678             # FIXME: this needs to be checked before, or we have a half submitted request
679             render_error :status => 403, :errorcode => "devel_cycle", :message => e.message
680             return
681           end
682       elsif action.data.attributes["type"] == "submit"
683           sourceupdate = nil
684           if action.has_element? 'options' and action.options.has_element? 'sourceupdate'
685             sourceupdate = action.options.sourceupdate.text
686           end
687           src = action.source
688           cp_params = {
689             :cmd => "copy",
690             :user => @http_user.login,
691             :oproject => src.project,
692             :opackage => src.package,
693             :requestid => params[:id],
694             :comment => params[:comment]
695           }
696           cp_params[:orev] = src.rev if src.has_attribute? :rev
697           cp_params[:dontupdatesource] = 1 if sourceupdate == "noupdate"
698           unless action.has_element? 'options' and action.options.has_element? 'updatelink' and action.options.updatelink == "true"
699             cp_params[:expand] = 1
700             cp_params[:keeplink] = 1
701           end
702
703           #create package unless it exists already
704           target_project = DbProject.find_by_name(action.target.project)
705           if action.target.has_attribute? :package
706             target_package = target_project.db_packages.find_by_name(action.target.package)
707           else
708             target_package = target_project.db_packages.find_by_name(action.source.package)
709           end
710           unless target_package
711             # create package in database
712             linked_package = target_project.find_package(action.target.package)
713             source_project = DbProject.find_by_name(action.source.project)
714             source_package = source_project.db_packages.find_by_name(action.source.package)
715             target_package = Package.new(source_package.to_axml, :project => action.target.project)
716             target_package.name = action.target.package
717             target_package.remove_all_persons
718             target_package.remove_all_flags
719             target_package.remove_devel_project
720             target_package.save
721
722             # check if package was available via project link and create a branch from it in that case
723             if linked_package
724               r = Suse::Backend.post "/source/#{action.target.project}/#{action.target.package}?cmd=branch&oproject=#{CGI.escape(linked_package.db_project.name)}&opackage=#{CGI.escape(linked_package.name)}", nil
725             end
726           end
727
728           cp_path = "/source/#{action.target.project}/#{action.target.package}"
729           cp_path << build_query_from_hash(cp_params, [:cmd, :user, :oproject, :opackage, :orev, :expand, :keeplink, :comment, :requestid, :dontupdatesource])
730           Suse::Backend.post cp_path, nil
731
732           # cleanup source project
733           if sourceupdate == "cleanup"
734             source_project = DbProject.find_by_name(action.source.project)
735             source_package = source_project.db_packages.find_by_name(action.source.package)
736             if source_project.db_packages.count == 1
737               #find linking repos
738               lreps = Array.new
739               source_project.repositories.each do |repo|
740                 repo.linking_repositories.each do |lrep|
741                   lreps << lrep
742                 end
743               end
744               if lreps.length > 0
745                 #replace links to this projects with links to the "deleted" project
746                 del_repo = DbProject.find_by_name("deleted").repositories[0]
747                 lreps.each do |link_rep|
748                   link_rep.path_elements.find(:all, :include => ["link"]) do |pe|
749                     next unless Repository.find_by_id(pe.repository_id).db_project_id == source_project.id
750                     pe.link = del_repo
751                     pe.save
752                     #update backend
753                     link_prj = link_rep.db_project
754                     logger.info "updating project '#{link_prj.name}'"
755                     Suse::Backend.put_source "/source/#{link_prj.name}/_meta", link_prj.to_axml
756                   end
757                 end
758               end
759
760               if source_project.name != "home:" + user.login
761                 # remove source project, if this is the only package and not the user's home project
762                 source_project.destroy
763                 Suse::Backend.delete "/source/#{action.source.project}"
764               end
765             else
766               # just remove package
767               source_package.destroy
768               Suse::Backend.delete "/source/#{action.source.project}/#{action.source.package}"
769             end
770           end
771       elsif action.data.attributes["type"] == "delete"
772           project = DbProject.find_by_name(action.target.project)
773           if not action.target.has_attribute? :package
774             project.destroy
775             Suse::Backend.delete "/source/#{action.target.project}"
776           else
777             DbPackage.transaction do
778               package = project.db_packages.find_by_name(action.target.package)
779               package.destroy
780               Suse::Backend.delete "/source/#{action.target.project}/#{action.target.package}"
781             end
782           end
783       end
784
785       if action.target.has_attribute? :package and action.target.package == "_product"
786         update_product_autopackages action.target.project
787       end
788     end
789     pass_to_backend path
790   end
791 end