xref: /trunk/test/odf-validate/fixtures/signed/make.py (revision 0e16a31d78a652c56c5c0fc79c752f48b9048df9)
1*0e16a31dSJim Jagielski#**************************************************************
2*0e16a31dSJim Jagielski#
3*0e16a31dSJim Jagielski#  Licensed to the Apache Software Foundation (ASF) under one
4*0e16a31dSJim Jagielski#  or more contributor license agreements.  See the NOTICE file
5*0e16a31dSJim Jagielski#  distributed with this work for additional information
6*0e16a31dSJim Jagielski#  regarding copyright ownership.  The ASF licenses this file
7*0e16a31dSJim Jagielski#  to you under the Apache License, Version 2.0 (the
8*0e16a31dSJim Jagielski#  "License"); you may not use this file except in compliance
9*0e16a31dSJim Jagielski#  with the License.  You may obtain a copy of the License at
10*0e16a31dSJim Jagielski#
11*0e16a31dSJim Jagielski#    http://www.apache.org/licenses/LICENSE-2.0
12*0e16a31dSJim Jagielski#
13*0e16a31dSJim Jagielski#  Unless required by applicable law or agreed to in writing,
14*0e16a31dSJim Jagielski#  software distributed under the License is distributed on an
15*0e16a31dSJim Jagielski#  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16*0e16a31dSJim Jagielski#  KIND, either express or implied.  See the License for the
17*0e16a31dSJim Jagielski#  specific language governing permissions and limitations
18*0e16a31dSJim Jagielski#  under the License.
19*0e16a31dSJim Jagielski#
20*0e16a31dSJim Jagielski
21*0e16a31dSJim Jagielski# usage: runpy.sh fixtures/signed/make.py <port> <outdir>
22*0e16a31dSJim Jagielski# Certificate: fixtures/signed/setup-cert.sh builds runs/task12/gen-signed/nssdb (certutil, one self-signed RSA key).
23*0e16a31dSJim Jagielski# Needs an office started by fixtures/signed/start-ui.sh <strict|ext> <port> (profile runs/profile_t12_sign_<mode>), not
24*0e16a31dSJim Jagielski# start.sh: -headless cancels every modal dialog and the Digital Signatures dialog is the only signing entry point.
25*0e16a31dSJim Jagielski# Saves with the office's configured ODF version, then signs each saved file in place by driving the dialog through
26*0e16a31dSJim Jagielski# its accessibility tree (Sign Document, pick the only certificate, OK, Close). The dialog runs on the office main
27*0e16a31dSJim Jagielski# thread (AsyncCallback), otherwise Execute() has nothing to run on.
28*0e16a31dSJim Jagielskiimport os, sys, time, threading, uno, unohelper
29*0e16a31dSJim Jagielskifrom com.sun.star.awt import XCallback
30*0e16a31dSJim Jagielskifrom com.sun.star.accessibility.AccessibleStateType import ENABLED
31*0e16a31dSJim Jagielskifrom unohelp import FILTERS, connect, desktop, pv
32*0e16a31dSJim Jagielski
33*0e16a31dSJim Jagielskiport, outdir = int(sys.argv[1]), sys.argv[2]
34*0e16a31dSJim Jagielskios.makedirs(outdir, exist_ok=True)
35*0e16a31dSJim Jagielskictx = connect(port)
36*0e16a31dSJim Jagielskism = ctx.ServiceManager
37*0e16a31dSJim Jagielskid = desktop(ctx)
38*0e16a31dSJim Jagielskitk = sm.createInstanceWithContext("com.sun.star.awt.Toolkit", ctx)
39*0e16a31dSJim Jagielskidisp = sm.createInstanceWithContext("com.sun.star.frame.DispatchHelper", ctx)
40*0e16a31dSJim JagielskiPUSH_BUTTON = 44
41*0e16a31dSJim Jagielski
42*0e16a31dSJim Jagielski
43*0e16a31dSJim Jagielskidef build_writer(doc):
44*0e16a31dSJim Jagielski    t = doc.Text
45*0e16a31dSJim Jagielski    t.insertString(t.createTextCursor(), "Signed text document.", False)
46*0e16a31dSJim Jagielski
47*0e16a31dSJim Jagielski
48*0e16a31dSJim Jagielskidef build_calc(doc):
49*0e16a31dSJim Jagielski    sh = doc.Sheets.getByIndex(0)
50*0e16a31dSJim Jagielski    sh.getCellByPosition(0, 0).String = "label"
51*0e16a31dSJim Jagielski    sh.getCellByPosition(1, 0).Value = 42
52*0e16a31dSJim Jagielski    sh.getCellByPosition(1, 1).Formula = "=B1*2"
53*0e16a31dSJim Jagielski
54*0e16a31dSJim Jagielski
55*0e16a31dSJim Jagielskidef build_impress(doc):
56*0e16a31dSJim Jagielski    page = doc.DrawPages.getByIndex(0)
57*0e16a31dSJim Jagielski    sh = doc.createInstance("com.sun.star.drawing.TextShape")
58*0e16a31dSJim Jagielski    page.add(sh)
59*0e16a31dSJim Jagielski    sh.String = "Signed presentation"
60*0e16a31dSJim Jagielski
61*0e16a31dSJim Jagielski
62*0e16a31dSJim Jagielskidef build_draw(doc):
63*0e16a31dSJim Jagielski    page = doc.DrawPages.getByIndex(0)
64*0e16a31dSJim Jagielski    sh = doc.createInstance("com.sun.star.drawing.RectangleShape")
65*0e16a31dSJim Jagielski    page.add(sh)
66*0e16a31dSJim Jagielski    sh.Size = uno.createUnoStruct("com.sun.star.awt.Size", 4000, 2000)
67*0e16a31dSJim Jagielski    sh.String = "Signed drawing"
68*0e16a31dSJim Jagielski
69*0e16a31dSJim Jagielski
70*0e16a31dSJim JagielskiKINDS = (("text", "odt", "swriter", build_writer), ("calc", "ods", "scalc", build_calc),
71*0e16a31dSJim Jagielski         ("impress", "odp", "simpress", build_impress), ("draw", "odg", "sdraw", build_draw))
72*0e16a31dSJim Jagielski
73*0e16a31dSJim Jagielski
74*0e16a31dSJim Jagielskidef find(acc, role, name):
75*0e16a31dSJim Jagielski    try:
76*0e16a31dSJim Jagielski        c = acc.getAccessibleContext()
77*0e16a31dSJim Jagielski    except Exception:
78*0e16a31dSJim Jagielski        return None
79*0e16a31dSJim Jagielski    if c.getAccessibleRole() == role and c.getAccessibleName() == name:
80*0e16a31dSJim Jagielski        return acc
81*0e16a31dSJim Jagielski    for i in range(c.getAccessibleChildCount()):
82*0e16a31dSJim Jagielski        r = find(c.getAccessibleChild(i), role, name)
83*0e16a31dSJim Jagielski        if r:
84*0e16a31dSJim Jagielski            return r
85*0e16a31dSJim Jagielski
86*0e16a31dSJim Jagielski
87*0e16a31dSJim Jagielskidef top(title):
88*0e16a31dSJim Jagielski    for k in range(tk.getTopWindowCount()):
89*0e16a31dSJim Jagielski        w = tk.getTopWindow(k)
90*0e16a31dSJim Jagielski        if w is not None and w.getAccessibleContext().getAccessibleName() == title:
91*0e16a31dSJim Jagielski            return w
92*0e16a31dSJim Jagielski
93*0e16a31dSJim Jagielski
94*0e16a31dSJim Jagielskidef wait_top(title, secs=30):
95*0e16a31dSJim Jagielski    end = time.time() + secs
96*0e16a31dSJim Jagielski    while time.time() < end:
97*0e16a31dSJim Jagielski        w = top(title)
98*0e16a31dSJim Jagielski        if w is not None:
99*0e16a31dSJim Jagielski            return w
100*0e16a31dSJim Jagielski        time.sleep(0.2)
101*0e16a31dSJim Jagielski    raise SystemExit("timeout waiting for window " + title)
102*0e16a31dSJim Jagielski
103*0e16a31dSJim Jagielski
104*0e16a31dSJim Jagielskidef press(win, name):
105*0e16a31dSJim Jagielski    b = find(win, PUSH_BUTTON, name)
106*0e16a31dSJim Jagielski    if b is None:
107*0e16a31dSJim Jagielski        raise SystemExit("no button " + name)
108*0e16a31dSJim Jagielski    t = threading.Thread(target=lambda: b.getAccessibleContext().doAccessibleAction(0), daemon=True)
109*0e16a31dSJim Jagielski    t.start()
110*0e16a31dSJim Jagielski    return t
111*0e16a31dSJim Jagielski
112*0e16a31dSJim Jagielski
113*0e16a31dSJim Jagielskiclass Sign(unohelper.Base, XCallback):
114*0e16a31dSJim Jagielski    def __init__(self, frame):
115*0e16a31dSJim Jagielski        self.frame, self.done, self.err = frame, False, None
116*0e16a31dSJim Jagielski
117*0e16a31dSJim Jagielski    def notify(self, data):
118*0e16a31dSJim Jagielski        try:
119*0e16a31dSJim Jagielski            disp.executeDispatch(self.frame, ".uno:Signature", "", 0, ())
120*0e16a31dSJim Jagielski        except Exception as e:
121*0e16a31dSJim Jagielski            self.err = e
122*0e16a31dSJim Jagielski        self.done = True
123*0e16a31dSJim Jagielski
124*0e16a31dSJim Jagielski
125*0e16a31dSJim Jagielskidef sign(path):
126*0e16a31dSJim Jagielski    doc = d.loadComponentFromURL(uno.systemPathToFileUrl(path), "_blank", 0, (pv("Hidden", True),))
127*0e16a31dSJim Jagielski    cb = Sign(doc.CurrentController.Frame)
128*0e16a31dSJim Jagielski    sm.createInstanceWithContext("com.sun.star.awt.AsyncCallback", ctx).addCallback(cb, None)
129*0e16a31dSJim Jagielski    dlg = wait_top("Digital Signatures")
130*0e16a31dSJim Jagielski    press(dlg, "Sign Document")
131*0e16a31dSJim Jagielski    chooser = wait_top("Select Certificate")
132*0e16a31dSJim Jagielski    pick_certificate(chooser)
133*0e16a31dSJim Jagielski    press(chooser, "OK")
134*0e16a31dSJim Jagielski    end = time.time() + 30
135*0e16a31dSJim Jagielski    while top("Select Certificate") is not None and time.time() < end:
136*0e16a31dSJim Jagielski        time.sleep(0.2)
137*0e16a31dSJim Jagielski    dlg = wait_top("Digital Signatures")
138*0e16a31dSJim Jagielski    time.sleep(1)
139*0e16a31dSJim Jagielski    press(dlg, "Close")
140*0e16a31dSJim Jagielski    end = time.time() + 30
141*0e16a31dSJim Jagielski    while not cb.done and time.time() < end:
142*0e16a31dSJim Jagielski        time.sleep(0.2)
143*0e16a31dSJim Jagielski    if not cb.done or cb.err:
144*0e16a31dSJim Jagielski        raise SystemExit("signing did not finish: %r" % cb.err)
145*0e16a31dSJim Jagielski    doc.close(True)
146*0e16a31dSJim Jagielski
147*0e16a31dSJim Jagielski
148*0e16a31dSJim Jagielskidef pick_certificate(chooser):
149*0e16a31dSJim Jagielski    ok = find(chooser, PUSH_BUTTON, "OK").getAccessibleContext()
150*0e16a31dSJim Jagielski    # The chooser preselects the only certificate of the throw-away database.
151*0e16a31dSJim Jagielski    if not ok.getAccessibleStateSet().contains(ENABLED):
152*0e16a31dSJim Jagielski        raise SystemExit("no certificate preselected in the chooser")
153*0e16a31dSJim Jagielski
154*0e16a31dSJim Jagielski
155*0e16a31dSJim Jagielskifor kind, ext, factory, build in KINDS:
156*0e16a31dSJim Jagielski    out = os.path.join(outdir, "signed-%s.%s" % (kind, ext))
157*0e16a31dSJim Jagielski    doc = d.loadComponentFromURL("private:factory/" + factory, "_blank", 0, (pv("Hidden", True),))
158*0e16a31dSJim Jagielski    build(doc)
159*0e16a31dSJim Jagielski    doc.storeToURL(uno.systemPathToFileUrl(out), (pv("FilterName", FILTERS[ext]),))
160*0e16a31dSJim Jagielski    doc.close(True)
161*0e16a31dSJim Jagielski    sign(out)
162*0e16a31dSJim Jagielski    print(out)
163