OpenPGP (#53) UNSTABLE

This commit is contained in:
RainLoop Team 2014-03-20 20:05:35 +04:00
parent 67b5a72a71
commit b330f77bc0
14 changed files with 526 additions and 260 deletions

View file

@ -16,7 +16,11 @@ function AbstractApp()
this.isLocalAutocomplete = true; this.isLocalAutocomplete = true;
this.popupVisibility = ko.observable(false); this.popupVisibilityNames = ko.observableArray([]);
this.popupVisibility = ko.computed(function () {
return 0 < this.popupVisibilityNames().length;
}, this);
this.iframe = $('<iframe style="display:none" src="javascript:;" />').appendTo('body'); this.iframe = $('<iframe style="display:none" src="javascript:;" />').appendTo('body');

View file

@ -333,17 +333,21 @@ RainLoopApp.prototype.reloadOpenPgpKeys = function ()
; ;
_.each(oOpenpgpKeys, function (oItem, iIndex) { _.each(oOpenpgpKeys, function (oItem, iIndex) {
if (oItem) if (oItem && oItem.primaryKey)
{ {
var var
aIDs = _.map(oItem.getKeyIds(), function (oID) { oPrimaryUser = oItem.getPrimaryUser(),
return oID ? oID.toHex() : ''; sUser = (oPrimaryUser && oPrimaryUser.user) ? oPrimaryUser.user.userId.userid
}) : (oItem.users && oItem.users[0] ? oItem.users[0].userId.userid : '')
; ;
aKeys.push( aKeys.push(new OpenPgpKeyModel(
new OpenPgpKeyModel(iIndex, aIDs.join(', '), oItem.getUserIds().join(', '), iIndex,
oItem.isPrivate(), oItem.armor()) oItem.primaryKey.getFingerprint(),
oItem.primaryKey.getKeyId().toHex().toLowerCase(),
sUser,
oItem.isPrivate(),
oItem.armor())
); );
} }
}); });

View file

@ -1716,3 +1716,48 @@ Utils.selectElement = function (element)
} }
/* jshint onevar: true */ /* jshint onevar: true */
}; };
Utils.openPgpImportPublicKeys = function (sPublicKeysArmored)
{
if (window.openpgp && RL)
{
var
oOpenpgpKeyring = RL.data().openpgpKeyring,
oImported = window.openpgp.key.readArmored(sPublicKeysArmored)
;
if (oOpenpgpKeyring && oImported && !oImported.err && Utils.isArray(oImported.keys) &&
0 < oImported.keys.length)
{
_.each(oImported.keys, function (oPrivKey) {
if (oPrivKey)
{
window.console.log(oPrivKey);
// var oKey = oOpenpgpKeyring.getKeysForKeyId(oPrivKey.primaryKey.getFingerprint());
// if (oKey && oKey[0])
// {
// if (oKey[0].isPublic())
// {
// oPrivKey.update(oKey[0]);
// oOpenpgpKeyring.publicKeys.removeForId(oPrivKey.primaryKey.getFingerprint());
// oOpenpgpKeyring.privateKeys.push(oPrivKey);
// }
// else
// {
// oKey[0].update(oPrivKey);
// }
// }
// else
// {
// oOpenpgpKeyring.importKey(oPrivKey.armored);
// }
}
});
oOpenpgpKeyring.store();
return true;
}
}
return false;
};

View file

