{"size": 3953, "ext": "js", "max_stars_repo_path": "packages/client-sync/src/models/file.js", "max_stars_repo_name": "cnheider/nylas-mail", "max_stars_repo_stars_event_min_datetime": "2015-10-05T16:35:18.000Z", "max_stars_repo_stars_event_max_datetime": "2017-01-27T15:33:55.000Z", "max_issues_repo_path": "packages/client-sync/src/models/file.js", "max_issues_repo_name": "cnheider/nylas-mail", "max_issues_repo_issues_event_min_datetime": "2015-10-05T17:37:34.000Z", "max_issues_repo_issues_event_max_datetime": "2017-01-27T18:36:01.000Z", "max_forks_repo_path": "packages/client-sync/src/models/file.js", "max_forks_repo_name": "cnheider/nylas-mail", "max_forks_repo_forks_event_min_datetime": "2015-10-05T17:14:18.000Z", "max_forks_repo_forks_event_max_datetime": "2017-01-24T19:36:40.000Z"}
{"max_stars_count": 24369, "max_issues_count": 3209, "max_forks_count": 1308, "avg_line_length": 30.8828125, "max_line_length": 92, "alphanum_fraction": 0.5615987857}
const base64 = require('base64-stream');
const {
  ExponentialBackoffScheduler,
  IMAPErrors,
  IMAPConnectionPool,
} = require('isomorphic-core')
const {QuotedPrintableStreamDecoder} = require('../shared/stream-decoders')
const {Actions} = require('nylas-exports')

const MAX_IMAP_TIMEOUT_ERRORS = 5;

module.exports = (sequelize, Sequelize) => {
  return sequelize.define('file', {
    id: { type: Sequelize.STRING(500), primaryKey: true },
    size: Sequelize.INTEGER,
    partId: Sequelize.STRING,
    version: Sequelize.INTEGER,
    charset: Sequelize.STRING,
    encoding: Sequelize.STRING,
    filename: Sequelize.STRING(500),
    messageId: { type: Sequelize.STRING, allowNull: false },
    accountId: { type: Sequelize.STRING, allowNull: false },
    contentType: Sequelize.STRING(500),
    contentId: Sequelize.STRING(500),
  }, {
    indexes: [
      {fields: ['messageId']},
    ],
    classMethods: {
      associate: ({File, Message}) => {
        File.belongsTo(Message)
      },
    },
    instanceMethods: {
      async fetch({account, db, logger}) {
        const message = await this.getMessage()
        const folder = await message.getFolder()

        let numTimeoutErrors = 0;
        let result = null;

        const onConnected = async ([connection], done) => {
          const imapBox = await connection.openBox(folder.name)
          const stream = await imapBox.fetchMessageStream(message.folderImapUID, {
            fetchOptions: {
              bodies: this.partId ? [this.partId] : [],
              struct: true,
            },
            onFetchComplete() {
              done();
            },
          });

          if (!stream) {
            throw new Error(`Unable to fetch binary data for File ${this.id}`)
          }

          if (/quoted-printable/i.test(this.encoding)) {
            result = stream.pipe(new QuotedPrintableStreamDecoder({charset: this.charset}));
            return true;
          } else if (/base64/i.test(this.encoding)) {
            result = stream.pipe(base64.decode());
            return true;
          }

          // If there is no encoding, or the encoding is something like
          // '7bit', '8bit', or 'binary', just return the raw stream. This
          // stream will be written directly to disk. It's then up to the
          // user's computer to decide how to interpret the bytes we've
          // dumped to disk.
          result = stream;
          return true;
        };

        const timeoutScheduler = new ExponentialBackoffScheduler({
          baseDelay: 15 * 1000,
          maxDelay: 5 * 60 * 1000,
        });

        const onTimeout = () => {
          numTimeoutErrors += 1;
          Actions.recordUserEvent('Timeout error downloading file', {
            accountId: account.id,
            provider: account.provider,
            socketTimeout: timeoutScheduler.currentDelay(),
            numTimeoutErrors,
          });
          timeoutScheduler.nextDelay();
        };

        while (numTimeoutErrors < MAX_IMAP_TIMEOUT_ERRORS) {
          try {
            await IMAPConnectionPool.withConnectionsForAccount(account, {
              desiredCount: 1,
              logger,
              socketTimeout: timeoutScheduler.currentDelay(),
              onConnected,
            });
            break;
          } catch (err) {
            if (err instanceof IMAPErrors.IMAPConnectionTimeoutError) {
              onTimeout();
              continue;
            }
            throw err;
          }
        }
        return result;
      },

      toJSON() {
        return {
          id: this.id,
          size: this.size,
          object: 'file',
          part_id: this.partId,
          encoding: this.encoding,
          filename: this.filename,
          message_id: this.messageId,
          account_id: this.accountId,
          content_type: this.contentType,
          content_id: this.contentId,
        };
      },
    },
  });
};
