index.js 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378
  1. /**
  2. * Create Actions
  3. * by id.ly Team
  4. */
  5. // Import libraries
  6. import Platform from 'react-native';
  7. import RNFetchBlob from 'react-native-fetch-blob';
  8. import SInfo from 'react-native-sensitive-info';
  9. import AesCrypto from 'react-native-aes-kit';
  10. // Export actions
  11. export const CARDS_AVAILABLE = 'CARDS_AVAILABLE';
  12. export const ADD_CARD = 'ADD_CARD';
  13. export const ADD_CARD_TO_END = 'ADD_CARD_TO_END';
  14. export const SET_DEFAULT = 'SET_DEFAULT';
  15. export const MESSAGES_AVAILABLE = 'MESSAGES_AVAILABLE';
  16. export const ADD_MESSAGE = 'ADD_MESSAGE';
  17. export const SET_MESSAGES_AS_READ = 'SET_MESSAGES_AS_READ';
  18. export const CLEAR_ALL = 'CLEAR_ALL';
  19. // ACTIONS
  20. // FUNCTION(S): The actions are the intermediary between the
  21. // components and the store. These actions handle the manipulation,
  22. // loading, and storage of message and card data.
  23. // Get Cards
  24. // Asynchronously reads the data from cards.dat and decrypts it.
  25. // Returns the CARDS_AVAILABLE dispatch if cards are found.
  26. export function getCards(){
  27. // Get the cards filepath.
  28. var paths = getPaths();
  29. // Perform the file read and decryption.
  30. return (dispatch) => {
  31. SInfo.getItem('key', {})
  32. .then((key) => {
  33. SInfo.getItem('iv', {})
  34. .then((iv) => {
  35. RNFetchBlob.fs.readFile(paths.cardsPath, 'utf8')
  36. .then((cards) => {
  37. // Check if there is any card data.
  38. if (cards !== ''){
  39. AesCrypto.decrypt(cards, key, iv)
  40. .then(decCards => {
  41. dispatch({type: CARDS_AVAILABLE, cards:JSON.parse(decCards)});
  42. });
  43. }
  44. });
  45. });
  46. });
  47. };
  48. }
  49. // Add Card
  50. // Asynchronously reads the data from cards.dat, decrypts it,
  51. // adds the new card data to the top, encrypts the card structure,
  52. // and writes it back to cards.dat.
  53. // Returns the ADD_CARD dispatch.
  54. export function addCard(card){
  55. // Get the cards filepath.
  56. var paths = getPaths();
  57. // Perform the calls to add the card.
  58. return (dispatch) => {
  59. SInfo.getItem('key', {})
  60. .then((key) => {
  61. SInfo.getItem('iv', {})
  62. .then((iv) => {
  63. RNFetchBlob.fs.readFile(paths.cardsPath, 'utf8')
  64. .then((cards) => {
  65. // Check if there is any card data.
  66. if (cards !== ''){
  67. AesCrypto.decrypt(cards, key, iv)
  68. .then(decCards => {
  69. decCards = JSON.parse(decCards);
  70. decCards.unshift(card); // Add the new card to the top.
  71. decCards = JSON.stringify(decCards);
  72. AesCrypto.encrypt(decCards, key, iv)
  73. .then(encCards => {
  74. RNFetchBlob.fs.writeFile(paths.cardsPath, encCards,'utf8')
  75. .then(() => {
  76. dispatch({type: ADD_CARD, card:card});
  77. });
  78. });
  79. });
  80. }
  81. });
  82. });
  83. });
  84. };
  85. }
  86. // Add Card To End
  87. // Asynchronously reads the data from cards.dat, decrypts it,
  88. // adds the new card data to the end, encrypts the card structure,
  89. // and writes it back to cards.dat.
  90. // Returns the ADD_CARD_TO_END dispatch.
  91. export function addCardToEnd(card){
  92. // Get the cards filepath.
  93. var paths = getPaths();
  94. // Perform the calls to add the card.
  95. return (dispatch) => {
  96. SInfo.getItem('key', {})
  97. .then((key) => {
  98. SInfo.getItem('iv', {})
  99. .then((iv) => {
  100. RNFetchBlob.fs.readFile(paths.cardsPath, 'utf8')
  101. .then((cards) => {
  102. // Check if there is any card data.
  103. if (cards !== ''){
  104. AesCrypto.decrypt(cards, key, iv)
  105. .then(decCards => {
  106. decCards = JSON.parse(decCards);
  107. decCards.push(card); // Add the new card to the end.
  108. decCards = JSON.stringify(decCards);
  109. AesCrypto.encrypt(decCards, key, iv)
  110. .then(encCards => {
  111. RNFetchBlob.fs.writeFile(paths.cardsPath, encCards,'utf8')
  112. .then(() => {
  113. dispatch({type: ADD_CARD_TO_END, card:card});
  114. });
  115. });
  116. });
  117. }
  118. // If no card data is found, add the first card.
  119. else {
  120. var firstCard = [];
  121. firstCard.unshift(card); // Add the card to the new structure.
  122. firstCard = JSON.stringify(firstCard);
  123. AesCrypto.encrypt(firstCard, key, iv)
  124. .then(encCards => {
  125. RNFetchBlob.fs.writeFile(paths.cardsPath, encCards,'utf8')
  126. .then(() => {
  127. dispatch({type: ADD_CARD_TO_END, card:card});
  128. });
  129. });
  130. }
  131. });
  132. });
  133. });
  134. };
  135. }
  136. // Set Default
  137. // Asynchronously reads the data from cards.dat, decrypts it,
  138. // moves the card passed in to be the first card in the structure,
  139. // encrypts the card structure, and writes it back to cards.dat.
  140. // Returns the SET_DEFAULT dispatch.
  141. export function setDefault(card){
  142. // Get the cards filepath.
  143. var paths = getPaths();
  144. // Perform the calls to move the card.
  145. return (dispatch) => {
  146. SInfo.getItem('key', {})
  147. .then((key) => {
  148. SInfo.getItem('iv', {})
  149. .then((iv) => {
  150. RNFetchBlob.fs.readFile(paths.cardsPath, 'utf8')
  151. .then((cards) => {
  152. // Check if there is any card data.
  153. if (cards !== ''){
  154. AesCrypto.decrypt(cards, key, iv)
  155. .then(decCards => {
  156. decCards = JSON.parse(decCards);
  157. var index = getIndex(decCards, card.id); // Find the index of the card passed in.
  158. // Check that the card is in the structure.
  159. if(index !== -1) {
  160. decCards.splice(index, 1); // Remove the card from the card structure.
  161. decCards.splice(0, 0, card); // Insert the card at the top.
  162. }
  163. decCards = JSON.stringify(decCards);
  164. AesCrypto.encrypt(decCards, key, iv)
  165. .then(encCards => {
  166. RNFetchBlob.fs.writeFile(paths.cardsPath, encCards,'utf8')
  167. .then(() => {
  168. dispatch({type: SET_DEFAULT, card:card});
  169. });
  170. });
  171. });
  172. }
  173. });
  174. });
  175. });
  176. };
  177. }
  178. // Get Messages
  179. // Asynchronously reads the data from messages.dat and decrypts it.
  180. // Returns the MESSAGES_AVAILABLE dispatch if messages are found.
  181. export function getMessages(){
  182. // Get the messages filepath.
  183. var paths = getPaths();
  184. // Perform the file read and decryption.
  185. return (dispatch) => {
  186. SInfo.getItem('key', {})
  187. .then((key) => {
  188. SInfo.getItem('iv', {})
  189. .then((iv) => {
  190. RNFetchBlob.fs.readFile(paths.messagesPath, 'utf8')
  191. .then((messages) => {
  192. // Check if there is any message data.
  193. if (messages !== ''){
  194. AesCrypto.decrypt(messages, key, iv)
  195. .then(decMessages => {
  196. dispatch({type: MESSAGES_AVAILABLE, messages:JSON.parse(decMessages)});
  197. });
  198. }
  199. });
  200. });
  201. });
  202. };
  203. }
  204. // Add Message
  205. // Asynchronously reads the data from messages.dat, decrypts it,
  206. // adds the new message data to the top, encrypts the message structure,
  207. // and writes it back to messages.dat.
  208. // Returns the ADD_MESSAGE dispatch.
  209. export function addMessage(message){
  210. // Get the messages filepath.
  211. var paths = getPaths();
  212. // Perform the calls to add the message.
  213. return (dispatch) => {
  214. SInfo.getItem('key', {})
  215. .then((key) => {
  216. SInfo.getItem('iv', {})
  217. .then((iv) => {
  218. RNFetchBlob.fs.readFile(paths.messagesPath, 'utf8')
  219. .then((messages) => {
  220. // Check if there is any message data.
  221. if (messages !== ''){
  222. AesCrypto.decrypt(messages, key, iv)
  223. .then(decMessages => {
  224. decMessages = JSON.parse(decMessages);
  225. decMessages.unshift(message); // Add the new message to the top.
  226. decMessages = JSON.stringify(decMessages);
  227. AesCrypto.encrypt(decMessages, key, iv)
  228. .then(encMessages => {
  229. RNFetchBlob.fs.writeFile(paths.messagesPath, encMessages,'utf8')
  230. .then(() => {
  231. dispatch({type: ADD_MESSAGE, message:message});
  232. });
  233. });
  234. });
  235. }
  236. // If no message data is found, add the first message.
  237. else {
  238. var firstMessage = [];
  239. firstMessage.unshift(message); // Add the message data to the new structure.
  240. firstMessage = JSON.stringify(firstMessage);
  241. AesCrypto.encrypt(firstMessage, key, iv)
  242. .then(encMessages => {
  243. RNFetchBlob.fs.writeFile(paths.messagesPath, encMessages,'utf8')
  244. .then(() => {
  245. dispatch({type: ADD_MESSAGE, message:message});
  246. });
  247. });
  248. }
  249. });
  250. });
  251. });
  252. };
  253. }
  254. // Set Messages As Read
  255. // Asynchronously reads the data from messages.dat, decrypts it,
  256. // sets the desired message data as read, encrypts the message structure,
  257. // and writes it back to messages.dat.
  258. // Returns the SET_MESSAGES_AS_READ dispatch.
  259. export function setMessagesAsRead(keys) {
  260. // Get the messages filepath.
  261. var paths = getPaths();
  262. // Perform the calls to set the messages as read.
  263. return (dispatch) => {
  264. SInfo.getItem('key', {})
  265. .then((key) => {
  266. SInfo.getItem('iv', {})
  267. .then((iv) => {
  268. RNFetchBlob.fs.readFile(paths.messagesPath, 'utf8')
  269. .then((messages) => {
  270. // Check if there is any message data.
  271. if(messages !== '') {
  272. AesCrypto.decrypt(messages, key, iv)
  273. .then(decMessages => {
  274. decMessages = JSON.parse(decMessages);
  275. // Find the desired message and set it as read.
  276. let key1 = keys._1,
  277. key2 = keys._2;
  278. for(let i = 0; i < decMessages.length; ++i) {
  279. if((decMessages[i].to === key1 && decMessages[i].from === key2) ||
  280. (decMessages[i].from === key1 && decMessages[i].to === key2)) {
  281. decMessages[i].read = true;
  282. }
  283. }
  284. decMessages = JSON.stringify(decMessages);
  285. AesCrypto.encrypt(decMessages, key, iv)
  286. .then(encMessages => {
  287. RNFetchBlob.fs.writeFile(paths.messagesPath, encMessages, 'utf8')
  288. .then(() => {
  289. dispatch({type: SET_MESSAGES_AS_READ, keys:keys});
  290. });
  291. });
  292. });
  293. }
  294. });
  295. });
  296. });
  297. }
  298. };
  299. // Clear All
  300. // Clears the card and message data from cards.dat and messages.dat.
  301. // Clears out any items stored in the keychain/keystore.
  302. // Returns the CLEAR_ALL dispatch.
  303. export function clearAll(){
  304. // Call the function to remove the data.
  305. removeFiles();
  306. return (dispatch) => {
  307. dispatch({type: CLEAR_ALL});
  308. };
  309. }
  310. // Remove Files
  311. // Helper function for clearAll() to remove the data.
  312. function removeFiles(){
  313. // Get the card and message filepaths.
  314. var paths = getPaths();
  315. var len = 10;
  316. var pubStore = 'pubkey';
  317. var privStore = 'privkey';
  318. // Write the cards.dat and messages.dat files to be empty.
  319. RNFetchBlob.fs.writeFile(paths.cardsPath, '','utf8');
  320. RNFetchBlob.fs.writeFile(paths.messagesPath, '','utf8');
  321. // Remove all items from the keychain/keystore.
  322. SInfo.deleteItem('key', {});
  323. SInfo.deleteItem('iv', {});
  324. for (var i = 0; i < len; i++){
  325. SInfo.deleteItem(pubStore + i, {});
  326. SInfo.deleteItem(privStore + i, {});
  327. }
  328. }
  329. // Get Index
  330. // Helper function to get the index of a passed in card in
  331. // the card structure.
  332. function getIndex(card, id){
  333. let clone = JSON.parse(JSON.stringify(card));
  334. return clone.findIndex((obj) => parseInt(obj.id) === parseInt(id));
  335. }
  336. // Get Paths
  337. // Helper function to get the filepaths for cards.dat and messages.dat.
  338. function getPaths(){
  339. const dirs = RNFetchBlob.fs.dirs;
  340. var cardsPath = '/idly/cards.dat';
  341. var messagesPath = '/idly/messages.dat';
  342. // Get the iOS or Android specific filepaths.
  343. if (Platform.OS === 'ios') {
  344. cardsPath = `${dirs.DocumentDir}${cardsPath}`;
  345. messagesPath = `${dirs.DocumentDir}${messagesPath}`;
  346. } else {
  347. cardsPath = dirs.DocumentDir + cardsPath;
  348. messagesPath = dirs.DocumentDir + messagesPath;
  349. }
  350. return {cardsPath: cardsPath, messagesPath: messagesPath};
  351. }