@ -9,15 +9,6 @@ function Knoin()
this.oScreens = {}; this.oScreens = {};
this.oBoot = null; this.oBoot = null;
this.oCurrentScreen = null; this.oCurrentScreen = null;
this.popupVisibility = ko.observable(false);
this.popupVisibility.subscribe(function (bValue) {
if (RL)
{
RL.popupVisibility(bValue);
}
});
} }
/** /**
@ -152,7 +143,8 @@ Knoin.prototype.hideScreenPopup = function (ViewModelClassToHide)
{ {
ViewModelClassToHide.__vm.modalVisibility(false); ViewModelClassToHide.__vm.modalVisibility(false);
Utils.delegateRun(ViewModelClassToHide.__vm, 'onHide'); Utils.delegateRun(ViewModelClassToHide.__vm, 'onHide');
this.popupVisibility(false);
RL.popupVisibilityNames.remove(ViewModelClassToHide.__name);
Plugins.runHook('view-model-on-hide', [ViewModelClassToHide.__name, ViewModelClassToHide.__vm]); Plugins.runHook('view-model-on-hide', [ViewModelClassToHide.__name, ViewModelClassToHide.__vm]);
@ -177,7 +169,8 @@ Knoin.prototype.showScreenPopup = function (ViewModelClassToShow, aParameters)
ViewModelClassToShow.__dom.show(); ViewModelClassToShow.__dom.show();
ViewModelClassToShow.__vm.modalVisibility(true); ViewModelClassToShow.__vm.modalVisibility(true);
Utils.delegateRun(ViewModelClassToShow.__vm, 'onShow', aParameters || []); Utils.delegateRun(ViewModelClassToShow.__vm, 'onShow', aParameters || []);
this.popupVisibility(true);
RL.popupVisibilityNames.push(ViewModelClassToShow.__name);
Plugins.runHook('view-model-on-show', [ViewModelClassToShow.__name, ViewModelClassToShow.__vm, aParameters || []]); Plugins.runHook('view-model-on-show', [ViewModelClassToShow.__name, ViewModelClassToShow.__vm, aParameters || []]);

View file

@ -2,16 +2,18 @@
/** /**
* @param {string} iIndex * @param {string} iIndex
* @param {string} sGuID
* @param {string} sID * @param {string} sID
* @param {string} sUserID * @param {string} sUserID
* @param {boolean} bIsPrivate * @param {boolean} bIsPrivate
* @param {string} sArmor * @param {string} sArmor
* @constructor * @constructor
*/ */
function OpenPgpKeyModel(iIndex, sID, sUserID, bIsPrivate, sArmor) function OpenPgpKeyModel(iIndex, sGuID, sID, sUserID, bIsPrivate, sArmor)
{ {
this.index = iIndex; this.index = iIndex;
this.id = sID; this.id = sID;
this.guid = sGuID;
this.user = sUserID; this.user = sUserID;
this.armor = sArmor; this.armor = sArmor;
this.isPrivate = !!bIsPrivate; this.isPrivate = !!bIsPrivate;
@ -21,6 +23,7 @@ function OpenPgpKeyModel(iIndex, sID, sUserID, bIsPrivate, sArmor)
OpenPgpKeyModel.prototype.index = 0; OpenPgpKeyModel.prototype.index = 0;
OpenPgpKeyModel.prototype.id = ''; OpenPgpKeyModel.prototype.id = '';
OpenPgpKeyModel.prototype.guid = '';
OpenPgpKeyModel.prototype.user = ''; OpenPgpKeyModel.prototype.user = '';
OpenPgpKeyModel.prototype.armor = ''; OpenPgpKeyModel.prototype.armor = '';
OpenPgpKeyModel.prototype.isPrivate = false; OpenPgpKeyModel.prototype.isPrivate = false;

View file

@ -64,6 +64,7 @@ SettingsOpenPGP.prototype.deleteOpenPgpKey = function (oOpenPgpKeyToRemove)
this.openPgpKeyForDeletion(null); this.openPgpKeyForDeletion(null);
var var
iFindIndex = -1,
oOpenpgpKeyring = RL.data().openpgpKeyring, oOpenpgpKeyring = RL.data().openpgpKeyring,
fRemoveAccount = function (oOpenPgpKey) { fRemoveAccount = function (oOpenPgpKey) {
return oOpenPgpKeyToRemove === oOpenPgpKey; return oOpenPgpKeyToRemove === oOpenPgpKey;
@ -74,7 +75,20 @@ SettingsOpenPGP.prototype.deleteOpenPgpKey = function (oOpenPgpKeyToRemove)
{ {
this.openpgpkeys.remove(fRemoveAccount); this.openpgpkeys.remove(fRemoveAccount);
oOpenpgpKeyring.removeKey(oOpenPgpKeyToRemove.index); _.each(oOpenpgpKeyring.keys, function (oKey, iIndex) {
if (-1 === iFindIndex && oKey && oKey.primaryKey &&
oOpenPgpKeyToRemove.guid === oKey.primaryKey.getFingerprint() &&
oOpenPgpKeyToRemove.isPrivate === oKey.isPrivate())
{
iFindIndex = iIndex;
}
});
if (0 <= iFindIndex)
{
oOpenpgpKeyring.removeKey(iFindIndex);
}
oOpenpgpKeyring.store(); oOpenpgpKeyring.store();
RL.reloadOpenPgpKeys(); RL.reloadOpenPgpKeys();

View file

@ -47,11 +47,61 @@ PopupsComposeOpenPgpViewModel.prototype.onShow = function (fCallback, sText, sFr
{ {
this.clearPopup(); this.clearPopup();
var
oEmail = new EmailModel(),
sResultFromEmail = '',
aRec = []
;
if ('' === sTo + sCc + sBcc) if ('' === sTo + sCc + sBcc)
{ {
this.notification('Please specify at least one recipient'); this.notification('Please specify at least one recipient');
return false;
} }
oEmail.clear();
oEmail.mailsoParse(sFromEmail);
if ('' === oEmail.email)
{
this.notification('Please specify From email address');
return false;
}
else
{
sResultFromEmail = oEmail.email;
}
if ('' !== sTo)
{
aRec.push(sTo);
}
if ('' !== sCc)
{
aRec.push(sCc);
}
if ('' !== sBcc)
{
aRec.push(sBcc);
}
aRec = aRec.join(', ').split(',');
aRec = _.compact(_.map(aRec, function (sValue) {
oEmail.clear();
oEmail.mailsoParse(Utils.trim(sValue));
return '' === oEmail.email ? false : oEmail.email;
}));
if (0 === aRec.length)
{
this.notification('Please specify at least one recipient');
return false;
}
window.console.log(sResultFromEmail);
window.console.log(aRec);
// TODO // TODO
}; };

View file

@ -51,7 +51,6 @@ function PopupsGenerateNewOpenPgpKeyViewModel()
{ {
oOpenpgpKeyring.importKey(mKeyPair.privateKeyArmored); oOpenpgpKeyring.importKey(mKeyPair.privateKeyArmored);
oOpenpgpKeyring.importKey(mKeyPair.publicKeyArmored); oOpenpgpKeyring.importKey(mKeyPair.publicKeyArmored);
oOpenpgpKeyring.store(); oOpenpgpKeyring.store();
RL.reloadOpenPgpKeys(); RL.reloadOpenPgpKeys();

View file

@ -2401,6 +2401,51 @@ Utils.selectElement = function (element)
/* jshint onevar: true */ /* jshint onevar: true */
}; };
Utils.openPgpImportPublicKeys = function (sPublicKeysArmored)
{
if (window.openpgp && RL)
{
var
oOpenpgpKeyring = RL.data().openpgpKeyring,
oImported = window.openpgp.key.readArmored(sPublicKeysArmored)
;
if (oOpenpgpKeyring && oImported && !oImported.err && Utils.isArray(oImported.keys) &&
0 < oImported.keys.length)
{
_.each(oImported.keys, function (oPrivKey) {
if (oPrivKey)
{
window.console.log(oPrivKey);
// var oKey = oOpenpgpKeyring.getKeysForKeyId(oPrivKey.primaryKey.getFingerprint());
// if (oKey && oKey[0])
// {
// if (oKey[0].isPublic())
// {
// oPrivKey.update(oKey[0]);
// oOpenpgpKeyring.publicKeys.removeForId(oPrivKey.primaryKey.getFingerprint());
// oOpenpgpKeyring.privateKeys.push(oPrivKey);
// }
// else
// {
// oKey[0].update(oPrivKey);
// }
// }
// else
// {
// oOpenpgpKeyring.importKey(oPrivKey.armored);
// }
}
});
oOpenpgpKeyring.store();
return true;
}
}
return false;
};
// Base64 encode / decode // Base64 encode / decode
// http://www.webtoolkit.info/ // http://www.webtoolkit.info/
@ -3966,15 +4011,6 @@ function Knoin()
this.oScreens = {}; this.oScreens = {};
this.oBoot = null; this.oBoot = null;
this.oCurrentScreen = null; this.oCurrentScreen = null;
this.popupVisibility = ko.observable(false);
this.popupVisibility.subscribe(function (bValue) {
if (RL)
{
RL.popupVisibility(bValue);
}
});
} }
/** /**
@ -4109,7 +4145,8 @@ Knoin.prototype.hideScreenPopup = function (ViewModelClassToHide)
{ {
ViewModelClassToHide.__vm.modalVisibility(false); ViewModelClassToHide.__vm.modalVisibility(false);
Utils.delegateRun(ViewModelClassToHide.__vm, 'onHide'); Utils.delegateRun(ViewModelClassToHide.__vm, 'onHide');
this.popupVisibility(false);
RL.popupVisibilityNames.remove(ViewModelClassToHide.__name);
Plugins.runHook('view-model-on-hide', [ViewModelClassToHide.__name, ViewModelClassToHide.__vm]); Plugins.runHook('view-model-on-hide', [ViewModelClassToHide.__name, ViewModelClassToHide.__vm]);
@ -4134,7 +4171,8 @@ Knoin.prototype.showScreenPopup = function (ViewModelClassToShow, aParameters)
ViewModelClassToShow.__dom.show(); ViewModelClassToShow.__dom.show();
ViewModelClassToShow.__vm.modalVisibility(true); ViewModelClassToShow.__vm.modalVisibility(true);
Utils.delegateRun(ViewModelClassToShow.__vm, 'onShow', aParameters || []); Utils.delegateRun(ViewModelClassToShow.__vm, 'onShow', aParameters || []);
this.popupVisibility(true);
RL.popupVisibilityNames.push(ViewModelClassToShow.__name);
Plugins.runHook('view-model-on-show', [ViewModelClassToShow.__name, ViewModelClassToShow.__vm, aParameters || []]); Plugins.runHook('view-model-on-show', [ViewModelClassToShow.__name, ViewModelClassToShow.__vm, aParameters || []]);
@ -7417,7 +7455,11 @@ function AbstractApp()
this.isLocalAutocomplete = true; this.isLocalAutocomplete = true;
this.popupVisibility = ko.observable(false); this.popupVisibilityNames = ko.observableArray([]);
this.popupVisibility = ko.computed(function () {
return 0 < this.popupVisibilityNames().length;
}, this);
this.iframe = $('<iframe style="display:none" src="javascript:;" />').appendTo('body'); this.iframe = $('<iframe style="display:none" src="javascript:;" />').appendTo('body');

File diff suppressed because one or more lines are too long

View file

@ -2401,6 +2401,51 @@ Utils.selectElement = function (element)
/* jshint onevar: true */ /* jshint onevar: true */
}; };
Utils.openPgpImportPublicKeys = function (sPublicKeysArmored)
{
if (window.openpgp && RL)
{
var
oOpenpgpKeyring = RL.data().openpgpKeyring,
oImported = window.openpgp.key.readArmored(sPublicKeysArmored)
;
if (oOpenpgpKeyring && oImported && !oImported.err && Utils.isArray(oImported.keys) &&
0 < oImported.keys.length)
{
_.each(oImported.keys, function (oPrivKey) {
if (oPrivKey)
{
window.console.log(oPrivKey);
// var oKey = oOpenpgpKeyring.getKeysForKeyId(oPrivKey.primaryKey.getFingerprint());
// if (oKey && oKey[0])
// {
// if (oKey[0].isPublic())
// {
// oPrivKey.update(oKey[0]);
// oOpenpgpKeyring.publicKeys.removeForId(oPrivKey.primaryKey.getFingerprint());
// oOpenpgpKeyring.privateKeys.push(oPrivKey);
// }
// else
// {
// oKey[0].update(oPrivKey);
// }
// }
// else
// {
// oOpenpgpKeyring.importKey(oPrivKey.armored);
// }
}
});
oOpenpgpKeyring.store();
return true;
}
}
return false;
};
// Base64 encode / decode // Base64 encode / decode
// http://www.webtoolkit.info/ // http://www.webtoolkit.info/
@ -4778,15 +4823,6 @@ function Knoin()
this.oScreens = {}; this.oScreens = {};
this.oBoot = null; this.oBoot = null;
this.oCurrentScreen = null; this.oCurrentScreen = null;
this.popupVisibility = ko.observable(false);
this.popupVisibility.subscribe(function (bValue) {
if (RL)
{
RL.popupVisibility(bValue);
}
});
} }
/** /**
@ -4921,7 +4957,8 @@ Knoin.prototype.hideScreenPopup = function (ViewModelClassToHide)
{ {
ViewModelClassToHide.__vm.modalVisibility(false); ViewModelClassToHide.__vm.modalVisibility(false);
Utils.delegateRun(ViewModelClassToHide.__vm, 'onHide'); Utils.delegateRun(ViewModelClassToHide.__vm, 'onHide');
this.popupVisibility(false);
RL.popupVisibilityNames.remove(ViewModelClassToHide.__name);
Plugins.runHook('view-model-on-hide', [ViewModelClassToHide.__name, ViewModelClassToHide.__vm]); Plugins.runHook('view-model-on-hide', [ViewModelClassToHide.__name, ViewModelClassToHide.__vm]);
@ -4946,7 +4983,8 @@ Knoin.prototype.showScreenPopup = function (ViewModelClassToShow, aParameters)
ViewModelClassToShow.__dom.show(); ViewModelClassToShow.__dom.show();
ViewModelClassToShow.__vm.modalVisibility(true); ViewModelClassToShow.__vm.modalVisibility(true);
Utils.delegateRun(ViewModelClassToShow.__vm, 'onShow', aParameters || []); Utils.delegateRun(ViewModelClassToShow.__vm, 'onShow', aParameters || []);
this.popupVisibility(true);
RL.popupVisibilityNames.push(ViewModelClassToShow.__name);
Plugins.runHook('view-model-on-show', [ViewModelClassToShow.__name, ViewModelClassToShow.__vm, aParameters || []]); Plugins.runHook('view-model-on-show', [ViewModelClassToShow.__name, ViewModelClassToShow.__vm, aParameters || []]);
@ -7342,16 +7380,18 @@ IdentityModel.prototype.formattedNameForEmail = function ()
/** /**
* @param {string} iIndex * @param {string} iIndex
* @param {string} sGuID
* @param {string} sID * @param {string} sID
* @param {string} sUserID * @param {string} sUserID
* @param {boolean} bIsPrivate * @param {boolean} bIsPrivate
* @param {string} sArmor * @param {string} sArmor
* @constructor * @constructor
*/ */
function OpenPgpKeyModel(iIndex, sID, sUserID, bIsPrivate, sArmor) function OpenPgpKeyModel(iIndex, sGuID, sID, sUserID, bIsPrivate, sArmor)
{ {
this.index = iIndex; this.index = iIndex;
this.id = sID; this.id = sID;
this.guid = sGuID;
this.user = sUserID; this.user = sUserID;
this.armor = sArmor; this.armor = sArmor;
this.isPrivate = !!bIsPrivate; this.isPrivate = !!bIsPrivate;
@ -7361,6 +7401,7 @@ function OpenPgpKeyModel(iIndex, sID, sUserID, bIsPrivate, sArmor)
OpenPgpKeyModel.prototype.index = 0; OpenPgpKeyModel.prototype.index = 0;
OpenPgpKeyModel.prototype.id = ''; OpenPgpKeyModel.prototype.id = '';
OpenPgpKeyModel.prototype.guid = '';
OpenPgpKeyModel.prototype.user = ''; OpenPgpKeyModel.prototype.user = '';
OpenPgpKeyModel.prototype.armor = ''; OpenPgpKeyModel.prototype.armor = '';
OpenPgpKeyModel.prototype.isPrivate = false; OpenPgpKeyModel.prototype.isPrivate = false;
@ -10180,7 +10221,6 @@ function PopupsGenerateNewOpenPgpKeyViewModel()
{ {
oOpenpgpKeyring.importKey(mKeyPair.privateKeyArmored); oOpenpgpKeyring.importKey(mKeyPair.privateKeyArmored);
oOpenpgpKeyring.importKey(mKeyPair.publicKeyArmored); oOpenpgpKeyring.importKey(mKeyPair.publicKeyArmored);
oOpenpgpKeyring.store(); oOpenpgpKeyring.store();
RL.reloadOpenPgpKeys(); RL.reloadOpenPgpKeys();
@ -10265,11 +10305,61 @@ PopupsComposeOpenPgpViewModel.prototype.onShow = function (fCallback, sText, sFr
{ {
this.clearPopup(); this.clearPopup();
var
oEmail = new EmailModel(),
sResultFromEmail = '',
aRec = []
;
if ('' === sTo + sCc + sBcc) if ('' === sTo + sCc + sBcc)
{ {
this.notification('Please specify at least one recipient'); this.notification('Please specify at least one recipient');
return false;
} }
oEmail.clear();
oEmail.mailsoParse(sFromEmail);
if ('' === oEmail.email)
{
this.notification('Please specify From email address');
return false;
}
else
{
sResultFromEmail = oEmail.email;
}
if ('' !== sTo)
{
aRec.push(sTo);
}
if ('' !== sCc)
{
aRec.push(sCc);
}
if ('' !== sBcc)
{
aRec.push(sBcc);
}
aRec = aRec.join(', ').split(',');
aRec = _.compact(_.map(aRec, function (sValue) {
oEmail.clear();
oEmail.mailsoParse(Utils.trim(sValue));
return '' === oEmail.email ? false : oEmail.email;
}));
if (0 === aRec.length)
{
this.notification('Please specify at least one recipient');
return false;
}
window.console.log(sResultFromEmail);
window.console.log(aRec);
// TODO // TODO
}; };
@ -12886,6 +12976,7 @@ SettingsOpenPGP.prototype.deleteOpenPgpKey = function (oOpenPgpKeyToRemove)
this.openPgpKeyForDeletion(null); this.openPgpKeyForDeletion(null);
var var
iFindIndex = -1,
oOpenpgpKeyring = RL.data().openpgpKeyring, oOpenpgpKeyring = RL.data().openpgpKeyring,
fRemoveAccount = function (oOpenPgpKey) { fRemoveAccount = function (oOpenPgpKey) {
return oOpenPgpKeyToRemove === oOpenPgpKey; return oOpenPgpKeyToRemove === oOpenPgpKey;
@ -12896,7 +12987,20 @@ SettingsOpenPGP.prototype.deleteOpenPgpKey = function (oOpenPgpKeyToRemove)
{ {
this.openpgpkeys.remove(fRemoveAccount); this.openpgpkeys.remove(fRemoveAccount);
oOpenpgpKeyring.removeKey(oOpenPgpKeyToRemove.index); _.each(oOpenpgpKeyring.keys, function (oKey, iIndex) {
if (-1 === iFindIndex && oKey && oKey.primaryKey &&
oOpenPgpKeyToRemove.guid === oKey.primaryKey.getFingerprint() &&
oOpenPgpKeyToRemove.isPrivate === oKey.isPrivate())
{
iFindIndex = iIndex;
}
});
if (0 <= iFindIndex)
{
oOpenpgpKeyring.removeKey(iFindIndex);
}
oOpenpgpKeyring.store(); oOpenpgpKeyring.store();
RL.reloadOpenPgpKeys(); RL.reloadOpenPgpKeys();
@ -16377,7 +16481,11 @@ function AbstractApp()
this.isLocalAutocomplete = true; this.isLocalAutocomplete = true;
this.popupVisibility = ko.observable(false); this.popupVisibilityNames = ko.observableArray([]);
this.popupVisibility = ko.computed(function () {
return 0 < this.popupVisibilityNames().length;
}, this);
this.iframe = $('<iframe style="display:none" src="javascript:;" />').appendTo('body'); this.iframe = $('<iframe style="display:none" src="javascript:;" />').appendTo('body');
@ -17010,17 +17118,21 @@ RainLoopApp.prototype.reloadOpenPgpKeys = function ()
; ;
_.each(oOpenpgpKeys, function (oItem, iIndex) { _.each(oOpenpgpKeys, function (oItem, iIndex) {
if (oItem) if (oItem && oItem.primaryKey)
{ {
var var
aIDs = _.map(oItem.getKeyIds(), function (oID) { oPrimaryUser = oItem.getPrimaryUser(),
return oID ? oID.toHex() : ''; sUser = (oPrimaryUser && oPrimaryUser.user) ? oPrimaryUser.user.userId.userid
}) : (oItem.users && oItem.users[0] ? oItem.users[0].userId.userid : '')
; ;
aKeys.push( aKeys.push(new OpenPgpKeyModel(
new OpenPgpKeyModel(iIndex, aIDs.join(', '), oItem.getUserIds().join(', '), iIndex,
oItem.isPrivate(), oItem.armor()) oItem.primaryKey.getFingerprint(),
oItem.primaryKey.getKeyId().toHex().toLowerCase(),
sUser,
oItem.isPrivate(),
oItem.armor())
); );
} }
}); });

File diff suppressed because one or more lines are too long