Second round of Telegram work, can now verify a user Telegram account (#4650)

Ylian Saint-Hilaire committed Oct 22, 2022 at 09:20 UTC 0bd154a937dc3b1c989c988206bc1232c05f5629
5 files changed +163 -12
MeshCentralServer.njsproj
+1
@@ -113,6 +113,7 @@
113 <Compile Include="meshdesktopmultiplex.js" />
114 <Compile Include="meshipkvm.js" />
115 <Compile Include="meshmail.js" />
116 + <Compile Include="meshmessaging.js" />
117 <Compile Include="meshrelay.js" />
118 <Compile Include="meshsms.js" />
119 <Compile Include="meshscanner.js" />
meshmessaging.js
+7 -7
@@ -117,8 +117,8 @@ module.exports.CreateServer = function (parent) {
117 return lines[templateNumber];
118 }
119
120 - // Send phone number verification SMS
121 - obj.sendPhoneCheck = function (domain, to, verificationCode, language, func) {
120 + // Send messaging account verification
121 + obj.sendMessagingCheck = function (domain, to, verificationCode, language, func) {
122 parent.debug('email', "Sending verification message to " + to);
123
124 var sms = getTemplate(0, domain, language);
@@ -128,11 +128,11 @@ module.exports.CreateServer = function (parent) {
128 sms = sms.split('[[0]]').join(domain.title ? domain.title : 'MeshCentral');
129 sms = sms.split('[[1]]').join(verificationCode);
130
131 - // Send the SMS
132 - obj.sendSMS(to, sms, func);
131 + // Send the message
132 + obj.sendMessage(to, sms, func);
133 };
134
135 - // Send phone number verification SMS
135 + // Send 2FA verification
136 obj.sendToken = function (domain, to, verificationCode, language, func) {
137 parent.debug('email', "Sending login token message to " + to);
138
@@ -143,8 +143,8 @@ module.exports.CreateServer = function (parent) {
143 sms = sms.split('[[0]]').join(domain.title ? domain.title : 'MeshCentral');
144 sms = sms.split('[[1]]').join(verificationCode);
145
146 - // Send the SMS
147 - obj.sendSMS(to, sms, func);
146 + // Send the message
147 + obj.sendMessage(to, sms, func);
148 };
149
150 return obj;
meshuser.js
+77 -1
@@ -5233,6 +5233,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
5233 'changelang': serverCommandChangeLang,
5234 'close': serverCommandClose,
5235 'confirmPhone': serverCommandConfirmPhone,
5236 + 'confirmMessaging': serverCommandConfirmMessaging,
5237 'emailuser': serverCommandEmailUser,
5238 'files': serverCommandFiles,
5239 'getClip': serverCommandGetClip,
@@ -5261,6 +5262,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
5262 'serverversion': serverCommandServerVersion,
5263 'setClip': serverCommandSetClip,
5264 'smsuser': serverCommandSmsUser,
5265 + 'msguser': serverCommandMsgUser,
5266 'trafficdelta': serverCommandTrafficDelta,
5267 'trafficstats': serverCommandTrafficStats,
5268 'updateAgents': serverCommandUpdateAgents,
@@ -5268,7 +5270,8 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
5270 'urlargs': serverCommandUrlArgs,
5271 'users': serverCommandUsers,
5272 'verifyemail': serverCommandVerifyEmail,
5271 - 'verifyPhone': serverCommandVerifyPhone
5273 + 'verifyPhone': serverCommandVerifyPhone,
5274 + 'verifyMessaging': serverCommandVerifyMessaging
5275 };
5276
5277 const serverUserCommands = {
@@ -6026,6 +6029,35 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
6029 parent.parent.DispatchEvent(['*', 'server-users', user._id], obj, event);
6030 }
6031
6032 + function serverCommandConfirmMessaging(command) {
6033 + // Do not allow this command when logged in using a login token
6034 + if (req.session.loginToken != null) return;
6035 +
6036 + if ((user.siteadmin != 0xFFFFFFFF) && ((user.siteadmin & 1024) != 0)) return; // If this account is settings locked, return here.
6037 + if ((parent.parent.msgserver == null) || (typeof command.cookie != 'string') || (typeof command.code != 'string') || (obj.failedMsgCookieCheck == 1)) return; // Input checks
6038 + var cookie = parent.parent.decodeCookie(command.cookie);
6039 + if (cookie == null) return; // Invalid cookie
6040 + if (cookie.s != ws.sessionId) return; // Invalid session
6041 + if (cookie.c != command.code) {
6042 + obj.failedMsgCookieCheck = 1;
6043 + // Code does not match, delay the response to limit how many guesses we can make and don't allow more than 1 guess at any given time.
6044 + setTimeout(function () {
6045 + ws.send(JSON.stringify({ action: 'verifyMessaging', cookie: command.cookie, success: true }));
6046 + delete obj.failedMsgCookieCheck;
6047 + }, 2000 + (parent.crypto.randomBytes(2).readUInt16BE(0) % 4095));
6048 + return;
6049 + }
6050 +
6051 + // Set the user's messaging handle
6052 + user.msghandle = cookie.p;
6053 + db.SetUser(user);
6054 +
6055 + // Event the change
6056 + var event = { etype: 'user', userid: user._id, username: user.name, account: parent.CloneSafeUser(user), action: 'accountchange', msgid: 156, msgArgs: [user.name], msg: 'Verified messaging account of user ' + EscapeHtml(user.name), domain: domain.id };
6057 + if (db.changeStream) { event.noact = 1; } // If DB change stream is active, don't use this event to change the user. Another event will come.
6058 + parent.parent.DispatchEvent(['*', 'server-users', user._id], obj, event);
6059 + }
6060 +
6061 function serverCommandEmailUser(command) {
6062 var errMsg = null, emailuser = null;
6063 if (domain.mailserver == null) { errMsg = 'Email server not enabled'; }
@@ -6498,6 +6530,29 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
6530 });
6531 }
6532
6533 + function serverCommandMsgUser(command) {
6534 + var errMsg = null, msguser = null;
6535 + if ((parent.parent.msgserver == null) || (parent.parent.msgserver.providers == 0)) { errMsg = "Messaging server not enabled"; }
6536 + else if ((user.siteadmin & 2) == 0) { errMsg = "No user management rights"; }
6537 + else if (common.validateString(command.userid, 1, 2048) == false) { errMsg = "Invalid username"; }
6538 + else if (common.validateString(command.msg, 1, 160) == false) { errMsg = "Invalid message"; }
6539 + else {
6540 + msguser = parent.users[command.userid];
6541 + if (msguser == null) { errMsg = "Invalid username"; }
6542 + else if (msguser.msghandle == null) { errMsg = "No messaging service configured for this user"; }
6543 + }
6544 +
6545 + if (errMsg != null) { displayNotificationMessage(errMsg); return; }
6546 +
6547 + parent.parent.msgserver.sendMessage(msguser.msghandle, command.msg, function (success, msg) {
6548 + if (success) {
6549 + displayNotificationMessage("Message succesfuly sent.", null, null, null, 32);
6550 + } else {
6551 + if (typeof msg == 'string') { displayNotificationMessage("Messaging error: " + msg, null, null, null, 34, [msg]); } else { displayNotificationMessage("Messaging error", null, null, null, 33); }
6552 + }
6553 + });
6554 + }
6555 +
6556 function serverCommandTrafficDelta(command) {
6557 const stats = parent.getTrafficDelta(obj.trafficStats);
6558 obj.trafficStats = stats.current;
@@ -6606,6 +6661,27 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
6661 });
6662 }
6663
6664 + function serverCommandVerifyMessaging(command) {
6665 + // Do not allow this command when logged in using a login token
6666 + if (req.session.loginToken != null) return;
6667 +
6668 + if ((user.siteadmin != 0xFFFFFFFF) && ((user.siteadmin & 1024) != 0)) return; // If this account is settings locked, return here.
6669 + if (parent.parent.msgserver == null) return;
6670 + if (common.validateString(command.handle, 1, 64) == false) return; // Check handle length
6671 +
6672 + // Setup the handle for the right messaging service
6673 + var handle = null;
6674 + if ((command.service == 1) && ((parent.parent.msgserver.providers & 1) != 0)) { handle = 'telegram:@' + command.handle; }
6675 + if (handle == null) return;
6676 +
6677 + // Send a verification message
6678 + const code = common.zeroPad(getRandomSixDigitInteger(), 6);
6679 + const messagingCookie = parent.parent.encodeCookie({ a: 'verifyMessaging', c: code, p: handle, s: ws.sessionId });
6680 + parent.parent.msgserver.sendMessagingCheck(domain, handle, code, parent.getLanguageCodes(req), function (success) {
6681 + ws.send(JSON.stringify({ action: 'verifyMessaging', cookie: messagingCookie, success: success }));
6682 + });
6683 + }
6684 +
6685 function serverUserCommandHelp(cmdData) {
6686 var fin = '', f = '', availcommands = [];
6687 for (var i in serverUserCommands) { availcommands.push(i); }
public/images/messaging40.png
Binary files /dev/null and b/public/images/messaging40.png differ
views/default.handlebars
+78 -4
@@ -425,6 +425,7 @@
425 <div id="manageHardwareOtp"><div class="p2AccountActions"><span id="authKeySetupCheck"><strong>&#x2713;</strong></span></div><span><a href=# onclick="return account_manageHardwareOtp(0)">Manage security keys</a><br /></span></div>
426 <div id="managePushAuthDev"><div class="p2AccountActions"><span id="authPushAuthDevCheck"><strong>&#x2713;</strong></span></div><span><a href=# onclick="return account_managePushAuthDev()">Manage push authentication</a><br /></span></div>
427 <div id="manageOtp"><div class="p2AccountActions"><span id="authCodesSetupCheck"><strong>&#x2713;</strong></span></div><span><a href=# onclick="return account_manageOtp(0)">Manage backup codes</a><br /></span></div>
428 + <div id="manageMessaging1"><div class="p2AccountActions"><span id="authMessagingCheck"><strong>&#x2713;</strong></span></div><span><a href=# onclick="return account_manageMessaging()">Manage messaging</a><br /></span></div>
429 <div class="p2AccountActions"></div><span><a href=# onclick="return account_viewPreviousLogins()">View previous logins</a><br /></span>
430 </div>
431 </div>
@@ -432,6 +433,7 @@
433 <p><strong>Account actions</strong></p>
434 <p class="mL">
435 <span id="managePhoneNumber2" style="display:none"><a href=# onclick="return account_managePhone()">Manage phone number</a><br /></span>
436 + <span id="manageMessaging2" style="display:none"><a href=# onclick="return account_manageMessaging()">Manage messaging</a><br /></span>
437 <span id="verifyEmailId" style="display:none"><a href=# onclick="return account_showVerifyEmail()">Verify email</a><br /></span>
438 <span id="accountEnableNotificationsSpan" style="display:none"><a href=# onclick="return account_enableNotifications()">Enable web notifications</a><br /></span>
439 <a href=# onclick="return account_showLocalizationSettings()">Localization Settings</a><br />
@@ -2139,6 +2141,8 @@
2141 QV('p2AccountActions', !accountSettingsLocked)
2142 QV('managePhoneNumber1', (features & 0x02000000) && (features & 0x04000000) && (serverinfo.lock2factor != true));
2143 QV('managePhoneNumber2', (features & 0x02000000) && !(features & 0x04000000) && (serverinfo.lock2factor != true));
2144 + QV('manageMessaging1', (features2 & 0x02000000) && (features2 & 0x04000000) && (serverinfo.lock2factor != true));
2145 + QV('manageMessaging2', (features2 & 0x02000000) && !(features2 & 0x04000000) && (serverinfo.lock2factor != true));
2146 QV('manageEmail2FA', (features & 0x00800000) && (serverinfo.lock2factor != true));
2147 QV('p2AccountPassActions', ((features & 4) == 0) && (serverinfo.domainauth == false) && (userinfo != null) && (userinfo._id.split('/')[2].startsWith('~') == false)); // Hide Account Actions if in single user mode or domain authentication
2148 //QV('p2AccountImage', ((features & 4) == 0) && (serverinfo.domainauth == false)); // If account actions are not visible, also remove the image on that panel
@@ -2238,6 +2242,7 @@
2242 QV('verifyEmailId2', (userinfo.emailVerified !== true) && (userinfo.email != null) && (serverinfo.emailcheck == true) && (accountSettingsLocked == false));
2243 QV('manageOtp', (serverinfo.lock2factor != true) && (authFactorCount > 0) && ((features2 & 0x40000) == 0));
2244 QV('authPhoneNumberCheck', (userinfo.phone != null));
2245 + QV('authMessagingCheck', (userinfo.msghandle != null));
2246 QV('authEmailSetupCheck', (userinfo.otpekey == 1) && (userinfo.email != null) && (userinfo.emailVerified == true));
2247 QV('authAppSetupCheck', userinfo.otpsecret == 1);
2248 QV('manageAuthApp', (serverinfo.lock2factor != true) && ((userinfo.otpsecret == 1) || ((features2 & 0x00020000) == 0)));
@@ -2953,6 +2958,16 @@
2958 account_managePhoneCodeValidate();
2959 break;
2960 }
2961 + case 'verifyMessaging': {
2962 + if (xxdialogMode && (xxdialogTag != 'verifyMessaging')) return;
2963 + var x = '<table><tr><td><img src="images/messaging40.png" style=padding:8px>';
2964 + x += '<td>' + "Check your messaging application and enter the verification code.";
2965 + x += '<br /><br /><div style=width:100%;text-align:center>' + "Verification code:" + ' <input type=tel pattern="[0-9]" inputmode="number" maxlength=6 id=d2phoneCodeInput onKeyUp=account_managePhoneCodeValidate() onkeypress="if (event.key==\'Enter\') account_managePhoneCodeValidate(1)"></div></table>';
2966 + setDialogMode(2, "Messaging Notifications", 3, account_manageMessagingConfirm, x, message.cookie);
2967 + Q('d2phoneCodeInput').focus();
2968 + account_managePhoneCodeValidate();
2969 + break;
2970 + }
2971 case 'fileoperation': {
2972 // View the file in the dialog box
2973 var p5editSaveBack = function(b, tag) {
@@ -11920,7 +11935,7 @@
11935 var x;
11936 if (userinfo.phone != null) {
11937 x = '<table style=width:100%><tr><td style=width:56px><img src="images/phone80.png" style=padding:8px>';
11923 - x += '<td style=text-align:center><div style=padding:6px>' + "Verified phone number" + '</div><div style=font-size:20px>' + userinfo.phone + '</div>';
11938 + x += '<td style=text-align:center><div style=padding:6px>' + "Verified phone number" + '</div><div style=font-size:20px>' + EscapeHtml(userinfo.phone) + '</div>';
11939 x += '<div style=margin:10px><label><input id=d2delPhone type=checkbox onclick=account_managePhoneRemoveValidate() />' + "Remove phone number" + '</label></div>';
11940 setDialogMode(2, "Phone Notifications", 3, account_managePhoneRemove, x);
11941 account_managePhoneRemoveValidate();
@@ -11942,6 +11957,35 @@
11957 function account_managePhoneRemove() { if (Q('d2delPhone').checked) { meshserver.send({ action: 'removePhone' }); } }
11958 function account_managePhoneRemoveValidate() { QE('idx_dlgOkButton', Q('d2delPhone').checked); }
11959
11960 + function account_manageMessaging() {
11961 + if (xxdialogMode || ((features2 & 0x02000000) == 0)) return;
11962 + var x;
11963 + if (userinfo.msghandle != null) {
11964 + x = '<table style=width:100%><tr><td style=width:56px><img src="images/messaging40.png" style=padding:8px>';
11965 + x += '<td style=text-align:center><div style=padding:6px>' + "Verified handle" + '</div><div style=font-size:20px>' + EscapeHtml(userinfo.msghandle) + '</div>';
11966 + x += '<div style=margin:10px><label><input id=d2delPhone type=checkbox onclick=account_managePhoneRemoveValidate() />' + "Remove messaging" + '</label></div>';
11967 + setDialogMode(2, "Messaging Notifications", 3, account_managePhoneRemove, x);
11968 + account_managePhoneRemoveValidate();
11969 + } else {
11970 + x = '<table style=width:100%><tr><td style=width:56px;vertical-align:top><img src="images/messaging40.png" style=padding:8px>';
11971 + x += '<td>' + "Enter your messaging service and handle. Once verified, this server can send you login verification and other notifications." + '<br /><br />';
11972 + var y = '<select id=d2serviceselect style=width:160px;margin-left:8px>';
11973 + if ((serverinfo.userMsgProviders & 1) != 0) { y += '<option value=1>' + "Telegram" + '</option>'; }
11974 + if ((serverinfo.userMsgProviders & 2) != 0) { y += '<option value=2>' + "Signal Messenger" + '</option>'; }
11975 + y += '</select>';
11976 + x += '<table><tr><td>' + "Service" + '<td>' + y;
11977 + x += '<tr><td>' + "Handle" + '<td><input maxlength=64 style=width:160px;margin-left:8px id=d2handleinput onKeyUp=account_manageMessagingValidate() onkeypress="if (event.key==\'Enter\') account_manageMessagingValidate(1)">';
11978 + x += '</table>';
11979 + setDialogMode(2, "Messaging Notifications", 3, account_manageMessagingAdd, x, 'verifyMessaging');
11980 + Q('d2handleinput').focus();
11981 + account_manageMessagingValidate();
11982 + }
11983 + }
11984 +
11985 + function account_manageMessagingValidate(x) { var ok = (Q('d2handleinput').value.length > 0); QE('idx_dlgOkButton', ok); if ((x == 1) && ok) { dialogclose(1); } }
11986 + function account_manageMessagingAdd() { if (Q('d2handleinput').value.length == 0) return; QE('d2handleinput', false); meshserver.send({ action: 'verifyMessaging', service: Q('d2serviceselect').value, handle: Q('d2handleinput').value }); }
11987 + function account_manageMessagingConfirm(b, tag) { meshserver.send({ action: 'confirmMessaging', code: Q('d2phoneCodeInput').value, cookie: tag }); }
11988 +
11989 function account_manageAuthEmail() {
11990 if (xxdialogMode || ((features & 0x00800000) == 0)) return;
11991 var emailU2Fenabled = ((userinfo.otpekey == 1) && (userinfo.email != null) && (userinfo.emailVerified == true));
@@ -14279,7 +14323,9 @@
14323 152: "No longer a relay for \"{0}\".",
14324 153: "Is a relay for \"{0}\".",
14325 154: "Account changed to sync with LDAP data.",
14282 - 155: "Denied user login from {0}, {1}, {2}"
14326 + 155: "Denied user login from {0}, {1}, {2}",
14327 + 156: "Verified messaging account of user {0}",
14328 + 157: "Removed messaging account of user {0}"
14329 };
14330
14331 var eventsShortMessageId = {
@@ -14723,6 +14769,15 @@
14769 function showSendSMSValidate() { QE('idx_dlgOkButton', Q('d2smsText').value.length > 0); }
14770 function showSendSMSEx(b, tag) { if (Q('d2smsText').value.length > 0) { meshserver.send({ action: 'smsuser', userid: decodeURIComponent(tag), msg: Q('d2smsText').value }); } }
14771
14772 + function showSendMessage(userid) {
14773 + if (xxdialogMode) return;
14774 + setDialogMode(2, "Send Message", 3, showSendMessageEx, '<textarea id=d2smsText maxlength=160 style=background-color:#fcf3cf;width:100%;height:100px;resize:none onKeyUp=showSendSMSValidate()></textarea><span style=font-size:10px><span>', userid);
14775 + Q('d2smsText').focus();
14776 + showSendSMSValidate();
14777 + }
14778 +
14779 + function showSendMessageEx(b, tag) { if (Q('d2smsText').value.length > 0) { meshserver.send({ action: 'msguser', userid: decodeURIComponent(tag), msg: Q('d2smsText').value }); } }
14780 +
14781 function showSendEmail(userid) {
14782 if (xxdialogMode) return;
14783 var x = '<input id=d2emailSubject style=background-color:#fcf3cf;width:100% placeholder="' + "Subject" + '"></input>';
@@ -15630,6 +15685,10 @@
15685 x += addDeviceAttribute("Phone Number", (user.phone?user.phone:('<i>' + "None" + '</i>')) + ' <img class=hoverButton style=cursor:pointer src="images/link5.png" onclick=p30editPhone() />');
15686 }
15687
15688 + if ((features2 & 0x02000000) || (user.msghandle != null)) { // If user messaging is enabled on the server or user has a messaging handle
15689 + x += addDeviceAttribute("Messaging", (user.msghandle?user.msghandle:('<i>' + "None" + '</i>')) + ' <img class=hoverButton style=cursor:pointer src="images/link5.png" onclick=p30editMessaging() />');
15690 + }
15691 +
15692 // Display features
15693 var userFeatures = [];
15694 if ((serverinfo.usersSessionRecording == 1) && (user.flags) && (user.flags & 2)) { userFeatures.push("Record Sessions"); }
@@ -15706,6 +15765,7 @@
15765 // Add action buttons
15766 x += '<input type=button value="' + "Notes" + '" title="' + "View notes about this user" + '" onclick=showNotes(false,"' + encodeURIComponentEx(user._id) + '") />';
15767 if (user.phone && (features & 0x02000000)) { x += '<input type=button value="' + "SMS" + '" title="' + "Send a SMS message to this user" + '" onclick=showSendSMS("' + encodeURIComponentEx(user._id) + '") />'; }
15768 + if (user.msghandle && (features2 & 0x02000000)) { x += '<input type=button value="' + "Message" + '" title="' + "Send a message to this user" + '" onclick=showSendMessage("' + encodeURIComponentEx(user._id) + '") />'; }
15769 if ((typeof user.email == 'string') && (user.emailVerified === true) && (features & 0x00000040)) { x += '<input type=button value="' + "Email" + '" title="' + "Send a email message to this user" + '" onclick=showSendEmail("' + encodeURIComponentEx(user._id) + '") />'; }
15770 if (!self && ((activeSessions > 0) || ((features2 & 8) && (user.webpush)))) {
15771 x += '<input type=button value="' + "Notify" + '" title="' + "Send user notification" + '" onclick=showUserAlertDialog(event,"' + encodeURIComponentEx(user._id) + '") />';
@@ -15771,6 +15831,16 @@
15831 p30editPhoneValidate();
15832 }
15833
15834 + function p30editMessaging() { // TODO
15835 + if (xxdialogMode) return;
15836 + var x = '<table style=width:100%><tr><td style=width:56px><img src="images/phone80.png" style=padding:8px>';
15837 + x += '<td style=width:100%;text-align:center>' + "SMS capable phone number for this user." + '<br />' + "Leave blank for none.";
15838 + x += '<br /><br /><div style=width:100%;text-align:center>' + "Phone number:" + ' <input type=tel pattern="[0-9]" autocomplete="tel" value="' + (currentUser.phone?currentUser.phone:'') + '" inputmode="tel" maxlength=18 id=d2phoneinput onKeyUp=p30editPhoneValidate() onkeypress="if (event.key==\'Enter\') p30editPhoneValidate(1)"></div></table>';
15839 + setDialogMode(2, "Phone Notifications", 3, p30editPhoneEx, x, 'verifyPhone');
15840 + Q('d2phoneinput').focus();
15841 + p30editPhoneValidate();
15842 + }
15843 +
15844 function p20edituserfeatures() {
15845 if (xxdialogMode) return;
15846 var flags = (currentUser.flags)?currentUser.flags:0, x = ''; // Flags: 1 = Account Image, 2 = Session Recording
@@ -16866,10 +16936,14 @@
16936 "No user management rights",
16937 "Invalid SMS message",
16938 "No phone number for this user",
16869 - "SMS succesfuly sent.",
16939 + "SMS succesfully sent.",
16940 "SMS error",
16941 "SMS error: {0}",
16872 - "Email domain \"{0}\" is not allowed. Only ({1}) are allowed" // 30
16942 + "Email domain \"{0}\" is not allowed. Only ({1}) are allowed", // 30,
16943 + "Invalid message",
16944 + "Message succesfully sent.",
16945 + "Message error",
16946 + "Message error: {0}"
16947 ];
16948 if (typeof n.titleid == 'number') { try { n.title = translatedTitles[n.titleid]; } catch (ex) {} }
16949 if (typeof n.msgid == 'number') { try { n.text = translatedMessages[n.msgid]; if (Array.isArray(n.args)) { n.text = format(n.text, n.args[0], n.args[1], n.args[2], n.args[3], n.args[4], n.args[5]); } } catch (ex) { } }