<commit_before>"use strict";

var WildEmitter = require('wildemitter');
var _ = require('underscore');
var Promise = require('bluebird');
var async = require('async');
var uuid = require('node-uuid');
var SASL = require('./stanza/sasl');
var Message = require('./stanza/message');
var Presence = require('./stanza/presence');
var StreamError = require('./stanza/streamError');
var Iq = require('./stanza/iq');
var JID = require('./jid');
var StreamManagement = require('./sm');
var WSConnection = require('./websocket');
var getHostMeta = require('hostmeta');
var SASLFactory = require('saslmechanisms');

SASLFactory = new SASLFactory();
SASLFactory.use(require('sasl-external'));
SASLFactory.use(require('sasl-scram-sha-1'));
SASLFactory.use(require('sasl-digest-md5'));
SASLFactory.use(require('sasl-plain'));
SASLFactory.use(require('sasl-anonymous'));


// Ensure that all basic stanza relationships are established
require('./stanza/stream');
require('./stanza/sm');
require('./stanza/roster');
require('./stanza/error');
require('./stanza/streamFeatures');
require('./stanza/bind');
require('./stanza/session');


function Client(opts) {
    var self = this;

    WildEmitter.call(this);

    opts = opts || {};
    this.config = _.extend({
        useStreamManagement: true,
        transport: 'websocket'
    }, opts);

    this.jid = new JID();

    this._idPrefix = uuid.v4();
    this._idCount = 0;

    this.negotiatedFeatures = {};
    this.featureOrder = [
        'sasl',
        'streamManagement',
        'bind',
        'streamManagement',
        'session'
    ];
    this.features = {};

    this.sm = new StreamManagement();
    this.transport = new WSConnection(this.sm);
    this.transport.on('*', function (eventName, data) {
        self.emit(eventName, data);
    });

    this.on('streamFeatures', function (features) {
        var series = [function (cb) { cb(null, features); }];
        var seriesNames = ['setup'];

        self.featureOrder.forEach(function (name) {
            if (features._extensions[name] && !self.negotiatedFeatures[name]) {
                series.push(function (features, cb) {
                    if (!self.negotiatedFeatures[name] && self.features[name]) {
                        self.features[name](features, cb);
                    } else {
                        cb(null, features);
                    }
                });
                seriesNames.push(name);
            }
        });

        async.waterfall(series, function (cmd) {
            if (cmd === 'restart') {
                self.transport.restart();
            } else if (cmd === 'disconnect') {
                self.disconnect();
            }
        });
    });

    this.features.sasl = function (features, cb) {
        var mech = SASLFactory.create(features.sasl.mechanisms);

        self.on('sasl:success', 'sasl', function () {
            self.negotiatedFeatures.sasl = true;
            self.releaseGroup('sasl');
            self.emit('auth:success');
            cb('restart');
        });
        self.on('sasl:challenge', 'sasl', function (challenge) {
            mech.challenge(challenge.value);
            self.send(new SASL.Response({
                value: mech.response(self.getCredentials())
            }));

            if (mech.cache) {
                _.each(mech.cache, function (val, key) {
                    self.config.credentials[key] = btoa(val);
                });
                self.emit('credentials:update', self.config.credentials);
            }

            cb();
        });
        self.on('sasl:failure', 'sasl', function () {
            self.releaseGroup('sasl');
            self.emit('auth:failed');
            cb('disconnect');
        });
        self.on('sasl:abort', 'sasl', function () {
            self.releaseGroup('sasl');
            self.emit('auth:failed');
            cb('disconnect');
        });

        var auth = {
            mechanism: mech.name
        };

        if (mech.clientFirst) {
            auth.value = mech.response(self.getCredentials());
        }
        self.send(new SASL.Auth(auth));
    };

    this.features.bind = function (features, cb) {
        self.sendIq({
            type: 'set',
            bind: {
                resource: self.config.resource
            }
        }, function (err, resp) {
            self.negotiatedFeatures.bind = true;
            self.emit('session:bound', resp.bind.jid);
            self.jid = new JID(resp.bind.jid);
            if (!features._extensions.session) {
                self.sessionStarted = true;
                self.emit('session:started', resp.bind.jid);
            }
            cb(null, features);
        });
    };

    this.features.session = function (features, cb) {
        self.sendIq({
            type: 'set',
            session: {}
        }, function () {
            self.negotiatedFeatures.session = true;
            self.sessionStarted = true;
            self.emit('session:started', self.jid);
            cb(null, features);
        });
    };

    this.features.streamManagement = function (features, cb) {
        if (!self.config.useStreamManagement) {
            return cb(null, features);
        }

        self.on('stream:management:enabled', 'sm', function (enabled) {
            self.sm.enabled(enabled);
            self.negotiatedFeatures.streamManagement = true;

            self.on('stream:management:ack', 'connection', function (ack) {
                self.sm.process(ack);
            });

            self.on('stream:management:request', 'connection', function (request) {
                self.sm.ack();
            });

            self.releaseGroup('sm');
            cb(null, features);
        });

        self.on('stream:management:resumed', 'sm', function (resumed) {
            self.sm.enabled(resumed);
            self.negotiatedFeatures.streamManagement = true;
            self.negotiatedFeatures.bind = true;
            self.sessionStarted = true;

            self.on('stream:management:ack', 'connection', function (ack) {
                self.sm.process(ack);
            });

            self.on('stream:management:request', 'connection', function (request) {
                self.sm.ack();
            });

            self.releaseGroup('sm');
            cb(null, features);
        });

        self.on('stream:management:failed', 'sm', function (failed) {
            self.sm.failed();
            self.emit('session:end');
            self.releaseGroup('session');
            self.releaseGroup('sm');
            cb(null, features);
        });

        if (!self.sm.id) {
            if (self.negotiatedFeatures.bind) {
                self.sm.enable(self.transport);
            } else {
                cb(null, features);
            }
        } else if (self.sm.id && self.sm.allowResume) {
            self.sm.resume(self.transport);
        } else {
            cb(null, features);
        }
    };

    this.on('disconnected', function () {
        self.sessionStarted = false;
        self.negotiatedFeatures.sasl = false;
        self.negotiatedFeatures.streamManagement = false;
        self.negotiatedFeatures.bind = false;
        self.negotiatedFeatures.session = false;
        self.releaseGroup('connection');
    });

    this.on('iq:set:roster', function (iq) {
        self.emit('roster:update', iq);
        self.sendIq({
            id: iq.id,
            type: 'result'
        });
    });

    this.on('iq', function (iq) {
        var iqType = iq.type;
        var exts = Object.keys(iq._extensions);
        var children = iq.xml.childNodes;

        var childCount = 0;
        _.each(children, function (child) {<commit_msg>Restrict processing of roster pushes.<commit_after>"use strict";

var WildEmitter = require('wildemitter');
var _ = require('underscore');
var Promise = require('bluebird');
var async = require('async');
var uuid = require('node-uuid');
var SASL = require('./stanza/sasl');
var Message = require('./stanza/message');
var Presence = require('./stanza/presence');
var StreamError = require('./stanza/streamError');
var Iq = require('./stanza/iq');
var JID = require('./jid');
var StreamManagement = require('./sm');
var WSConnection = require('./websocket');
var getHostMeta = require('hostmeta');
var SASLFactory = require('saslmechanisms');

SASLFactory = new SASLFactory();
SASLFactory.use(require('sasl-external'));
SASLFactory.use(require('sasl-scram-sha-1'));
SASLFactory.use(require('sasl-digest-md5'));
SASLFactory.use(require('sasl-plain'));
SASLFactory.use(require('sasl-anonymous'));


// Ensure that all basic stanza relationships are established
require('./stanza/stream');
require('./stanza/sm');
require('./stanza/roster');
require('./stanza/error');
require('./stanza/streamFeatures');
require('./stanza/bind');
require('./stanza/session');


function Client(opts) {
    var self = this;

    WildEmitter.call(this);

    opts = opts || {};
    this.config = _.extend({
        useStreamManagement: true,
        transport: 'websocket'
    }, opts);

    this.jid = new JID();

    this._idPrefix = uuid.v4();
    this._idCount = 0;

    this.negotiatedFeatures = {};
    this.featureOrder = [
        'sasl',
        'streamManagement',
        'bind',
        'streamManagement',
        'session'
    ];
    this.features = {};

    this.sm = new StreamManagement();
    this.transport = new WSConnection(this.sm);
    this.transport.on('*', function (eventName, data) {
        self.emit(eventName, data);
    });

    this.on('streamFeatures', function (features) {
        var series = [function (cb) { cb(null, features); }];
        var seriesNames = ['setup'];

        self.featureOrder.forEach(function (name) {
            if (features._extensions[name] && !self.negotiatedFeatures[name]) {
                series.push(function (features, cb) {
                    if (!self.negotiatedFeatures[name] && self.features[name]) {
                        self.features[name](features, cb);
                    } else {
                        cb(null, features);
                    }
                });
                seriesNames.push(name);
            }
        });

        async.waterfall(series, function (cmd) {
            if (cmd === 'restart') {
                self.transport.restart();
            } else if (cmd === 'disconnect') {
                self.disconnect();
            }
        });
    });

    this.features.sasl = function (features, cb) {
        var mech = SASLFactory.create(features.sasl.mechanisms);

        self.on('sasl:success', 'sasl', function () {
            self.negotiatedFeatures.sasl = true;
            self.releaseGroup('sasl');
            self.emit('auth:success');
            cb('restart');
        });
        self.on('sasl:challenge', 'sasl', function (challenge) {
            mech.challenge(challenge.value);
            self.send(new SASL.Response({
                value: mech.response(self.getCredentials())
            }));

            if (mech.cache) {
                _.each(mech.cache, function (val, key) {
                    self.config.credentials[key] = btoa(val);
                });
                self.emit('credentials:update', self.config.credentials);
            }

            cb();
        });
        self.on('sasl:failure', 'sasl', function () {
            self.releaseGroup('sasl');
            self.emit('auth:failed');
            cb('disconnect');
        });
        self.on('sasl:abort', 'sasl', function () {
            self.releaseGroup('sasl');
            self.emit('auth:failed');
            cb('disconnect');
        });

        var auth = {
            mechanism: mech.name
        };

        if (mech.clientFirst) {
            auth.value = mech.response(self.getCredentials());
        }
        self.send(new SASL.Auth(auth));
    };

    this.features.bind = function (features, cb) {
        self.sendIq({
            type: 'set',
            bind: {
                resource: self.config.resource
            }
        }, function (err, resp) {
            self.negotiatedFeatures.bind = true;
            self.emit('session:bound', resp.bind.jid);
            self.jid = new JID(resp.bind.jid);
            if (!features._extensions.session) {
                self.sessionStarted = true;
                self.emit('session:started', resp.bind.jid);
            }
            cb(null, features);
        });
    };

    this.features.session = function (features, cb) {
        self.sendIq({
            type: 'set',
            session: {}
        }, function () {
            self.negotiatedFeatures.session = true;
            self.sessionStarted = true;
            self.emit('session:started', self.jid);
            cb(null, features);
        });
    };

    this.features.streamManagement = function (features, cb) {
        if (!self.config.useStreamManagement) {
            return cb(null, features);
        }

        self.on('stream:management:enabled', 'sm', function (enabled) {
            self.sm.enabled(enabled);
            self.negotiatedFeatures.streamManagement = true;

            self.on('stream:management:ack', 'connection', function (ack) {
                self.sm.process(ack);
            });

            self.on('stream:management:request', 'connection', function (request) {
                self.sm.ack();
            });

            self.releaseGroup('sm');
            cb(null, features);
        });

        self.on('stream:management:resumed', 'sm', function (resumed) {
            self.sm.enabled(resumed);
            self.negotiatedFeatures.streamManagement = true;
            self.negotiatedFeatures.bind = true;
            self.sessionStarted = true;

            self.on('stream:management:ack', 'connection', function (ack) {
                self.sm.process(ack);
            });

            self.on('stream:management:request', 'connection', function (request) {
                self.sm.ack();
            });

            self.releaseGroup('sm');
            cb(null, features);
        });

        self.on('stream:management:failed', 'sm', function (failed) {
            self.sm.failed();
            self.emit('session:end');
            self.releaseGroup('session');
            self.releaseGroup('sm');
            cb(null, features);
        });

        if (!self.sm.id) {
            if (self.negotiatedFeatures.bind) {
                self.sm.enable(self.transport);
            } else {
                cb(null, features);
            }
        } else if (self.sm.id && self.sm.allowResume) {
            self.sm.resume(self.transport);
        } else {
            cb(null, features);
        }
    };

    this.on('disconnected', function () {
        self.sessionStarted = false;
        self.negotiatedFeatures.sasl = false;
        self.negotiatedFeatures.streamManagement = false;
        self.negotiatedFeatures.bind = false;
        self.negotiatedFeatures.session = false;
        self.releaseGroup('connection');
    });

    this.on('iq:set:roster', function (iq) {
        var allowed = {};
        allowed[''] = true;
        allowed[self.jid.bare] = true;
        allowed[self.jid.domain] = true;

        if (!allowed[iq.from.full]) {
            return self.sendIq(iq.errorReply({
                error: {
                    type: 'cancel',
                    condition: 'service-unavailable'
                }
            }));
        }

        self.emit('roster:update', iq);
        self.sendIq({
            id: iq.id,
            type: 'result'
        });
    });

    this.on('iq', function (iq) {
        var iqType = iq.type;
        var exts = Object.keys(iq._extensions);
        var children = iq.xml.childNodes;

        var childCount = 0;
        _.each(children, function (child) {
{'fraction_non_alphanumeric': 0.15106275303643724, 'fraction_numerical': 0.0006325910931174089, 'mean_word_length': 1.8785506190823016, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 2}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1491222', 'n_tokens_mistral': 4591, 'n_tokens_neox': 4412, 'n_words': 769}