~ubuntu-branches/ubuntu/saucy/jenkins/saucy

« back to all changes in this revision

Viewing changes to core/src/main/java/hudson/model/View.java

  • Committer: Package Import Robot
  • Author(s): James Page
  • Date: 2013-01-10 09:50:50 UTC
  • mfrom: (5.1.10 experimental)
  • Revision ID: package-import@ubuntu.com-20130110095050-kj8xuw20gcfh62k3
Tags: 1.480.2+dfsg-1~exp1
* New upstream release (Closes: #696816, #697617):
  - d/control: Added new BD on libjbcrypt-java.
  - d/control: Versioned BD jenkins-winstone >= 0.9.10-jenkins-40.
  - d/control: Versioned BD jenkins-trilead-ssh2 >= 214-jenkins-1.
  - Fixes the following security vulnerabilities:
    CVE-2012-6072, CVE-2012-6073, CVE-2012-6072, CVE-2013-0158.
* Tidied lintian warnings.
* Bumped Standards-Version: 3.9.4, no changes.

Show diffs side-by-side

added added

removed removed

Lines of Context:
24
24
 */
25
25
package hudson.model;
26
26
 
 
27
import com.thoughtworks.xstream.converters.ConversionException;
 
28
import com.thoughtworks.xstream.io.StreamException;
 
29
import com.thoughtworks.xstream.io.xml.XppDriver;
27
30
import hudson.DescriptorExtensionList;
28
31
import hudson.Extension;
29
32
import hudson.ExtensionPoint;
 
33
import hudson.Functions;
30
34
import hudson.Indenter;
31
35
import hudson.Util;
 
36
import hudson.XmlFile;
32
37
import hudson.model.Descriptor.FormException;
33
38
import hudson.model.Node.Mode;
 
39
import hudson.model.labels.LabelAtom;
34
40
import hudson.model.labels.LabelAtomPropertyDescriptor;
 
41
import hudson.scm.ChangeLogSet;
35
42
import hudson.scm.ChangeLogSet.Entry;
36
43
import hudson.search.CollectionSearchIndex;
37
44
import hudson.search.SearchIndexBuilder;
40
47
import hudson.security.Permission;
41
48
import hudson.security.PermissionGroup;
42
49
import hudson.security.PermissionScope;
 
50
import hudson.tasks.UserAvatarResolver;
43
51
import hudson.util.AlternativeUiTextProvider;
44
52
import hudson.util.AlternativeUiTextProvider.Message;
 
53
import hudson.util.AtomicFileWriter;
45
54
import hudson.util.DescribableList;
46
55
import hudson.util.DescriptorList;
 
56
import hudson.util.IOException2;
 
57
import hudson.util.IOUtils;
47
58
import hudson.util.RunList;
 
59
import hudson.util.XStream2;
48
60
import hudson.views.ListViewColumn;
49
61
import hudson.widgets.Widget;
50
62
import jenkins.model.Jenkins;
 
63
import jenkins.util.ProgressiveRendering;
 
64
import net.sf.json.JSON;
 
65
import net.sf.json.JSONArray;
 
66
import net.sf.json.JSONObject;
 
67
import org.kohsuke.stapler.HttpResponse;
 
68
import org.kohsuke.stapler.HttpResponses;
 
69
import org.kohsuke.stapler.Stapler;
51
70
import org.kohsuke.stapler.StaplerRequest;
52
71
import org.kohsuke.stapler.StaplerResponse;
 
72
import org.kohsuke.stapler.WebMethod;
53
73
import org.kohsuke.stapler.export.Exported;
54
74
import org.kohsuke.stapler.export.ExportedBean;
55
75
import org.kohsuke.stapler.interceptor.RequirePOST;
56
76
 
57
77
import javax.servlet.ServletException;
 
78
import javax.servlet.http.HttpServletResponse;
 
79
import javax.xml.transform.Source;
 
80
import javax.xml.transform.Transformer;
 
81
import javax.xml.transform.TransformerException;
 
82
import javax.xml.transform.TransformerFactory;
 
83
import javax.xml.transform.stream.StreamResult;
 
84
import javax.xml.transform.stream.StreamSource;
 
85
import java.io.BufferedInputStream;
 
86
import java.io.ByteArrayInputStream;
 
87
import java.io.FileInputStream;
58
88
import java.io.IOException;
 
89
import java.io.InputStream;
 
90
import java.io.StringWriter;
59
91
import java.util.ArrayList;
60
92
import java.util.Arrays;
61
93
import java.util.Calendar;
67
99
import java.util.HashSet;
68
100
import java.util.List;
69
101
import java.util.Map;
 
102
import java.util.Set;
70
103
import java.util.logging.Level;
71
104
import java.util.logging.Logger;
72
105
 
 
106
import static javax.servlet.http.HttpServletResponse.SC_BAD_REQUEST;
73
107
import static jenkins.model.Jenkins.*;
74
108
 
75
109
/**
383
417
        for (Computer c : computers) {
384
418
            Node n = c.getNode();
385
419
            if (n != null) {
386
 
                if (labels.contains(null) && n.getMode() == Mode.NORMAL || !Collections.disjoint(n.getAssignedLabels(), labels)) {
 
420
                if (labels.contains(null) && n.getMode() == Mode.NORMAL || !isDisjoint(n.getAssignedLabels(), labels)) {
387
421
                    result.add(c);
388
422
                }
389
423
            }
391
425
 
392
426
        return result;
393
427
    }
394
 
    
 
428
 
 
429
    private boolean isDisjoint(Collection c1, Collection c2) {
 
430
        for (Object o : c1)
 
431
            if (c2.contains(o))
 
432
                return false;
 
433
        return true;
 
434
    }
 
435
 
395
436
    public List<Queue.Item> getQueueItems() {
396
437
        if (!isFilterQueue()) {
397
438
            return Arrays.asList(Jenkins.getInstance().getQueue().getItems());
449
490
        result.addAll(getOwnerViewActions());
450
491
        synchronized (this) {
451
492
                if (transientActions == null) {
452
 
                        transientActions = TransientViewActionFactory.createAllFor(this); 
 
493
                updateTransientActions();
453
494
                }
454
495
                result.addAll(transientActions);
455
496
        }
456
497
        return result;
457
498
    }
458
499
    
 
500
    public synchronized void updateTransientActions() {
 
501
        transientActions = TransientViewActionFactory.createAllFor(this); 
 
502
    }
 
503
    
459
504
    public Object getDynamic(String token) {
460
505
        for (Action a : getActions()) {
461
506
            String url = a.getUrlName();
594
639
        return new People(this);
595
640
    }
596
641
 
 
642
    /**
 
643
     * @since 1.484
 
644
     */
 
645
    public AsynchPeople getAsynchPeople() {
 
646
        return new AsynchPeople(this);
 
647
    }
 
648
 
597
649
    @ExportedBean
598
650
    public static final class People  {
599
651
        @Exported
675
727
        }
676
728
    }
