diff --git a/.meteor/packages b/.meteor/packages
index ddf589282d7..da66a3df2f9 100644
--- a/.meteor/packages
+++ b/.meteor/packages
@@ -64,6 +64,7 @@ rocketchat:emoji-custom
rocketchat:emoji-emojione
rocketchat:error-handler
rocketchat:favico
+rocketchat:federation
rocketchat:file
rocketchat:file-upload
rocketchat:github-enterprise
diff --git a/.meteor/versions b/.meteor/versions
index ada8b493485..a249651b813 100644
--- a/.meteor/versions
+++ b/.meteor/versions
@@ -159,6 +159,7 @@ rocketchat:emoji-custom@1.0.0
rocketchat:emoji-emojione@0.0.1
rocketchat:error-handler@1.0.0
rocketchat:favico@0.0.1
+rocketchat:federation@0.0.1
rocketchat:file@0.0.1
rocketchat:file-upload@0.0.1
rocketchat:github-enterprise@0.0.1
diff --git a/imports/message-read-receipt/server/hooks.js b/imports/message-read-receipt/server/hooks.js
index 22666d6df06..f9ffe22905c 100644
--- a/imports/message-read-receipt/server/hooks.js
+++ b/imports/message-read-receipt/server/hooks.js
@@ -15,3 +15,7 @@ callbacks.add('afterSaveMessage', (message, room) => {
// mark message as read as well
ReadReceipt.markMessageAsReadBySender(message, room._id, message.u._id);
});
+
+callbacks.add('afterReadMessages', (rid, { userId, lastSeen }) => {
+ ReadReceipt.markMessagesAsRead(rid, userId, lastSeen);
+});
diff --git a/package-lock.json b/package-lock.json
index a97aca62ca1..46ecfd847a4 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -2825,19 +2825,23 @@
"dependencies": {
"abbrev": {
"version": "1.1.1",
- "bundled": true
+ "resolved": false,
+ "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q=="
},
"ansi-regex": {
"version": "2.1.1",
- "bundled": true
+ "resolved": false,
+ "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8="
},
"aproba": {
"version": "1.2.0",
- "bundled": true
+ "resolved": false,
+ "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw=="
},
"are-we-there-yet": {
"version": "1.1.5",
- "bundled": true,
+ "resolved": false,
+ "integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==",
"requires": {
"delegates": "^1.0.0",
"readable-stream": "^2.0.6"
@@ -2845,11 +2849,13 @@
},
"balanced-match": {
"version": "1.0.0",
- "bundled": true
+ "resolved": false,
+ "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c="
},
"brace-expansion": {
"version": "1.1.11",
- "bundled": true,
+ "resolved": false,
+ "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
"requires": {
"balanced-match": "^1.0.0",
"concat-map": "0.0.1"
@@ -2857,57 +2863,69 @@
},
"chownr": {
"version": "1.1.1",
- "bundled": true
+ "resolved": false,
+ "integrity": "sha512-j38EvO5+LHX84jlo6h4UzmOwi0UgW61WRyPtJz4qaadK5eY3BTS5TY/S1Stc3Uk2lIM6TPevAlULiEJwie860g=="
},
"code-point-at": {
"version": "1.1.0",
- "bundled": true
+ "resolved": false,
+ "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c="
},
"concat-map": {
"version": "0.0.1",
- "bundled": true
+ "resolved": false,
+ "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s="
},
"console-control-strings": {
"version": "1.1.0",
- "bundled": true
+ "resolved": false,
+ "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4="
},
"core-util-is": {
"version": "1.0.2",
- "bundled": true
+ "resolved": false,
+ "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac="
},
"debug": {
"version": "2.6.9",
- "bundled": true,
+ "resolved": false,
+ "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
"requires": {
"ms": "2.0.0"
}
},
"deep-extend": {
"version": "0.6.0",
- "bundled": true
+ "resolved": false,
+ "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA=="
},
"delegates": {
"version": "1.0.0",
- "bundled": true
+ "resolved": false,
+ "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o="
},
"detect-libc": {
"version": "1.0.3",
- "bundled": true
+ "resolved": false,
+ "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups="
},
"fs-minipass": {
"version": "1.2.5",
- "bundled": true,
+ "resolved": false,
+ "integrity": "sha512-JhBl0skXjUPCFH7x6x61gQxrKyXsxB5gcgePLZCwfyCGGsTISMoIeObbrvVeP6Xmyaudw4TT43qV2Gz+iyd2oQ==",
"requires": {
"minipass": "^2.2.1"
}
},
"fs.realpath": {
"version": "1.0.0",
- "bundled": true
+ "resolved": false,
+ "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8="
},
"gauge": {
"version": "2.7.4",
- "bundled": true,
+ "resolved": false,
+ "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=",
"requires": {
"aproba": "^1.0.3",
"console-control-strings": "^1.0.0",
@@ -2921,7 +2939,8 @@
},
"glob": {
"version": "7.1.2",
- "bundled": true,
+ "resolved": false,
+ "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==",
"requires": {
"fs.realpath": "^1.0.0",
"inflight": "^1.0.4",
@@ -2933,25 +2952,29 @@
},
"has-unicode": {
"version": "2.0.1",
- "bundled": true
+ "resolved": false,
+ "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk="
},
"iconv-lite": {
"version": "0.4.24",
- "bundled": true,
+ "resolved": false,
+ "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
"requires": {
"safer-buffer": ">= 2.1.2 < 3"
}
},
"ignore-walk": {
"version": "3.0.1",
- "bundled": true,
+ "resolved": false,
+ "integrity": "sha512-DTVlMx3IYPe0/JJcYP7Gxg7ttZZu3IInhuEhbchuqneY9wWe5Ojy2mXLBaQFUQmo0AW2r3qG7m1mg86js+gnlQ==",
"requires": {
"minimatch": "^3.0.4"
}
},
"inflight": {
"version": "1.0.6",
- "bundled": true,
+ "resolved": false,
+ "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
"requires": {
"once": "^1.3.0",
"wrappy": "1"
@@ -2959,37 +2982,44 @@
},
"inherits": {
"version": "2.0.3",
- "bundled": true
+ "resolved": false,
+ "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4="
},
"ini": {
"version": "1.3.5",
- "bundled": true
+ "resolved": false,
+ "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw=="
},
"is-fullwidth-code-point": {
"version": "1.0.0",
- "bundled": true,
+ "resolved": false,
+ "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=",
"requires": {
"number-is-nan": "^1.0.0"
}
},
"isarray": {
"version": "1.0.0",
- "bundled": true
+ "resolved": false,
+ "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE="
},
"minimatch": {
"version": "3.0.4",
- "bundled": true,
+ "resolved": false,
+ "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
"requires": {
"brace-expansion": "^1.1.7"
}
},
"minimist": {
"version": "0.0.8",
- "bundled": true
+ "resolved": false,
+ "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0="
},
"minipass": {
"version": "2.3.4",
- "bundled": true,
+ "resolved": false,
+ "integrity": "sha512-mlouk1OHlaUE8Odt1drMtG1bAJA4ZA6B/ehysgV0LUIrDHdKgo1KorZq3pK0b/7Z7LJIQ12MNM6aC+Tn6lUZ5w==",
"requires": {
"safe-buffer": "^5.1.2",
"yallist": "^3.0.0"
@@ -2997,35 +3027,41 @@
"dependencies": {
"safe-buffer": {
"version": "5.1.2",
- "bundled": true
+ "resolved": false,
+ "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
},
"yallist": {
"version": "3.0.2",
- "bundled": true
+ "resolved": false,
+ "integrity": "sha1-hFK0u36Dx8GI2AQcGoN8dz1ti7k="
}
}
},
"minizlib": {
"version": "1.1.0",
- "bundled": true,
+ "resolved": false,
+ "integrity": "sha512-4T6Ur/GctZ27nHfpt9THOdRZNgyJ9FZchYO1ceg5S8Q3DNLCKYy44nCZzgCJgcvx2UM8czmqak5BCxJMrq37lA==",
"requires": {
"minipass": "^2.2.1"
}
},
"mkdirp": {
"version": "0.5.1",
- "bundled": true,
+ "resolved": false,
+ "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=",
"requires": {
"minimist": "0.0.8"
}
},
"ms": {
"version": "2.0.0",
- "bundled": true
+ "resolved": false,
+ "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
},
"needle": {
"version": "2.2.3",
- "bundled": true,
+ "resolved": false,
+ "integrity": "sha512-GPL22d/U9cai87FcCPO6e+MT3vyHS2j+zwotakDc7kE2DtUAqFKMXLJCTtRp+5S75vXIwQPvIxkvlctxf9q4gQ==",
"requires": {
"debug": "^2.1.2",
"iconv-lite": "^0.4.4",
@@ -3034,7 +3070,8 @@
},
"node-pre-gyp": {
"version": "0.11.0",
- "bundled": true,
+ "resolved": false,
+ "integrity": "sha512-TwWAOZb0j7e9eGaf9esRx3ZcLaE5tQ2lvYy1pb5IAaG1a2e2Kv5Lms1Y4hpj+ciXJRofIxxlt5haeQ/2ANeE0Q==",
"requires": {
"detect-libc": "^1.0.2",
"mkdirp": "^0.5.1",
@@ -3050,7 +3087,8 @@
},
"nopt": {
"version": "4.0.1",
- "bundled": true,
+ "resolved": false,
+ "integrity": "sha1-0NRoWv1UFRk8jHUFYC0NF81kR00=",
"requires": {
"abbrev": "1",
"osenv": "^0.1.4"
@@ -3058,11 +3096,13 @@
},
"npm-bundled": {
"version": "1.0.5",
- "bundled": true
+ "resolved": false,
+ "integrity": "sha512-m/e6jgWu8/v5niCUKQi9qQl8QdeEduFA96xHDDzFGqly0OOjI7c+60KM/2sppfnUU9JJagf+zs+yGhqSOFj71g=="
},
"npm-packlist": {
"version": "1.1.11",
- "bundled": true,
+ "resolved": false,
+ "integrity": "sha512-CxKlZ24urLkJk+9kCm48RTQ7L4hsmgSVzEk0TLGPzzyuFxD7VNgy5Sl24tOLMzQv773a/NeJ1ce1DKeacqffEA==",
"requires": {
"ignore-walk": "^3.0.1",
"npm-bundled": "^1.0.1"
@@ -3070,7 +3110,8 @@
},
"npmlog": {
"version": "4.1.2",
- "bundled": true,
+ "resolved": false,
+ "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==",
"requires": {
"are-we-there-yet": "~1.1.2",
"console-control-strings": "~1.1.0",
@@ -3080,30 +3121,36 @@
},
"number-is-nan": {
"version": "1.0.1",
- "bundled": true
+ "resolved": false,
+ "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0="
},
"object-assign": {
"version": "4.1.1",
- "bundled": true
+ "resolved": false,
+ "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM="
},
"once": {
"version": "1.4.0",
- "bundled": true,
+ "resolved": false,
+ "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
"requires": {
"wrappy": "1"
}
},
"os-homedir": {
"version": "1.0.2",
- "bundled": true
+ "resolved": false,
+ "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M="
},
"os-tmpdir": {
"version": "1.0.2",
- "bundled": true
+ "resolved": false,
+ "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ="
},
"osenv": {
"version": "0.1.5",
- "bundled": true,
+ "resolved": false,
+ "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==",
"requires": {
"os-homedir": "^1.0.0",
"os-tmpdir": "^1.0.0"
@@ -3111,15 +3158,18 @@
},
"path-is-absolute": {
"version": "1.0.1",
- "bundled": true
+ "resolved": false,
+ "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18="
},
"process-nextick-args": {
"version": "2.0.0",
- "bundled": true
+ "resolved": false,
+ "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw=="
},
"rc": {
"version": "1.2.8",
- "bundled": true,
+ "resolved": false,
+ "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==",
"requires": {
"deep-extend": "^0.6.0",
"ini": "~1.3.0",
@@ -3129,13 +3179,15 @@
"dependencies": {
"minimist": {
"version": "1.2.0",
- "bundled": true
+ "resolved": false,
+ "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ="
}
}
},
"readable-stream": {
"version": "2.3.5",
- "bundled": true,
+ "resolved": false,
+ "integrity": "sha512-tK0yDhrkygt/knjowCUiWP9YdV7c5R+8cR0r/kt9ZhBU906Fs6RpQJCEilamRJj1Nx2rWI6LkW9gKqjTkshhEw==",
"requires": {
"core-util-is": "~1.0.0",
"inherits": "~2.0.3",
@@ -3148,38 +3200,46 @@
},
"rimraf": {
"version": "2.6.2",
- "bundled": true,
+ "resolved": false,
+ "integrity": "sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w==",
"requires": {
"glob": "^7.0.5"
}
},
"safe-buffer": {
"version": "5.1.1",
- "bundled": true
+ "resolved": false,
+ "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg=="
},
"safer-buffer": {
"version": "2.1.2",
- "bundled": true
+ "resolved": false,
+ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
},
"sax": {
"version": "1.2.4",
- "bundled": true
+ "resolved": false,
+ "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw=="
},
"semver": {
"version": "5.5.1",
- "bundled": true
+ "resolved": false,
+ "integrity": "sha512-PqpAxfrEhlSUWge8dwIp4tZnQ25DIOthpiaHNIthsjEFQD6EvqUKUDM7L8O2rShkFccYo1VjJR0coWfNkCubRw=="
},
"set-blocking": {
"version": "2.0.0",
- "bundled": true
+ "resolved": false,
+ "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc="
},
"signal-exit": {
"version": "3.0.2",
- "bundled": true
+ "resolved": false,
+ "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0="
},
"string-width": {
"version": "1.0.2",
- "bundled": true,
+ "resolved": false,
+ "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=",
"requires": {
"code-point-at": "^1.0.0",
"is-fullwidth-code-point": "^1.0.0",
@@ -3188,25 +3248,29 @@
},
"string_decoder": {
"version": "1.0.3",
- "bundled": true,
+ "resolved": false,
+ "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==",
"requires": {
"safe-buffer": "~5.1.0"
}
},
"strip-ansi": {
"version": "3.0.1",
- "bundled": true,
+ "resolved": false,
+ "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
"requires": {
"ansi-regex": "^2.0.0"
}
},
"strip-json-comments": {
"version": "2.0.1",
- "bundled": true
+ "resolved": false,
+ "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo="
},
"tar": {
"version": "4.4.6",
- "bundled": true,
+ "resolved": false,
+ "integrity": "sha512-tMkTnh9EdzxyfW+6GK6fCahagXsnYk6kE6S9Gr9pjVdys769+laCTbodXDhPAjzVtEBazRgP0gYqOjnk9dQzLg==",
"requires": {
"chownr": "^1.0.1",
"fs-minipass": "^1.2.5",
@@ -3219,28 +3283,33 @@
"dependencies": {
"safe-buffer": {
"version": "5.1.2",
- "bundled": true
+ "resolved": false,
+ "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
},
"yallist": {
"version": "3.0.2",
- "bundled": true
+ "resolved": false,
+ "integrity": "sha1-hFK0u36Dx8GI2AQcGoN8dz1ti7k="
}
}
},
"util-deprecate": {
"version": "1.0.2",
- "bundled": true
+ "resolved": false,
+ "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8="
},
"wide-align": {
"version": "1.1.3",
- "bundled": true,
+ "resolved": false,
+ "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==",
"requires": {
"string-width": "^1.0.2 || 2"
}
},
"wrappy": {
"version": "1.0.2",
- "bundled": true
+ "resolved": false,
+ "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
}
}
},
@@ -7066,9 +7135,9 @@
"integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8="
},
"fsevents": {
- "version": "1.2.4",
- "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.4.tgz",
- "integrity": "sha512-z8H8/diyk76B7q5wg+Ud0+CqzcAF3mBBI/bA5ne5zrRUUIvNkJY//D3BqyH571KuAC4Nr7Rw7CjWX4r0y9DvNg==",
+ "version": "1.2.7",
+ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.7.tgz",
+ "integrity": "sha512-Pxm6sI2MeBD7RdD12RYsqaP0nMiwx8eZBXCa6z2L+mRHm2DYrOYwihmhjpkdjUHwQhslWQjRpEgNq4XvBmaAuw==",
"dev": true,
"optional": true,
"requires": {
@@ -7094,7 +7163,7 @@
"optional": true
},
"are-we-there-yet": {
- "version": "1.1.4",
+ "version": "1.1.5",
"bundled": true,
"dev": true,
"optional": true,
@@ -7118,7 +7187,7 @@
}
},
"chownr": {
- "version": "1.0.1",
+ "version": "1.1.1",
"bundled": true,
"dev": true,
"optional": true
@@ -7154,7 +7223,7 @@
}
},
"deep-extend": {
- "version": "0.5.1",
+ "version": "0.6.0",
"bundled": true,
"dev": true,
"optional": true
@@ -7203,7 +7272,7 @@
}
},
"glob": {
- "version": "7.1.2",
+ "version": "7.1.3",
"bundled": true,
"dev": true,
"optional": true,
@@ -7223,12 +7292,12 @@
"optional": true
},
"iconv-lite": {
- "version": "0.4.21",
+ "version": "0.4.24",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
- "safer-buffer": "^2.1.0"
+ "safer-buffer": ">= 2.1.2 < 3"
}
},
"ignore-walk": {
@@ -7289,16 +7358,16 @@
"dev": true
},
"minipass": {
- "version": "2.2.4",
+ "version": "2.3.5",
"bundled": true,
"dev": true,
"requires": {
- "safe-buffer": "^5.1.1",
+ "safe-buffer": "^5.1.2",
"yallist": "^3.0.0"
}
},
"minizlib": {
- "version": "1.1.0",
+ "version": "1.2.1",
"bundled": true,
"dev": true,
"optional": true,
@@ -7321,7 +7390,7 @@
"optional": true
},
"needle": {
- "version": "2.2.0",
+ "version": "2.2.4",
"bundled": true,
"dev": true,
"optional": true,
@@ -7332,18 +7401,18 @@
}
},
"node-pre-gyp": {
- "version": "0.10.0",
+ "version": "0.10.3",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"detect-libc": "^1.0.2",
"mkdirp": "^0.5.1",
- "needle": "^2.2.0",
+ "needle": "^2.2.1",
"nopt": "^4.0.1",
"npm-packlist": "^1.1.6",
"npmlog": "^4.0.2",
- "rc": "^1.1.7",
+ "rc": "^1.2.7",
"rimraf": "^2.6.1",
"semver": "^5.3.0",
"tar": "^4"
@@ -7360,13 +7429,13 @@
}
},
"npm-bundled": {
- "version": "1.0.3",
+ "version": "1.0.5",
"bundled": true,
"dev": true,
"optional": true
},
"npm-packlist": {
- "version": "1.1.10",
+ "version": "1.2.0",
"bundled": true,
"dev": true,
"optional": true,
@@ -7441,12 +7510,12 @@
"optional": true
},
"rc": {
- "version": "1.2.7",
+ "version": "1.2.8",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
- "deep-extend": "^0.5.1",
+ "deep-extend": "^0.6.0",
"ini": "~1.3.0",
"minimist": "^1.2.0",
"strip-json-comments": "~2.0.1"
@@ -7476,16 +7545,16 @@
}
},
"rimraf": {
- "version": "2.6.2",
+ "version": "2.6.3",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
- "glob": "^7.0.5"
+ "glob": "^7.1.3"
}
},
"safe-buffer": {
- "version": "5.1.1",
+ "version": "5.1.2",
"bundled": true,
"dev": true
},
@@ -7502,7 +7571,7 @@
"optional": true
},
"semver": {
- "version": "5.5.0",
+ "version": "5.6.0",
"bundled": true,
"dev": true,
"optional": true
@@ -7553,17 +7622,17 @@
"optional": true
},
"tar": {
- "version": "4.4.1",
+ "version": "4.4.8",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
- "chownr": "^1.0.1",
+ "chownr": "^1.1.1",
"fs-minipass": "^1.2.5",
- "minipass": "^2.2.4",
- "minizlib": "^1.1.0",
+ "minipass": "^2.3.4",
+ "minizlib": "^1.1.1",
"mkdirp": "^0.5.0",
- "safe-buffer": "^5.1.1",
+ "safe-buffer": "^5.1.2",
"yallist": "^3.0.2"
}
},
@@ -7574,12 +7643,12 @@
"optional": true
},
"wide-align": {
- "version": "1.1.2",
+ "version": "1.1.3",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
- "string-width": "^1.0.2"
+ "string-width": "^1.0.2 || 2"
}
},
"wrappy": {
@@ -7588,7 +7657,7 @@
"dev": true
},
"yallist": {
- "version": "3.0.2",
+ "version": "3.0.3",
"bundled": true,
"dev": true
}
@@ -7671,7 +7740,7 @@
},
"get-stream": {
"version": "3.0.0",
- "resolved": "http://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz",
+ "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz",
"integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ="
},
"get-value": {
@@ -8198,19 +8267,23 @@
"dependencies": {
"abbrev": {
"version": "1.1.1",
- "bundled": true
+ "resolved": false,
+ "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q=="
},
"ansi-regex": {
"version": "2.1.1",
- "bundled": true
+ "resolved": false,
+ "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8="
},
"aproba": {
"version": "1.2.0",
- "bundled": true
+ "resolved": false,
+ "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw=="
},
"are-we-there-yet": {
"version": "1.1.5",
- "bundled": true,
+ "resolved": false,
+ "integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==",
"requires": {
"delegates": "^1.0.0",
"readable-stream": "^2.0.6"
@@ -8218,11 +8291,13 @@
},
"balanced-match": {
"version": "1.0.0",
- "bundled": true
+ "resolved": false,
+ "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c="
},
"brace-expansion": {
"version": "1.1.11",
- "bundled": true,
+ "resolved": false,
+ "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
"requires": {
"balanced-match": "^1.0.0",
"concat-map": "0.0.1"
@@ -8230,57 +8305,69 @@
},
"chownr": {
"version": "1.1.1",
- "bundled": true
+ "resolved": false,
+ "integrity": "sha512-j38EvO5+LHX84jlo6h4UzmOwi0UgW61WRyPtJz4qaadK5eY3BTS5TY/S1Stc3Uk2lIM6TPevAlULiEJwie860g=="
},
"code-point-at": {
"version": "1.1.0",
- "bundled": true
+ "resolved": false,
+ "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c="
},
"concat-map": {
"version": "0.0.1",
- "bundled": true
+ "resolved": false,
+ "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s="
},
"console-control-strings": {
"version": "1.1.0",
- "bundled": true
+ "resolved": false,
+ "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4="
},
"core-util-is": {
"version": "1.0.2",
- "bundled": true
+ "resolved": false,
+ "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac="
},
"debug": {
"version": "2.6.9",
- "bundled": true,
+ "resolved": false,
+ "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
"requires": {
"ms": "2.0.0"
}
},
"deep-extend": {
"version": "0.6.0",
- "bundled": true
+ "resolved": false,
+ "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA=="
},
"delegates": {
"version": "1.0.0",
- "bundled": true
+ "resolved": false,
+ "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o="
},
"detect-libc": {
"version": "1.0.3",
- "bundled": true
+ "resolved": false,
+ "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups="
},
"fs-minipass": {
"version": "1.2.5",
- "bundled": true,
+ "resolved": false,
+ "integrity": "sha512-JhBl0skXjUPCFH7x6x61gQxrKyXsxB5gcgePLZCwfyCGGsTISMoIeObbrvVeP6Xmyaudw4TT43qV2Gz+iyd2oQ==",
"requires": {
"minipass": "^2.2.1"
}
},
"fs.realpath": {
"version": "1.0.0",
- "bundled": true
+ "resolved": false,
+ "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8="
},
"gauge": {
"version": "2.7.4",
- "bundled": true,
+ "resolved": false,
+ "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=",
"requires": {
"aproba": "^1.0.3",
"console-control-strings": "^1.0.0",
@@ -8294,7 +8381,8 @@
},
"glob": {
"version": "7.1.3",
- "bundled": true,
+ "resolved": false,
+ "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==",
"requires": {
"fs.realpath": "^1.0.0",
"inflight": "^1.0.4",
@@ -8306,25 +8394,29 @@
},
"has-unicode": {
"version": "2.0.1",
- "bundled": true
+ "resolved": false,
+ "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk="
},
"iconv-lite": {
"version": "0.4.24",
- "bundled": true,
+ "resolved": false,
+ "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
"requires": {
"safer-buffer": ">= 2.1.2 < 3"
}
},
"ignore-walk": {
"version": "3.0.1",
- "bundled": true,
+ "resolved": false,
+ "integrity": "sha512-DTVlMx3IYPe0/JJcYP7Gxg7ttZZu3IInhuEhbchuqneY9wWe5Ojy2mXLBaQFUQmo0AW2r3qG7m1mg86js+gnlQ==",
"requires": {
"minimatch": "^3.0.4"
}
},
"inflight": {
"version": "1.0.6",
- "bundled": true,
+ "resolved": false,
+ "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
"requires": {
"once": "^1.3.0",
"wrappy": "1"
@@ -8332,37 +8424,44 @@
},
"inherits": {
"version": "2.0.3",
- "bundled": true
+ "resolved": false,
+ "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4="
},
"ini": {
"version": "1.3.5",
- "bundled": true
+ "resolved": false,
+ "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw=="
},
"is-fullwidth-code-point": {
"version": "1.0.0",
- "bundled": true,
+ "resolved": false,
+ "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=",
"requires": {
"number-is-nan": "^1.0.0"
}
},
"isarray": {
"version": "1.0.0",
- "bundled": true
+ "resolved": false,
+ "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE="
},
"minimatch": {
"version": "3.0.4",
- "bundled": true,
+ "resolved": false,
+ "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
"requires": {
"brace-expansion": "^1.1.7"
}
},
"minimist": {
"version": "1.2.0",
- "bundled": true
+ "resolved": false,
+ "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ="
},
"minipass": {
"version": "2.3.5",
- "bundled": true,
+ "resolved": false,
+ "integrity": "sha512-Gi1W4k059gyRbyVUZQ4mEqLm0YIUiGYfvxhF6SIlk3ui1WVxMTGfGdQ2SInh3PDrRTVvPKgULkpJtT4RH10+VA==",
"requires": {
"safe-buffer": "^5.1.2",
"yallist": "^3.0.0"
@@ -8370,31 +8469,36 @@
},
"minizlib": {
"version": "1.1.1",
- "bundled": true,
+ "resolved": false,
+ "integrity": "sha512-TrfjCjk4jLhcJyGMYymBH6oTXcWjYbUAXTHDbtnWHjZC25h0cdajHuPE1zxb4DVmu8crfh+HwH/WMuyLG0nHBg==",
"requires": {
"minipass": "^2.2.1"
}
},
"mkdirp": {
"version": "0.5.1",
- "bundled": true,
+ "resolved": false,
+ "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=",
"requires": {
"minimist": "0.0.8"
},
"dependencies": {
"minimist": {
"version": "0.0.8",
- "bundled": true
+ "resolved": false,
+ "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0="
}
}
},
"ms": {
"version": "2.0.0",
- "bundled": true
+ "resolved": false,
+ "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
},
"needle": {
"version": "2.2.4",
- "bundled": true,
+ "resolved": false,
+ "integrity": "sha512-HyoqEb4wr/rsoaIDfTH2aVL9nWtQqba2/HvMv+++m8u0dz808MaagKILxtfeSN7QU7nvbQ79zk3vYOJp9zsNEA==",
"requires": {
"debug": "^2.1.2",
"iconv-lite": "^0.4.4",
@@ -8403,7 +8507,8 @@
},
"node-pre-gyp": {
"version": "0.12.0",
- "bundled": true,
+ "resolved": false,
+ "integrity": "sha512-4KghwV8vH5k+g2ylT+sLTjy5wmUOb9vPhnM8NHvRf9dHmnW/CndrFXy2aRPaPST6dugXSdHXfeaHQm77PIz/1A==",
"requires": {
"detect-libc": "^1.0.2",
"mkdirp": "^0.5.1",
@@ -8419,7 +8524,8 @@
},
"nopt": {
"version": "4.0.1",
- "bundled": true,
+ "resolved": false,
+ "integrity": "sha1-0NRoWv1UFRk8jHUFYC0NF81kR00=",
"requires": {
"abbrev": "1",
"osenv": "^0.1.4"
@@ -8427,11 +8533,13 @@
},
"npm-bundled": {
"version": "1.0.5",
- "bundled": true
+ "resolved": false,
+ "integrity": "sha512-m/e6jgWu8/v5niCUKQi9qQl8QdeEduFA96xHDDzFGqly0OOjI7c+60KM/2sppfnUU9JJagf+zs+yGhqSOFj71g=="
},
"npm-packlist": {
"version": "1.1.12",
- "bundled": true,
+ "resolved": false,
+ "integrity": "sha512-WJKFOVMeAlsU/pjXuqVdzU0WfgtIBCupkEVwn+1Y0ERAbUfWw8R4GjgVbaKnUjRoD2FoQbHOCbOyT5Mbs9Lw4g==",
"requires": {
"ignore-walk": "^3.0.1",
"npm-bundled": "^1.0.1"
@@ -8439,7 +8547,8 @@
},
"npmlog": {
"version": "4.1.2",
- "bundled": true,
+ "resolved": false,
+ "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==",
"requires": {
"are-we-there-yet": "~1.1.2",
"console-control-strings": "~1.1.0",
@@ -8449,30 +8558,36 @@
},
"number-is-nan": {
"version": "1.0.1",
- "bundled": true
+ "resolved": false,
+ "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0="
},
"object-assign": {
"version": "4.1.1",
- "bundled": true
+ "resolved": false,
+ "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM="
},
"once": {
"version": "1.4.0",
- "bundled": true,
+ "resolved": false,
+ "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
"requires": {
"wrappy": "1"
}
},
"os-homedir": {
"version": "1.0.2",
- "bundled": true
+ "resolved": false,
+ "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M="
},
"os-tmpdir": {
"version": "1.0.2",
- "bundled": true
+ "resolved": false,
+ "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ="
},
"osenv": {
"version": "0.1.5",
- "bundled": true,
+ "resolved": false,
+ "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==",
"requires": {
"os-homedir": "^1.0.0",
"os-tmpdir": "^1.0.0"
@@ -8480,11 +8595,13 @@
},
"path-is-absolute": {
"version": "1.0.1",
- "bundled": true
+ "resolved": false,
+ "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18="
},
"process-nextick-args": {
"version": "2.0.0",
- "bundled": true
+ "resolved": false,
+ "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw=="
},
"protobufjs": {
"version": "5.0.3",
@@ -8499,7 +8616,8 @@
},
"rc": {
"version": "1.2.8",
- "bundled": true,
+ "resolved": false,
+ "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==",
"requires": {
"deep-extend": "^0.6.0",
"ini": "~1.3.0",
@@ -8509,7 +8627,8 @@
},
"readable-stream": {
"version": "2.3.6",
- "bundled": true,
+ "resolved": false,
+ "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
"requires": {
"core-util-is": "~1.0.0",
"inherits": "~2.0.3",
@@ -8522,38 +8641,46 @@
},
"rimraf": {
"version": "2.6.2",
- "bundled": true,
+ "resolved": false,
+ "integrity": "sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w==",
"requires": {
"glob": "^7.0.5"
}
},
"safe-buffer": {
"version": "5.1.2",
- "bundled": true
+ "resolved": false,
+ "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
},
"safer-buffer": {
"version": "2.1.2",
- "bundled": true
+ "resolved": false,
+ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
},
"sax": {
"version": "1.2.4",
- "bundled": true
+ "resolved": false,
+ "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw=="
},
"semver": {
"version": "5.6.0",
- "bundled": true
+ "resolved": false,
+ "integrity": "sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg=="
},
"set-blocking": {
"version": "2.0.0",
- "bundled": true
+ "resolved": false,
+ "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc="
},
"signal-exit": {
"version": "3.0.2",
- "bundled": true
+ "resolved": false,
+ "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0="
},
"string-width": {
"version": "1.0.2",
- "bundled": true,
+ "resolved": false,
+ "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=",
"requires": {
"code-point-at": "^1.0.0",
"is-fullwidth-code-point": "^1.0.0",
@@ -8562,25 +8689,29 @@
},
"string_decoder": {
"version": "1.1.1",
- "bundled": true,
+ "resolved": false,
+ "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
"requires": {
"safe-buffer": "~5.1.0"
}
},
"strip-ansi": {
"version": "3.0.1",
- "bundled": true,
+ "resolved": false,
+ "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
"requires": {
"ansi-regex": "^2.0.0"
}
},
"strip-json-comments": {
"version": "2.0.1",
- "bundled": true
+ "resolved": false,
+ "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo="
},
"tar": {
"version": "4.4.8",
- "bundled": true,
+ "resolved": false,
+ "integrity": "sha512-LzHF64s5chPQQS0IYBn9IN5h3i98c12bo4NCO7e0sGM2llXQ3p2FGC5sdENN4cTW48O915Sh+x+EXx7XW96xYQ==",
"requires": {
"chownr": "^1.1.1",
"fs-minipass": "^1.2.5",
@@ -8593,22 +8724,26 @@
},
"util-deprecate": {
"version": "1.0.2",
- "bundled": true
+ "resolved": false,
+ "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8="
},
"wide-align": {
"version": "1.1.3",
- "bundled": true,
+ "resolved": false,
+ "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==",
"requires": {
"string-width": "^1.0.2 || 2"
}
},
"wrappy": {
"version": "1.0.2",
- "bundled": true
+ "resolved": false,
+ "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
},
"yallist": {
"version": "3.0.2",
- "bundled": true
+ "resolved": false,
+ "integrity": "sha1-hFK0u36Dx8GI2AQcGoN8dz1ti7k="
}
}
},
@@ -8810,9 +8945,9 @@
"dev": true
},
"moment": {
- "version": "2.24.0",
- "resolved": "https://registry.npmjs.org/moment/-/moment-2.24.0.tgz",
- "integrity": "sha512-bV7f+6l2QigeBBZSM/6yTNq4P2fNpSWj/0e7jQcy87A8e7o2nAfP/34/2ky5Vw4B9S446EtIhodAzkFCcR4dQg==",
+ "version": "2.10.3",
+ "resolved": "https://registry.npmjs.org/moment/-/moment-2.10.3.tgz",
+ "integrity": "sha1-CruZ8wf2UhgwjGk17+KcV7Ggon8=",
"dev": true
}
}
@@ -11635,7 +11770,7 @@
"minimatch": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
- "integrity": "sha1-UWbihkV/AzBgZL5Ul+jbsMPTIIM=",
+ "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
"requires": {
"brace-expansion": "^1.1.7"
}
@@ -12207,6 +12342,14 @@
"semver": "^5.3.0"
}
},
+ "node-rsa": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/node-rsa/-/node-rsa-1.0.3.tgz",
+ "integrity": "sha512-gQowjnOunjmojrpO+d8x1ubL9X2Zpj4MRmY2J2hPtVF8g1VgOX1yNWUeCCoyzkRHunJf1/3orLzit5PiRtDz1A==",
+ "requires": {
+ "asn1": "^0.2.4"
+ }
+ },
"nodejieba": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/nodejieba/-/nodejieba-2.3.0.tgz",
@@ -12279,7 +12422,7 @@
"npmlog": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz",
- "integrity": "sha1-CKfyqL9zRgR3mp76StXMcXq7lUs=",
+ "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==",
"requires": {
"are-we-there-yet": "~1.1.2",
"console-control-strings": "~1.1.0",
@@ -14012,7 +14155,7 @@
},
"pify": {
"version": "2.3.0",
- "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
+ "resolved": "http://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
"integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=",
"dev": true
}
@@ -15255,6 +15398,11 @@
"readable-stream": "^2.0.2"
}
},
+ "stream-buffers": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/stream-buffers/-/stream-buffers-3.0.2.tgz",
+ "integrity": "sha512-DQi1h8VEBA/lURbSwFtEHnSTb9s2/pwLEaFuNhXwy1Dx3Sa0lOuYT2yNUr4/j2fs8oCAMANtrZ5OrPZtyVs3MQ=="
+ },
"stream-each": {
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/stream-each/-/stream-each-1.2.3.tgz",
@@ -16757,535 +16905,6 @@
"readdirp": "^2.2.1",
"upath": "^1.1.0"
}
- },
- "fsevents": {
- "version": "1.2.7",
- "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.7.tgz",
- "integrity": "sha512-Pxm6sI2MeBD7RdD12RYsqaP0nMiwx8eZBXCa6z2L+mRHm2DYrOYwihmhjpkdjUHwQhslWQjRpEgNq4XvBmaAuw==",
- "dev": true,
- "optional": true,
- "requires": {
- "nan": "^2.9.2",
- "node-pre-gyp": "^0.10.0"
- },
- "dependencies": {
- "abbrev": {
- "version": "1.1.1",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "ansi-regex": {
- "version": "2.1.1",
- "bundled": true,
- "dev": true
- },
- "aproba": {
- "version": "1.2.0",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "are-we-there-yet": {
- "version": "1.1.5",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "delegates": "^1.0.0",
- "readable-stream": "^2.0.6"
- }
- },
- "balanced-match": {
- "version": "1.0.0",
- "bundled": true,
- "dev": true
- },
- "brace-expansion": {
- "version": "1.1.11",
- "bundled": true,
- "dev": true,
- "requires": {
- "balanced-match": "^1.0.0",
- "concat-map": "0.0.1"
- }
- },
- "chownr": {
- "version": "1.1.1",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "code-point-at": {
- "version": "1.1.0",
- "bundled": true,
- "dev": true
- },
- "concat-map": {
- "version": "0.0.1",
- "bundled": true,
- "dev": true
- },
- "console-control-strings": {
- "version": "1.1.0",
- "bundled": true,
- "dev": true
- },
- "core-util-is": {
- "version": "1.0.2",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "debug": {
- "version": "2.6.9",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "ms": "2.0.0"
- }
- },
- "deep-extend": {
- "version": "0.6.0",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "delegates": {
- "version": "1.0.0",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "detect-libc": {
- "version": "1.0.3",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "fs-minipass": {
- "version": "1.2.5",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "minipass": "^2.2.1"
- }
- },
- "fs.realpath": {
- "version": "1.0.0",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "gauge": {
- "version": "2.7.4",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "aproba": "^1.0.3",
- "console-control-strings": "^1.0.0",
- "has-unicode": "^2.0.0",
- "object-assign": "^4.1.0",
- "signal-exit": "^3.0.0",
- "string-width": "^1.0.1",
- "strip-ansi": "^3.0.1",
- "wide-align": "^1.1.0"
- }
- },
- "glob": {
- "version": "7.1.3",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "fs.realpath": "^1.0.0",
- "inflight": "^1.0.4",
- "inherits": "2",
- "minimatch": "^3.0.4",
- "once": "^1.3.0",
- "path-is-absolute": "^1.0.0"
- }
- },
- "has-unicode": {
- "version": "2.0.1",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "iconv-lite": {
- "version": "0.4.24",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "safer-buffer": ">= 2.1.2 < 3"
- }
- },
- "ignore-walk": {
- "version": "3.0.1",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "minimatch": "^3.0.4"
- }
- },
- "inflight": {
- "version": "1.0.6",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "once": "^1.3.0",
- "wrappy": "1"
- }
- },
- "inherits": {
- "version": "2.0.3",
- "bundled": true,
- "dev": true
- },
- "ini": {
- "version": "1.3.5",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "is-fullwidth-code-point": {
- "version": "1.0.0",
- "bundled": true,
- "dev": true,
- "requires": {
- "number-is-nan": "^1.0.0"
- }
- },
- "isarray": {
- "version": "1.0.0",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "minimatch": {
- "version": "3.0.4",
- "bundled": true,
- "dev": true,
- "requires": {
- "brace-expansion": "^1.1.7"
- }
- },
- "minimist": {
- "version": "0.0.8",
- "bundled": true,
- "dev": true
- },
- "minipass": {
- "version": "2.3.5",
- "bundled": true,
- "dev": true,
- "requires": {
- "safe-buffer": "^5.1.2",
- "yallist": "^3.0.0"
- }
- },
- "minizlib": {
- "version": "1.2.1",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "minipass": "^2.2.1"
- }
- },
- "mkdirp": {
- "version": "0.5.1",
- "bundled": true,
- "dev": true,
- "requires": {
- "minimist": "0.0.8"
- }
- },
- "ms": {
- "version": "2.0.0",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "needle": {
- "version": "2.2.4",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "debug": "^2.1.2",
- "iconv-lite": "^0.4.4",
- "sax": "^1.2.4"
- }
- },
- "node-pre-gyp": {
- "version": "0.10.3",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "detect-libc": "^1.0.2",
- "mkdirp": "^0.5.1",
- "needle": "^2.2.1",
- "nopt": "^4.0.1",
- "npm-packlist": "^1.1.6",
- "npmlog": "^4.0.2",
- "rc": "^1.2.7",
- "rimraf": "^2.6.1",
- "semver": "^5.3.0",
- "tar": "^4"
- }
- },
- "nopt": {
- "version": "4.0.1",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "abbrev": "1",
- "osenv": "^0.1.4"
- }
- },
- "npm-bundled": {
- "version": "1.0.5",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "npm-packlist": {
- "version": "1.2.0",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "ignore-walk": "^3.0.1",
- "npm-bundled": "^1.0.1"
- }
- },
- "npmlog": {
- "version": "4.1.2",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "are-we-there-yet": "~1.1.2",
- "console-control-strings": "~1.1.0",
- "gauge": "~2.7.3",
- "set-blocking": "~2.0.0"
- }
- },
- "number-is-nan": {
- "version": "1.0.1",
- "bundled": true,
- "dev": true
- },
- "object-assign": {
- "version": "4.1.1",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "once": {
- "version": "1.4.0",
- "bundled": true,
- "dev": true,
- "requires": {
- "wrappy": "1"
- }
- },
- "os-homedir": {
- "version": "1.0.2",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "os-tmpdir": {
- "version": "1.0.2",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "osenv": {
- "version": "0.1.5",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "os-homedir": "^1.0.0",
- "os-tmpdir": "^1.0.0"
- }
- },
- "path-is-absolute": {
- "version": "1.0.1",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "process-nextick-args": {
- "version": "2.0.0",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "rc": {
- "version": "1.2.8",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "deep-extend": "^0.6.0",
- "ini": "~1.3.0",
- "minimist": "^1.2.0",
- "strip-json-comments": "~2.0.1"
- },
- "dependencies": {
- "minimist": {
- "version": "1.2.0",
- "bundled": true,
- "dev": true,
- "optional": true
- }
- }
- },
- "readable-stream": {
- "version": "2.3.6",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "core-util-is": "~1.0.0",
- "inherits": "~2.0.3",
- "isarray": "~1.0.0",
- "process-nextick-args": "~2.0.0",
- "safe-buffer": "~5.1.1",
- "string_decoder": "~1.1.1",
- "util-deprecate": "~1.0.1"
- }
- },
- "rimraf": {
- "version": "2.6.3",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "glob": "^7.1.3"
- }
- },
- "safe-buffer": {
- "version": "5.1.2",
- "bundled": true,
- "dev": true
- },
- "safer-buffer": {
- "version": "2.1.2",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "sax": {
- "version": "1.2.4",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "semver": {
- "version": "5.6.0",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "set-blocking": {
- "version": "2.0.0",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "signal-exit": {
- "version": "3.0.2",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "string-width": {
- "version": "1.0.2",
- "bundled": true,
- "dev": true,
- "requires": {
- "code-point-at": "^1.0.0",
- "is-fullwidth-code-point": "^1.0.0",
- "strip-ansi": "^3.0.0"
- }
- },
- "string_decoder": {
- "version": "1.1.1",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "safe-buffer": "~5.1.0"
- }
- },
- "strip-ansi": {
- "version": "3.0.1",
- "bundled": true,
- "dev": true,
- "requires": {
- "ansi-regex": "^2.0.0"
- }
- },
- "strip-json-comments": {
- "version": "2.0.1",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "tar": {
- "version": "4.4.8",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "chownr": "^1.1.1",
- "fs-minipass": "^1.2.5",
- "minipass": "^2.3.4",
- "minizlib": "^1.1.1",
- "mkdirp": "^0.5.0",
- "safe-buffer": "^5.1.2",
- "yallist": "^3.0.2"
- }
- },
- "util-deprecate": {
- "version": "1.0.2",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "wide-align": {
- "version": "1.1.3",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "string-width": "^1.0.2 || 2"
- }
- },
- "wrappy": {
- "version": "1.0.2",
- "bundled": true,
- "dev": true
- },
- "yallist": {
- "version": "3.0.3",
- "bundled": true,
- "dev": true
- }
- }
}
}
},
diff --git a/package.json b/package.json
index 94d2ad66b8a..7c3b9949d26 100644
--- a/package.json
+++ b/package.json
@@ -194,6 +194,7 @@
"moment": "^2.22.2",
"moment-timezone": "^0.5.23",
"node-dogstatsd": "^0.0.7",
+ "node-rsa": "^1.0.3",
"object-path": "^0.11.4",
"pdfjs-dist": "^2.0.943",
"photoswipe": "^4.1.2",
@@ -205,6 +206,7 @@
"semver": "^5.6.0",
"sharp": "^0.21.0",
"speakeasy": "^2.0.0",
+ "stream-buffers": "^3.0.2",
"subscriptions-transport-ws": "^0.9.11",
"tar-stream": "^1.6.2",
"toastr": "^2.1.4",
@@ -214,6 +216,7 @@
"ua-parser-js": "^0.7.19",
"underscore": "^1.9.1",
"underscore.string": "^3.3.5",
+ "uuid": "^3.3.2",
"webdav": "^2.0.0",
"wolfy87-eventemitter": "^5.2.5",
"xml-crypto": "^1.0.2",
diff --git a/packages/rocketchat-api/server/v1/misc.js b/packages/rocketchat-api/server/v1/misc.js
index 7528fe4f0bb..bb8d8820bc3 100644
--- a/packages/rocketchat-api/server/v1/misc.js
+++ b/packages/rocketchat-api/server/v1/misc.js
@@ -152,7 +152,7 @@ API.v1.addRoute('directory', { authRequired: true }, {
const { offset, count } = this.getPaginationItems();
const { sort, query } = this.parseJsonQuery();
- const { text, type } = query;
+ const { text, type, workspace = 'local' } = query;
if (sort && Object.keys(sort).length > 1) {
return API.v1.failure('This method support only one "sort" parameter');
}
@@ -162,6 +162,7 @@ API.v1.addRoute('directory', { authRequired: true }, {
const result = Meteor.runAsUser(this.userId, () => Meteor.call('browseChannels', {
text,
type,
+ workspace,
sortBy,
sortDirection,
offset: Math.max(0, offset),
diff --git a/packages/rocketchat-channel-settings/server/methods/saveRoomSettings.js b/packages/rocketchat-channel-settings/server/methods/saveRoomSettings.js
index 4477411577d..462a252cee0 100644
--- a/packages/rocketchat-channel-settings/server/methods/saveRoomSettings.js
+++ b/packages/rocketchat-channel-settings/server/methods/saveRoomSettings.js
@@ -2,6 +2,8 @@ import { Meteor } from 'meteor/meteor';
import { Match, check } from 'meteor/check';
import { hasPermission } from 'meteor/rocketchat:authorization';
import { Rooms } from 'meteor/rocketchat:models';
+import { callbacks } from 'meteor/rocketchat:callbacks';
+
import { saveRoomName } from '../functions/saveRoomName';
import { saveRoomTopic } from '../functions/saveRoomTopic';
import { saveRoomAnnouncement } from '../functions/saveRoomAnnouncement';
@@ -212,6 +214,11 @@ Meteor.methods({
}
});
+ Meteor.defer(function() {
+ const room = Rooms.findOneById(rid);
+ callbacks.run('afterSaveRoomSettings', room);
+ });
+
return {
result: true,
rid: room._id,
diff --git a/packages/rocketchat-federation/README.md b/packages/rocketchat-federation/README.md
new file mode 100644
index 00000000000..e8f08c10233
--- /dev/null
+++ b/packages/rocketchat-federation/README.md
@@ -0,0 +1 @@
+##Rocket.Chat Federation
\ No newline at end of file
diff --git a/packages/rocketchat-federation/client/main.js b/packages/rocketchat-federation/client/main.js
new file mode 100644
index 00000000000..b93db97bfb7
--- /dev/null
+++ b/packages/rocketchat-federation/client/main.js
@@ -0,0 +1,23 @@
+import { MessageTypes } from 'meteor/rocketchat:ui-utils';
+
+// Register message types
+MessageTypes.registerType({
+ id: 'rejected-message-by-peer',
+ system: true,
+ message: 'This_message_was_rejected_by__peer__peer',
+ data(message) {
+ return {
+ peer: message.peer,
+ };
+ },
+});
+MessageTypes.registerType({
+ id: 'peer-does-not-exist',
+ system: true,
+ message: 'The_peer__peer__does_not_exist',
+ data(message) {
+ return {
+ peer: message.peer,
+ };
+ },
+});
diff --git a/packages/rocketchat-federation/package.js b/packages/rocketchat-federation/package.js
new file mode 100644
index 00000000000..2876da93d30
--- /dev/null
+++ b/packages/rocketchat-federation/package.js
@@ -0,0 +1,23 @@
+Package.describe({
+ name: 'rocketchat:federation',
+ version: '0.0.1',
+ summary: 'RocketChat support for federating with other RocketChat servers',
+ git: '',
+});
+
+Package.onUse(function(api) {
+ api.use([
+ 'ecmascript',
+ 'rocketchat:api',
+ 'rocketchat:lib',
+ 'rocketchat:reactions',
+ 'rocketchat:models',
+ 'rocketchat:settings',
+ ]);
+
+ api.use('accounts-base', 'server');
+ api.use('accounts-password', 'server');
+
+ api.mainModule('client/main.js', 'client');
+ api.mainModule('server/main.js', 'server');
+});
diff --git a/packages/rocketchat-federation/server/federatedResources/FederatedMessage.js b/packages/rocketchat-federation/server/federatedResources/FederatedMessage.js
new file mode 100644
index 00000000000..68c4099358c
--- /dev/null
+++ b/packages/rocketchat-federation/server/federatedResources/FederatedMessage.js
@@ -0,0 +1,264 @@
+import { Meteor } from 'meteor/meteor';
+import { sendMessage, updateMessage } from 'meteor/rocketchat:lib';
+import { Messages, Rooms, Users } from 'meteor/rocketchat:models';
+import { FileUpload } from 'meteor/rocketchat:file-upload';
+
+import FederatedResource from './FederatedResource';
+import FederatedRoom from './FederatedRoom';
+import FederatedUser from './FederatedUser';
+import peerClient from '../peerClient';
+
+class FederatedMessage extends FederatedResource {
+ constructor(localPeerIdentifier, message) {
+ super('message');
+
+ if (!message) {
+ throw new Error('message param cannot be empty');
+ }
+
+ this.localPeerIdentifier = localPeerIdentifier;
+
+ // Make sure room dates are correct
+ message.ts = new Date(message.ts);
+ message._updatedAt = new Date(message._updatedAt);
+
+ // Set the message author
+ if (message.u.federation) {
+ this.federatedAuthor = FederatedUser.loadByFederationId(localPeerIdentifier, message.u.federation._id);
+ } else {
+ const author = Users.findOneById(message.u._id);
+ this.federatedAuthor = new FederatedUser(localPeerIdentifier, author);
+ }
+
+ message.u = {
+ username: this.federatedAuthor.user.username,
+ federation: {
+ _id: this.federatedAuthor.user.federation._id,
+ },
+ };
+
+ // Set the room
+ const room = Rooms.findOneById(message.rid);
+
+ // Prepare the federation property
+ if (!message.federation) {
+ const federation = {
+ _id: message._id,
+ peer: localPeerIdentifier,
+ roomId: room.federation._id,
+ };
+
+ // Prepare the user
+ message.federation = federation;
+
+ // Update the user
+ Messages.update(message._id, { $set: { federation } });
+
+ // Prepare mentions
+ for (const mention of message.mentions) {
+
+ mention.federation = mention.federation || {};
+
+ if (mention.username.indexOf('@') === -1) {
+ mention.federation.peer = localPeerIdentifier;
+ } else {
+ const [username, peer] = mention.username.split('@');
+
+ mention.username = username;
+ mention.federation.peer = peer;
+ }
+ }
+
+ // Prepare channels
+ for (const channel of message.channels) {
+ channel.federation = channel.federation || {};
+
+ if (channel.name.indexOf('@') === -1) {
+ channel.federation.peer = localPeerIdentifier;
+ } else {
+ channel.name = channel.name.split('@')[0];
+ channel.federation.peer = channel.name.split('@')[1];
+ }
+ }
+ }
+
+ // Set message property
+ this.message = message;
+ }
+
+ getFederationId() {
+ return this.message.federation._id;
+ }
+
+ getMessage() {
+ return this.message;
+ }
+
+ getLocalMessage() {
+ this.log('getLocalMessage');
+
+ const { localPeerIdentifier, message } = this;
+
+ const localMessage = Object.assign({}, message);
+
+ // Make sure `u` is correct
+ if (!this.federatedAuthor) {
+ throw new Error('Author does not exist');
+ }
+
+ const localAuthor = this.federatedAuthor.getLocalUser();
+
+ localMessage.u = {
+ _id: localAuthor._id,
+ username: localAuthor.username,
+ };
+
+ // Make sure `rid` is correct
+ const federatedRoom = FederatedRoom.loadByFederationId(localPeerIdentifier, message.federation.roomId);
+
+ if (!federatedRoom) {
+ throw new Error('Room does not exist');
+ }
+
+ const localRoom = federatedRoom.getLocalRoom();
+
+ localMessage.rid = localRoom._id;
+
+ return localMessage;
+ }
+
+ create() {
+ this.log('create');
+
+ // Get the local message object
+ const localMessageObject = this.getLocalMessage();
+
+ // Grab the federation id
+ const { federation: { _id: federationId } } = localMessageObject;
+
+ // Check if the message exists
+ let localMessage = Messages.findOne({ 'federation._id': federationId });
+
+ // Create if needed
+ if (!localMessage) {
+ delete localMessageObject._id;
+
+ localMessage = localMessageObject;
+
+ const localRoom = Rooms.findOneById(localMessage.rid);
+
+ // Normalize mentions
+ for (const mention of localMessage.mentions) {
+ // Ignore if we are dealing with all, here or rocket.cat
+ if (['all', 'here', 'rocket.cat'].indexOf(mention.username) !== -1) { continue; }
+
+ let usernameToReplace = '';
+
+ if (mention.federation.peer !== this.localPeerIdentifier) {
+ usernameToReplace = mention.username;
+
+ mention.username = `${ mention.username }@${ mention.federation.peer }`;
+ } else {
+ usernameToReplace = `${ mention.username }@${ mention.federation.peer }`;
+ }
+
+ localMessage.msg = localMessage.msg.split(usernameToReplace).join(mention.username);
+ }
+
+ // Normalize channels
+ for (const channel of localMessage.channels) {
+ if (channel.federation.peer !== this.localPeerIdentifier) {
+ channel.name = `${ channel.name }@${ channel.federation.peer }`;
+ }
+ }
+
+ // Is there a file?
+ if (localMessage.file) {
+ const fileStore = FileUpload.getStore('Uploads');
+
+ const { federation: { peer: identifier } } = localMessage;
+
+ const { upload, buffer } = peerClient.getUpload({ identifier, localMessage });
+
+ const oldUploadId = upload._id;
+
+ // Normalize upload
+ delete upload._id;
+ upload.rid = localMessage.rid;
+ upload.userId = localMessage.u._id;
+ upload.federation = {
+ _id: localMessage.file._id,
+ peer: identifier,
+ };
+
+ Meteor.runAsUser(upload.userId, () => Meteor.wrapAsync(fileStore.insert.bind(fileStore))(upload, buffer));
+
+ // Update the message's file
+ localMessage.file._id = upload._id;
+
+ // Update the message's attachments
+ for (const attachment of localMessage.attachments) {
+ attachment.title_link = attachment.title_link.replace(oldUploadId, upload._id);
+ attachment.image_url = attachment.image_url.replace(oldUploadId, upload._id);
+ }
+ }
+
+ // Create the message
+ const { _id } = sendMessage(localMessage.u, localMessage, localRoom, false);
+
+ localMessage._id = _id;
+ }
+
+ return localMessage;
+ }
+
+ update(updatedByFederatedUser) {
+ this.log('update');
+
+ // Get the original message
+ const originalMessage = Messages.findOne({ 'federation._id': this.getFederationId() });
+
+ // Error if message does not exist
+ if (!originalMessage) {
+ throw new Error('Message does not exist');
+ }
+
+ // Get the local message object
+ const localMessage = this.getLocalMessage();
+
+ // Make sure the message has the correct _id
+ localMessage._id = originalMessage._id;
+
+ // Get the user who updated
+ const user = updatedByFederatedUser.getLocalUser();
+
+ // Update the message
+ updateMessage(localMessage, user, originalMessage);
+
+ return localMessage;
+ }
+}
+
+FederatedMessage.loadByFederationId = function loadByFederationId(localPeerIdentifier, federationId) {
+ const localMessage = Messages.findOne({ 'federation._id': federationId });
+
+ if (!localMessage) { return; }
+
+ return new FederatedMessage(localPeerIdentifier, localMessage);
+};
+
+FederatedMessage.loadOrCreate = function loadOrCreate(localPeerIdentifier, message) {
+ const { federation } = message;
+
+ if (federation) {
+ const federatedMessage = FederatedMessage.loadByFederationId(localPeerIdentifier, federation._id);
+
+ if (federatedMessage) {
+ return federatedMessage;
+ }
+ }
+
+ return new FederatedMessage(localPeerIdentifier, message);
+};
+
+export default FederatedMessage;
diff --git a/packages/rocketchat-federation/server/federatedResources/FederatedResource.js b/packages/rocketchat-federation/server/federatedResources/FederatedResource.js
new file mode 100644
index 00000000000..772dc30348b
--- /dev/null
+++ b/packages/rocketchat-federation/server/federatedResources/FederatedResource.js
@@ -0,0 +1,19 @@
+import { logger } from '../logger';
+
+class FederatedResource {
+ constructor(name) {
+ this.resourceName = `federated-${ name }`;
+
+ this.log('Creating federated resource');
+ }
+
+ log(message) {
+ FederatedResource.log(this.resourceName, message);
+ }
+}
+
+FederatedResource.log = function log(name, message) {
+ logger.resource.info(`[${ name }] ${ message }`);
+};
+
+export default FederatedResource;
diff --git a/packages/rocketchat-federation/server/federatedResources/FederatedRoom.js b/packages/rocketchat-federation/server/federatedResources/FederatedRoom.js
new file mode 100644
index 00000000000..c546f0941e0
--- /dev/null
+++ b/packages/rocketchat-federation/server/federatedResources/FederatedRoom.js
@@ -0,0 +1,270 @@
+import { createRoom } from 'meteor/rocketchat:lib';
+import { Rooms, Subscriptions, Users } from 'meteor/rocketchat:models';
+
+import FederatedResource from './FederatedResource';
+import FederatedUser from './FederatedUser';
+
+class FederatedRoom extends FederatedResource {
+ constructor(localPeerIdentifier, room, extras = {}) {
+ super('room');
+
+ if (!room) {
+ throw new Error('room param cannot be empty');
+ }
+
+ this.localPeerIdentifier = localPeerIdentifier;
+
+ // Make sure room dates are correct
+ room.ts = new Date(room.ts);
+ room._updatedAt = new Date(room._updatedAt);
+
+ // Set the name
+ if (room.t !== 'd' && room.name.indexOf('@') === -1) {
+ room.name = `${ room.name }@${ localPeerIdentifier }`;
+ }
+
+ // Set the federated owner, if there is one
+ const { owner } = extras;
+
+ if (owner) {
+ if (!owner && room.federation) {
+ this.federatedOwner = FederatedUser.loadByFederationId(localPeerIdentifier, room.federation.ownerId);
+ } else {
+ this.federatedOwner = FederatedUser.loadOrCreate(localPeerIdentifier, owner);
+ }
+ }
+
+ // Set base federation
+ room.federation = room.federation || {
+ _id: room._id,
+ peer: localPeerIdentifier,
+ ownerId: this.federatedOwner ? this.federatedOwner.getFederationId() : null,
+ };
+
+ // Set room property
+ this.room = room;
+ }
+
+ getFederationId() {
+ return this.room.federation._id;
+ }
+
+ getPeers() {
+ return this.room.federation.peers;
+ }
+
+ getRoom() {
+ return this.room;
+ }
+
+ getOwner() {
+ return this.federatedOwner ? this.federatedOwner.getUser() : null;
+ }
+
+ getUsers() {
+ return this.federatedUsers.map((u) => u.getUser());
+ }
+
+ loadUsers() {
+ const { room } = this;
+
+ // Get all room users
+ const users = FederatedRoom.loadRoomUsers(room);
+
+ this.setUsers(users);
+ }
+
+ setUsers(users) {
+ const { localPeerIdentifier } = this;
+
+ // Initialize federatedUsers
+ this.federatedUsers = [];
+
+ for (const user of users) {
+ const federatedUser = FederatedUser.loadOrCreate(localPeerIdentifier, user);
+
+ // Keep the federated user
+ this.federatedUsers.push(federatedUser);
+ }
+ }
+
+ refreshFederation() {
+ const { room } = this;
+
+ // Prepare the federated users
+ let federation = {
+ peers: [],
+ users: [],
+ };
+
+ // Check all the peers
+ for (const federatedUser of this.federatedUsers) {
+ // Add federation data to the room
+ const { user: { federation: { _id, peer } } } = federatedUser;
+
+ federation.peers.push(peer);
+ federation.users.push({ _id, peer });
+ }
+
+ federation.peers = [...new Set(federation.peers)];
+
+ federation = Object.assign(room.federation || {}, federation);
+
+ // Prepare the room
+ room.federation = federation;
+
+ // Update the room
+ Rooms.update(room._id, { $set: { federation } });
+ }
+
+ getLocalRoom() {
+ this.log('getLocalRoom');
+
+ const { localPeerIdentifier, room, room: { federation } } = this;
+
+ const localRoom = Object.assign({}, room);
+
+ if (federation.peer === localPeerIdentifier) {
+ if (localRoom.t !== 'd') {
+ localRoom.name = room.name.split('@')[0];
+ }
+ }
+
+ return localRoom;
+ }
+
+ createUsers() {
+ this.log('createUsers');
+
+ const { federatedUsers } = this;
+
+ // Create, if needed, all room's users
+ for (const federatedUser of federatedUsers) {
+ federatedUser.create();
+ }
+ }
+
+ create() {
+ this.log('create');
+
+ // Get the local room object (with or without suffixes)
+ const localRoomObject = this.getLocalRoom();
+
+ // Grab the federation id
+ const { federation: { _id: federationId } } = localRoomObject;
+
+ // Check if the user exists
+ let localRoom = FederatedRoom.loadByFederationId(this.localPeerIdentifier, federationId);
+
+ // Create if needed
+ if (!localRoom) {
+ delete localRoomObject._id;
+
+ localRoom = localRoomObject;
+
+ const { t: type, name, broadcast, customFields, federation, sysMes } = localRoom;
+ const { federatedOwner, federatedUsers } = this;
+
+ // Get usernames for the owner and members
+ const ownerUsername = federatedOwner.user.username;
+ const members = [];
+
+ if (type !== 'd') {
+ for (const federatedUser of federatedUsers) {
+ const localUser = federatedUser.getLocalUser();
+ members.push(localUser.username);
+ }
+ } else {
+ for (const federatedUser of federatedUsers) {
+ const localUser = federatedUser.getLocalUser();
+ members.push(localUser);
+ }
+ }
+
+ // Is this a broadcast channel? Then mute everyone but the owner
+ let muted = [];
+
+ if (broadcast) {
+ muted = members.filter((u) => u !== ownerUsername);
+ }
+
+ // Set the extra data and create room options
+ let extraData = {
+ federation,
+ };
+
+ let createRoomOptions = {
+ subscriptionExtra: {
+ alert: true,
+ open: true,
+ },
+ };
+
+ if (type !== 'd') {
+ extraData = Object.assign(extraData, {
+ broadcast,
+ customFields,
+ encrypted: false, // Always false for now
+ muted,
+ sysMes,
+ });
+
+ createRoomOptions = Object.assign(extraData, {
+ nameValidationRegex: '^[0-9a-zA-Z-_.@]+$',
+ subscriptionExtra: {
+ alert: true,
+ },
+ });
+ }
+
+ // Create the room
+ // !!!! Forcing direct or private only, no public rooms for now
+ const { rid } = createRoom(type === 'd' ? type : 'p', name, ownerUsername, members, false, extraData, createRoomOptions);
+
+ localRoom._id = rid;
+ }
+
+ return localRoom;
+ }
+}
+
+FederatedRoom.loadByFederationId = function loadByFederationId(localPeerIdentifier, federationId) {
+ const localRoom = Rooms.findOne({ 'federation._id': federationId });
+
+ if (!localRoom) { return; }
+
+ return new FederatedRoom(localPeerIdentifier, localRoom);
+};
+
+FederatedRoom.loadRoomUsers = function loadRoomUsers(room) {
+ const subscriptions = Subscriptions.findByRoomIdWhenUsernameExists(room._id, { fields: { 'u._id': 1 } }).fetch();
+ const userIds = subscriptions.map((s) => s.u._id);
+ return Users.findUsersWithUsernameByIds(userIds).fetch();
+};
+
+FederatedRoom.isFederated = function isFederated(localPeerIdentifier, room, options = {}) {
+ this.log('federated-room', `${ room._id } - isFederated?`);
+
+ let isFederated = false;
+
+ if (options.checkUsingUsers) {
+ // Get all room users
+ const users = FederatedRoom.loadRoomUsers(room);
+
+ // Check all the users
+ for (const user of users) {
+ if (user.federation && user.federation.peer !== localPeerIdentifier) {
+ isFederated = true;
+ break;
+ }
+ }
+ } else {
+ isFederated = room.federation && room.federation.peers.length > 1;
+ }
+
+ this.log('federated-room', `${ room._id } - isFederated? ${ isFederated ? 'yes' : 'no' }`);
+
+ return isFederated;
+};
+
+export default FederatedRoom;
diff --git a/packages/rocketchat-federation/server/federatedResources/FederatedUser.js b/packages/rocketchat-federation/server/federatedResources/FederatedUser.js
new file mode 100644
index 00000000000..d5d5b7c03a1
--- /dev/null
+++ b/packages/rocketchat-federation/server/federatedResources/FederatedUser.js
@@ -0,0 +1,124 @@
+import { Users } from 'meteor/rocketchat:models';
+
+import FederatedResource from './FederatedResource';
+
+class FederatedUser extends FederatedResource {
+ constructor(localPeerIdentifier, user) {
+ super('user');
+
+ if (!user) {
+ throw new Error('user param cannot be empty');
+ }
+
+ this.localPeerIdentifier = localPeerIdentifier;
+
+ // Make sure all properties are normalized
+ // Prepare the federation property
+ if (!user.federation) {
+ const federation = {
+ _id: user._id,
+ peer: localPeerIdentifier,
+ };
+
+ // Prepare the user
+ user.federation = federation;
+
+ // Update the user
+ Users.update(user._id, { $set: { federation } });
+ }
+
+ // Make sure user dates are correct
+ user.createdAt = new Date(user.createdAt);
+ user.lastLogin = new Date(user.lastLogin);
+ user._updatedAt = new Date(user._updatedAt);
+
+ // Delete sensitive data as well
+ delete user.roles;
+ delete user.services;
+
+ // Make sure some other properties are ready
+ user.name = user.name;
+ user.username = user.username.indexOf('@') === -1 ? `${ user.username }@${ user.federation.peer }` : user.username;
+ user.roles = ['user'];
+ user.status = 'online';
+ user.statusConnection = 'online';
+ user.type = 'user';
+
+ // Set user property
+ this.user = user;
+ }
+
+ getFederationId() {
+ return this.user.federation._id;
+ }
+
+ getUser() {
+ return this.user;
+ }
+
+ getLocalUser() {
+ this.log('getLocalUser');
+
+ const { localPeerIdentifier, user, user: { federation } } = this;
+
+ const localUser = Object.assign({}, user);
+
+ if (federation.peer === localPeerIdentifier || user.username === 'rocket.cat') {
+ localUser.username = user.username.split('@')[0];
+ localUser.name = user.name.split('@')[0];
+ }
+
+ return localUser;
+ }
+
+ create() {
+ this.log('create');
+
+ // Get the local user object (with or without suffixes)
+ const localUserObject = this.getLocalUser();
+
+ // Grab the federation id
+ const { federation: { _id: federationId } } = localUserObject;
+
+ // Check if the user exists
+ let localUser = Users.findOne({ 'federation._id': federationId });
+
+ // Create if needed
+ if (!localUser) {
+ delete localUserObject._id;
+
+ localUser = localUserObject;
+
+ localUser._id = Users.create(localUserObject);
+ }
+
+ // Update the id
+ this.user._id = localUser._id;
+
+ return localUser;
+ }
+}
+
+FederatedUser.loadByFederationId = function loadByFederationId(localPeerIdentifier, federationId) {
+ const localUser = Users.findOne({ 'federation._id': federationId });
+
+ if (!localUser) { return; }
+
+ return new FederatedUser(localPeerIdentifier, localUser);
+};
+
+FederatedUser.loadOrCreate = function loadOrCreate(localPeerIdentifier, user) {
+ const { federation } = user;
+
+ if (federation) {
+ const federatedUser = FederatedUser.loadByFederationId(localPeerIdentifier, federation._id);
+
+ if (federatedUser) {
+ return federatedUser;
+ }
+ }
+
+ return new FederatedUser(localPeerIdentifier, user);
+};
+
+export default FederatedUser;
diff --git a/packages/rocketchat-federation/server/federatedResources/index.js b/packages/rocketchat-federation/server/federatedResources/index.js
new file mode 100644
index 00000000000..0bd6fdd4cbb
--- /dev/null
+++ b/packages/rocketchat-federation/server/federatedResources/index.js
@@ -0,0 +1,4 @@
+export { default as FederatedMessage } from './FederatedMessage';
+export { default as FederatedResource } from './FederatedResource';
+export { default as FederatedRoom } from './FederatedRoom';
+export { default as FederatedUser } from './FederatedUser';
diff --git a/packages/rocketchat-federation/server/federation-settings.js b/packages/rocketchat-federation/server/federation-settings.js
new file mode 100644
index 00000000000..82e082747f5
--- /dev/null
+++ b/packages/rocketchat-federation/server/federation-settings.js
@@ -0,0 +1,68 @@
+import { Meteor } from 'meteor/meteor';
+import { settings } from 'meteor/rocketchat:settings';
+import { FederationKeys } from 'meteor/rocketchat:models';
+
+Meteor.startup(function() {
+ // const federationUniqueId = FederationKeys.getUniqueId();
+ const federationPublicKey = FederationKeys.getPublicKeyString();
+
+ settings.addGroup('Federation', function() {
+ this.add('FEDERATION_Enabled', false, {
+ type: 'boolean',
+ i18nLabel: 'Enabled',
+ i18nDescription: 'FEDERATION_Enabled',
+ alert: 'FEDERATION_Enabled_Alert',
+ public: true,
+ });
+
+ this.add('FEDERATION_Status', '-', {
+ readonly: true,
+ type: 'string',
+ i18nLabel: 'FEDERATION_Status',
+ });
+
+ // this.add('FEDERATION_Unique_Id', federationUniqueId, {
+ // readonly: true,
+ // type: 'string',
+ // i18nLabel: 'FEDERATION_Unique_Id',
+ // i18nDescription: 'FEDERATION_Unique_Id_Description',
+ // });
+
+ this.add('FEDERATION_Domain', '', {
+ type: 'string',
+ i18nLabel: 'FEDERATION_Domain',
+ i18nDescription: 'FEDERATION_Domain_Description',
+ alert: 'FEDERATION_Domain_Alert',
+ });
+
+ this.add('FEDERATION_Public_Key', federationPublicKey, {
+ readonly: true,
+ type: 'string',
+ multiline: true,
+ i18nLabel: 'FEDERATION_Public_Key',
+ i18nDescription: 'FEDERATION_Public_Key_Description',
+ });
+
+ this.add('FEDERATION_Hub_URL', 'https://hub.rocket.chat', {
+ group: 'Federation Hub',
+ type: 'string',
+ i18nLabel: 'FEDERATION_Hub_URL',
+ i18nDescription: 'FEDERATION_Hub_URL_Description',
+ });
+
+ this.add('FEDERATION_Discovery_Method', 'dns', {
+ type: 'select',
+ values: [{
+ key: 'dns',
+ i18nLabel: 'DNS',
+ }, {
+ key: 'hub',
+ i18nLabel: 'Hub',
+ }],
+ i18nLabel: 'FEDERATION_Discovery_Method',
+ i18nDescription: 'FEDERATION_Discovery_Method_Description',
+ public: true,
+ });
+
+ });
+});
diff --git a/packages/rocketchat-federation/server/logger.js b/packages/rocketchat-federation/server/logger.js
new file mode 100644
index 00000000000..bad916731ab
--- /dev/null
+++ b/packages/rocketchat-federation/server/logger.js
@@ -0,0 +1,12 @@
+import { Logger } from 'meteor/rocketchat:logger';
+
+export const logger = new Logger('Federation', {
+ sections: {
+ resource: 'Resource',
+ setup: 'Setup',
+ peerClient: 'Peer Client',
+ peerServer: 'Peer Server',
+ dns: 'DNS',
+ http: 'HTTP',
+ },
+});
diff --git a/packages/rocketchat-federation/server/main.js b/packages/rocketchat-federation/server/main.js
new file mode 100644
index 00000000000..33e4c4b31e1
--- /dev/null
+++ b/packages/rocketchat-federation/server/main.js
@@ -0,0 +1,152 @@
+import { Meteor } from 'meteor/meteor';
+import { _ } from 'meteor/underscore';
+import { settings } from 'meteor/rocketchat:settings';
+import { FederationKeys } from 'meteor/rocketchat:models';
+
+import './federation-settings';
+import './methods';
+
+import { logger } from './logger';
+import peerClient from './peerClient';
+import peerServer from './peerServer';
+import peerDNS from './peerDNS';
+import peerHTTP from './peerHTTP';
+import * as SettingsUpdater from './settingsUpdater';
+
+export const Federation = {
+ enabled: false,
+ privateKey: null,
+ publicKey: null,
+ usingHub: null,
+ uniqueId: null,
+ localIdentifier: null,
+};
+
+// Generate keys
+
+// Create unique id if needed
+if (!FederationKeys.getUniqueId()) {
+ FederationKeys.generateUniqueId();
+}
+
+// Create key pair if needed
+if (!FederationKeys.getPublicKey()) {
+ FederationKeys.generateKeys();
+}
+
+// Initializations
+
+// Start the client, setting up all the callbacks
+peerClient.start();
+
+// Start the server, setting up all the endpoints
+peerServer.start();
+
+const updateSettings = _.debounce(Meteor.bindEnvironment(function() {
+ const _enabled = settings.get('FEDERATION_Enabled');
+
+ if (!_enabled) { return; }
+
+ // If it is enabled, check if the settings are there
+ const _uniqueId = settings.get('FEDERATION_Unique_Id');
+ const _domain = settings.get('FEDERATION_Domain');
+ const _discoveryMethod = settings.get('FEDERATION_Discovery_Method');
+ const _hubUrl = settings.get('FEDERATION_Hub_URL');
+ const _peerUrl = settings.get('Site_Url');
+
+ if (!_domain || !_discoveryMethod || !_hubUrl || !_peerUrl) {
+ SettingsUpdater.updateStatus('Could not enable, settings are not fully set');
+
+ logger.setup.error('Could not enable Federation, settings are not fully set');
+
+ return;
+ }
+
+ logger.setup.info('Updating settings...');
+
+ // Normalize the config values
+ const config = {
+ hub: {
+ active: _discoveryMethod === 'hub',
+ url: _hubUrl.replace(/\/+$/, ''),
+ },
+ peer: {
+ uniqueId: _uniqueId,
+ domain: _domain.replace('@', '').trim(),
+ url: _peerUrl.replace(/\/+$/, ''),
+ public_key: FederationKeys.getPublicKeyString(),
+ },
+ };
+
+ // If the settings are correctly set, let's update the configuration
+
+ // Get the key pair
+ Federation.privateKey = FederationKeys.getPrivateKey();
+ Federation.publicKey = FederationKeys.getPublicKey();
+
+ // Set important information
+ Federation.enabled = true;
+ Federation.usingHub = config.hub.active;
+ Federation.uniqueId = config.peer.uniqueId;
+ Federation.localIdentifier = config.peer.domain;
+
+ // Set DNS
+ peerDNS.setConfig(config);
+
+ // Set HTTP
+ peerHTTP.setConfig(config);
+
+ // Set Client
+ peerClient.setConfig(config);
+ peerClient.enable();
+
+ // Set server
+ peerServer.setConfig(config);
+ peerServer.enable();
+
+ // Register the client
+ if (peerClient.register()) {
+ SettingsUpdater.updateStatus('Running');
+ } else {
+ SettingsUpdater.updateNextStatusTo('Disabled, could not register with Hub');
+ SettingsUpdater.updateEnabled(false);
+ }
+}), 150);
+
+function enableOrDisable() {
+ const _enabled = settings.get('FEDERATION_Enabled');
+
+ // If it was enabled, and was disabled now,
+ // make sure we disable everything: callbacks and endpoints
+ if (Federation.enabled && !_enabled) {
+ peerClient.disable();
+ peerServer.disable();
+
+ // Disable federation
+ Federation.enabled = false;
+
+ SettingsUpdater.updateStatus('Disabled');
+
+ logger.setup.info('Shutting down...');
+
+ return;
+ }
+
+ // If not enabled, skip
+ if (!_enabled) {
+ SettingsUpdater.updateStatus('Disabled');
+ return;
+ }
+
+ logger.setup.info('Booting...');
+
+ SettingsUpdater.updateStatus('Booting...');
+
+ updateSettings();
+}
+
+// Add settings listeners
+settings.get('FEDERATION_Enabled', enableOrDisable);
+settings.get('FEDERATION_Domain', updateSettings);
+settings.get('FEDERATION_Discovery_Method', updateSettings);
+settings.get('FEDERATION_Hub_URL', updateSettings);
diff --git a/packages/rocketchat-federation/server/methods/federationAddUser.js b/packages/rocketchat-federation/server/methods/federationAddUser.js
new file mode 100644
index 00000000000..7304af7ccee
--- /dev/null
+++ b/packages/rocketchat-federation/server/methods/federationAddUser.js
@@ -0,0 +1,48 @@
+import { Meteor } from 'meteor/meteor';
+import { Users } from 'meteor/rocketchat:models';
+
+import { logger } from '../logger';
+import peerClient from '../peerClient';
+import peerServer from '../peerClient';
+
+Meteor.methods({
+ federationAddUser(emailAddress, domainOverride) {
+ if (!Meteor.userId()) {
+ throw new Meteor.Error('error-invalid-user', 'Invalid user', { method: 'federationAddUser' });
+ }
+
+ if (!peerServer.enabled) {
+ throw new Meteor.Error('error-federation-disabled', 'Federation disabled', { method: 'federationAddUser' });
+ }
+
+ // Make sure the federated user still exists, and get the unique one, by email address
+ const [federatedUser] = peerClient.findUsers(emailAddress, { domainOverride, emailOnly: true });
+
+ if (!federatedUser) {
+ throw new Meteor.Error('federation-invalid-user', 'There is no user to add.');
+ }
+
+ let user = null;
+
+ const localUser = federatedUser.getLocalUser();
+
+ localUser.name += `@${ federatedUser.user.federation.peer }`;
+
+ // Delete the _id
+ delete localUser._id;
+
+ try {
+ // Create the local user
+ user = Users.create(localUser);
+ } catch (err) {
+ // If the user already exists, return the existing user
+ if (err.code === 11000) {
+ user = Users.findOne({ 'federation._id': localUser.federation._id });
+ }
+
+ logger.error(err);
+ }
+
+ return user;
+ },
+});
diff --git a/packages/rocketchat-federation/server/methods/federationSearchUsers.js b/packages/rocketchat-federation/server/methods/federationSearchUsers.js
new file mode 100644
index 00000000000..cad994083e3
--- /dev/null
+++ b/packages/rocketchat-federation/server/methods/federationSearchUsers.js
@@ -0,0 +1,24 @@
+import { Meteor } from 'meteor/meteor';
+
+import peerClient from '../peerClient';
+import peerServer from '../peerServer';
+
+Meteor.methods({
+ federationSearchUsers(email) {
+ if (!Meteor.userId()) {
+ throw new Meteor.Error('error-invalid-user', 'Invalid user', { method: 'federationSearchUsers' });
+ }
+
+ if (!peerServer.enabled) {
+ throw new Meteor.Error('error-federation-disabled', 'Federation disabled', { method: 'federationAddUser' });
+ }
+
+ const federatedUsers = peerClient.findUsers(email);
+
+ if (!federatedUsers.length) {
+ throw new Meteor.Error('federation-user-not-found', `Could not find federated users using "${ email }"`);
+ }
+
+ return federatedUsers;
+ },
+});
diff --git a/packages/rocketchat-federation/server/methods/index.js b/packages/rocketchat-federation/server/methods/index.js
new file mode 100644
index 00000000000..ce95109beb4
--- /dev/null
+++ b/packages/rocketchat-federation/server/methods/index.js
@@ -0,0 +1,2 @@
+import './federationSearchUsers';
+import './federationAddUser';
diff --git a/packages/rocketchat-federation/server/peerClient.js b/packages/rocketchat-federation/server/peerClient.js
new file mode 100644
index 00000000000..4681cd42975
--- /dev/null
+++ b/packages/rocketchat-federation/server/peerClient.js
@@ -0,0 +1,614 @@
+import qs from 'querystring';
+import { Meteor } from 'meteor/meteor';
+import { callbacks } from 'meteor/rocketchat:callbacks';
+import { settings } from 'meteor/rocketchat:settings';
+import { FederationEvents, FederationKeys, Messages, Rooms, Subscriptions, Users } from 'meteor/rocketchat:models';
+
+import { Federation } from './main';
+import peerDNS from './peerDNS';
+import peerHTTP from './peerHTTP';
+import { updateStatus } from './settingsUpdater';
+import { logger } from './logger';
+import { FederatedMessage, FederatedRoom, FederatedUser } from './federatedResources';
+
+class PeerClient {
+ constructor() {
+ this.config = {};
+
+ this.enabled = false;
+
+ // Keep resources we should skip callbacks
+ this.callbacksToSkip = {};
+ }
+
+ setConfig(config) {
+ // General
+ this.config = config;
+
+ // Setup HubPeer
+ const { hub: { url } } = this.config;
+
+ // Remove trailing slash
+ this.HubPeer = { url };
+
+ // Set the local peer
+ this.peer = {
+ domain: this.config.peer.domain,
+ url: this.config.peer.url,
+ public_key: this.config.peer.public_key,
+ };
+ }
+
+ log(message) {
+ logger.peerClient.info(message);
+ }
+
+ disable() {
+ this.log('Disabling...');
+
+ this.enabled = false;
+ }
+
+ enable() {
+ this.log('Enabling...');
+
+ this.enabled = true;
+ }
+
+ start() {
+ this.setupCallbacks();
+ }
+
+ // ###########
+ //
+ // Registering
+ //
+ // ###########
+ register() {
+ if (this.config.hub.active) {
+ updateStatus('Registering with Hub...');
+
+ return peerDNS.register(this.peer);
+ }
+
+ return true;
+ }
+
+ // ###################
+ //
+ // Callback management
+ //
+ // ###################
+ addCallbackToSkip(callback, resourceId) {
+ this.callbacksToSkip[`${ callback }_${ resourceId }`] = true;
+ }
+
+ skipCallbackIfNeeded(callback, resource) {
+ const { federation } = resource;
+
+ if (!federation) { return false; }
+
+ const { _id } = federation;
+
+ const callbackName = `${ callback }_${ _id }`;
+
+ const skipCallback = this.callbacksToSkip[callbackName];
+
+ delete this.callbacksToSkip[callbackName];
+
+ this.log(`${ callbackName } callback ${ skipCallback ? '' : 'not ' }skipped`);
+
+ return skipCallback;
+ }
+
+ wrapEnabled(callbackHandler) {
+ return function(...parameters) {
+ if (!this.enabled) { return; }
+
+ callbackHandler.apply(this, parameters);
+ }.bind(this);
+ }
+
+ setupCallbacks() {
+ // Accounts.onLogin(onLoginCallbackHandler.bind(this));
+ // Accounts.onLogout(onLogoutCallbackHandler.bind(this));
+
+ FederationEvents.on('createEvent', this.wrapEnabled(this.onCreateEvent.bind(this)));
+
+ callbacks.add('afterCreateDirectRoom', this.wrapEnabled(this.afterCreateDirectRoom.bind(this)), callbacks.priority.LOW, 'federation-create-direct-room');
+ callbacks.add('afterCreateRoom', this.wrapEnabled(this.afterCreateRoom.bind(this)), callbacks.priority.LOW, 'federation-join-room');
+ callbacks.add('afterSaveRoomSettings', this.wrapEnabled(this.afterSaveRoomSettings.bind(this)), callbacks.priority.LOW, 'federation-after-save-room-settings');
+ callbacks.add('afterAddedToRoom', this.wrapEnabled(this.afterAddedToRoom.bind(this)), callbacks.priority.LOW, 'federation-join-room');
+ callbacks.add('beforeLeaveRoom', this.wrapEnabled(this.beforeLeaveRoom.bind(this)), callbacks.priority.LOW, 'federation-leave-room');
+ callbacks.add('beforeRemoveFromRoom', this.wrapEnabled(this.beforeRemoveFromRoom.bind(this)), callbacks.priority.LOW, 'federation-leave-room');
+ callbacks.add('afterSaveMessage', this.wrapEnabled(this.afterSaveMessage.bind(this)), callbacks.priority.LOW, 'federation-save-message');
+ callbacks.add('afterDeleteMessage', this.wrapEnabled(this.afterDeleteMessage.bind(this)), callbacks.priority.LOW, 'federation-delete-message');
+ callbacks.add('afterReadMessages', this.wrapEnabled(this.afterReadMessages.bind(this)), callbacks.priority.LOW, 'federation-read-messages');
+ callbacks.add('afterSetReaction', this.wrapEnabled(this.afterSetReaction.bind(this)), callbacks.priority.LOW, 'federation-after-set-reaction');
+ callbacks.add('afterUnsetReaction', this.wrapEnabled(this.afterUnsetReaction.bind(this)), callbacks.priority.LOW, 'federation-after-unset-reaction');
+ callbacks.add('afterMuteUser', this.wrapEnabled(this.afterMuteUser.bind(this)), callbacks.priority.LOW, 'federation-mute-user');
+ callbacks.add('afterUnmuteUser', this.wrapEnabled(this.afterUnmuteUser.bind(this)), callbacks.priority.LOW, 'federation-unmute-user');
+
+ this.log('Callbacks set');
+ }
+
+ // ################
+ //
+ // Event management
+ //
+ // ################
+ propagateEvent(e) {
+ this.log(`propagateEvent: ${ e.t }`);
+
+ const { peer: domain } = e;
+
+ const peer = peerDNS.searchPeer(domain);
+
+ if (!peer || !peer.public_key) {
+ this.log(`Could not find valid peer:${ domain }`);
+
+ FederationEvents.setEventAsErrored(e, 'Could not find valid peer');
+ } else {
+ try {
+ const stringPayload = JSON.stringify({ event: e });
+
+ // Encrypt with the peer's public key
+ let payload = FederationKeys.loadKey(peer.public_key, 'public').encrypt(stringPayload);
+
+ // Encrypt with the local private key
+ payload = Federation.privateKey.encryptPrivate(payload);
+
+ peerHTTP.request(peer, 'POST', '/api/v1/federation.events', { payload }, { total: 5, stepSize: 500, stepMultiplier: 10 });
+
+ FederationEvents.setEventAsFullfilled(e);
+ } catch (err) {
+ this.log(`[${ e.t }] Event could not be sent to peer:${ domain }`);
+
+ if (err.response) {
+ const { response: { data: error } } = err;
+
+ if (error.errorType === 'error-app-prevented-sending') {
+ const { payload: {
+ message: {
+ rid: roomId,
+ u: {
+ username,
+ federation: { _id: userId },
+ },
+ },
+ } } = e;
+
+ const localUsername = username.split('@')[0];
+
+ // Create system message
+ Messages.createRejectedMessageByPeer(roomId, localUsername, {
+ u: {
+ _id: userId,
+ username: localUsername,
+ },
+ peer: domain,
+ });
+
+ return FederationEvents.setEventAsErrored(e, err.error, true);
+ }
+ }
+
+ if (err.error === 'federation-peer-does-not-exist') {
+ const { payload: {
+ message: {
+ rid: roomId,
+ u: {
+ username,
+ federation: { _id: userId },
+ },
+ },
+ } } = e;
+
+ const localUsername = username.split('@')[0];
+
+ // Create system message
+ Messages.createPeerDoesNotExist(roomId, localUsername, {
+ u: {
+ _id: userId,
+ username: localUsername,
+ },
+ peer: domain,
+ });
+
+ return FederationEvents.setEventAsErrored(e, err.error, true);
+ }
+
+ return FederationEvents.setEventAsErrored(e, `Could not send request to ${ domain }`);
+ }
+ }
+ }
+
+ onCreateEvent(e) {
+ this.propagateEvent(e);
+ }
+
+ resendUnfulfilledEvents() {
+ // Should we use queues in here?
+ const events = FederationEvents.getUnfulfilled();
+
+ for (const e of events) {
+ this.propagateEvent(e);
+ }
+ }
+
+ // #####
+ //
+ // Users
+ //
+ // #####
+ findUsers(email, options = {}) {
+ const [username, domain] = email.split('@');
+
+ const { peer: { domain: localPeerDomain } } = this;
+
+ let peer = null;
+
+ try {
+ peer = peerDNS.searchPeer(options.domainOverride || domain);
+ } catch (err) {
+ this.log(`Could not find peer using domain:${ domain }`);
+ throw new Meteor.Error('federation-peer-does-not-exist', `Could not find peer using domain:${ domain }`);
+ }
+
+ try {
+ const { data: { federatedUsers: remoteFederatedUsers } } = peerHTTP.request(peer, 'GET', `/api/v1/federation.users?${ qs.stringify({ username, domain, emailOnly: options.emailOnly }) }`);
+
+ const federatedUsers = [];
+
+ for (const federatedUser of remoteFederatedUsers) {
+ federatedUsers.push(new FederatedUser(localPeerDomain, federatedUser.user));
+ }
+
+ return federatedUsers;
+ } catch (err) {
+ this.log(`Could not find user:${ username } at ${ peer.domain }`);
+ throw new Meteor.Error('federation-user-does-not-exist', `Could not find user:${ email } at ${ peer.domain }`);
+ }
+ }
+
+ // #######
+ //
+ // Uploads
+ //
+ // #######
+ getUpload(options) {
+ const { identifier: domain, localMessage: { file: { _id: fileId } } } = options;
+
+ let peer = null;
+
+ try {
+ peer = peerDNS.searchPeer(domain);
+ } catch (err) {
+ this.log(`Could not find peer using domain:${ domain }`);
+ throw new Meteor.Error('federation-peer-does-not-exist', `Could not find peer using domain:${ domain }`);
+ }
+
+ const { data: { upload, buffer } } = peerHTTP.request(peer, 'GET', `/api/v1/federation.uploads?${ qs.stringify({ upload_id: fileId }) }`);
+
+ return { upload, buffer: Buffer.from(buffer) };
+ }
+
+ // #################
+ //
+ // Callback handlers
+ //
+ // #################
+ afterCreateDirectRoom(room, { from: owner }) {
+ this.log('afterCreateDirectRoom');
+
+ const { peer: { domain: localPeerDomain } } = this;
+
+ // Check if room is federated
+ if (!FederatedRoom.isFederated(localPeerDomain, room, { checkUsingUsers: true })) { return room; }
+
+ const federatedRoom = new FederatedRoom(localPeerDomain, room, { owner });
+
+ // Check if this should be skipped
+ if (this.skipCallbackIfNeeded('afterCreateDirectRoom', federatedRoom.getLocalRoom())) { return room; }
+
+ // Load federated users
+ federatedRoom.loadUsers();
+
+ // Refresh room's federation
+ federatedRoom.refreshFederation();
+
+ FederationEvents.directRoomCreated(federatedRoom, { skipPeers: [localPeerDomain] });
+ }
+
+ afterCreateRoom(roomOwner, room) {
+ this.log('afterCreateRoom');
+
+ const { _id: ownerId } = roomOwner;
+
+ const { peer: { domain: localPeerDomain } } = this;
+
+ // Check if room is federated
+ if (!FederatedRoom.isFederated(localPeerDomain, room, { checkUsingUsers: true })) { return roomOwner; }
+
+ const owner = Users.findOneById(ownerId);
+
+ const federatedRoom = new FederatedRoom(localPeerDomain, room, { owner });
+
+ // Check if this should be skipped
+ if (this.skipCallbackIfNeeded('afterCreateRoom', federatedRoom.getLocalRoom())) { return roomOwner; }
+
+ // Load federated users
+ federatedRoom.loadUsers();
+
+ // Refresh room's federation
+ federatedRoom.refreshFederation();
+
+ FederationEvents.roomCreated(federatedRoom, { skipPeers: [localPeerDomain] });
+ }
+
+ afterSaveRoomSettings(/* room */) {
+ this.log('afterSaveRoomSettings - NOT IMPLEMENTED');
+ }
+
+ afterAddedToRoom(users, room) {
+ this.log('afterAddedToRoom');
+
+ const { user: userWhoJoined, inviter: userWhoInvited } = users;
+
+ // Check if this should be skipped
+ if (this.skipCallbackIfNeeded('afterAddedToRoom', userWhoJoined)) { return users; }
+
+ const { peer: { domain: localPeerDomain } } = this;
+
+ // Check if room is federated
+ if (!FederatedRoom.isFederated(localPeerDomain, room, { checkUsingUsers: true })) { return users; }
+
+ const extras = {};
+
+ // If the room is not federated and has an owner
+ if (!room.federation) {
+ let ownerId;
+
+ // If the room does not have an owner, get the first user subscribed to that room
+ if (!room.u) {
+ const userSubscription = Subscriptions.findOne({ rid: room._id }, {
+ sort: {
+ ts: 1,
+ },
+ });
+
+ ownerId = userSubscription.u._id;
+ } else {
+ ownerId = room.u._id;
+ }
+
+ extras.owner = Users.findOneById(ownerId);
+ }
+
+ const federatedRoom = new FederatedRoom(localPeerDomain, room, extras);
+
+ // Load federated users
+ federatedRoom.loadUsers();
+
+ // Refresh room's federation
+ federatedRoom.refreshFederation();
+
+ // If the user who joined is from a different peer...
+ if (userWhoJoined.federation && userWhoJoined.federation.peer !== localPeerDomain) {
+ // ...create a "create room" event for that peer
+ FederationEvents.roomCreated(federatedRoom, { peers: [userWhoJoined.federation.peer] });
+ }
+
+ // Then, create a "user join/added" event to the other peers
+ const federatedUserWhoJoined = FederatedUser.loadOrCreate(localPeerDomain, userWhoJoined);
+
+ if (userWhoInvited) {
+ const federatedInviter = FederatedUser.loadOrCreate(localPeerDomain, userWhoInvited);
+
+ FederationEvents.userAdded(federatedRoom, federatedUserWhoJoined, federatedInviter, { skipPeers: [localPeerDomain] });
+ } else {
+ FederationEvents.userJoined(federatedRoom, federatedUserWhoJoined, { skipPeers: [localPeerDomain] });
+ }
+ }
+
+ beforeLeaveRoom(userWhoLeft, room) {
+ this.log('beforeLeaveRoom');
+
+ // Check if this should be skipped
+ if (this.skipCallbackIfNeeded('beforeLeaveRoom', userWhoLeft)) { return userWhoLeft; }
+
+ const { peer: { domain: localPeerDomain } } = this;
+
+ // Check if room is federated
+ if (!FederatedRoom.isFederated(localPeerDomain, room)) { return userWhoLeft; }
+
+ const federatedRoom = FederatedRoom.loadByFederationId(localPeerDomain, room.federation._id);
+
+ const federatedUserWhoLeft = FederatedUser.loadByFederationId(localPeerDomain, userWhoLeft.federation._id);
+
+ // Then, create a "user left" event to the other peers
+ FederationEvents.userLeft(federatedRoom, federatedUserWhoLeft, { skipPeers: [localPeerDomain] });
+
+ // Load federated users
+ federatedRoom.loadUsers();
+
+ // Refresh room's federation
+ federatedRoom.refreshFederation();
+ }
+
+ beforeRemoveFromRoom(users, room) {
+ this.log('beforeRemoveFromRoom');
+
+ const { removedUser, userWhoRemoved } = users;
+
+ // Check if this should be skipped
+ if (this.skipCallbackIfNeeded('beforeRemoveFromRoom', removedUser)) { return users; }
+
+ const { peer: { domain: localPeerDomain } } = this;
+
+ // Check if room is federated
+ if (!FederatedRoom.isFederated(localPeerDomain, room)) { return users; }
+
+ const federatedRoom = FederatedRoom.loadByFederationId(localPeerDomain, room.federation._id);
+
+ const federatedRemovedUser = FederatedUser.loadByFederationId(localPeerDomain, removedUser.federation._id);
+
+ const federatedUserWhoRemoved = FederatedUser.loadByFederationId(localPeerDomain, userWhoRemoved.federation._id);
+
+ FederationEvents.userRemoved(federatedRoom, federatedRemovedUser, federatedUserWhoRemoved, { skipPeers: [localPeerDomain] });
+
+ // Load federated users
+ federatedRoom.loadUsers();
+
+ // Refresh room's federation
+ federatedRoom.refreshFederation();
+ }
+
+ afterSaveMessage(message, room) {
+ this.log('afterSaveMessage');
+
+ // Check if this should be skipped
+ if (this.skipCallbackIfNeeded('afterSaveMessage', message)) { return message; }
+
+ const { peer: { domain: localPeerDomain } } = this;
+
+ // Check if room is federated
+ if (!FederatedRoom.isFederated(localPeerDomain, room)) { return message; }
+
+ const federatedRoom = FederatedRoom.loadByFederationId(localPeerDomain, room.federation._id);
+
+ const federatedMessage = FederatedMessage.loadOrCreate(localPeerDomain, message);
+
+ // If editedAt exists, it means it is an update
+ if (message.editedAt) {
+ const user = Users.findOneById(message.editedBy._id);
+
+ const federatedUser = FederatedUser.loadByFederationId(localPeerDomain, user.federation._id);
+
+ FederationEvents.messageUpdated(federatedRoom, federatedMessage, federatedUser, { skipPeers: [localPeerDomain] });
+ } else {
+ FederationEvents.messageCreated(federatedRoom, federatedMessage, { skipPeers: [localPeerDomain] });
+ }
+ }
+
+ afterDeleteMessage(message) {
+ this.log('afterDeleteMessage');
+
+ // Check if this should be skipped
+ if (this.skipCallbackIfNeeded('afterDeleteMessage', message)) { return message; }
+
+ const { peer: { domain: localPeerDomain } } = this;
+
+ const room = Rooms.findOneById(message.rid);
+
+ // Check if room is federated
+ if (!FederatedRoom.isFederated(localPeerDomain, room)) { return message; }
+
+ const federatedRoom = FederatedRoom.loadByFederationId(localPeerDomain, room.federation._id);
+
+ const federatedMessage = new FederatedMessage(localPeerDomain, message);
+
+ FederationEvents.messageDeleted(federatedRoom, federatedMessage, { skipPeers: [localPeerDomain] });
+ }
+
+ afterReadMessages(roomId, { userId }) {
+ this.log('afterReadMessages');
+
+ if (!settings.get('Message_Read_Receipt_Enabled')) { this.log('Skipping: read receipts are not enabled'); return roomId; }
+
+ const { peer: { domain: localPeerDomain } } = this;
+
+ const room = Rooms.findOneById(roomId);
+
+ // Check if room is federated
+ if (!FederatedRoom.isFederated(localPeerDomain, room)) { return roomId; }
+
+ const federatedRoom = FederatedRoom.loadByFederationId(localPeerDomain, room.federation._id);
+
+ if (this.skipCallbackIfNeeded('afterReadMessages', federatedRoom.getLocalRoom())) { return roomId; }
+
+ const user = Users.findOneById(userId);
+
+ const federatedUser = FederatedUser.loadByFederationId(localPeerDomain, user.federation._id);
+
+ FederationEvents.messagesRead(federatedRoom, federatedUser, { skipPeers: [localPeerDomain] });
+ }
+
+ afterSetReaction(message, { user, reaction, shouldReact }) {
+ this.log('afterSetReaction');
+
+ const room = Rooms.findOneById(message.rid);
+
+ const { peer: { domain: localPeerDomain } } = this;
+
+ // Check if room is federated
+ if (!FederatedRoom.isFederated(localPeerDomain, room)) { return message; }
+
+ const federatedUser = FederatedUser.loadByFederationId(localPeerDomain, user.federation._id);
+
+ const federatedMessage = FederatedMessage.loadByFederationId(localPeerDomain, message.federation._id);
+
+ const federatedRoom = FederatedRoom.loadByFederationId(localPeerDomain, room.federation._id);
+
+ FederationEvents.messagesSetReaction(federatedRoom, federatedMessage, federatedUser, reaction, shouldReact, { skipPeers: [localPeerDomain] });
+ }
+
+ afterUnsetReaction(message, { user, reaction, shouldReact }) {
+ this.log('afterUnsetReaction');
+
+ const room = Rooms.findOneById(message.rid);
+
+ const { peer: { domain: localPeerDomain } } = this;
+
+ // Check if room is federated
+ if (!FederatedRoom.isFederated(localPeerDomain, room)) { return message; }
+
+ const federatedUser = FederatedUser.loadByFederationId(localPeerDomain, user.federation._id);
+
+ const federatedMessage = FederatedMessage.loadByFederationId(localPeerDomain, message.federation._id);
+
+ const federatedRoom = FederatedRoom.loadByFederationId(localPeerDomain, room.federation._id);
+
+ FederationEvents.messagesUnsetReaction(federatedRoom, federatedMessage, federatedUser, reaction, shouldReact, { skipPeers: [localPeerDomain] });
+ }
+
+ afterMuteUser(users, room) {
+ this.log('afterMuteUser');
+
+ const { mutedUser, fromUser } = users;
+
+ const { peer: { domain: localPeerDomain } } = this;
+
+ // Check if room is federated
+ if (!FederatedRoom.isFederated(localPeerDomain, room)) { return users; }
+
+ const federatedRoom = FederatedRoom.loadByFederationId(localPeerDomain, room.federation._id);
+
+ const federatedMutedUser = FederatedUser.loadByFederationId(localPeerDomain, mutedUser.federation._id);
+
+ const federatedUserWhoMuted = FederatedUser.loadByFederationId(localPeerDomain, fromUser.federation._id);
+
+ FederationEvents.userMuted(federatedRoom, federatedMutedUser, federatedUserWhoMuted, { skipPeers: [localPeerDomain] });
+ }
+
+ afterUnmuteUser(users, room) {
+ this.log('afterUnmuteUser');
+
+ const { unmutedUser, fromUser } = users;
+
+ const { peer: { domain: localPeerDomain } } = this;
+
+ // Check if room is federated
+ if (!FederatedRoom.isFederated(localPeerDomain, room)) { return users; }
+
+ const federatedRoom = FederatedRoom.loadByFederationId(localPeerDomain, room.federation._id);
+
+ const federatedUnmutedUser = FederatedUser.loadByFederationId(localPeerDomain, unmutedUser.federation._id);
+
+ const federatedUserWhoUnmuted = FederatedUser.loadByFederationId(localPeerDomain, fromUser.federation._id);
+
+ FederationEvents.userUnmuted(federatedRoom, federatedUnmutedUser, federatedUserWhoUnmuted, { skipPeers: [localPeerDomain] });
+ }
+}
+
+export default new PeerClient();
diff --git a/packages/rocketchat-federation/server/peerDNS.js b/packages/rocketchat-federation/server/peerDNS.js
new file mode 100644
index 00000000000..4af0b03a2f6
--- /dev/null
+++ b/packages/rocketchat-federation/server/peerDNS.js
@@ -0,0 +1,175 @@
+import dns from 'dns';
+import { Meteor } from 'meteor/meteor';
+import { FederationDNSCache } from 'meteor/rocketchat:models';
+
+import { logger } from './logger';
+import peerHTTP from './peerHTTP';
+import { updateStatus } from './settingsUpdater';
+
+const dnsResolveSRV = Meteor.wrapAsync(dns.resolveSrv);
+const dnsResolveTXT = Meteor.wrapAsync(dns.resolveTxt);
+
+class PeerDNS {
+ constructor() {
+ this.config = {};
+ }
+
+ setConfig(config) {
+ // General
+ this.config = config;
+
+ // Setup HubPeer
+ const { hub: { url } } = config;
+ this.HubPeer = { url };
+ }
+
+ log(message) {
+ logger.dns.info(message);
+ }
+
+ // ########
+ //
+ // Register
+ //
+ // ########
+ register(peerConfig) {
+ const { uniqueId, domain, url, public_key } = peerConfig;
+
+ this.log(`Registering peer with domain ${ domain }...`);
+
+ // Attempt to register peer
+ try {
+ peerHTTP.request(this.HubPeer, 'POST', '/api/v1/peers', { uniqueId, domain, url, public_key }, { total: 5, stepSize: 1000, tryToUpdateDNS: false });
+
+ this.log('Peer registered!');
+
+ updateStatus('Running, registered to Hub');
+
+ return true;
+ } catch (err) {
+ this.log(err);
+
+ this.log('Could not register peer');
+
+ return false;
+ }
+ }
+
+ // #############
+ //
+ // Peer Handling
+ //
+ // #############
+ searchPeer(domain) {
+ this.log(`searchPeer: ${ domain }`);
+
+ let peer = FederationDNSCache.findOneByDomain(domain);
+
+ // Try to lookup at the DNS Cache
+ if (!peer) {
+ this.updatePeerDNS(domain);
+
+ peer = FederationDNSCache.findOneByDomain(domain);
+ }
+
+ return peer;
+ }
+
+ getPeerUsingDNS(domain) {
+ this.log(`getPeerUsingDNS: ${ domain }`);
+
+ // Try searching by DNS first
+ const srvEntries = dnsResolveSRV(`_rocketchat._tcp.${ domain }`);
+
+ const [srvEntry] = srvEntries;
+
+ // Get the public key from the TXT record
+ const txtRecords = dnsResolveTXT(domain);
+
+ let publicKey;
+
+ for (const txtRecord of txtRecords) {
+ const joinedTxtRecord = txtRecord.join('');
+
+ if (joinedTxtRecord.indexOf('rocketchat-public-key=') === 0) {
+ publicKey = joinedTxtRecord;
+ break;
+ }
+ }
+
+ if (!publicKey) {
+ throw new Meteor.Error('ENOTFOUND', 'Could not find public key entry on TXT records');
+ }
+
+ publicKey = publicKey.replace('rocketchat-public-key=', '');
+
+ const protocol = srvEntry.name === 'localhost' ? 'http' : 'https';
+
+ return {
+ domain,
+ url: `${ protocol }://${ srvEntry.name }:${ srvEntry.port }`,
+ public_key: publicKey,
+ };
+ }
+
+ getPeerUsingHub(domain) {
+ this.log(`getPeerUsingHub: ${ domain }`);
+
+ // If there is no DNS entry for that, get from the Hub
+ const { data: { peer } } = peerHTTP.simpleRequest(this.HubPeer, 'GET', `/api/v1/peers?search=${ domain }`);
+
+ return peer;
+ }
+
+ // ##############
+ //
+ // DNS Management
+ //
+ // ##############
+ updatePeerDNS(domain) {
+ this.log(`updatePeerDNS: ${ domain }`);
+
+ let peer;
+
+ try {
+ peer = this.getPeerUsingDNS(domain);
+ } catch (err) {
+ if (err.code !== 'ENOTFOUND') {
+ this.log(err);
+
+ throw new Error(`Error trying to fetch SRV DNS entries for ${ domain }`);
+ }
+
+ peer = this.getPeerUsingHub(domain);
+ }
+
+ this.updateDNSCache.call(this, peer);
+
+ return peer;
+ }
+
+ updateDNSEntry(peer) {
+ this.log('updateDNSEntry');
+
+ const { domain } = peer;
+
+ delete peer._id;
+
+ // Make sure public_key has no line breaks
+ peer.public_key = peer.public_key.replace(/\n|\r/g, '');
+
+ return FederationDNSCache.upsert({ domain }, peer);
+ }
+
+ updateDNSCache(peers) {
+ this.log('updateDNSCache');
+
+ peers = Array.isArray(peers) ? peers : [peers];
+
+ for (const peer of peers) {
+ this.updateDNSEntry.call(this, peer);
+ }
+ }
+}
+
+export default new PeerDNS();
diff --git a/packages/rocketchat-federation/server/peerHTTP.js b/packages/rocketchat-federation/server/peerHTTP.js
new file mode 100644
index 00000000000..26813b8ed84
--- /dev/null
+++ b/packages/rocketchat-federation/server/peerHTTP.js
@@ -0,0 +1,126 @@
+import { Meteor } from 'meteor/meteor';
+import { HTTP } from 'meteor/http';
+
+import { logger } from './logger';
+import peerDNS from './peerDNS';
+
+// Should skip the retry if the error is one of the below?
+const errorsToSkipRetrying = [
+ 'error-app-prevented-sending',
+];
+
+function skipRetryOnSpecificError(err) {
+ return errorsToSkipRetrying.includes(err && err.errorType);
+}
+
+// Delay method to wait a little bit before retrying
+const delay = Meteor.wrapAsync(function(ms, callback) {
+ Meteor.setTimeout(function() {
+ callback(null);
+ }, ms);
+});
+
+function doSimpleRequest(peer, method, uri, body) {
+ this.log(`Request: ${ method } ${ uri }`);
+
+ const { url: serverBaseURL } = peer;
+
+ const url = `${ serverBaseURL }${ uri }`;
+
+ let data = null;
+
+ if (method === 'POST' || method === 'PUT') {
+ data = body;
+ }
+
+ this.log(`Sending request: ${ method } - ${ uri }`);
+
+ return HTTP.call(method, url, { data, timeout: 2000, headers: { 'x-federation-domain': this.config.peer.domain } });
+}
+
+//
+// Actually does the request, handling retries and everything
+function doRequest(peer, method, uri, body, retryInfo = {}) {
+ // Normalize retry info
+ retryInfo = {
+ total: retryInfo.total || 1,
+ stepSize: retryInfo.stepSize || 100,
+ stepMultiplier: retryInfo.stepMultiplier || 1,
+ tryToUpdateDNS: retryInfo.tryToUpdateDNS === undefined ? true : retryInfo.tryToUpdateDNS,
+ DNSUpdated: false,
+ };
+
+ for (let i = 0; i <= retryInfo.total; i++) {
+ try {
+ return doSimpleRequest.call(this, peer, method, uri, body);
+ } catch (err) {
+ try {
+ if (retryInfo.tryToUpdateDNS && !retryInfo.DNSUpdated) {
+ i--;
+
+ retryInfo.DNSUpdated = true;
+
+ this.log(`Trying to update local DNS cache for peer:${ peer.domain }`);
+
+ peer = peerDNS.updatePeerDNS(peer.domain);
+
+ continue;
+ }
+ } catch (err) {
+ if (err.response && err.response.statusCode === 404) {
+ throw new Meteor.Error('federation-peer-does-not-exist', 'Peer does not exist');
+ }
+ }
+
+ // Check if we need to skip due to specific error
+ if (skipRetryOnSpecificError(err && err.response && err.response.data)) {
+ this.log('Retry: skipping due to specific error');
+
+ throw err;
+ }
+
+ if (i === retryInfo.total - 1) {
+ // Throw the error, as we could not fulfill the request
+ this.log('Retry: could not fulfill the request');
+
+ throw err;
+ }
+
+ const timeToRetry = retryInfo.stepSize * (i + 1) * retryInfo.stepMultiplier;
+
+ this.log(`Trying again in ${ timeToRetry / 1000 }s: ${ method } - ${ uri }`);
+
+ // Otherwise, wait and try again
+ delay(timeToRetry);
+ }
+ }
+}
+
+class PeerHTTP {
+ constructor() {
+ this.config = {};
+ }
+
+ setConfig(config) {
+ // General
+ this.config = config;
+ }
+
+ log(message) {
+ logger.http.info(message);
+ }
+
+ //
+ // Direct request
+ simpleRequest(peer, method, uri, body) {
+ return doSimpleRequest.call(this, peer, method, uri, body);
+ }
+
+ //
+ // Request trying to find DNS entries
+ request(peer, method, uri, body, retryInfo = {}) {
+ return doRequest.call(this, peer, method, uri, body, retryInfo);
+ }
+}
+
+export default new PeerHTTP();
diff --git a/packages/rocketchat-federation/server/peerServer/index.js b/packages/rocketchat-federation/server/peerServer/index.js
new file mode 100644
index 00000000000..d8144d71ed1
--- /dev/null
+++ b/packages/rocketchat-federation/server/peerServer/index.js
@@ -0,0 +1,8 @@
+import peerServer from './peerServer';
+
+// Setup routes
+import './routes/events';
+import './routes/uploads';
+import './routes/users';
+
+export default peerServer;
diff --git a/packages/rocketchat-federation/server/peerServer/peerServer.js b/packages/rocketchat-federation/server/peerServer/peerServer.js
new file mode 100644
index 00000000000..96db2a634e0
--- /dev/null
+++ b/packages/rocketchat-federation/server/peerServer/peerServer.js
@@ -0,0 +1,388 @@
+import { callbacks } from 'meteor/rocketchat:callbacks';
+import { setReaction } from 'meteor/rocketchat:reactions';
+import { addUserToRoom, removeUserFromRoom, deleteMessage } from 'meteor/rocketchat:lib';
+import { Rooms, Subscriptions } from 'meteor/rocketchat:models';
+
+import { FederatedMessage, FederatedRoom, FederatedUser } from '../federatedResources';
+import { logger } from '../logger.js';
+import peerClient from '../peerClient';
+
+class PeerServer {
+ constructor() {
+ this.config = {};
+ this.enabled = false;
+ }
+
+ setConfig(config) {
+ // General
+ this.config = config;
+ }
+
+ log(message) {
+ logger.peerServer.info(message);
+ }
+
+ disable() {
+ this.log('Disabling...');
+
+ this.enabled = false;
+ }
+
+ enable() {
+ this.log('Enabling...');
+
+ this.enabled = true;
+ }
+
+ start() {
+ this.log('Routes are set');
+ }
+
+ handleDirectRoomCreatedEvent(e) {
+ this.log('handleDirectRoomCreatedEvent');
+
+ const { peer: { domain: localPeerDomain } } = this.config;
+
+ const { payload: { room, owner, users } } = e;
+
+ // Load the federated room
+ const federatedRoom = new FederatedRoom(localPeerDomain, room, { owner });
+
+ // Set users
+ federatedRoom.setUsers(users);
+
+ // Create, if needed, all room's users
+ federatedRoom.createUsers();
+
+ // Then, create the room, if needed
+ federatedRoom.create();
+ }
+
+ handleRoomCreatedEvent(e) {
+ this.log('handleRoomCreatedEvent');
+
+ const { peer: { domain: localPeerDomain } } = this.config;
+
+ const { payload: { room, owner, users } } = e;
+
+ // Load the federated room
+ const federatedRoom = new FederatedRoom(localPeerDomain, room, { owner });
+
+ // Set users
+ federatedRoom.setUsers(users);
+
+ // Create, if needed, all room's users
+ federatedRoom.createUsers();
+
+ // Then, create the room, if needed
+ federatedRoom.create();
+ }
+
+ handleUserJoinedEvent(e) {
+ this.log('handleUserJoinedEvent');
+
+ const { peer: { domain: localPeerDomain } } = this.config;
+
+ const { payload: { federated_room_id, user } } = e;
+
+ // Load the federated room
+ const federatedRoom = FederatedRoom.loadByFederationId(localPeerDomain, federated_room_id);
+
+ // Create the user, if needed
+ const federatedUser = FederatedUser.loadOrCreate(localPeerDomain, user);
+ const localUser = federatedUser.create();
+
+ // Callback management
+ peerClient.addCallbackToSkip('afterAddedToRoom', federatedUser.getFederationId());
+
+ // Add the user to the room
+ addUserToRoom(federatedRoom.room._id, localUser, null, false);
+
+ // Load federated users
+ federatedRoom.loadUsers();
+
+ // Refresh room's federation
+ federatedRoom.refreshFederation();
+ }
+
+ handleUserAddedEvent(e) {
+ this.log('handleUserAddedEvent');
+
+ const { peer: { domain: localPeerDomain } } = this.config;
+
+ const { payload: { federated_room_id, federated_inviter_id, user } } = e;
+
+ // Load the federated room
+ const federatedRoom = FederatedRoom.loadByFederationId(localPeerDomain, federated_room_id);
+
+ // Load the inviter
+ const federatedInviter = FederatedUser.loadByFederationId(localPeerDomain, federated_inviter_id);
+
+ if (!federatedInviter) {
+ throw new Error('Inviting user does not exist');
+ }
+
+ const localInviter = federatedInviter.getLocalUser();
+
+ // Create the user, if needed
+ const federatedUser = FederatedUser.loadOrCreate(localPeerDomain, user);
+ const localUser = federatedUser.create();
+
+ // Callback management
+ peerClient.addCallbackToSkip('afterAddedToRoom', federatedUser.getFederationId());
+
+ // Add the user to the room
+ addUserToRoom(federatedRoom.room._id, localUser, localInviter, false);
+
+ // Load federated users
+ federatedRoom.loadUsers();
+
+ // Refresh room's federation
+ federatedRoom.refreshFederation();
+ }
+
+ handleUserLeftEvent(e) {
+ this.log('handleUserLeftEvent');
+
+ const { peer: { domain: localPeerDomain } } = this.config;
+
+ const { payload: { federated_room_id, federated_user_id } } = e;
+
+ // Load the federated room
+ const federatedRoom = FederatedRoom.loadByFederationId(localPeerDomain, federated_room_id);
+
+ // Load the user who left
+ const federatedUser = FederatedUser.loadByFederationId(localPeerDomain, federated_user_id);
+ const localUser = federatedUser.getLocalUser();
+
+ // Callback management
+ peerClient.addCallbackToSkip('beforeLeaveRoom', federatedUser.getFederationId());
+
+ // Remove the user from the room
+ removeUserFromRoom(federatedRoom.room._id, localUser);
+
+ // Load federated users
+ federatedRoom.loadUsers();
+
+ // Refresh room's federation
+ federatedRoom.refreshFederation();
+ }
+
+ handleUserRemovedEvent(e) {
+ this.log('handleUserRemovedEvent');
+
+ const { peer: { domain: localPeerDomain } } = this.config;
+
+ const { payload: { federated_room_id, federated_user_id, federated_removed_by_user_id } } = e;
+
+ // Load the federated room
+ const federatedRoom = FederatedRoom.loadByFederationId(localPeerDomain, federated_room_id);
+
+ // Load the user who left
+ const federatedUser = FederatedUser.loadByFederationId(localPeerDomain, federated_user_id);
+ const localUser = federatedUser.getLocalUser();
+
+ // Load the user who removed
+ const federatedUserWhoRemoved = FederatedUser.loadByFederationId(localPeerDomain, federated_removed_by_user_id);
+ const localUserWhoRemoved = federatedUserWhoRemoved.getLocalUser();
+
+ // Callback management
+ peerClient.addCallbackToSkip('beforeRemoveFromRoom', federatedUser.getFederationId());
+
+ // Remove the user from the room
+ removeUserFromRoom(federatedRoom.room._id, localUser, { byUser: localUserWhoRemoved });
+
+ // Load federated users
+ federatedRoom.loadUsers();
+
+ // Refresh room's federation
+ federatedRoom.refreshFederation();
+ }
+
+ handleUserMutedEvent(e) {
+ this.log('handleUserMutedEvent');
+
+ const { peer: { domain: localPeerDomain } } = this.config;
+
+ const { payload: { federated_room_id, federated_user_id } } = e;
+ // const { payload: { federated_room_id, federated_user_id, federated_muted_by_user_id } } = e;
+
+ // Load the federated room
+ const federatedRoom = FederatedRoom.loadByFederationId(localPeerDomain, federated_room_id);
+
+ // Load the user who left
+ const federatedUser = FederatedUser.loadByFederationId(localPeerDomain, federated_user_id);
+ const localUser = federatedUser.getLocalUser();
+
+ // // Load the user who muted
+ // const federatedUserWhoMuted = FederatedUser.loadByFederationId(localPeerDomain, federated_muted_by_user_id);
+ // const localUserWhoMuted = federatedUserWhoMuted.getLocalUser();
+
+ // Mute user
+ Rooms.muteUsernameByRoomId(federatedRoom.room._id, localUser.username);
+
+ // TODO: should we create a message?
+ }
+
+ handleUserUnmutedEvent(e) {
+ this.log('handleUserUnmutedEvent');
+
+ const { peer: { domain: localPeerDomain } } = this.config;
+
+ const { payload: { federated_room_id, federated_user_id } } = e;
+ // const { payload: { federated_room_id, federated_user_id, federated_unmuted_by_user_id } } = e;
+
+ // Load the federated room
+ const federatedRoom = FederatedRoom.loadByFederationId(localPeerDomain, federated_room_id);
+
+ // Load the user who left
+ const federatedUser = FederatedUser.loadByFederationId(localPeerDomain, federated_user_id);
+ const localUser = federatedUser.getLocalUser();
+
+ // // Load the user who muted
+ // const federatedUserWhoUnmuted = FederatedUser.loadByFederationId(localPeerDomain, federated_unmuted_by_user_id);
+ // const localUserWhoUnmuted = federatedUserWhoUnmuted.getLocalUser();
+
+ // Unmute user
+ Rooms.unmuteUsernameByRoomId(federatedRoom.room._id, localUser.username);
+
+ // TODO: should we create a message?
+ }
+
+ handleMessageCreatedEvent(e) {
+ this.log('handleMessageCreatedEvent');
+
+ const { peer: { domain: localPeerDomain } } = this.config;
+
+ const { payload: { message } } = e;
+
+ // Load the federated message
+ const federatedMessage = new FederatedMessage(localPeerDomain, message);
+
+ // Callback management
+ peerClient.addCallbackToSkip('afterSaveMessage', federatedMessage.getFederationId());
+
+ // Create the federated message
+ federatedMessage.create();
+ }
+
+ handleMessageUpdatedEvent(e) {
+ this.log('handleMessageUpdatedEvent');
+
+ const { peer: { domain: localPeerDomain } } = this.config;
+
+ const { payload: { message, federated_user_id } } = e;
+
+ // Load the federated message
+ const federatedMessage = new FederatedMessage(localPeerDomain, message);
+
+ // Load the federated user
+ const federatedUser = FederatedUser.loadByFederationId(localPeerDomain, federated_user_id);
+
+ // Callback management
+ peerClient.addCallbackToSkip('afterSaveMessage', federatedMessage.getFederationId());
+
+ // Update the federated message
+ federatedMessage.update(federatedUser);
+ }
+
+ handleMessageDeletedEvent(e) {
+ this.log('handleMessageDeletedEvent');
+
+ const { peer: { domain: localPeerDomain } } = this.config;
+
+ const { payload: { federated_message_id } } = e;
+
+ const federatedMessage = FederatedMessage.loadByFederationId(localPeerDomain, federated_message_id);
+
+ // Load the federated message
+ const localMessage = federatedMessage.getLocalMessage();
+
+ // Load the author
+ const localAuthor = federatedMessage.federatedAuthor.getLocalUser();
+
+ // Callback management
+ peerClient.addCallbackToSkip('afterDeleteMessage', federatedMessage.getFederationId());
+
+ // Create the federated message
+ deleteMessage(localMessage, localAuthor);
+ }
+
+ handleMessagesReadEvent(e) {
+ this.log('handleMessagesReadEvent');
+
+ const { peer: { domain: localPeerDomain } } = this.config;
+
+ const { payload: { federated_room_id, federated_user_id } } = e;
+
+ // Load the federated room
+ const federatedRoom = FederatedRoom.loadByFederationId(localPeerDomain, federated_room_id);
+
+ peerClient.addCallbackToSkip('afterReadMessages', federatedRoom.getFederationId());
+
+ // Load the user who left
+ const federatedUser = FederatedUser.loadByFederationId(localPeerDomain, federated_user_id);
+ const localUser = federatedUser.getLocalUser();
+
+ // Mark the messages as read
+ // TODO: move below calls to an exported function
+ const userSubscription = Subscriptions.findOneByRoomIdAndUserId(federatedRoom.room._id, localUser._id, { fields: { ls: 1 } });
+ Subscriptions.setAsReadByRoomIdAndUserId(federatedRoom.room._id, localUser._id);
+
+ callbacks.run('afterReadMessages', federatedRoom.room._id, { userId: localUser._id, lastSeen: userSubscription.ls });
+ }
+
+ handleMessagesSetReactionEvent(e) {
+ this.log('handleMessagesSetReactionEvent');
+
+ const { peer: { domain: localPeerDomain } } = this.config;
+
+ const { payload: { federated_room_id, federated_message_id, federated_user_id, reaction, shouldReact } } = e;
+
+ // Load the federated room
+ const federatedRoom = FederatedRoom.loadByFederationId(localPeerDomain, federated_room_id);
+ const localRoom = federatedRoom.getLocalRoom();
+
+ // Load the user who reacted
+ const federatedUser = FederatedUser.loadByFederationId(localPeerDomain, federated_user_id);
+ const localUser = federatedUser.getLocalUser();
+
+ // Load the message
+ const federatedMessage = FederatedMessage.loadByFederationId(localPeerDomain, federated_message_id);
+ const localMessage = federatedMessage.getLocalMessage();
+
+ // Callback management
+ peerClient.addCallbackToSkip('afterSetReaction', federatedMessage.getFederationId());
+
+ // Set message reaction
+ setReaction(localRoom, localUser, localMessage, reaction, shouldReact);
+ }
+
+ handleMessagesUnsetReactionEvent(e) {
+ this.log('handleMessagesUnsetReactionEvent');
+
+ const { peer: { domain: localPeerDomain } } = this.config;
+
+ const { payload: { federated_room_id, federated_message_id, federated_user_id, reaction, shouldReact } } = e;
+
+ // Load the federated room
+ const federatedRoom = FederatedRoom.loadByFederationId(localPeerDomain, federated_room_id);
+ const localRoom = federatedRoom.getLocalRoom();
+
+ // Load the user who reacted
+ const federatedUser = FederatedUser.loadByFederationId(localPeerDomain, federated_user_id);
+ const localUser = federatedUser.getLocalUser();
+
+ // Load the message
+ const federatedMessage = FederatedMessage.loadByFederationId(localPeerDomain, federated_message_id);
+ const localMessage = federatedMessage.getLocalMessage();
+
+ // Callback management
+ peerClient.addCallbackToSkip('afterUnsetReaction', federatedMessage.getFederationId());
+
+ // Unset message reaction
+ setReaction(localRoom, localUser, localMessage, reaction, shouldReact);
+ }
+}
+
+export default new PeerServer();
diff --git a/packages/rocketchat-federation/server/peerServer/routes/events.js b/packages/rocketchat-federation/server/peerServer/routes/events.js
new file mode 100644
index 00000000000..252ad6ec754
--- /dev/null
+++ b/packages/rocketchat-federation/server/peerServer/routes/events.js
@@ -0,0 +1,106 @@
+import { API } from 'meteor/rocketchat:api';
+import { FederationKeys } from 'meteor/rocketchat:models';
+
+import { Federation } from '../../main';
+
+import peerDNS from '../../peerDNS';
+import peerServer from '../peerServer';
+
+API.v1.addRoute('federation.events', { authRequired: false }, {
+ post() {
+ if (!peerServer.enabled) {
+ return API.v1.failure('Not found');
+ }
+
+ if (!this.bodyParams.payload) {
+ return API.v1.failure('Payload was not sent');
+ }
+
+ if (!this.request.headers['x-federation-domain']) {
+ return API.v1.failure('Cannot handle that request');
+ }
+
+ const remotePeerDomain = this.request.headers['x-federation-domain'];
+
+ const peer = peerDNS.searchPeer(remotePeerDomain);
+
+ if (!peer) {
+ return API.v1.failure('Could not find valid peer');
+ }
+
+ const payloadBuffer = Buffer.from(this.bodyParams.payload.data);
+
+ // Decrypt with the peer's public key
+ let payload = FederationKeys.loadKey(peer.public_key, 'public').decryptPublic(payloadBuffer);
+
+ // Decrypt with the local private key
+ payload = Federation.privateKey.decrypt(payload);
+
+ // Get the event
+ const { event: e } = JSON.parse(payload.toString());
+
+ if (!e) {
+ return API.v1.failure('Event was not sent');
+ }
+
+ peerServer.log(`Received event:${ e.t }`);
+
+ try {
+ switch (e.t) {
+ case 'drc':
+ peerServer.handleDirectRoomCreatedEvent(e);
+ break;
+ case 'roc':
+ peerServer.handleRoomCreatedEvent(e);
+ break;
+ case 'usj':
+ peerServer.handleUserJoinedEvent(e);
+ break;
+ case 'usa':
+ peerServer.handleUserAddedEvent(e);
+ break;
+ case 'usl':
+ peerServer.handleUserLeftEvent(e);
+ break;
+ case 'usr':
+ peerServer.handleUserRemovedEvent(e);
+ break;
+ case 'usm':
+ peerServer.handleUserMutedEvent(e);
+ break;
+ case 'usu':
+ peerServer.handleUserUnmutedEvent(e);
+ break;
+ case 'msc':
+ peerServer.handleMessageCreatedEvent(e);
+ break;
+ case 'msu':
+ peerServer.handleMessageUpdatedEvent(e);
+ break;
+ case 'msd':
+ peerServer.handleMessageDeletedEvent(e);
+ break;
+ case 'msr':
+ peerServer.handleMessagesReadEvent(e);
+ break;
+ case 'mrs':
+ peerServer.handleMessagesSetReactionEvent(e);
+ break;
+ case 'mru':
+ peerServer.handleMessagesUnsetReactionEvent(e);
+ break;
+ default:
+ throw new Error(`Invalid event:${ e.t }`);
+ }
+
+ peerServer.log('Success, responding...');
+
+ // Respond
+ return API.v1.success();
+ } catch (err) {
+ peerServer.log(`Error handling event:${ e.t } - ${ err.toString() }`);
+
+ return API.v1.failure(`Error handling event:${ e.t } - ${ err.toString() }`, err.error || 'unknown-error');
+ }
+ },
+});
diff --git a/packages/rocketchat-federation/server/peerServer/routes/uploads.js b/packages/rocketchat-federation/server/peerServer/routes/uploads.js
new file mode 100644
index 00000000000..4233e859fb3
--- /dev/null
+++ b/packages/rocketchat-federation/server/peerServer/routes/uploads.js
@@ -0,0 +1,28 @@
+import { Meteor } from 'meteor/meteor';
+import { API } from 'meteor/rocketchat:api';
+import { Uploads } from 'meteor/rocketchat:models';
+import { FileUpload } from 'meteor/rocketchat:file-upload';
+
+import peerServer from '../peerServer';
+
+API.v1.addRoute('federation.uploads', { authRequired: false }, {
+ get() {
+ if (!peerServer.enabled) {
+ return API.v1.failure('Not found');
+ }
+
+ const { upload_id } = this.requestParams();
+
+ const upload = Uploads.findOneById(upload_id);
+
+ if (!upload) {
+ return API.v1.failure('There is no such file in this server');
+ }
+
+ const getFileBuffer = Meteor.wrapAsync(FileUpload.getBuffer, FileUpload);
+
+ const buffer = getFileBuffer(upload);
+
+ return API.v1.success({ upload, buffer });
+ },
+});
diff --git a/packages/rocketchat-federation/server/peerServer/routes/users.js b/packages/rocketchat-federation/server/peerServer/routes/users.js
new file mode 100644
index 00000000000..f81731d2cb3
--- /dev/null
+++ b/packages/rocketchat-federation/server/peerServer/routes/users.js
@@ -0,0 +1,49 @@
+import { API } from 'meteor/rocketchat:api';
+import { Users } from 'meteor/rocketchat:models';
+
+import { FederatedUser } from '../../federatedResources';
+import peerServer from '../peerServer';
+
+API.v1.addRoute('federation.users', { authRequired: false }, {
+ get() {
+ if (!peerServer.enabled) {
+ return API.v1.failure('Not found');
+ }
+
+ const { peer: { domain: localPeerDomain } } = peerServer.config;
+
+ const { username, domain, emailOnly } = this.requestParams();
+
+ const email = `${ username }@${ domain }`;
+
+ peerServer.log(`[users] Trying to find user by username:${ username } and email:${ email }`);
+
+ const query = {
+ type: 'user',
+ };
+
+ if (emailOnly === 'true') {
+ query['emails.address'] = email;
+ } else {
+ query.$or = [
+ { name: username },
+ { username },
+ { 'emails.address': email },
+ ];
+ }
+
+ const users = Users.find(query, { fields: { services: 0, roles: 0 } }).fetch();
+
+ if (!users.length) {
+ return API.v1.failure('There is no such user in this server');
+ }
+
+ const federatedUsers = [];
+
+ for (const user of users) {
+ federatedUsers.push(new FederatedUser(localPeerDomain, user));
+ }
+
+ return API.v1.success({ federatedUsers });
+ },
+});
diff --git a/packages/rocketchat-federation/server/settingsUpdater.js b/packages/rocketchat-federation/server/settingsUpdater.js
new file mode 100644
index 00000000000..81d96f16045
--- /dev/null
+++ b/packages/rocketchat-federation/server/settingsUpdater.js
@@ -0,0 +1,17 @@
+import { Settings } from 'meteor/rocketchat:models';
+
+let nextStatus;
+
+export function updateStatus(status) {
+ Settings.updateValueById('FEDERATION_Status', nextStatus || status);
+
+ nextStatus = null;
+}
+
+export function updateNextStatusTo(status) {
+ nextStatus = status;
+}
+
+export function updateEnabled(enabled) {
+ Settings.updateValueById('FEDERATION_Enabled', enabled);
+}
diff --git a/packages/rocketchat-file-upload/lib/FileUpload.js b/packages/rocketchat-file-upload/lib/FileUpload.js
index 82e628f626d..b7abf01991a 100644
--- a/packages/rocketchat-file-upload/lib/FileUpload.js
+++ b/packages/rocketchat-file-upload/lib/FileUpload.js
@@ -14,8 +14,10 @@ export const FileUpload = {
if (!Match.test(file.rid, String)) {
return false;
}
+
// livechat users can upload files but they don't have an userId
- const user = file.userId ? Meteor.user() : null;
+ const user = file.userId ? Meteor.users.findOne(file.userId) : null;
+
const room = Rooms.findOneById(file.rid);
const directMessageAllow = settings.get('FileUpload_Enabled_Direct');
const fileUploadAllowed = settings.get('FileUpload_Enabled');
diff --git a/packages/rocketchat-file-upload/server/lib/FileUpload.js b/packages/rocketchat-file-upload/server/lib/FileUpload.js
index da6a7358d8a..61360bb3170 100644
--- a/packages/rocketchat-file-upload/server/lib/FileUpload.js
+++ b/packages/rocketchat-file-upload/server/lib/FileUpload.js
@@ -1,6 +1,7 @@
import { Meteor } from 'meteor/meteor';
import fs from 'fs';
import stream from 'stream';
+import streamBuffers from 'stream-buffers';
import mime from 'mime-type/with-db';
import Future from 'fibers/future';
import sharp from 'sharp';
@@ -274,6 +275,22 @@ export const FileUpload = Object.assign(_FileUpload, {
res.end();
},
+ getBuffer(file, cb) {
+ const store = this.getStoreByName(file.store);
+
+ if (!store || !store.get) { cb(new Error('Store is invalid'), null); }
+
+ const buffer = new streamBuffers.WritableStreamBuffer({
+ initialSize: file.size,
+ });
+
+ buffer.on('finish', () => {
+ cb(null, buffer.getContents());
+ });
+
+ store.copy(file, buffer);
+ },
+
copy(file, targetFile) {
const store = this.getStoreByName(file.store);
const out = fs.createWriteStream(targetFile);
diff --git a/packages/rocketchat-i18n/i18n/en.i18n.json b/packages/rocketchat-i18n/i18n/en.i18n.json
index d9bbff22ef9..800e8d2e4db 100644
--- a/packages/rocketchat-i18n/i18n/en.i18n.json
+++ b/packages/rocketchat-i18n/i18n/en.i18n.json
@@ -1250,6 +1250,7 @@
"every_hour": "Once every hour",
"every_six_hours": "Once every six hours",
"every_day": "Once every day",
+ "Every_Workspace": "Every Workspace",
"Everyone_can_access_this_channel": "Everyone can access this channel",
"Example_s": "Example: %s",
"Exclude_Botnames": "Exclude Bots",
@@ -1270,6 +1271,20 @@
"Favorites": "Favorites",
"Feature_Depends_on_Livechat_Visitor_navigation_as_a_message_to_be_enabled": "This feature depends on \"Send Visitor Navigation History as a Message\" to be enabled.",
"Features_Enabled": "Features Enabled",
+ "FEDERATION_Enabled": "Attempt to integrate federation support. Changing this value requires restarting Rocket.Chat.",
+ "FEDERATION_Enabled_Alert": "Federation Support is a work in progress. Use on a production system is not recommended at this time.",
+ "FEDERATION_Discovery_Method": "Discovery Method",
+ "FEDERATION_Discovery_Method_Description": "You can use the hub or a SRV and a TXT entry on your DNS records.",
+ "FEDERATION_Domain": "Domain",
+ "FEDERATION_Domain_Description": "Add the domain that this server should be linked to - for example: @rocket.chat.",
+ "FEDERATION_Domain_Alert": "Do not change this after enabling the feature, we can't handle domain changes yet.",
+ "FEDERATION_Public_Key": "Public Key",
+ "FEDERATION_Public_Key_Description": "This is the key you need to share with your peers.",
+ "FEDERATION_Hub_URL": "Hub URL",
+ "FEDERATION_Hub_URL_Description": "Set the hub URL, for example: https://hub.rocket.chat. Ports are accepted as well.",
+ "FEDERATION_Unique_Id": "Unique ID",
+ "FEDERATION_Unique_Id_Description": "This is your federation unique ID, used to identify your peer on the mesh.",
+ "FEDERATION_Status": "Status",
"Field": "Field",
"Field_removed": "Field removed",
"Field_required": "Field required",
@@ -1816,6 +1831,7 @@
"Loading_more_from_history": "Loading more from history",
"Loading_suggestion": "Loading suggestions",
"Local_Password": "Local Password",
+ "Local_Workspace": "Local Workspace",
"Localization": "Localization",
"Log_Exceptions_to_Channel_Description": "A channel that will receive all captured exceptions. Leave empty to ignore exceptions.",
"Log_Exceptions_to_Channel": "Log Exceptions to Channel",
@@ -2692,6 +2708,7 @@
"The_emails_are_being_sent": "The emails are being sent.",
"The_field_is_required": "The field %s is required.",
"The_image_resize_will_not_work_because_we_can_not_detect_ImageMagick_or_GraphicsMagick_installed_in_your_server": "The image resize will not work because we can not detect ImageMagick or GraphicsMagick installed on your server.",
+ "The_peer__peer__does_not_exist": "The peer __peer__ does not exist.",
"The_redirectUri_is_required": "The redirectUri is required",
"The_server_will_restart_in_s_seconds": "The server will restart in %s seconds",
"The_setting_s_is_configured_to_s_and_you_are_accessing_from_s": "The setting %s is configured to %s and you are accessing from %s!",
@@ -2760,6 +2777,7 @@
"This_email_has_already_been_used_and_has_not_been_verified__Please_change_your_password": "This email has already been used and has not been verified. Please change your password.",
"This_is_a_desktop_notification": "This is a desktop notification",
"This_is_a_push_test_messsage": "This is a push test message",
+ "This_message_was_rejected_by__peer__peer": "This message was rejected by __peer__ peer.",
"This_month": "This Month",
"This_room_has_been_archived_by__username_": "This room has been archived by __username__",
"This_room_has_been_unarchived_by__username_": "This room has been unarchived by __username__",
diff --git a/packages/rocketchat-lib/server/functions/addUserToRoom.js b/packages/rocketchat-lib/server/functions/addUserToRoom.js
index 9efa0602d8b..2dc06448e91 100644
--- a/packages/rocketchat-lib/server/functions/addUserToRoom.js
+++ b/packages/rocketchat-lib/server/functions/addUserToRoom.js
@@ -14,6 +14,10 @@ export const addUserToRoom = function(rid, user, inviter, silenced) {
}
if (room.t === 'c' || room.t === 'p') {
+ // Add a new event, with an optional inviter
+ callbacks.run('beforeAddedToRoom', { user, inviter }, room);
+
+ // Keep the current event
callbacks.run('beforeJoinRoom', user, room);
}
@@ -47,6 +51,10 @@ export const addUserToRoom = function(rid, user, inviter, silenced) {
if (room.t === 'c' || room.t === 'p') {
Meteor.defer(function() {
+ // Add a new event, with an optional inviter
+ callbacks.run('afterAddedToRoom', { user, inviter }, room);
+
+ // Keep the current event
callbacks.run('afterJoinRoom', user, room);
});
}
diff --git a/packages/rocketchat-lib/server/functions/createRoom.js b/packages/rocketchat-lib/server/functions/createRoom.js
index 86a75ac1e64..80daa229fcb 100644
--- a/packages/rocketchat-lib/server/functions/createRoom.js
+++ b/packages/rocketchat-lib/server/functions/createRoom.js
@@ -7,7 +7,57 @@ import { Apps } from 'meteor/rocketchat:apps';
import _ from 'underscore';
import s from 'underscore.string';
-export const createRoom = function(type, name, owner, members, readOnly, extraData = {}) {
+function createDirectRoom(source, target, extraData, options) {
+ const rid = [source._id, target._id].sort().join('');
+
+ Rooms.upsert({ _id: rid }, {
+ $setOnInsert: Object.assign({
+ t: 'd',
+ usernames: [source.username, target.username],
+ msgs: 0,
+ ts: new Date(),
+ }, extraData),
+ });
+
+ Subscriptions.upsert({ rid, 'u._id': target._id }, {
+ $setOnInsert: Object.assign({
+ name: source.username,
+ t: 'd',
+ open: true,
+ alert: true,
+ unread: 0,
+ u: {
+ _id: target._id,
+ username: target.username,
+ },
+ }, options.subscriptionExtra),
+ });
+
+ Subscriptions.upsert({ rid, 'u._id': source._id }, {
+ $setOnInsert: Object.assign({
+ name: target.username,
+ t: 'd',
+ open: true,
+ alert: true,
+ unread: 0,
+ u: {
+ _id: source._id,
+ username: source.username,
+ },
+ }, options.subscriptionExtra),
+ });
+
+ return {
+ _id: rid,
+ t: 'd',
+ };
+}
+
+export const createRoom = function(type, name, owner, members, readOnly, extraData = {}, options = {}) {
+ if (type === 'd') {
+ return createDirectRoom(members[0], members[1], extraData, options);
+ }
+
name = s.trim(name);
owner = s.trim(owner);
members = [].concat(members);
@@ -31,8 +81,15 @@ export const createRoom = function(type, name, owner, members, readOnly, extraDa
}
const now = new Date();
+
+ const validRoomNameOptions = {};
+
+ if (options.nameValidationRegex) {
+ validRoomNameOptions.nameValidationRegex = options.nameValidationRegex;
+ }
+
let room = Object.assign({
- name: getValidRoomName(name),
+ name: getValidRoomName(name, null, validRoomNameOptions),
fname: name,
t: type,
msgs: 0,
@@ -84,7 +141,9 @@ export const createRoom = function(type, name, owner, members, readOnly, extraDa
Rooms.muteUsernameByRoomId(room._id, username);
}
- const extra = { open: true };
+ const extra = options.subscriptionExtra || {};
+
+ extra.open = true;
if (username === owner.username) {
extra.ls = now;
diff --git a/packages/rocketchat-lib/server/functions/removeUserFromRoom.js b/packages/rocketchat-lib/server/functions/removeUserFromRoom.js
index 6c623e53aa3..07fbdb675a8 100644
--- a/packages/rocketchat-lib/server/functions/removeUserFromRoom.js
+++ b/packages/rocketchat-lib/server/functions/removeUserFromRoom.js
@@ -2,7 +2,7 @@ import { Meteor } from 'meteor/meteor';
import { Rooms, Messages, Subscriptions } from 'meteor/rocketchat:models';
import { callbacks } from 'meteor/rocketchat:callbacks';
-export const removeUserFromRoom = function(rid, user) {
+export const removeUserFromRoom = function(rid, user, options = {}) {
const room = Rooms.findOneById(rid);
if (room) {
@@ -12,7 +12,13 @@ export const removeUserFromRoom = function(rid, user) {
if (subscription) {
const removedUser = user;
- Messages.createUserLeaveWithRoomIdAndUser(rid, removedUser);
+ if (options.byUser) {
+ Messages.createUserRemovedWithRoomIdAndUser(rid, user, {
+ u: options.byUser,
+ });
+ } else {
+ Messages.createUserLeaveWithRoomIdAndUser(rid, removedUser);
+ }
}
if (room.t === 'l') {
diff --git a/packages/rocketchat-livechat/.app/package-lock.json b/packages/rocketchat-livechat/.app/package-lock.json
index 5330eff4f5d..db363ce6d51 100644
--- a/packages/rocketchat-livechat/.app/package-lock.json
+++ b/packages/rocketchat-livechat/.app/package-lock.json
@@ -12,24 +12,6 @@
"regenerator-runtime": "^0.12.0"
}
},
- "asn1.js": {
- "version": "4.10.1",
- "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-4.10.1.tgz",
- "integrity": "sha512-p32cOF5q0Zqs9uBiONKYLm6BClCoBCM5O9JfeUSlnQLBTxYdTK+pW+nXflm8UkKd2UYlEbYz5qEi0JuZR9ckSw==",
- "requires": {
- "bn.js": "^4.0.0",
- "inherits": "^2.0.1",
- "minimalistic-assert": "^1.0.0"
- }
- },
- "assert": {
- "version": "1.4.1",
- "resolved": "https://registry.npmjs.org/assert/-/assert-1.4.1.tgz",
- "integrity": "sha1-mZEtWRg2tab1s0XA8H7vwI/GXZE=",
- "requires": {
- "util": "0.10.3"
- }
- },
"autolinker": {
"version": "1.8.1",
"resolved": "https://registry.npmjs.org/autolinker/-/autolinker-1.8.1.tgz",
@@ -40,11 +22,6 @@
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
"integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c="
},
- "base64-js": {
- "version": "1.3.0",
- "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.0.tgz",
- "integrity": "sha512-ccav/yGvoa80BQDljCxsmmQ3Xvx60/UpBIij5QN21W3wBi/hhIC9OoO+KLpu9IJTS9j4DRVJ3aDDF9cMSoa2lw=="
- },
"bcrypt": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/bcrypt/-/bcrypt-3.0.2.tgz",
@@ -475,11 +452,6 @@
}
}
},
- "bn.js": {
- "version": "4.11.8",
- "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz",
- "integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA=="
- },
"brace-expansion": {
"version": "1.1.11",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
@@ -489,123 +461,11 @@
"concat-map": "0.0.1"
}
},
- "brorand": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz",
- "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8="
- },
- "browserify-aes": {
- "version": "1.2.0",
- "resolved": "http://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz",
- "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==",
- "requires": {
- "buffer-xor": "^1.0.3",
- "cipher-base": "^1.0.0",
- "create-hash": "^1.1.0",
- "evp_bytestokey": "^1.0.3",
- "inherits": "^2.0.1",
- "safe-buffer": "^5.0.1"
- }
- },
- "browserify-cipher": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/browserify-cipher/-/browserify-cipher-1.0.1.tgz",
- "integrity": "sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==",
- "requires": {
- "browserify-aes": "^1.0.4",
- "browserify-des": "^1.0.0",
- "evp_bytestokey": "^1.0.0"
- }
- },
- "browserify-des": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/browserify-des/-/browserify-des-1.0.2.tgz",
- "integrity": "sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A==",
- "requires": {
- "cipher-base": "^1.0.1",
- "des.js": "^1.0.0",
- "inherits": "^2.0.1",
- "safe-buffer": "^5.1.2"
- }
- },
- "browserify-rsa": {
- "version": "4.0.1",
- "resolved": "http://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz",
- "integrity": "sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ=",
- "requires": {
- "bn.js": "^4.1.0",
- "randombytes": "^2.0.1"
- }
- },
- "browserify-sign": {
- "version": "4.0.4",
- "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.0.4.tgz",
- "integrity": "sha1-qk62jl17ZYuqa/alfmMMvXqT0pg=",
- "requires": {
- "bn.js": "^4.1.1",
- "browserify-rsa": "^4.0.0",
- "create-hash": "^1.1.0",
- "create-hmac": "^1.1.2",
- "elliptic": "^6.0.0",
- "inherits": "^2.0.1",
- "parse-asn1": "^5.0.0"
- }
- },
- "browserify-zlib": {
- "version": "0.1.4",
- "resolved": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.1.4.tgz",
- "integrity": "sha1-uzX4pRn2AOD6a4SFJByXnQFB+y0=",
- "requires": {
- "pako": "~0.2.0"
- }
- },
- "buffer": {
- "version": "4.9.1",
- "resolved": "http://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz",
- "integrity": "sha1-bRu2AbB6TvztlwlBMgkwJ8lbwpg=",
- "requires": {
- "base64-js": "^1.0.2",
- "ieee754": "^1.1.4",
- "isarray": "^1.0.0"
- }
- },
- "buffer-xor": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz",
- "integrity": "sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk="
- },
- "builtin-status-codes": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz",
- "integrity": "sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug="
- },
- "cipher-base": {
- "version": "1.0.4",
- "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz",
- "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==",
- "requires": {
- "inherits": "^2.0.1",
- "safe-buffer": "^5.0.1"
- }
- },
"concat-map": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
"integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s="
},
- "console-browserify": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.1.0.tgz",
- "integrity": "sha1-8CQcRXMKn8YyOyBtvzjtx0HQuxA=",
- "requires": {
- "date-now": "^0.1.4"
- }
- },
- "constants-browserify": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz",
- "integrity": "sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U="
- },
"core-js": {
"version": "2.5.7",
"resolved": "https://registry.npmjs.org/core-js/-/core-js-2.5.7.tgz",
@@ -616,175 +476,11 @@
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
"integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac="
},
- "create-ecdh": {
- "version": "4.0.3",
- "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.3.tgz",
- "integrity": "sha512-GbEHQPMOswGpKXM9kCWVrremUcBmjteUaQ01T9rkKCPDXfUHX0IoP9LpHYo2NPFampa4e+/pFDc3jQdxrxQLaw==",
- "requires": {
- "bn.js": "^4.1.0",
- "elliptic": "^6.0.0"
- }
- },
- "create-hash": {
- "version": "1.2.0",
- "resolved": "http://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz",
- "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==",
- "requires": {
- "cipher-base": "^1.0.1",
- "inherits": "^2.0.1",
- "md5.js": "^1.3.4",
- "ripemd160": "^2.0.1",
- "sha.js": "^2.4.0"
- }
- },
- "create-hmac": {
- "version": "1.1.7",
- "resolved": "http://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz",
- "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==",
- "requires": {
- "cipher-base": "^1.0.3",
- "create-hash": "^1.1.0",
- "inherits": "^2.0.1",
- "ripemd160": "^2.0.0",
- "safe-buffer": "^5.0.1",
- "sha.js": "^2.4.8"
- }
- },
- "crypto-browserify": {
- "version": "3.12.0",
- "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz",
- "integrity": "sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==",
- "requires": {
- "browserify-cipher": "^1.0.0",
- "browserify-sign": "^4.0.0",
- "create-ecdh": "^4.0.0",
- "create-hash": "^1.1.0",
- "create-hmac": "^1.1.0",
- "diffie-hellman": "^5.0.0",
- "inherits": "^2.0.1",
- "pbkdf2": "^3.0.3",
- "public-encrypt": "^4.0.0",
- "randombytes": "^2.0.0",
- "randomfill": "^1.0.3"
- }
- },
- "date-now": {
- "version": "0.1.4",
- "resolved": "https://registry.npmjs.org/date-now/-/date-now-0.1.4.tgz",
- "integrity": "sha1-6vQ5/U1ISK105cx9vvIAZyueNFs="
- },
- "des.js": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.0.tgz",
- "integrity": "sha1-wHTS4qpqipoH29YfmhXCzYPsjsw=",
- "requires": {
- "inherits": "^2.0.1",
- "minimalistic-assert": "^1.0.0"
- }
- },
- "diffie-hellman": {
- "version": "5.0.3",
- "resolved": "http://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz",
- "integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==",
- "requires": {
- "bn.js": "^4.1.0",
- "miller-rabin": "^4.0.0",
- "randombytes": "^2.0.0"
- }
- },
- "domain-browser": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.2.0.tgz",
- "integrity": "sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA=="
- },
- "elliptic": {
- "version": "6.4.1",
- "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.4.1.tgz",
- "integrity": "sha512-BsXLz5sqX8OHcsh7CqBMztyXARmGQ3LWPtGjJi6DiJHq5C/qvi9P3OqgswKSDftbu8+IoI/QDTAm2fFnQ9SZSQ==",
- "requires": {
- "bn.js": "^4.4.0",
- "brorand": "^1.0.1",
- "hash.js": "^1.0.0",
- "hmac-drbg": "^1.0.0",
- "inherits": "^2.0.1",
- "minimalistic-assert": "^1.0.0",
- "minimalistic-crypto-utils": "^1.0.0"
- }
- },
"escape-string-regexp": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
"integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ="
},
- "events": {
- "version": "1.1.1",
- "resolved": "http://registry.npmjs.org/events/-/events-1.1.1.tgz",
- "integrity": "sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ="
- },
- "evp_bytestokey": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz",
- "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==",
- "requires": {
- "md5.js": "^1.3.4",
- "safe-buffer": "^5.1.1"
- }
- },
- "hash-base": {
- "version": "3.0.4",
- "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.0.4.tgz",
- "integrity": "sha1-X8hoaEfs1zSZQDMZprCj8/auSRg=",
- "requires": {
- "inherits": "^2.0.1",
- "safe-buffer": "^5.0.1"
- }
- },
- "hash.js": {
- "version": "1.1.7",
- "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz",
- "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==",
- "requires": {
- "inherits": "^2.0.3",
- "minimalistic-assert": "^1.0.1"
- },
- "dependencies": {
- "inherits": {
- "version": "2.0.3",
- "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
- "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4="
- }
- }
- },
- "hmac-drbg": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz",
- "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=",
- "requires": {
- "hash.js": "^1.0.3",
- "minimalistic-assert": "^1.0.0",
- "minimalistic-crypto-utils": "^1.0.1"
- }
- },
- "https-browserify": {
- "version": "0.0.1",
- "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-0.0.1.tgz",
- "integrity": "sha1-P5E2XKvmC3ftDruiS0VOPgnZWoI="
- },
- "ieee754": {
- "version": "1.1.12",
- "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.12.tgz",
- "integrity": "sha512-GguP+DRY+pJ3soyIiGPTvdiVXjZ+DbXOxGpXn3eMvNW4x4irjqXm4wHKscC+TfxSJ0yw/S1F24tqdMNsMZTiLA=="
- },
- "indexof": {
- "version": "0.0.1",
- "resolved": "https://registry.npmjs.org/indexof/-/indexof-0.0.1.tgz",
- "integrity": "sha1-gtwzbSMrkGIXnQWrMpOmYFn9Q10="
- },
- "inherits": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz",
- "integrity": "sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE="
- },
"inherits-ex": {
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/inherits-ex/-/inherits-ex-1.2.3.tgz",
@@ -793,26 +489,11 @@
"xtend": "^4.0.0"
}
},
- "isarray": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
- "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE="
- },
"jquery": {
"version": "3.3.1",
"resolved": "https://registry.npmjs.org/jquery/-/jquery-3.3.1.tgz",
"integrity": "sha512-Ubldcmxp5np52/ENotGxlLe6aGMvmF4R8S6tZjsP6Knsaxd/xp3Zrh50cG93lR6nPXyUFwzN3ZSOQI0wRJNdGg=="
},
- "md5.js": {
- "version": "1.3.5",
- "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz",
- "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==",
- "requires": {
- "hash-base": "^3.0.0",
- "inherits": "^2.0.1",
- "safe-buffer": "^5.1.2"
- }
- },
"media-typer": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
@@ -848,11 +529,39 @@
"vm-browserify": "0.0.4"
},
"dependencies": {
+ "asn1.js": {
+ "version": "4.10.1",
+ "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-4.10.1.tgz",
+ "integrity": "sha512-p32cOF5q0Zqs9uBiONKYLm6BClCoBCM5O9JfeUSlnQLBTxYdTK+pW+nXflm8UkKd2UYlEbYz5qEi0JuZR9ckSw==",
+ "requires": {
+ "bn.js": "^4.0.0",
+ "inherits": "^2.0.1",
+ "minimalistic-assert": "^1.0.0"
+ }
+ },
+ "assert": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/assert/-/assert-1.4.1.tgz",
+ "integrity": "sha1-mZEtWRg2tab1s0XA8H7vwI/GXZE=",
+ "requires": {
+ "util": "0.10.3"
+ }
+ },
"balanced-match": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
"integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c="
},
+ "base64-js": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.2.3.tgz",
+ "integrity": "sha512-MsAhsUW1GxCdgYSO6tAfZrNapmUKk7mWx/k5mFY/A1gBtkaCaNapTg+FExCw1r9yeaZhqx/xPg43xgTFH6KL5w=="
+ },
+ "bn.js": {
+ "version": "4.11.8",
+ "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz",
+ "integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA=="
+ },
"brace-expansion": {
"version": "1.1.11",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
@@ -862,11 +571,230 @@
"concat-map": "0.0.1"
}
},
+ "brorand": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz",
+ "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8="
+ },
+ "browserify-aes": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.1.1.tgz",
+ "integrity": "sha512-UGnTYAnB2a3YuYKIRy1/4FB2HdM866E0qC46JXvVTYKlBlZlnvfpSfY6OKfXZAkv70eJ2a1SqzpAo5CRhZGDFg==",
+ "requires": {
+ "buffer-xor": "^1.0.3",
+ "cipher-base": "^1.0.0",
+ "create-hash": "^1.1.0",
+ "evp_bytestokey": "^1.0.3",
+ "inherits": "^2.0.1",
+ "safe-buffer": "^5.0.1"
+ }
+ },
+ "browserify-cipher": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/browserify-cipher/-/browserify-cipher-1.0.0.tgz",
+ "integrity": "sha1-mYgkSHS/XtTijalWZtzWasj8Njo=",
+ "requires": {
+ "browserify-aes": "^1.0.4",
+ "browserify-des": "^1.0.0",
+ "evp_bytestokey": "^1.0.0"
+ }
+ },
+ "browserify-des": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/browserify-des/-/browserify-des-1.0.0.tgz",
+ "integrity": "sha1-2qJ3cXRwki7S/hhZQRihdUOXId0=",
+ "requires": {
+ "cipher-base": "^1.0.1",
+ "des.js": "^1.0.0",
+ "inherits": "^2.0.1"
+ }
+ },
+ "browserify-rsa": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz",
+ "integrity": "sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ=",
+ "requires": {
+ "bn.js": "^4.1.0",
+ "randombytes": "^2.0.1"
+ }
+ },
+ "browserify-sign": {
+ "version": "4.0.4",
+ "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.0.4.tgz",
+ "integrity": "sha1-qk62jl17ZYuqa/alfmMMvXqT0pg=",
+ "requires": {
+ "bn.js": "^4.1.1",
+ "browserify-rsa": "^4.0.0",
+ "create-hash": "^1.1.0",
+ "create-hmac": "^1.1.2",
+ "elliptic": "^6.0.0",
+ "inherits": "^2.0.1",
+ "parse-asn1": "^5.0.0"
+ }
+ },
+ "browserify-zlib": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.1.4.tgz",
+ "integrity": "sha1-uzX4pRn2AOD6a4SFJByXnQFB+y0=",
+ "requires": {
+ "pako": "~0.2.0"
+ }
+ },
+ "buffer": {
+ "version": "4.9.1",
+ "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz",
+ "integrity": "sha1-bRu2AbB6TvztlwlBMgkwJ8lbwpg=",
+ "requires": {
+ "base64-js": "^1.0.2",
+ "ieee754": "^1.1.4",
+ "isarray": "^1.0.0"
+ }
+ },
+ "buffer-xor": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz",
+ "integrity": "sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk="
+ },
+ "builtin-status-codes": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz",
+ "integrity": "sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug="
+ },
+ "cipher-base": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz",
+ "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==",
+ "requires": {
+ "inherits": "^2.0.1",
+ "safe-buffer": "^5.0.1"
+ }
+ },
"concat-map": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
"integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s="
},
+ "console-browserify": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.1.0.tgz",
+ "integrity": "sha1-8CQcRXMKn8YyOyBtvzjtx0HQuxA=",
+ "requires": {
+ "date-now": "^0.1.4"
+ }
+ },
+ "constants-browserify": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz",
+ "integrity": "sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U="
+ },
+ "create-ecdh": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.0.tgz",
+ "integrity": "sha1-iIxyNZbN92EvZJgjPuvXo1MBc30=",
+ "requires": {
+ "bn.js": "^4.1.0",
+ "elliptic": "^6.0.0"
+ }
+ },
+ "create-hash": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.1.3.tgz",
+ "integrity": "sha1-YGBCrIuSYnUPSDyt2rD1gZFy2P0=",
+ "requires": {
+ "cipher-base": "^1.0.1",
+ "inherits": "^2.0.1",
+ "ripemd160": "^2.0.0",
+ "sha.js": "^2.4.0"
+ }
+ },
+ "create-hmac": {
+ "version": "1.1.6",
+ "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.6.tgz",
+ "integrity": "sha1-rLniIaThe9sHbpBlfEK5PjcmzwY=",
+ "requires": {
+ "cipher-base": "^1.0.3",
+ "create-hash": "^1.1.0",
+ "inherits": "^2.0.1",
+ "ripemd160": "^2.0.0",
+ "safe-buffer": "^5.0.1",
+ "sha.js": "^2.4.8"
+ }
+ },
+ "crypto-browserify": {
+ "version": "3.12.0",
+ "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz",
+ "integrity": "sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==",
+ "requires": {
+ "browserify-cipher": "^1.0.0",
+ "browserify-sign": "^4.0.0",
+ "create-ecdh": "^4.0.0",
+ "create-hash": "^1.1.0",
+ "create-hmac": "^1.1.0",
+ "diffie-hellman": "^5.0.0",
+ "inherits": "^2.0.1",
+ "pbkdf2": "^3.0.3",
+ "public-encrypt": "^4.0.0",
+ "randombytes": "^2.0.0",
+ "randomfill": "^1.0.3"
+ }
+ },
+ "date-now": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/date-now/-/date-now-0.1.4.tgz",
+ "integrity": "sha1-6vQ5/U1ISK105cx9vvIAZyueNFs="
+ },
+ "des.js": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.0.tgz",
+ "integrity": "sha1-wHTS4qpqipoH29YfmhXCzYPsjsw=",
+ "requires": {
+ "inherits": "^2.0.1",
+ "minimalistic-assert": "^1.0.0"
+ }
+ },
+ "diffie-hellman": {
+ "version": "5.0.2",
+ "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.2.tgz",
+ "integrity": "sha1-tYNXOScM/ias9jIJn97SoH8gnl4=",
+ "requires": {
+ "bn.js": "^4.1.0",
+ "miller-rabin": "^4.0.0",
+ "randombytes": "^2.0.0"
+ }
+ },
+ "domain-browser": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.2.0.tgz",
+ "integrity": "sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA=="
+ },
+ "elliptic": {
+ "version": "6.4.0",
+ "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.4.0.tgz",
+ "integrity": "sha1-ysmvh2LIWDYYcAPI3+GT5eLq5d8=",
+ "requires": {
+ "bn.js": "^4.4.0",
+ "brorand": "^1.0.1",
+ "hash.js": "^1.0.0",
+ "hmac-drbg": "^1.0.0",
+ "inherits": "^2.0.1",
+ "minimalistic-assert": "^1.0.0",
+ "minimalistic-crypto-utils": "^1.0.0"
+ }
+ },
+ "events": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz",
+ "integrity": "sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ="
+ },
+ "evp_bytestokey": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz",
+ "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==",
+ "requires": {
+ "md5.js": "^1.3.4",
+ "safe-buffer": "^5.1.1"
+ }
+ },
"fs.realpath": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
@@ -885,6 +813,55 @@
"path-is-absolute": "^1.0.0"
}
},
+ "hash-base": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-2.0.2.tgz",
+ "integrity": "sha1-ZuodhW206KVHDK32/OI65SRO8uE=",
+ "requires": {
+ "inherits": "^2.0.1"
+ }
+ },
+ "hash.js": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.3.tgz",
+ "integrity": "sha512-/UETyP0W22QILqS+6HowevwhEFJ3MBJnwTf75Qob9Wz9t0DPuisL8kW8YZMK62dHAKE1c1p+gY1TtOLY+USEHA==",
+ "requires": {
+ "inherits": "^2.0.3",
+ "minimalistic-assert": "^1.0.0"
+ },
+ "dependencies": {
+ "inherits": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
+ "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4="
+ }
+ }
+ },
+ "hmac-drbg": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz",
+ "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=",
+ "requires": {
+ "hash.js": "^1.0.3",
+ "minimalistic-assert": "^1.0.0",
+ "minimalistic-crypto-utils": "^1.0.1"
+ }
+ },
+ "https-browserify": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-0.0.1.tgz",
+ "integrity": "sha1-P5E2XKvmC3ftDruiS0VOPgnZWoI="
+ },
+ "ieee754": {
+ "version": "1.1.8",
+ "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.8.tgz",
+ "integrity": "sha1-vjPUCsEO8ZJnAfbwii2G+/0a0+Q="
+ },
+ "indexof": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/indexof/-/indexof-0.0.1.tgz",
+ "integrity": "sha1-gtwzbSMrkGIXnQWrMpOmYFn9Q10="
+ },
"inflight": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
@@ -894,6 +871,55 @@
"wrappy": "1"
}
},
+ "inherits": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz",
+ "integrity": "sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE="
+ },
+ "isarray": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
+ "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE="
+ },
+ "md5.js": {
+ "version": "1.3.4",
+ "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.4.tgz",
+ "integrity": "sha1-6b296UogpawYsENA/Fdk1bCdkB0=",
+ "requires": {
+ "hash-base": "^3.0.0",
+ "inherits": "^2.0.1"
+ },
+ "dependencies": {
+ "hash-base": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.0.4.tgz",
+ "integrity": "sha1-X8hoaEfs1zSZQDMZprCj8/auSRg=",
+ "requires": {
+ "inherits": "^2.0.1",
+ "safe-buffer": "^5.0.1"
+ }
+ }
+ }
+ },
+ "miller-rabin": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz",
+ "integrity": "sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==",
+ "requires": {
+ "bn.js": "^4.0.0",
+ "brorand": "^1.0.1"
+ }
+ },
+ "minimalistic-assert": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.0.tgz",
+ "integrity": "sha1-cCvi3aazf0g2vLP121ZkG2Sh09M="
+ },
+ "minimalistic-crypto-utils": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz",
+ "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo="
+ },
"minimatch": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
@@ -910,11 +936,133 @@
"wrappy": "1"
}
},
+ "os-browserify": {
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.2.1.tgz",
+ "integrity": "sha1-Y/xMzuXS13Y9Jrv4YBB45sLgBE8="
+ },
+ "pako": {
+ "version": "0.2.9",
+ "resolved": "https://registry.npmjs.org/pako/-/pako-0.2.9.tgz",
+ "integrity": "sha1-8/dSL073gjSNqBYbrZ7P1Rv4OnU="
+ },
+ "parse-asn1": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.0.tgz",
+ "integrity": "sha1-N8T5t+06tlx0gXtfJICTf7+XxxI=",
+ "requires": {
+ "asn1.js": "^4.0.0",
+ "browserify-aes": "^1.0.0",
+ "create-hash": "^1.1.0",
+ "evp_bytestokey": "^1.0.0",
+ "pbkdf2": "^3.0.3"
+ }
+ },
+ "path-browserify": {
+ "version": "0.0.0",
+ "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-0.0.0.tgz",
+ "integrity": "sha1-oLhwcpquIUAFt9UDLsLLuw+0RRo="
+ },
"path-is-absolute": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
"integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18="
},
+ "pbkdf2": {
+ "version": "3.0.14",
+ "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.0.14.tgz",
+ "integrity": "sha512-gjsZW9O34fm0R7PaLHRJmLLVfSoesxztjPjE9o6R+qtVJij90ltg1joIovN9GKrRW3t1PzhDDG3UMEMFfZ+1wA==",
+ "requires": {
+ "create-hash": "^1.1.2",
+ "create-hmac": "^1.1.4",
+ "ripemd160": "^2.0.1",
+ "safe-buffer": "^5.0.1",
+ "sha.js": "^2.4.8"
+ }
+ },
+ "process": {
+ "version": "0.11.10",
+ "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz",
+ "integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI="
+ },
+ "process-nextick-args": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz",
+ "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw=="
+ },
+ "public-encrypt": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.0.tgz",
+ "integrity": "sha1-OfaZ86RlYN1eusvKaTyvfGXBjMY=",
+ "requires": {
+ "bn.js": "^4.1.0",
+ "browserify-rsa": "^4.0.0",
+ "create-hash": "^1.1.0",
+ "parse-asn1": "^5.0.0",
+ "randombytes": "^2.0.1"
+ }
+ },
+ "punycode": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz",
+ "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4="
+ },
+ "querystring": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz",
+ "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA="
+ },
+ "querystring-es3": {
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/querystring-es3/-/querystring-es3-0.2.1.tgz",
+ "integrity": "sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM="
+ },
+ "randombytes": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.0.6.tgz",
+ "integrity": "sha512-CIQ5OFxf4Jou6uOKe9t1AOgqpeU5fd70A8NPdHSGeYXqXsPe6peOwI0cUl88RWZ6sP1vPMV3avd/R6cZ5/sP1A==",
+ "requires": {
+ "safe-buffer": "^5.1.0"
+ }
+ },
+ "randomfill": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/randomfill/-/randomfill-1.0.4.tgz",
+ "integrity": "sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==",
+ "requires": {
+ "randombytes": "^2.0.5",
+ "safe-buffer": "^5.1.0"
+ }
+ },
+ "readable-stream": {
+ "version": "2.3.6",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
+ "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
+ "requires": {
+ "core-util-is": "~1.0.0",
+ "inherits": "~2.0.3",
+ "isarray": "~1.0.0",
+ "process-nextick-args": "~2.0.0",
+ "safe-buffer": "~5.1.1",
+ "string_decoder": "~1.1.1",
+ "util-deprecate": "~1.0.1"
+ },
+ "dependencies": {
+ "inherits": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
+ "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4="
+ },
+ "string_decoder": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
+ "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
+ "requires": {
+ "safe-buffer": "~5.1.0"
+ }
+ }
+ }
+ },
"rimraf": {
"version": "2.6.2",
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz",
@@ -923,22 +1071,125 @@
"glob": "^7.0.5"
}
},
+ "ripemd160": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.1.tgz",
+ "integrity": "sha1-D0WEKVxTo2KK9+bXmsohzlfRxuc=",
+ "requires": {
+ "hash-base": "^2.0.0",
+ "inherits": "^2.0.1"
+ }
+ },
+ "safe-buffer": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz",
+ "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg=="
+ },
+ "sha.js": {
+ "version": "2.4.10",
+ "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.10.tgz",
+ "integrity": "sha512-vnwmrFDlOExK4Nm16J2KMWHLrp14lBrjxMxBJpu++EnsuBmpiYaM/MEs46Vxxm/4FvdP5yTwuCTO9it5FSjrqA==",
+ "requires": {
+ "inherits": "^2.0.1",
+ "safe-buffer": "^5.0.1"
+ }
+ },
+ "stream-browserify": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-2.0.1.tgz",
+ "integrity": "sha1-ZiZu5fm9uZQKTkUUyvtDu3Hlyds=",
+ "requires": {
+ "inherits": "~2.0.1",
+ "readable-stream": "^2.0.2"
+ }
+ },
+ "stream-http": {
+ "version": "2.8.0",
+ "resolved": "https://registry.npmjs.org/stream-http/-/stream-http-2.8.0.tgz",
+ "integrity": "sha512-sZOFxI/5xw058XIRHl4dU3dZ+TTOIGJR78Dvo0oEAejIt4ou27k+3ne1zYmCV+v7UucbxIFQuOgnkTVHh8YPnw==",
+ "requires": {
+ "builtin-status-codes": "^3.0.0",
+ "inherits": "^2.0.1",
+ "readable-stream": "^2.3.3",
+ "to-arraybuffer": "^1.0.0",
+ "xtend": "^4.0.0"
+ }
+ },
+ "string_decoder": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.0.tgz",
+ "integrity": "sha512-8zQpRF6juocE69ae7CSPmYEGJe4VCXwP6S6dxUWI7i53Gwv54/ec41fiUA+X7BPGGv7fRSQJjBQVa0gomGaOgg==",
+ "requires": {
+ "safe-buffer": "~5.1.0"
+ }
+ },
+ "timers-browserify": {
+ "version": "1.4.2",
+ "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-1.4.2.tgz",
+ "integrity": "sha1-ycWLV1voQHN1y14kYtrO50NZ9B0=",
+ "requires": {
+ "process": "~0.11.0"
+ }
+ },
+ "to-arraybuffer": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz",
+ "integrity": "sha1-fSKbH8xjfkZsoIEYCDanqr/4P0M="
+ },
+ "tty-browserify": {
+ "version": "0.0.0",
+ "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz",
+ "integrity": "sha1-oVe6QC2iTpv5V/mqadUk7tQpAaY="
+ },
+ "url": {
+ "version": "0.11.0",
+ "resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz",
+ "integrity": "sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE=",
+ "requires": {
+ "punycode": "1.3.2",
+ "querystring": "0.2.0"
+ },
+ "dependencies": {
+ "punycode": {
+ "version": "1.3.2",
+ "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz",
+ "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0="
+ }
+ }
+ },
+ "util": {
+ "version": "0.10.3",
+ "resolved": "https://registry.npmjs.org/util/-/util-0.10.3.tgz",
+ "integrity": "sha1-evsa/lCAUkZInj23/g7TeTNqwPk=",
+ "requires": {
+ "inherits": "2.0.1"
+ }
+ },
+ "util-deprecate": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
+ "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8="
+ },
+ "vm-browserify": {
+ "version": "0.0.4",
+ "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-0.0.4.tgz",
+ "integrity": "sha1-XX6kW7755Kb/ZflUOOCofDV9WnM=",
+ "requires": {
+ "indexof": "0.0.1"
+ }
+ },
"wrappy": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
+ },
+ "xtend": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz",
+ "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68="
}
}
},
- "miller-rabin": {
- "version": "4.0.1",
- "resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz",
- "integrity": "sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==",
- "requires": {
- "bn.js": "^4.0.0",
- "brorand": "^1.0.1"
- }
- },
"mime-db": {
"version": "1.37.0",
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.37.0.tgz",
@@ -955,16 +1206,6 @@
"util-ex": "^0.3.15"
}
},
- "minimalistic-assert": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz",
- "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A=="
- },
- "minimalistic-crypto-utils": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz",
- "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo="
- },
"minimatch": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
@@ -983,33 +1224,6 @@
"resolved": "https://registry.npmjs.org/nan/-/nan-2.11.1.tgz",
"integrity": "sha512-iji6k87OSXa0CcrLl9z+ZiYSuR2o+c0bGuNmXdrhTQTakxytAFsC56SArGYoiHlJlFoHSnvmhpceZJaXkVuOtA=="
},
- "os-browserify": {
- "version": "0.2.1",
- "resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.2.1.tgz",
- "integrity": "sha1-Y/xMzuXS13Y9Jrv4YBB45sLgBE8="
- },
- "pako": {
- "version": "0.2.9",
- "resolved": "http://registry.npmjs.org/pako/-/pako-0.2.9.tgz",
- "integrity": "sha1-8/dSL073gjSNqBYbrZ7P1Rv4OnU="
- },
- "parse-asn1": {
- "version": "5.1.1",
- "resolved": "http://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.1.tgz",
- "integrity": "sha512-KPx7flKXg775zZpnp9SxJlz00gTd4BmJ2yJufSc44gMCRrRQ7NSzAcSJQfifuOLgW6bEi+ftrALtsgALeB2Adw==",
- "requires": {
- "asn1.js": "^4.0.0",
- "browserify-aes": "^1.0.0",
- "create-hash": "^1.1.0",
- "evp_bytestokey": "^1.0.0",
- "pbkdf2": "^3.0.3"
- }
- },
- "path-browserify": {
- "version": "0.0.0",
- "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-0.0.0.tgz",
- "integrity": "sha1-oLhwcpquIUAFt9UDLsLLuw+0RRo="
- },
"path.js": {
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/path.js/-/path.js-1.0.7.tgz",
@@ -1020,174 +1234,21 @@
"util-ex": "^0.3.10"
}
},
- "pbkdf2": {
- "version": "3.0.17",
- "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.0.17.tgz",
- "integrity": "sha512-U/il5MsrZp7mGg3mSQfn742na2T+1/vHDCG5/iTI3X9MKUuYUZVLQhyRsg06mCgDBTd57TxzgZt7P+fYfjRLtA==",
- "requires": {
- "create-hash": "^1.1.2",
- "create-hmac": "^1.1.4",
- "ripemd160": "^2.0.1",
- "safe-buffer": "^5.0.1",
- "sha.js": "^2.4.8"
- }
- },
- "process": {
- "version": "0.11.10",
- "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz",
- "integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI="
- },
- "process-nextick-args": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz",
- "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw=="
- },
- "public-encrypt": {
- "version": "4.0.3",
- "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz",
- "integrity": "sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==",
- "requires": {
- "bn.js": "^4.1.0",
- "browserify-rsa": "^4.0.0",
- "create-hash": "^1.1.0",
- "parse-asn1": "^5.0.0",
- "randombytes": "^2.0.1",
- "safe-buffer": "^5.1.2"
- }
- },
- "punycode": {
- "version": "1.4.1",
- "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz",
- "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4="
- },
- "querystring": {
- "version": "0.2.0",
- "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz",
- "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA="
- },
- "querystring-es3": {
- "version": "0.2.1",
- "resolved": "https://registry.npmjs.org/querystring-es3/-/querystring-es3-0.2.1.tgz",
- "integrity": "sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM="
- },
- "randombytes": {
- "version": "2.0.6",
- "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.0.6.tgz",
- "integrity": "sha512-CIQ5OFxf4Jou6uOKe9t1AOgqpeU5fd70A8NPdHSGeYXqXsPe6peOwI0cUl88RWZ6sP1vPMV3avd/R6cZ5/sP1A==",
- "requires": {
- "safe-buffer": "^5.1.0"
- }
- },
- "randomfill": {
- "version": "1.0.4",
- "resolved": "https://registry.npmjs.org/randomfill/-/randomfill-1.0.4.tgz",
- "integrity": "sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==",
- "requires": {
- "randombytes": "^2.0.5",
- "safe-buffer": "^5.1.0"
- }
- },
- "readable-stream": {
- "version": "2.3.6",
- "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
- "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
- "requires": {
- "core-util-is": "~1.0.0",
- "inherits": "~2.0.3",
- "isarray": "~1.0.0",
- "process-nextick-args": "~2.0.0",
- "safe-buffer": "~5.1.1",
- "string_decoder": "~1.1.1",
- "util-deprecate": "~1.0.1"
- },
- "dependencies": {
- "inherits": {
- "version": "2.0.3",
- "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
- "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4="
- }
- }
- },
"regenerator-runtime": {
"version": "0.12.1",
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.12.1.tgz",
"integrity": "sha512-odxIc1/vDlo4iZcfXqRYFj0vpXFNoGdKMAUieAlFYO6m/nl5e9KR/beGf41z4a1FI+aQgtjhuaSlDxQ0hmkrHg=="
},
- "ripemd160": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz",
- "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==",
- "requires": {
- "hash-base": "^3.0.0",
- "inherits": "^2.0.1"
- }
- },
- "safe-buffer": {
- "version": "5.1.2",
- "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
- "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
- },
- "sha.js": {
- "version": "2.4.11",
- "resolved": "http://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz",
- "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==",
- "requires": {
- "inherits": "^2.0.1",
- "safe-buffer": "^5.0.1"
- }
- },
"sprintf-js": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.1.tgz",
"integrity": "sha1-Nr54Mgr+WAH2zqPueLblqrlA6gw="
},
- "stream-browserify": {
- "version": "2.0.1",
- "resolved": "http://registry.npmjs.org/stream-browserify/-/stream-browserify-2.0.1.tgz",
- "integrity": "sha1-ZiZu5fm9uZQKTkUUyvtDu3Hlyds=",
- "requires": {
- "inherits": "~2.0.1",
- "readable-stream": "^2.0.2"
- }
- },
- "stream-http": {
- "version": "2.8.1",
- "resolved": "http://registry.npmjs.org/stream-http/-/stream-http-2.8.1.tgz",
- "integrity": "sha512-cQ0jo17BLca2r0GfRdZKYAGLU6JRoIWxqSOakUMuKOT6MOK7AAlE856L33QuDmAy/eeOrhLee3dZKX0Uadu93A==",
- "requires": {
- "builtin-status-codes": "^3.0.0",
- "inherits": "^2.0.1",
- "readable-stream": "^2.3.3",
- "to-arraybuffer": "^1.0.0",
- "xtend": "^4.0.0"
- }
- },
- "string_decoder": {
- "version": "1.1.1",
- "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
- "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
- "requires": {
- "safe-buffer": "~5.1.0"
- }
- },
"sweetalert2": {
"version": "7.29.2",
"resolved": "https://registry.npmjs.org/sweetalert2/-/sweetalert2-7.29.2.tgz",
"integrity": "sha512-p+Zp2ly8vf9jGlzlUOmpVDZZoRHHatTHa3H3OnXVInQKJ5HHQr5Vg8XnledhS4Iih6TdSadbdGZ8Y4gE+OUgOQ=="
},
- "timers-browserify": {
- "version": "1.4.2",
- "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-1.4.2.tgz",
- "integrity": "sha1-ycWLV1voQHN1y14kYtrO50NZ9B0=",
- "requires": {
- "process": "~0.11.0"
- }
- },
- "to-arraybuffer": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz",
- "integrity": "sha1-fSKbH8xjfkZsoIEYCDanqr/4P0M="
- },
"toastr": {
"version": "2.1.4",
"resolved": "https://registry.npmjs.org/toastr/-/toastr-2.1.4.tgz",
@@ -1196,11 +1257,6 @@
"jquery": ">=1.12.0"
}
},
- "tty-browserify": {
- "version": "0.0.0",
- "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz",
- "integrity": "sha1-oVe6QC2iTpv5V/mqadUk7tQpAaY="
- },
"underscore": {
"version": "1.9.1",
"resolved": "https://registry.npmjs.org/underscore/-/underscore-1.9.1.tgz",
@@ -1215,30 +1271,6 @@
"util-deprecate": "^1.0.2"
}
},
- "url": {
- "version": "0.11.0",
- "resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz",
- "integrity": "sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE=",
- "requires": {
- "punycode": "1.3.2",
- "querystring": "0.2.0"
- },
- "dependencies": {
- "punycode": {
- "version": "1.3.2",
- "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz",
- "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0="
- }
- }
- },
- "util": {
- "version": "0.10.3",
- "resolved": "http://registry.npmjs.org/util/-/util-0.10.3.tgz",
- "integrity": "sha1-evsa/lCAUkZInj23/g7TeTNqwPk=",
- "requires": {
- "inherits": "2.0.1"
- }
- },
"util-deprecate": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
@@ -1253,14 +1285,6 @@
"xtend": "^4.0.0"
}
},
- "vm-browserify": {
- "version": "0.0.4",
- "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-0.0.4.tgz",
- "integrity": "sha1-XX6kW7755Kb/ZflUOOCofDV9WnM=",
- "requires": {
- "indexof": "0.0.1"
- }
- },
"xtend": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz",
diff --git a/packages/rocketchat-mentions/lib/Mentions.js b/packages/rocketchat-mentions/lib/Mentions.js
index 92feea80b94..3e33d29f5e0 100644
--- a/packages/rocketchat-mentions/lib/Mentions.js
+++ b/packages/rocketchat-mentions/lib/Mentions.js
@@ -28,10 +28,10 @@ export default class {
return typeof this._useRealName === 'function' ? this._useRealName() : this._useRealName;
}
get userMentionRegex() {
- return new RegExp(`(^|\\s|
|
?)@(${ this.pattern })`, 'gm');
+ return new RegExp(`(^|\\s|
|
?)@(${ this.pattern }(@(${ this.pattern }))?)`, 'gm');
}
get channelMentionRegex() {
- return new RegExp(`(^|\\s|
)#(${ this.pattern })`, 'gm'); + return new RegExp(`(^|\\s|
)#(${ this.pattern }(@(${ this.pattern }))?)`, 'gm'); } replaceUsers(str, message, me) { return str.replace(this.userMentionRegex, (match, prefix, username) => { @@ -40,9 +40,11 @@ export default class { } const mentionObj = message.mentions && message.mentions.find((m) => m.username === username); + if (message.temp == null && mentionObj == null) { return match; } + const name = this.useRealName && mentionObj && s.escapeHTML(mentionObj.name); return `${ prefix }${ name || `@${ username }` }`; diff --git a/packages/rocketchat-models/server/index.js b/packages/rocketchat-models/server/index.js index d1673f0ef45..af01b934c23 100644 --- a/packages/rocketchat-models/server/index.js +++ b/packages/rocketchat-models/server/index.js @@ -35,6 +35,9 @@ import ReadReceipts from './models/ReadReceipts'; export { AppsLogsModel } from './models/apps-logs-model'; export { AppsPersistenceModel } from './models/apps-persistence-model'; export { AppsModel } from './models/apps-model'; +export { FederationDNSCache } from './models/FederationDNSCache'; +export { FederationEvents } from './models/FederationEvents'; +export { FederationKeys } from './models/FederationKeys'; export { Base, diff --git a/packages/rocketchat-models/server/models/FederationDNSCache.js b/packages/rocketchat-models/server/models/FederationDNSCache.js new file mode 100644 index 00000000000..155deed53b9 --- /dev/null +++ b/packages/rocketchat-models/server/models/FederationDNSCache.js @@ -0,0 +1,13 @@ +import { Base } from './_Base'; + +class FederationDNSCacheModel extends Base { + constructor() { + super('federation_dns_cache'); + } + + findOneByDomain(domain) { + return this.findOne({ domain }); + } +} + +export const FederationDNSCache = new FederationDNSCacheModel(); diff --git a/packages/rocketchat-models/server/models/FederationEvents.js b/packages/rocketchat-models/server/models/FederationEvents.js new file mode 100644 index 00000000000..84c7bfbf565 --- /dev/null +++ b/packages/rocketchat-models/server/models/FederationEvents.js @@ -0,0 +1,255 @@ +import { Base } from './_Base'; + +const normalizePeers = (basePeers, options) => { + const { peers: sentPeers, skipPeers } = options; + + let peers = sentPeers || basePeers || []; + + if (skipPeers) { + peers = peers.filter((p) => skipPeers.indexOf(p) === -1); + } + + return peers; +}; + +// +// We should create a time to live index in this table to remove fulfilled events +// +class FederationEventsModel extends Base { + constructor() { + super('federation_events'); + } + + // Sometimes events errored but the error is final + setEventAsErrored(e, error, fulfilled = false) { + this.update({ _id: e._id }, { + $set: { + fulfilled, + lastAttemptAt: new Date(), + error, + }, + }); + } + + setEventAsFullfilled(e) { + this.update({ _id: e._id }, { + $set: { fulfilled: true }, + $unset: { error: 1 }, + }); + } + + createEvent(type, payload, peer) { + const record = { + t: type, + ts: new Date(), + fulfilled: false, + payload, + peer, + }; + + record._id = this.insert(record); + + this.emit('createEvent', record); + + return record; + } + + createEventForPeers(type, payload, peers) { + const records = []; + + for (const peer of peers) { + const record = this.createEvent(type, payload, peer); + + records.push(record); + } + + return records; + } + + // Create a `directRoomCreated(drc)` event + directRoomCreated(federatedRoom, options = {}) { + const peers = normalizePeers(federatedRoom.getPeers(), options); + + const payload = { + room: federatedRoom.getRoom(), + owner: federatedRoom.getOwner(), + users: federatedRoom.getUsers(), + }; + + return this.createEventForPeers('drc', payload, peers); + } + + // Create a `roomCreated(roc)` event + roomCreated(federatedRoom, options = {}) { + const peers = normalizePeers(federatedRoom.getPeers(), options); + + const payload = { + room: federatedRoom.getRoom(), + owner: federatedRoom.getOwner(), + users: federatedRoom.getUsers(), + }; + + return this.createEventForPeers('roc', payload, peers); + } + + // Create a `userJoined(usj)` event + userJoined(federatedRoom, federatedUser, options = {}) { + const peers = normalizePeers(federatedRoom.getPeers(), options); + + const payload = { + federated_room_id: federatedRoom.getFederationId(), + user: federatedUser.getUser(), + }; + + return this.createEventForPeers('usj', payload, peers); + } + + // Create a `userAdded(usa)` event + userAdded(federatedRoom, federatedUser, federatedInviter, options = {}) { + const peers = normalizePeers(federatedRoom.getPeers(), options); + + const payload = { + federated_room_id: federatedRoom.getFederationId(), + federated_inviter_id: federatedInviter.getFederationId(), + user: federatedUser.getUser(), + }; + + return this.createEventForPeers('usa', payload, peers); + } + + // Create a `userLeft(usl)` event + userLeft(federatedRoom, federatedUser, options = {}) { + const peers = normalizePeers(federatedRoom.getPeers(), options); + + const payload = { + federated_room_id: federatedRoom.getFederationId(), + federated_user_id: federatedUser.getFederationId(), + }; + + return this.createEventForPeers('usl', payload, peers); + } + + // Create a `userRemoved(usr)` event + userRemoved(federatedRoom, federatedUser, federatedRemovedByUser, options = {}) { + const peers = normalizePeers(federatedRoom.getPeers(), options); + + const payload = { + federated_room_id: federatedRoom.getFederationId(), + federated_user_id: federatedUser.getFederationId(), + federated_removed_by_user_id: federatedRemovedByUser.getFederationId(), + }; + + return this.createEventForPeers('usr', payload, peers); + } + + // Create a `userMuted(usm)` event + userMuted(federatedRoom, federatedUser, federatedMutedByUser, options = {}) { + const peers = normalizePeers(federatedRoom.getPeers(), options); + + const payload = { + federated_room_id: federatedRoom.getFederationId(), + federated_user_id: federatedUser.getFederationId(), + federated_muted_by_user_id: federatedMutedByUser.getFederationId(), + }; + + return this.createEventForPeers('usm', payload, peers); + } + + // Create a `userUnmuted(usu)` event + userUnmuted(federatedRoom, federatedUser, federatedUnmutedByUser, options = {}) { + const peers = normalizePeers(federatedRoom.getPeers(), options); + + const payload = { + federated_room_id: federatedRoom.getFederationId(), + federated_user_id: federatedUser.getFederationId(), + federated_unmuted_by_user_id: federatedUnmutedByUser.getFederationId(), + }; + + return this.createEventForPeers('usu', payload, peers); + } + + // Create a `messageCreated(msc)` event + messageCreated(federatedRoom, federatedMessage, options = {}) { + const peers = normalizePeers(federatedRoom.getPeers(), options); + + const payload = { + message: federatedMessage.getMessage(), + }; + + return this.createEventForPeers('msc', payload, peers); + } + + // Create a `messageUpdated(msu)` event + messageUpdated(federatedRoom, federatedMessage, federatedUser, options = {}) { + const peers = normalizePeers(federatedRoom.getPeers(), options); + + const payload = { + message: federatedMessage.getMessage(), + federated_user_id: federatedUser.getFederationId(), + }; + + return this.createEventForPeers('msu', payload, peers); + } + + // Create a `deleteMessage(msd)` event + messageDeleted(federatedRoom, federatedMessage, options = {}) { + const peers = normalizePeers(federatedRoom.getPeers(), options); + + const payload = { + federated_message_id: federatedMessage.getFederationId(), + }; + + return this.createEventForPeers('msd', payload, peers); + } + + // Create a `messagesRead(msr)` event + messagesRead(federatedRoom, federatedUser, options = {}) { + const peers = normalizePeers(federatedRoom.getPeers(), options); + + const payload = { + federated_room_id: federatedRoom.getFederationId(), + federated_user_id: federatedUser.getFederationId(), + }; + + return this.createEventForPeers('msr', payload, peers); + } + + // Create a `messagesSetReaction(mrs)` event + messagesSetReaction(federatedRoom, federatedMessage, federatedUser, reaction, shouldReact, options = {}) { + const peers = normalizePeers(federatedRoom.getPeers(), options); + + const payload = { + federated_room_id: federatedRoom.getFederationId(), + federated_message_id: federatedMessage.getFederationId(), + federated_user_id: federatedUser.getFederationId(), + reaction, + shouldReact, + }; + + return this.createEventForPeers('mrs', payload, peers); + } + + // Create a `messagesUnsetReaction(mru)` event + messagesUnsetReaction(federatedRoom, federatedMessage, federatedUser, reaction, shouldReact, options = {}) { + const peers = normalizePeers(federatedRoom.getPeers(), options); + + const payload = { + federated_room_id: federatedRoom.getFederationId(), + federated_message_id: federatedMessage.getFederationId(), + federated_user_id: federatedUser.getFederationId(), + reaction, + shouldReact, + }; + + return this.createEventForPeers('mru', payload, peers); + } + + // Get all unfulfilled events + getUnfulfilled() { + return this.find({ fulfilled: false }, { sort: { ts: 1 } }).fetch(); + } + + +} + +export const FederationEvents = new FederationEventsModel(); diff --git a/packages/rocketchat-models/server/models/FederationKeys.js b/packages/rocketchat-models/server/models/FederationKeys.js new file mode 100644 index 00000000000..8e2e9c26756 --- /dev/null +++ b/packages/rocketchat-models/server/models/FederationKeys.js @@ -0,0 +1,69 @@ +import NodeRSA from 'node-rsa'; +import uuid from 'uuid/v4'; + +import { Base } from './_Base'; + +class FederationKeysModel extends Base { + constructor() { + super('federation_keys'); + } + + getKey(type) { + const keyResource = this.findOne({ type }); + + if (!keyResource) { return null; } + + return keyResource.key; + } + + loadKey(keyData, type) { + return new NodeRSA(keyData, `pkcs8-${ type }-pem`); + } + + generateKeys() { + const key = new NodeRSA({ b: 512 }); + + key.generateKeyPair(); + + this.update({ type: 'private' }, { type: 'private', key: key.exportKey('pkcs8-private-pem').replace(/\n|\r/g, '') }, { upsert: true }); + + this.update({ type: 'public' }, { type: 'public', key: key.exportKey('pkcs8-public-pem').replace(/\n|\r/g, '') }, { upsert: true }); + + return { + privateKey: this.getPrivateKey(), + publicKey: this.getPublicKey(), + }; + } + + generateUniqueId() { + const uniqueId = uuid(); + + this.update({ type: 'unique' }, { type: 'unique', key: uniqueId }, { upsert: true }); + } + + getUniqueId() { + return (this.findOne({ type: 'unique' }) || {}).key; + } + + getPrivateKey() { + const keyData = this.getKey('private'); + + return keyData && this.loadKey(keyData, 'private'); + } + + getPrivateKeyString() { + return this.getKey('private'); + } + + getPublicKey() { + const keyData = this.getKey('public'); + + return keyData && this.loadKey(keyData, 'public'); + } + + getPublicKeyString() { + return this.getKey('public'); + } +} + +export const FederationKeys = new FederationKeysModel(); diff --git a/packages/rocketchat-models/server/models/Messages.js b/packages/rocketchat-models/server/models/Messages.js index cff4ddd3536..c33eb9532c7 100644 --- a/packages/rocketchat-models/server/models/Messages.js +++ b/packages/rocketchat-models/server/models/Messages.js @@ -813,6 +813,16 @@ export class Messages extends Base { return this.createWithTypeRoomIdMessageAndUser('subscription-role-removed', roomId, message, user, extraData); } + createRejectedMessageByPeer(roomId, user, extraData) { + const message = user.username; + return this.createWithTypeRoomIdMessageAndUser('rejected-message-by-peer', roomId, message, user, extraData); + } + + createPeerDoesNotExist(roomId, user, extraData) { + const message = user.username; + return this.createWithTypeRoomIdMessageAndUser('peer-does-not-exist', roomId, message, user, extraData); + } + // REMOVE removeById(_id) { const query = { _id }; diff --git a/packages/rocketchat-models/server/models/Users.js b/packages/rocketchat-models/server/models/Users.js index 1f9377daea2..7b2e368709e 100644 --- a/packages/rocketchat-models/server/models/Users.js +++ b/packages/rocketchat-models/server/models/Users.js @@ -487,7 +487,7 @@ export class Users extends Base { return this.find(query, options); } - findByActiveUsersExcept(searchTerm, exceptions, options) { + findByActiveUsersExcept(searchTerm, exceptions, options, forcedSearchFields) { if (exceptions == null) { exceptions = []; } if (options == null) { options = {}; } if (!_.isArray(exceptions)) { @@ -496,7 +496,9 @@ export class Users extends Base { const termRegex = new RegExp(s.escapeRegExp(searchTerm), 'i'); - const orStmt = _.reduce(this.settings.get('Accounts_SearchFields').trim().split(','), function(acc, el) { + const searchFields = forcedSearchFields || this.settings.get('Accounts_SearchFields').trim().split(','); + + const orStmt = _.reduce(searchFields, function(acc, el) { acc.push({ [el.trim()]: termRegex }); return acc; }, []); diff --git a/packages/rocketchat-reactions/server/index.js b/packages/rocketchat-reactions/server/index.js index 8c5644023db..e7490bab582 100644 --- a/packages/rocketchat-reactions/server/index.js +++ b/packages/rocketchat-reactions/server/index.js @@ -1 +1 @@ -import './setReaction'; +export { setReaction } from './setReaction'; diff --git a/packages/rocketchat-reactions/server/setReaction.js b/packages/rocketchat-reactions/server/setReaction.js index 25c3104423b..5b5a8c738b8 100644 --- a/packages/rocketchat-reactions/server/setReaction.js +++ b/packages/rocketchat-reactions/server/setReaction.js @@ -16,88 +16,95 @@ const removeUserReaction = (message, reaction, username) => { return message; }; -Meteor.methods({ - setReaction(reaction, messageId, shouldReact) { - if (!Meteor.userId()) { - throw new Meteor.Error('error-invalid-user', 'Invalid user', { method: 'setReaction' }); - } - - const message = Messages.findOneById(messageId); +export function setReaction(room, user, message, reaction, shouldReact) { + reaction = `:${ reaction.replace(/:/g, '') }:`; - if (!message) { - throw new Meteor.Error('error-not-allowed', 'Not allowed', { method: 'setReaction' }); - } + if (!emoji.list[reaction] && EmojiCustom.findByNameOrAlias(reaction).count() === 0) { + throw new Meteor.Error('error-not-allowed', 'Invalid emoji provided.', { method: 'setReaction' }); + } - const room = Meteor.call('canAccessRoom', message.rid, Meteor.userId()); + if (Array.isArray(room.muted) && room.muted.indexOf(user.username) !== -1 && !room.reactWhenReadOnly) { + Notifications.notifyUser(Meteor.userId(), 'message', { + _id: Random.id(), + rid: room._id, + ts: new Date(), + msg: TAPi18n.__('You_have_been_muted', {}, user.language), + }); + return false; + } else if (!Subscriptions.findOne({ rid: message.rid })) { + return false; + } - if (!room) { - throw new Meteor.Error('error-not-allowed', 'Not allowed', { method: 'setReaction' }); - } + const userAlreadyReacted = Boolean(message.reactions) && Boolean(message.reactions[reaction]) && message.reactions[reaction].usernames.indexOf(user.username) !== -1; + // When shouldReact was not informed, toggle the reaction. + if (shouldReact === undefined) { + shouldReact = !userAlreadyReacted; + } - reaction = `:${ reaction.replace(/:/g, '') }:`; + if (userAlreadyReacted === shouldReact) { + return; + } + if (userAlreadyReacted) { + removeUserReaction(message, reaction, user.username); - if (!emoji.list[reaction] && EmojiCustom.findByNameOrAlias(reaction).count() === 0) { - throw new Meteor.Error('error-not-allowed', 'Invalid emoji provided.', { method: 'setReaction' }); + if (_.isEmpty(message.reactions)) { + delete message.reactions; + if (isTheLastMessage(room, message)) { + Rooms.unsetReactionsInLastMessage(room._id); + } + Messages.unsetReactions(message._id); + callbacks.run('unsetReaction', message._id, reaction); + callbacks.run('afterUnsetReaction', message, { user, reaction, shouldReact }); + } else { + if (isTheLastMessage(room, message)) { + Rooms.setReactionsInLastMessage(room._id, message); + } + Messages.setReactions(message._id, message.reactions); + callbacks.run('setReaction', message._id, reaction); + callbacks.run('afterSetReaction', message, { user, reaction, shouldReact }); } + } else { + if (!message.reactions) { + message.reactions = {}; + } + if (!message.reactions[reaction]) { + message.reactions[reaction] = { + usernames: [], + }; + } + message.reactions[reaction].usernames.push(user.username); + if (isTheLastMessage(room, message)) { + Rooms.setReactionsInLastMessage(room._id, message); + } + Messages.setReactions(message._id, message.reactions); + callbacks.run('setReaction', message._id, reaction); + callbacks.run('afterSetReaction', message, { user, reaction, shouldReact }); + } + + msgStream.emit(message.rid, message); +} +Meteor.methods({ + setReaction(reaction, messageId, shouldReact) { const user = Meteor.user(); - if (Array.isArray(room.muted) && room.muted.indexOf(user.username) !== -1 && !room.reactWhenReadOnly) { - Notifications.notifyUser(Meteor.userId(), 'message', { - _id: Random.id(), - rid: room._id, - ts: new Date(), - msg: TAPi18n.__('You_have_been_muted', {}, user.language), - }); - return false; - } else if (!Subscriptions.findOne({ rid: message.rid })) { - return false; - } + const message = Messages.findOneById(messageId); - const userAlreadyReacted = Boolean(message.reactions) && Boolean(message.reactions[reaction]) && message.reactions[reaction].usernames.indexOf(user.username) !== -1; - // When shouldReact was not informed, toggle the reaction. - if (shouldReact === undefined) { - shouldReact = !userAlreadyReacted; + const room = Meteor.call('canAccessRoom', message.rid, Meteor.userId()); + + if (!user) { + throw new Meteor.Error('error-invalid-user', 'Invalid user', { method: 'setReaction' }); } - if (userAlreadyReacted === shouldReact) { - return; + if (!message) { + throw new Meteor.Error('error-not-allowed', 'Not allowed', { method: 'setReaction' }); } - if (userAlreadyReacted) { - removeUserReaction(message, reaction, user.username); - - if (_.isEmpty(message.reactions)) { - delete message.reactions; - if (isTheLastMessage(room, message)) { - Rooms.unsetReactionsInLastMessage(room._id); - } - Messages.unsetReactions(messageId); - callbacks.run('unsetReaction', messageId, reaction); - } else { - if (isTheLastMessage(room, message)) { - Rooms.setReactionsInLastMessage(room._id, message); - } - Messages.setReactions(messageId, message.reactions); - callbacks.run('setReaction', messageId, reaction); - } - } else { - if (!message.reactions) { - message.reactions = {}; - } - if (!message.reactions[reaction]) { - message.reactions[reaction] = { - usernames: [], - }; - } - message.reactions[reaction].usernames.push(user.username); - if (isTheLastMessage(room, message)) { - Rooms.setReactionsInLastMessage(room._id, message); - } - Messages.setReactions(messageId, message.reactions); - callbacks.run('setReaction', messageId, reaction); + + if (!room) { + throw new Meteor.Error('error-not-allowed', 'Not allowed', { method: 'setReaction' }); } - msgStream.emit(message.rid, message); + setReaction(room, user, message, reaction, shouldReact); return; }, diff --git a/packages/rocketchat-slashcommands-invite/server/server.js b/packages/rocketchat-slashcommands-invite/server/server.js index 51ace676175..b922680c8f1 100644 --- a/packages/rocketchat-slashcommands-invite/server/server.js +++ b/packages/rocketchat-slashcommands-invite/server/server.js @@ -17,7 +17,8 @@ function Invite(command, params, item) { if (command !== 'invite' || !Match.test(params, String)) { return; } - const usernames = params.split(/[\s,]/).map((username) => username.replace(/^@/, '')).filter((a) => a !== ''); + + const usernames = params.split(/[\s,]/).map((username) => username.replace(/(^@)|( @)/, '')).filter((a) => a !== ''); if (usernames.length === 0) { return; } diff --git a/packages/rocketchat-ui/client/views/app/directory.html b/packages/rocketchat-ui/client/views/app/directory.html index 7cfc14d05a9..85327e825f6 100644 --- a/packages/rocketchat-ui/client/views/app/directory.html +++ b/packages/rocketchat-ui/client/views/app/directory.html @@ -3,14 +3,25 @@ {{> header sectionName="Directory" hideHelp=true fullpage=true}}