1
/* ***** BEGIN LICENSE BLOCK *****
2
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
4
* The contents of this file are subject to the Mozilla Public
5
* License Version 1.1 (the "MPL"); you may not use this file
6
* except in compliance with the MPL. You may obtain a copy of
7
* the MPL at http://www.mozilla.org/MPL/
9
* Software distributed under the MPL is distributed on an "AS
10
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
11
* implied. See the MPL for the specific language governing
12
* rights and limitations under the MPL.
14
* The Original Code is Enigmail.
16
* The Initial Developer of the Original Code is Patrick Brunschwig.
17
* Portions created by Patrick Brunschwig <patrick@mozilla-enigmail.org> are
18
* Copyright (C) 2004 Patrick Brunschwig. All Rights Reserved.
22
* Alternatively, the contents of this file may be used under the terms of
23
* either the GNU General Public License Version 2 or later (the "GPL"), or
24
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
25
* in which case the provisions of the GPL or the LGPL are applicable instead
26
* of those above. If you wish to allow use of your version of this file only
27
* under the terms of either the GPL or the LGPL, and not to allow others to
28
* use your version of this file under the terms of the MPL, indicate your
29
* decision by deleting the provisions above and replace them with the notice
30
* and other provisions required by the GPL or the LGPL. If you do not delete
31
* the provisions above, a recipient may use your version of this file under
32
* the terms of any one of the MPL, the GPL or the LGPL.
33
* ***** END LICENSE BLOCK ***** */
35
// Uses: chrome://enigmail/content/enigmailCommon.js
37
Components.utils.import("resource://enigmail/enigmailCommon.jsm");
38
const Ec = EnigmailCommon;
43
const ENIG_DEFAULT_HKP_PORT = "11371";
44
const ENIG_DEFAULT_LDAP_PORT = "389";
46
const ENIG_IMG_NOT_SELECTED = "chrome://enigmail/content/check0.png";
47
const ENIG_IMG_SELECTED = "chrome://enigmail/content/check1.png";
48
const ENIG_IMG_DISABLED = "chrome://enigmail/content/check2.png";
50
const ENIG_CONN_TYPE_HTTP = 1;
51
const ENIG_CONN_TYPE_GPGKEYS = 2;
53
const KEY_EXPIRED="e";
54
const KEY_REVOKED="r";
55
const KEY_INVALID="i";
56
const KEY_DISABLED="d";
57
const KEY_NOT_VALID=KEY_EXPIRED+KEY_REVOKED+KEY_INVALID+KEY_DISABLED;
64
return str.replace(/^(\s*)(.*)/, "$2").replace(/\s+$/,"");
69
window.arguments[RESULT].importedKeys=0;
71
var keyserver = window.arguments[INPUT].keyserver;
73
if (keyserver.search(/[a-zA-Z0-9\-\_\.]+:\/\//)==0) {
74
protocol=keyserver.replace(/^([a-zA-Z0-9\-\_\.]+)(:\/\/.*)/, "$1");
75
if (protocol.search(/hkp/i) >= 0) {
78
keyserver=keyserver.replace(/^[a-zA-Z0-9\-\_\.]+:\/\//, "");
87
port = ENIG_DEFAULT_HKP_PORT;
90
port = ENIG_DEFAULT_LDAP_PORT;
94
var m = keyserver.match(/^(.+)(:)(\d+)$/);
95
if (m && m.length==4) {
101
searchList: window.arguments[INPUT].searchList,
103
keyserver: keyserver,
107
requestType: (Ec.getPref("useGpgKeysTool") ? ENIG_CONN_TYPE_GPGKEYS : ENIG_CONN_TYPE_HTTP),
108
gpgkeysRequest: null,
109
progressMeter: document.getElementById("dialog.progress"),
110
httpInProgress: false
113
gEnigRequest.progressMeter.mode="undetermined";
115
if (window.arguments[INPUT].searchList.length == 1 &&
116
window.arguments[INPUT].searchList[0].search(/^0x[A-Fa-f0-9]{8,16}$/) == 0) {
117
// shrink dialog and start download if just one key ID provided
119
gEnigRequest.dlKeyList = window.arguments[INPUT].searchList;
120
document.getElementById("keySelGroup").setAttribute("collapsed", "true");
121
window.sizeToContent();
122
window.resizeBy(0, -320);
123
Ec.dispatchEvent(startDownload, 10);
126
switch (gEnigRequest.requestType) {
127
case ENIG_CONN_TYPE_HTTP:
128
enigNewHttpRequest(nsIEnigmail.SEARCH_KEY, enigScanKeys);
130
case ENIG_CONN_TYPE_GPGKEYS:
131
enigNewGpgKeysRequest(nsIEnigmail.SEARCH_KEY, enigScanKeys);
139
function onAccept () {
140
Ec.DEBUG_LOG("enigmailSearchKey.js: onAccept\n");
142
var keySelList = document.getElementById("enigmailKeySel");
143
var treeChildren=keySelList.getElementsByAttribute("id", "enigmailKeySelChildren")[0];
145
gEnigRequest.dlKeyList = [];
146
var item=treeChildren.firstChild;
148
var aRows = item.getElementsByAttribute("id","indicator")
151
if (elem.getAttribute("active") == "1") {
152
gEnigRequest.dlKeyList.push(item.getAttribute("id"));
155
item = item.nextSibling;
157
return startDownload();
161
function startDownload() {
162
Ec.DEBUG_LOG("enigmailSearchKey.js: startDownload\n");
163
if (gEnigRequest.dlKeyList.length>0) {
164
gEnigRequest.progressMeter.value = 0;
165
gEnigRequest.progressMeter.mode = "undetermined";
166
document.getElementById("progress.box").removeAttribute("hidden");
167
document.getElementById("dialog.accept").setAttribute("disabled", "true");
168
gEnigRequest.keyNum = 0;
169
gEnigRequest.errorTxt="";
170
switch (gEnigRequest.requestType) {
171
case ENIG_CONN_TYPE_HTTP:
172
enigNewHttpRequest(nsIEnigmail.DOWNLOAD_KEY, enigImportKeys);
174
case ENIG_CONN_TYPE_GPGKEYS:
175
enigNewGpgKeysRequest(nsIEnigmail.DOWNLOAD_KEY, enigImportKeys);
179
// do not yet close the window, so that we can display some progress info
187
function onCancel() {
188
Ec.DEBUG_LOG("enigmailSearchKey.js: onCancel\n");
190
if (gEnigRequest.httpInProgress) {
193
if ((typeof(window.enigHttpReq) == "object") &&
194
(window.enigHttpReq.readyState != 4)) {
195
window.enigHttpReq.abort();
197
gEnigRequest.httpInProgress=false;
202
if (gEnigRequest.gpgkeysRequest) {
204
var p = gEnigRequest.gpgkeysRequest;
205
gEnigRequest.gpgkeysRequest = null;
214
function enigStatusError () {
215
Ec.DEBUG_LOG("enigmailSearchKey.js: enigStatusError\n");
216
gEnigRequest.httpInProgress=false;
217
Ec.alert(window, Ec.getString("noKeyserverConn", this.channel.originalURI.prePath));
221
function enigCloseDialog() {
222
document.getElementById("enigmailSearchKeyDlg").cancelDialog();
226
function enigStatusLoaded (event) {
227
Ec.DEBUG_LOG("enigmailSearchKey.js: enigStatusLoaded\n");
229
if (this.status == 200) {
230
// de-HTMLize the result
231
var htmlTxt = this.responseText.replace(/<([^<>]+)>/g, "");
233
this.requestCallbackFunc(ENIG_CONN_TYPE_HTTP, htmlTxt);
235
else if (this.status == 500 && this.statusText=="OK") {
236
this.requestCallbackFunc(ENIG_CONN_TYPE_HTTP, "no keys found");
238
else if (this.statusText!="OK") {
239
Ec.alert(window, Ec.getString("keyDownloadFailed", this.statusText));
247
function enigImportKeys (connType, txt, errorTxt) {
248
Ec.DEBUG_LOG("enigmailSearchKey.js: enigImportKeys\n");
250
gEnigRequest.keyNum++;
251
gEnigRequest.progressMeter.mode = "determined";
252
gEnigRequest.progressMeter.value = (100 * gEnigRequest.keyNum / gEnigRequest.dlKeyList.length).toFixed(0);
254
if (txt.search(/^\[GNUPG:\] IMPORT_RES/m) < 0) {
255
if (!enigImportHtmlKeys(txt)) return;
258
gEnigRequest.errorTxt +=errorTxt+"\n";
261
if (txt.search(/^\[GNUPG:\] IMPORT_RES/m) >= 0) {
262
window.arguments[RESULT].importedKeys++;
265
if (gEnigRequest.dlKeyList.length > gEnigRequest.keyNum) {
267
case ENIG_CONN_TYPE_HTTP:
268
enigNewHttpRequest(nsIEnigmail.DOWNLOAD_KEY, window.enigHttpReq.requestCallbackFunc);
270
case ENIG_CONN_TYPE_GPGKEYS:
271
enigNewGpgKeysRequest(nsIEnigmail.DOWNLOAD_KEY, gEnigRequest.callbackFunction);
275
else if (gEnigRequest.errorTxt) {
276
Ec.longAlert(window, Ec.convertGpgToUnicode(gEnigRequest.errorTxt));
279
gEnigRequest.httpInProgress=false;
284
function enigImportHtmlKeys(txt) {
285
var errorMsgObj = new Object();
287
var enigmailSvc = GetEnigmailSvc();
291
var uiFlags = nsIEnigmail.UI_ALLOW_KEY_IMPORT;
292
var r = enigmailSvc.importKey(window, uiFlags, txt,
293
gEnigRequest.dlKeyList[gEnigRequest.keyNum-1],
295
if (errorMsgObj.value)
296
Ec.alert(window, errorMsgObj.value);
298
window.arguments[RESULT].importedKeys++;
305
function enigNewHttpRequest(requestType, requestCallbackFunc) {
306
Ec.DEBUG_LOG("enigmailSearchKey.js: enigNewHttpRequest\n");
308
switch (gEnigRequest.protocol) {
310
gEnigRequest.protocol = "http";
315
var msg=Ec.getString("protocolNotSupported", gEnigRequest.protocol);
316
if (! Ec.getPref("useGpgKeysTool"))
317
msg += " "+Ec.getString("gpgkeysDisabled");
318
Ec.alert(window, msg);
323
var httpReq = new XMLHttpRequest();
325
switch (requestType) {
326
case nsIEnigmail.SEARCH_KEY:
327
var pubKey = escape("<"+trim(gEnigRequest.searchList[gEnigRequest.keyNum])+">");
328
reqCommand = gEnigRequest.protocol+"://"+gEnigRequest.keyserver+":"+gEnigRequest.port+"/pks/lookup?search="+pubKey+"&op=index";
330
case nsIEnigmail.DOWNLOAD_KEY:
331
var keyId = escape(trim(gEnigRequest.dlKeyList[gEnigRequest.keyNum]));
332
reqCommand = gEnigRequest.protocol+"://"+gEnigRequest.keyserver+":"+gEnigRequest.port+"/pks/lookup?search="+keyId+"&op=get";
335
Ec.alert(window, "Unknown request type "+requestType);
339
gEnigRequest.httpInProgress=true;
340
httpReq.open("GET", reqCommand);
341
httpReq.onerror=enigStatusError;
342
httpReq.onload=enigStatusLoaded;
343
httpReq.requestCallbackFunc = requestCallbackFunc;
344
window.enigHttpReq = httpReq;
349
function enigScanKeys(connType, htmlTxt) {
350
Ec.DEBUG_LOG("enigmailSearchKey.js: enigScanKeys\n");
352
gEnigRequest.keyNum++;
353
gEnigRequest.progressMeter.mode = "determined";
354
gEnigRequest.progressMeter.value = (100 * gEnigRequest.keyNum / gEnigRequest.searchList.length).toFixed(0);
357
case ENIG_CONN_TYPE_HTTP:
358
// interpret HTML codes (e.g. <)
359
var domParser = new DOMParser();
360
// needs improvement: result is max. 4096 bytes long!
361
var htmlNode = domParser.parseFromString("<p>" + htmlTxt + "</p>", "text/xml");
363
if (htmlNode.firstChild.nodeName=="parsererror") {
364
Ec.alert(window, "internalError");
367
enigScanHtmlKeys(htmlNode.firstChild.firstChild.data);
369
case ENIG_CONN_TYPE_GPGKEYS:
370
enigScanGpgKeys(Ec.convertGpgToUnicode(unescape(htmlTxt)));
373
Ec.ERROR_LOG("Unkonwn connType: "+connType+"\n");
376
if (gEnigRequest.searchList.length > gEnigRequest.keyNum) {
378
case ENIG_CONN_TYPE_HTTP:
379
enigNewHttpRequest(nsIEnigmail.SEARCH_KEY, window.enigHttpReq.requestCallbackFunc);
381
case ENIG_CONN_TYPE_GPGKEYS:
382
enigNewGpgKeysRequest(nsIEnigmail.SEARCH_KEY, gEnigRequest.callbackFunction);
387
gEnigRequest.httpInProgress=false;
388
enigPopulateList(gEnigRequest.keyList);
389
document.getElementById("progress.box").setAttribute("hidden", "true");
390
if (gEnigRequest.keyList.length == 0) {
391
Ec.alert(window, Ec.getString("noKeyFound"));
395
document.getElementById("dialog.accept").removeAttribute("disabled");
400
function enigScanHtmlKeys (txt) {
401
Ec.DEBUG_LOG("enigmailSearchKey.js: enigScanHtmlKeys\n");
403
var lines=txt.split(/(\n\r|\n|\r)/);
405
for (i=0; i<lines.length; i++) {
406
if (lines[i].search(/^\s*pub /)==0) {
409
// first, append prev. key to keylist
410
gEnigRequest.keyList.push(key);
413
var m=lines[i].match(/(\d+[a-zA-Z]?\/)([0-9a-fA-F]+)(\s+[\d\/\-\.]+\s+)(.*)/);
414
if (m && m.length>0 ) {
420
if (m[4].search(/.+<.+@.+>/)>=0) {
421
if (! ignoreUid(m[4])) key.uid.push(trim(m[4]));
423
else if (m[4].search(/key (revoked|expired|disabled)/i)>=0) {
424
Ec.DEBUG_LOG("revoked key id "+m[4]+"\n");
432
var uid = trim(lines[i]);
433
if (uid.length>0 && ! ignoreUid(uid))
439
// append prev. key to keylist
441
gEnigRequest.keyList.push(key);
446
function enigScanGpgKeys(txt) {
447
Ec.DEBUG_LOG("enigmailSearchKey.js: enigScanGpgKeys\n");
448
Ec.DEBUG_LOG("got text: "+txt+"\n");
450
var lines=txt.split(/(\r\n|\n|\r)/);
453
for (var i=0; i<lines.length; i++) {
454
if (outputType == 0 && lines[i].search(/^COUNT \d+\s*$/)==0) {
458
if (outputType == 0 && lines[i].search(/^pub:[\da-fA-F]{8}/)==0) {
461
if (outputType==1 && (lines[i].search(/^([a-fA-F0-9]{8}){1,2}:/))==0) {
462
// output from gpgkeys_* protocol version 0
464
var m=lines[i].split(/:/);
465
if (m && m.length>0 ) {
467
if (key.keyId == m[0]) {
468
if (! ignoreUid(m[i])) key.uid.push(trim(m[1]));
471
gEnigRequest.keyList.push(key);
476
var dat=new Date(m[3]*1000);
477
var month=String(dat.getMonth()+101).substr(1);
478
var day=String(dat.getDate()+100).substr(1);
481
created: dat.getFullYear()+"-"+month+"-"+day,
484
if (! ignoreUid(m[1])) key.uid.push(m[1]);
488
if (outputType==2 && (lines[i].search(/^pub:/))==0) {
489
// output from gpgkeys_* protocol version 1
491
m=lines[i].split(/:/);
492
if (m && m.length>1 ) {
494
gEnigRequest.keyList.push(key);
497
dat=new Date(m[4]*1000);
498
month=String(dat.getMonth()+101).substr(1);
499
day=String(dat.getDate()+100).substr(1);
502
created: dat.getFullYear()+"-"+month+"-"+day,
507
if (outputType==2 && (lines[i].search(/^uid:.+/))==0) {
508
// output from gpgkeys_* protocol version 1
510
m=lines[i].split(/:/);
511
if (m && m.length>1 ) {
512
if (key && ! ignoreUid(m[1])) key.uid.push(trim(m[1]));
517
// append prev. key to keylist
519
gEnigRequest.keyList.push(key);
523
// interaction with gpgkeys_xxx
525
function enigNewGpgKeysRequest(requestType, callbackFunction) {
526
Ec.DEBUG_LOG("enigmailSearchkey.js: enigNewGpgKeysRequest\n");
528
var enigmailSvc = GetEnigmailSvc();
530
Ec.alert(window, Ec.getString("accessError"));
534
gEnigRequest.callbackFunction = callbackFunction;
535
gEnigRequest.gpgkeysRequest = null;
541
onStopRequest: function (exitCode) {
542
enigmailGpgkeysTerminate(exitCode);
544
onStdoutData: function(data) {
547
onErrorData: function(data) {
552
if (requestType == nsIEnigmail.SEARCH_KEY) {
553
var keyValue = gEnigRequest.searchList[gEnigRequest.keyNum];
556
keyValue = gEnigRequest.dlKeyList[gEnigRequest.keyNum];
560
var errorMsgObj = {};
561
gEnigRequest.gpgkeysRequest = Ec.searchKey(requestType,
562
gEnigRequest.protocol,
563
gEnigRequest.keyserver,
569
if (!gEnigRequest.gpgkeysRequest) {
570
// calling gpgkeys_xxx failed, let's try builtin http variant
571
switch (gEnigRequest.protocol) {
575
gEnigRequest.requestType = ENIG_CONN_TYPE_HTTP;
576
enigNewHttpRequest(requestType, enigScanKeys);
579
Ec.alert(window, Ec.getString("gpgKeysFailed", gEnigRequest.protocol));
585
Ec.DEBUG_LOG("enigmailSearchkey.js: Start: gEnigRequest.gpgkeysRequest = "+gEnigRequest.gpgkeysRequest+"\n");
589
function enigmailGpgkeysTerminate(exitCode) {
590
Ec.DEBUG_LOG("enigmailSearchkey.js: Terminate: exitCode="+exitCode+"\n");
592
gEnigRequest.gpgkeysRequest = null;
595
if (gErrorData.length > 0) {
596
Ec.DEBUG_LOG("enigmailSearchkey.js: Terminate(): stderr has data:\n");
597
Ec.CONSOLE_LOG(gErrorData+"\n");
600
exitCode = Ec.fixExitCode(exitCode, 0);
602
if (gOutputData.length > 0) {
603
gEnigRequest.callbackFunction(ENIG_CONN_TYPE_GPGKEYS, gOutputData, gErrorData);
611
function enigPopulateList(keyList) {
612
Ec.DEBUG_LOG("enigmailSearchKey.js: enigPopulateList\n");
614
var sortUsers = function (a,b) {
615
if (a.uid[0]<b.uid[0]) { return -1; } else {return 1; }
618
keyList.sort(sortUsers);
620
var treeList = document.getElementById("enigmailKeySel");
621
var treeChildren=treeList.getElementsByAttribute("id", "enigmailKeySelChildren")[0];
624
for (var i=0; i<keyList.length; i++) {
625
treeItem = enigUserSelCreateRow(keyList[i].keyId, false, keyList[i].uid[0], keyList[i].created, "");
626
if (keyList[i].uid.length>1) {
627
treeItem.setAttribute("container", "true");
628
var subChildren=document.createElement("treechildren");
629
for (j=1; j<keyList[i].uid.length; j++) {
630
var subItem=enigUserSelCreateRow(keyList[i].keyId, true, keyList[i].uid[j], "", "");
631
subChildren.appendChild(subItem);
633
treeItem.appendChild(subChildren);
635
treeChildren.appendChild(treeItem);
638
if (keyList.length == 1) {
639
// activate found item if just one key found
640
enigSetActive(treeItem.firstChild.firstChild, 1);
644
function enigUserSelCreateRow (keyId, subKey, userId, dateField, trustStatus) {
645
var selectCol=document.createElement("treecell");
646
selectCol.setAttribute("id", "indicator");
647
var expCol=document.createElement("treecell");
648
var userCol=document.createElement("treecell");
649
userCol.setAttribute("id", "name");
650
if (trustStatus.charAt(0)==KEY_EXPIRED) {
651
expCol.setAttribute("label", Ec.getString("selKeyExpired", dateField));
654
expCol.setAttribute("label", dateField);
657
expCol.setAttribute("id", "expiry");
658
userCol.setAttribute("label", userId);
659
var keyCol=document.createElement("treecell");
660
keyCol.setAttribute("id", "keyid");
662
enigSetActive(selectCol, -1);
663
keyCol.setAttribute("label", "");
666
enigSetActive(selectCol, 0);
667
keyCol.setAttribute("label", keyId.substr(-8));
671
var userRow=document.createElement("treerow");
672
userRow.appendChild(selectCol);
673
userRow.appendChild(userCol);
674
userRow.appendChild(expCol);
675
userRow.appendChild(keyCol);
676
var treeItem=document.createElement("treeitem");
677
treeItem.setAttribute("id", "0x"+keyId);
678
treeItem.appendChild(userRow);
682
function enigmailKeySelCallback(event) {
683
Ec.DEBUG_LOG("enigmailSearchKey.js: enigmailKeySelCallback\n");
685
var Tree = document.getElementById("enigmailKeySel");
689
Tree.treeBoxObject.getCellAt(event.clientX, event.clientY, row, col, elt);
694
var treeItem = Tree.contentView.getItemAtIndex(row.value);
695
Tree.currentItem=treeItem;
696
if (col.value.id != "selectionCol")
699
var aRows = treeItem.getElementsByAttribute("id","indicator")
703
if (elem.getAttribute("active") == "1") {
704
enigSetActive(elem, 0);
705
} else if (elem.getAttribute("active") == "0") {
706
enigSetActive(elem, 1);
711
// set the "active" flag and the corresponding image
712
function enigSetActive(element, status) {
715
element.setAttribute("active", status.toString());
720
element.setAttribute("src", ENIG_IMG_NOT_SELECTED);
723
element.setAttribute("src", ENIG_IMG_SELECTED);
726
element.setAttribute("src", ENIG_IMG_DISABLED);
729
element.setAttribute("active", -1);
733
function ignoreUid(uid) {
734
const ignoreList = "{Test 555 <sdfg@gga.com>}";
735
return (ignoreList.indexOf("{"+trim(uid)+"}") >= 0);