677
729
 
 
730
    /**
 
731
     * Variant of {@link People} which can be displayed progressively, since it may be slow.
 
732
     * @since 1.484
 
733
     */
 
734
    public static final class AsynchPeople extends ProgressiveRendering { // JENKINS-15206
 
735
 
 
736
        private final Collection<TopLevelItem> items;
 
737
        private final User unknown;
 
738
        private final Map<User,UserInfo> users = new HashMap<User,UserInfo>();
 
739
        private final Set<User> modified = new HashSet<User>();
 
740
        private final String iconSize;
 
741
        public final ModelObject parent;
 
742
 
 
743
        /** @see Jenkins#getAsynchPeople} */
 
744
        public AsynchPeople(Jenkins parent) {
 
745
            this.parent = parent;
 
746
            items = parent.getItems();
 
747
            unknown = User.getUnknown();
 
748
        }
 
749
 
 
750
        /** @see View#getAsynchPeople */
 
751
        public AsynchPeople(View parent) {
 
752
            this.parent = parent;
 
753
            items = parent.getItems();
 
754
            unknown = null;
 
755
        }
 
756
 
 
757
        {
 
758
            StaplerRequest req = Stapler.getCurrentRequest();
 
759
            iconSize = req != null ? Functions.getCookie(req, "iconSize", "32x32") : "32x32";
 
760
        }
 
761
 
 
762
        @Override protected void compute() throws Exception {
 
763
            int itemCount = 0;
 
764
            for (Item item : items) {
 
765
                for (Job<?,?> job : item.getAllJobs()) {
 
766
                    if (job instanceof AbstractProject) {
 
767
                        AbstractProject<?,?> p = (AbstractProject) job;
 
768
                        RunList<? extends AbstractBuild<?,?>> builds = p.getBuilds();
 
769
                        int buildCount = 0;
 
770
                        for (AbstractBuild<?,?> build : builds) {
 
771
                            if (canceled()) {
 
772
                                return;
 
773
                            }
 
774
                            for (ChangeLogSet.Entry entry : build.getChangeSet()) {
 
775
                                User user = entry.getAuthor();
 
776
                                synchronized (this) {
 
777
                                    UserInfo info = users.get(user);
 
778
                                    if (info == null) {
 
779
                                        users.put(user, new UserInfo(user, p, build.getTimestamp()));
 
780
                                        modified.add(user);
 
781
                                    } else if (info.getLastChange().before(build.getTimestamp())) {
 
782
                                        info.project = p;
 
783
                                        info.lastChange = build.getTimestamp();
 
784
                                        modified.add(user);
 
785
                                    }
 
786
                                }
 
787
                            }
 
788
                            buildCount++;
 
789
                            progress((itemCount + 1.0 * buildCount / builds.size()) / (items.size() + 1));
 
790
                        }
 
791
                    }
 
792
                }
 
793
                itemCount++;
 
794
                progress(1.0 * itemCount / (items.size() + /* handling User.getAll */1));
 
795
            }
 
796
            if (unknown != null) {
 
797
                if (canceled()) {
 
798
                    return;
 
799
                }
 
800
                for (User u : User.getAll()) { // XXX nice to have a method to iterate these lazily
 
801
                    if (u == unknown) {
 
802
                        continue;
 
803
                    }
 
804
                    if (!users.containsKey(u)) {
 
805
                        synchronized (this) {
 
806
                            users.put(u, new UserInfo(u, null, null));
 
807
                            modified.add(u);
 
808
                        }
 
809
                    }
 
810
                }
 
811
            }
 
812
        }
 
813
 
 
814
        @Override protected synchronized JSON data() {
 
815
            JSONArray r = new JSONArray();
 
816
            for (User u : modified) {
 
817
                UserInfo i = users.get(u);
 
818
                JSONObject entry = new JSONObject().
 
819
                        accumulate("id", u.getId()).
 
820
                        accumulate("fullName", u.getFullName()).
 
821
                        accumulate("url", u.getUrl()).
 
822
                        accumulate("avatar", UserAvatarResolver.resolve(u, iconSize)).
 
823
                        accumulate("timeSortKey", i.getTimeSortKey()).
 
824
                        accumulate("lastChangeTimeString", i.getLastChangeTimeString());
 
825
                AbstractProject<?,?> p = i.getProject();
 
826
                if (p != null) {
 
827
                    entry.accumulate("projectUrl", p.getUrl()).accumulate("projectFullDisplayName", p.getFullDisplayName());
 
828
                }
 
829
                r.add(entry);
 
830
            }
 
831
            modified.clear();
 
832
            return r;
 
833
        }
 
834
 
 
835
        public Api getApi() {
 
836
            return new Api(parent instanceof Jenkins ? new People((Jenkins) parent) : new People((View) parent));
 
837
        }
 
838
 
 
839
    }
 
