summaryrefslogtreecommitdiff
path: root/src/ui/certimportwidget.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/ui/certimportwidget.c')
-rw-r--r--src/ui/certimportwidget.c225
1 files changed, 225 insertions, 0 deletions
diff --git a/src/ui/certimportwidget.c b/src/ui/certimportwidget.c
new file mode 100644
index 00000000..51743e89
--- /dev/null
+++ b/src/ui/certimportwidget.c
@@ -0,0 +1,225 @@
1/* Copyright 2021 Jaakko Keränen <jaakko.keranen@iki.fi>
2
3Redistribution and use in source and binary forms, with or without
4modification, are permitted provided that the following conditions are met:
5
61. Redistributions of source code must retain the above copyright notice, this
7 list of conditions and the following disclaimer.
82. Redistributions in binary form must reproduce the above copyright notice,
9 this list of conditions and the following disclaimer in the documentation
10 and/or other materials provided with the distribution.
11
12THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
13ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
14WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
15DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
16ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
17(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
18LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
19ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
20(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
21SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
22
23#include "certimportwidget.h"
24#include "labelwidget.h"
25#include "inputwidget.h"
26#include "color.h"
27#include "text.h"
28#include "ui/util.h"
29#include "app.h"
30
31#include <the_Foundation/tlsrequest.h>
32#include <SDL_clipboard.h>
33
34iDefineObjectConstruction(CertImportWidget)
35
36struct Impl_CertImportWidget {
37 iWidget widget;
38 iLabelWidget *info;
39 iLabelWidget *crtLabel;
40 iLabelWidget *keyLabel;
41 iInputWidget *filename;
42 iInputWidget *notes;
43 iTlsCertificate *cert;
44};
45
46static const char *infoText_ = "Paste a PEM-encoded certificate and/or private key,\n"
47 "or drop a .crt/.key file on the window.";
48
49static iBool tryImport_CertImportWidget_(iCertImportWidget *d, const iBlock *data) {
50 iBool ok = iFalse;
51 iString pem;
52 initBlock_String(&pem, data);
53 iTlsCertificate *newCert = newPemKey_TlsCertificate(&pem, &pem);
54 const iBool gotNewCrt = !isEmpty_TlsCertificate(newCert);
55 const iBool gotNewKey = hasPrivateKey_TlsCertificate(newCert);
56 if (d->cert && (gotNewCrt ^ gotNewKey)) { /* One new part? Merge with existing. */
57 const iString *crt = collect_String(pem_TlsCertificate(gotNewCrt ? newCert : d->cert));
58 const iString *key = collect_String(privateKeyPem_TlsCertificate(gotNewKey ? newCert : d->cert));
59 delete_TlsCertificate(d->cert);
60 delete_TlsCertificate(newCert);
61 d->cert = newPemKey_TlsCertificate(crt, key);
62 ok = iTrue;
63 }
64 else if (gotNewCrt || gotNewKey) {
65 delete_TlsCertificate(d->cert);
66 d->cert = newCert;
67 ok = iTrue;
68 }
69 else {
70 delete_TlsCertificate(newCert);
71 }
72 deinit_String(&pem);
73 /* Update the labels. */ {
74 if (d->cert && !isEmpty_TlsCertificate(d->cert)) {
75 setTextCStr_LabelWidget(
76 d->crtLabel,
77 format_CStr("%s%s",
78 uiTextAction_ColorEscape,
79 cstrCollect_String(subject_TlsCertificate(d->cert))));
80 setFrameColor_Widget(as_Widget(d->crtLabel), uiTextAction_ColorId);
81 }
82 else {
83 setTextCStr_LabelWidget(d->crtLabel, uiTextCaution_ColorEscape "No Certificate");
84 setFrameColor_Widget(as_Widget(d->crtLabel), uiTextCaution_ColorId);
85 }
86 if (d->cert && hasPrivateKey_TlsCertificate(d->cert)) {
87 iString *fng = collect_String(
88 hexEncode_Block(collect_Block(privateKeyFingerprint_TlsCertificate(d->cert))));
89 insertData_Block(&fng->chars, size_String(fng) / 2, "\n", 1);
90 setTextCStr_LabelWidget(
91 d->keyLabel, format_CStr("%s%s", uiTextAction_ColorEscape, cstr_String(fng)));
92 setFrameColor_Widget(as_Widget(d->keyLabel), uiTextAction_ColorId);
93 }
94 else {
95 setTextCStr_LabelWidget(d->keyLabel, uiTextCaution_ColorEscape "No Private Key");
96 setFrameColor_Widget(as_Widget(d->keyLabel), uiTextCaution_ColorId);
97 }
98 }
99 return ok;
100}
101
102void init_CertImportWidget(iCertImportWidget *d) {
103 iWidget *w = as_Widget(d);
104 init_Widget(w);
105 setId_Widget(w, "certimport");
106 d->cert = NULL;
107 /* This should behave similar to sheets. */ {
108 setPadding1_Widget(w, 3 * gap_UI);
109 setFrameColor_Widget(w, uiSeparator_ColorId);
110 setBackgroundColor_Widget(w, uiBackground_ColorId);
111 setFlags_Widget(w,
112 mouseModal_WidgetFlag | keepOnTop_WidgetFlag | arrangeVertical_WidgetFlag |
113 arrangeSize_WidgetFlag | centerHorizontal_WidgetFlag |
114 overflowScrollable_WidgetFlag,
115 iTrue);
116 }
117 addChildFlags_Widget(w,
118 iClob(new_LabelWidget(uiHeading_ColorEscape "IMPORT IDENTITY", NULL)),
119 frameless_WidgetFlag);
120 d->info = addChildFlags_Widget(w, iClob(new_LabelWidget(infoText_, NULL)), frameless_WidgetFlag);
121 addChild_Widget(w, iClob(makePadding_Widget(gap_UI)));
122 d->crtLabel = new_LabelWidget("", NULL); {
123 setFont_LabelWidget(d->crtLabel, uiContent_FontId);
124 addChildFlags_Widget(w, iClob(d->crtLabel), 0);
125 setFrameColor_Widget(as_Widget(d->crtLabel), uiTextCaution_ColorId);
126 }
127 d->keyLabel = new_LabelWidget("", NULL); {
128 setFont_LabelWidget(d->keyLabel, uiContent_FontId);
129 addChild_Widget(w, iClob(makePadding_Widget(gap_UI)));
130 addChildFlags_Widget(w, iClob(d->keyLabel), 0);
131 setFrameColor_Widget(as_Widget(d->keyLabel), uiTextCaution_ColorId);
132 }
133 addChild_Widget(w, iClob(makePadding_Widget(gap_UI)));
134 iWidget *page = new_Widget(); {
135 setFlags_Widget(page, arrangeHorizontal_WidgetFlag | arrangeSize_WidgetFlag, iTrue);
136 iWidget *headings = addChildFlags_Widget(
137 page, iClob(new_Widget()), arrangeVertical_WidgetFlag | arrangeSize_WidgetFlag);
138 iWidget *values = addChildFlags_Widget(
139 page, iClob(new_Widget()), arrangeVertical_WidgetFlag | arrangeSize_WidgetFlag);
140 addChild_Widget(headings, iClob(makeHeading_Widget("Save as:")));
141 addChild_Widget(values, iClob(d->filename = new_InputWidget(0)));
142 setHint_InputWidget(d->filename, "filename (no extension)");
143 addChild_Widget(headings, iClob(makeHeading_Widget("Notes:")));
144 addChild_Widget(values, iClob(d->notes = new_InputWidget(0)));
145 setHint_InputWidget(d->notes, "e.g., site name");
146 as_Widget(d->filename)->rect.size.x = gap_UI * 70;
147 as_Widget(d->notes)->rect.size.x = gap_UI * 70;
148 }
149 addChild_Widget(w, iClob(page));
150 arrange_Widget(w);
151 setSize_Widget(as_Widget(d->crtLabel), init_I2(width_Widget(w) - 6.5 * gap_UI, gap_UI * 12));
152 setSize_Widget(as_Widget(d->keyLabel), init_I2(width_Widget(w) - 6.5 * gap_UI, gap_UI * 12));
153 /* Buttons. */
154 iWidget *div = new_Widget(); {
155 setFlags_Widget(div, arrangeHorizontal_WidgetFlag | arrangeSize_WidgetFlag, iTrue);
156 addChild_Widget(div, iClob(newKeyMods_LabelWidget("Cancel", SDLK_ESCAPE, 0, "cancel")));
157 iLabelWidget *accept = addChild_Widget(
158 div,
159 iClob(newKeyMods_LabelWidget(
160 uiTextAction_ColorEscape "Import", SDLK_RETURN, KMOD_PRIMARY, "certimport.accept")));
161 setFont_LabelWidget(accept, uiLabelBold_FontId);
162 }
163 addChild_Widget(w, iClob(div));
164 arrange_Widget(w);
165}
166
167void deinit_CertImportWidget(iCertImportWidget *d) {
168 delete_TlsCertificate(d->cert);
169}
170
171static iBool isComplete_CertImportWidget_(const iCertImportWidget *d) {
172 return d->cert && !isEmpty_TlsCertificate(d->cert) && hasPrivateKey_TlsCertificate(d->cert);
173}
174
175void setPageContent_CertImportWidget(iCertImportWidget *d, const iBlock *content) {
176 if (tryImport_CertImportWidget_(d, content)) {
177 setTextCStr_LabelWidget(d->info, infoText_);
178 if (isComplete_CertImportWidget_(d)) {
179 setFocus_Widget(as_Widget(d->filename));
180 }
181 }
182 else {
183 setTextCStr_LabelWidget(
184 d->info, format_CStr("No certificate/key found on the current page.\n%s", infoText_));
185 }
186 arrange_Widget(as_Widget(d));
187}
188
189static iBool processEvent_CertImportWidget_(iCertImportWidget *d, const SDL_Event *ev) {
190 iWidget *w = as_Widget(d);
191 if (ev->type == SDL_KEYDOWN) {
192 const int key = ev->key.keysym.sym;
193 const int mods = keyMods_Sym(ev->key.keysym.mod);
194 if (key == SDLK_v && mods == KMOD_PRIMARY) {
195 if (!tryImport_CertImportWidget_(
196 d, collect_Block(newCStr_Block(SDL_GetClipboardText())))) {
197 makeMessage_Widget(uiTextCaution_ColorEscape "PASTED FROM CLIPBOARD",
198 "No certificate or private key was found.");
199 }
200 postRefresh_App();
201 return iTrue;
202 }
203 }
204 if (isCommand_Widget(w, ev, "cancel")) {
205 destroy_Widget(w);
206 return iTrue;
207 }
208 if (isCommand_Widget(w, ev, "certimport.accept")) {
209 if (d->cert) {
210 destroy_Widget(w);
211 }
212 return iTrue;
213 }
214 return processEvent_Widget(w, ev);
215}
216
217static void draw_CertImportWidget_(const iCertImportWidget *d) {
218 const iWidget *w = constAs_Widget(d);
219 draw_Widget(w);
220}
221
222iBeginDefineSubclass(CertImportWidget, Widget)
223 .processEvent = (iAny *) processEvent_CertImportWidget_,
224 .draw = (iAny *) draw_CertImportWidget_,
225iEndDefineSubclass(CertImportWidget)