840
 
678
841
    void addDisplayNamesToSearchIndex(SearchIndexBuilder sib, Collection<TopLevelItem> items) {
679
842
        for(TopLevelItem item : items) {
680
843
            
734
897
        rename(req.getParameter("name"));
735
898
 
736
899
        getProperties().rebuild(req, req.getSubmittedForm(), getApplicablePropertyDescriptors());
 
900
        updateTransientActions();  
737
901
 
738
902
        save();
739
903
 
807
971
    }
808
972
 
809
973
    /**
 
974
     * Accepts <tt>config.xml</tt> submission, as well as serve it.
 
975
     */
 
976
    @WebMethod(name = "config.xml")
 
977
    public HttpResponse doConfigDotXml(StaplerRequest req) throws IOException {
 
978
        if (req.getMethod().equals("GET")) {
 
979
            // read
 
980
            checkPermission(READ);
 
981
            return new HttpResponse() {
 
982
                public void generateResponse(StaplerRequest req, StaplerResponse rsp, Object node) throws IOException, ServletException {
 
983
                    rsp.setContentType("application/xml");
 
984
                    // pity we don't have a handy way to clone Jenkins.XSTREAM to temp add the omit Field
 
985
                    XStream2 xStream2 = new XStream2();
 
986
                    xStream2.omitField(View.class, "owner");
 
987
                    rsp.getOutputStream().write("<?xml version='1.0' encoding='UTF-8'?>\n".getBytes("UTF-8"));
 
988
                    xStream2.toXML(this,  rsp.getOutputStream());
 
989
                }
 
990
            };
 
991
        }
 
992
        if (req.getMethod().equals("POST")) {
 
993
            // submission
 
994
            updateByXml((Source)new StreamSource(req.getReader()));
 
995
            return HttpResponses.ok();
 
996
        }
 
997
 
 
998
        // huh?
 
999
        return HttpResponses.error(SC_BAD_REQUEST, "Unexpected request method " + req.getMethod());
 
1000
    }
 
1001
 
 
1002
    /**
 
1003
     * Updates Job by its XML definition.
 
1004
     */
 
1005
    public void updateByXml(Source source) throws IOException {
 
1006
        checkPermission(CONFIGURE);
 
1007
        StringWriter out = new StringWriter();
 
1008
        try {
 
1009
            // this allows us to use UTF-8 for storing data,
 
1010
            // plus it checks any well-formedness issue in the submitted
 
1011
            // data
 
1012
            Transformer t = TransformerFactory.newInstance()
 
1013
                    .newTransformer();
 
1014
            t.transform(source,
 
1015
                    new StreamResult(out));
 
1016
            out.close();
 
1017
        } catch (TransformerException e) {
 
1018
            throw new IOException2("Failed to persist configuration.xml", e);
 
1019
        }
 
1020
 
 
1021
        // try to reflect the changes by reloading
 
1022
        InputStream in = new BufferedInputStream(new ByteArrayInputStream(out.toString().getBytes("UTF-8")));
 
1023
        try {
 
1024
            Jenkins.XSTREAM.unmarshal(new XppDriver().createReader(in), this);
 
1025
        } catch (StreamException e) {
 
1026
            throw new IOException2("Unable to read",e);
 
1027
        } catch(ConversionException e) {
 
1028
            throw new IOException2("Unable to read",e);
 
1029
        } catch(Error e) {// mostly reflection errors
 
1030
            throw new IOException2("Unable to read",e);
 
1031
        } finally {
 
1032
            in.close();
 
1033
        }
 
1034
    }
 
1035
 
 
1036
 
 
1037
    /**
810
1038
     * A list of available view types.
811
1039
     * @deprecated as of 1.286
812
1040
     *      Use {@link #all()} for read access, and use {@link Extension} for registration.
850
1078
    
851
1079
    public static View create(StaplerRequest req, StaplerResponse rsp, ViewGroup owner)
852
1080
            throws FormException, IOException, ServletException {
 
1081
        String requestContentType = req.getContentType();
 
1082
        if(requestContentType==null)
 
1083
            throw new Failure("No Content-Type header set");
 
1084
 
 
1085
        boolean isXmlSubmission = requestContentType.startsWith("application/xml") || requestContentType.startsWith("text/xml");
 
1086
 
853
1087
        String name = req.getParameter("name");
854
1088
        checkGoodName(name);
855
1089
        if(owner.getView(name)!=null)
856
1090
            throw new FormException(Messages.Hudson_ViewAlreadyExists(name),"name");
857
1091
 
858
1092
        String mode = req.getParameter("mode");
859
 
        if (mode==null || mode.length()==0)
860
 
            throw new FormException(Messages.View_MissingMode(),"mode");
 
1093
        if (mode==null || mode.length()==0) {
 
1094
            if(isXmlSubmission) {
 
1095
                View v;
 
1096
                v = createViewFromXML(name, req.getInputStream());
 
1097
                v.owner = owner;
 
1098
                rsp.setStatus(HttpServletResponse.SC_OK);
 
1099
                return v;
 
1100
            } else
 
1101
                throw new FormException(Messages.View_MissingMode(),"mode");
 
1102
        }
861
1103
 
862
1104
        // create a view
863
1105
        View v = all().findByName(mode).newInstance(req,req.getSubmittedForm());
869
1111
        return v;
870
1112
    }
871
1113
 
 
1114
    public static View createViewFromXML(String name, InputStream xml) throws IOException {
 
1115
        InputStream in = new BufferedInputStream(xml);
 
1116
        try {
 
1117
            View v = (View) Jenkins.XSTREAM.fromXML(in);
 
1118
            v.name = name;
 
1119
            return v;
 
1120
        } catch(StreamException e) {
 
1121
            throw new IOException2("Unable to read",e);
 
1122
        } catch(ConversionException e) {
 
1123
            throw new IOException2("Unable to read",e);
 
1124
        } catch(Error e) {// mostly reflection errors
 
1125
            throw new IOException2("Unable to read",e);
 
1126
        } finally {
 
1127
            in.close();
 
1128
        }
 
1129
    }
 
1130
 
872
1131
    public static class PropertyList extends DescribableList<ViewProperty,ViewPropertyDescriptor> {
873
1132
        private PropertyList(View owner) {
874
1133
            super(owner);