<commit_before>/*
 Flexisip, a flexible SIP proxy server with media capabilities.
 Copyright (C) 2010  Belledonne Communications SARL.

 This program is free software: you can redistribute it and/or modify
 it under the terms of the GNU Affero General Public License as
 published by the Free Software Foundation, either version 3 of the
 License, or (at your option) any later version.

 This program is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU Affero General Public License for more details.

 You should have received a copy of the GNU Affero General Public License
 along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "module.hh"
#include "agent.hh"
#include "registrardb.hh"
#include "log/logmanager.hh"

#include <sofia-sip/sip_status.h>
#include <fstream>
#include <sstream>
#include <ostream>
#include <csignal>
#include "lateforkapplier.hh"

#include <functional>
#include <algorithm>

using namespace ::std;

class ModuleRegistrar;
static ModuleRegistrar *sRegistrarInstanceForSigAction=NULL;

struct RegistrarStats {
	unique_ptr<StatPair> mCountBind;
	unique_ptr<StatPair> mCountClear;
	StatCounter64 *mCountLocalActives;
};

class OnRequestBindListener;
class OnResponseBindListener;
class ResponseContext;
class ModuleRegistrar: public Module, public ModuleToolbox {
	friend class OnRequestBindListener;
	friend class OnResponseBindListener;
	RegistrarStats mStats;
	static void staticRoutesRereadTimerfunc(su_root_magic_t *magic, su_timer_t *t, void *data){
		ModuleRegistrar *r=(ModuleRegistrar *)data;
		r->readStaticRecords();
	}
public:
	void reply(shared_ptr<RequestSipEvent> &ev, int code, const char *reason, const sip_contact_t *contacts=NULL);
	void reply(shared_ptr<ResponseSipEvent> &ev, int code, const char *reason, const sip_contact_t *contacts=NULL);

	void routeRequest(shared_ptr<RequestSipEvent> &ev, Record *aorb, const url_t *sipUri);

	ModuleRegistrar(Agent *ag) : Module(ag),mStaticRecordsTimer(NULL) {
		sRegistrarInstanceForSigAction=this;
		memset(&mSigaction, 0, sizeof(mSigaction));
	}

	~ModuleRegistrar() {
	}

	virtual void onDeclare(GenericStruct *mc) {
		ConfigItemDescriptor configs[] = {
			{ StringList, "reg-domains", "List of whitelist separated domain names to be managed by the registrar.", "localhost" },
			{ Boolean, "reg-on-response", "Register users based on response obtained from a back-end server. "
				"This mode is for using flexisip as a front-end server to hold client connections but register"
				"acceptance is deferred to backend server to which the REGISTER is routed.", "false" },
			{ Integer, "max-contacts-by-aor", "Maximum number of registered contacts of an address of record.", "12" }, /*used by registrardb*/
			{ StringList, "unique-id-parameters", "List of contact uri parameters that can be used to identify a user's device. "
					"The contact parameters are searched in the order of the list, the first matching parameter is used and the others ignored.", "+sip.instance pn-tok line" },

			{ Integer, "max-expires"	, "Maximum expire time for a REGISTER, in seconds.", "86400" },
			{ Integer, "min-expires"	, "Minimum expire time for a REGISTER, in seconds.", "60" },

			{ String, "static-records-file", "File containing the static records to add to database at startup. "
					"Format: one 'sip_uri contact_header' by line. Example:\n"
					"<sip:contact@domain> <sip:127.0.0.1:5460>,<sip:192.168.0.1:5160>", "" },
			{ Integer, "static-records-timeout", "Timeout in seconds after which the static records file is re-read and the contacts updated.", "600" },

			{ String , "db-implementation", "Implementation used for storing address of records contact uris. [redis-async, internal]","internal"},
			// Redis config support
			{ String , "redis-server-domain", "Domain of the redis server. ","localhost"},
			{ Integer , "redis-server-port", "Port of the redis server.","6379"},
			{ String , "redis-auth-password", "Authentication password for redis. Empty to disable.",""},
			{ Integer , "redis-server-timeout", "Timeout in milliseconds of the redis connection.","1500"},
			{ String , "redis-record-serializer", "Serialize contacts with: [C, protobuf]","protobuf"},
			{ String , "service-route", "Sequence of proxies (space-separated) where requests will be redirected through (RFC3608)",""},
			config_item_end
		};
		mc->addChildrenValues(configs);

		mStats.mCountClear = mc->createStats("count-clear", "Number of cleared registrations.");
		mStats.mCountBind = mc->createStats("count-bind", "Number of registers.");
		mStats.mCountLocalActives = mc->createStat("count-local-registered-users", "Number of users currently registered through this server.");
	}

	virtual void onLoad(const GenericStruct *mc) {
		mUpdateOnResponse = mc->get<ConfigBoolean>("reg-on-response")->read();
		mDomains = mc->get<ConfigStringList>("reg-domains")->read();
		for (auto it = mDomains.begin(); it != mDomains.end(); ++it) {
			LOGD("Found registrar domain: %s", (*it).c_str());<commit_msg>Use "redis" for Redis registrar implementation option<commit_after>/*
 Flexisip, a flexible SIP proxy server with media capabilities.
 Copyright (C) 2010  Belledonne Communications SARL.

 This program is free software: you can redistribute it and/or modify
 it under the terms of the GNU Affero General Public License as
 published by the Free Software Foundation, either version 3 of the
 License, or (at your option) any later version.

 This program is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU Affero General Public License for more details.

 You should have received a copy of the GNU Affero General Public License
 along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "module.hh"
#include "agent.hh"
#include "registrardb.hh"
#include "log/logmanager.hh"

#include <sofia-sip/sip_status.h>
#include <fstream>
#include <sstream>
#include <ostream>
#include <csignal>
#include "lateforkapplier.hh"

#include <functional>
#include <algorithm>

using namespace ::std;

class ModuleRegistrar;
static ModuleRegistrar *sRegistrarInstanceForSigAction=NULL;

struct RegistrarStats {
	unique_ptr<StatPair> mCountBind;
	unique_ptr<StatPair> mCountClear;
	StatCounter64 *mCountLocalActives;
};

class OnRequestBindListener;
class OnResponseBindListener;
class ResponseContext;
class ModuleRegistrar: public Module, public ModuleToolbox {
	friend class OnRequestBindListener;
	friend class OnResponseBindListener;
	RegistrarStats mStats;
	static void staticRoutesRereadTimerfunc(su_root_magic_t *magic, su_timer_t *t, void *data){
		ModuleRegistrar *r=(ModuleRegistrar *)data;
		r->readStaticRecords();
	}
public:
	void reply(shared_ptr<RequestSipEvent> &ev, int code, const char *reason, const sip_contact_t *contacts=NULL);
	void reply(shared_ptr<ResponseSipEvent> &ev, int code, const char *reason, const sip_contact_t *contacts=NULL);

	void routeRequest(shared_ptr<RequestSipEvent> &ev, Record *aorb, const url_t *sipUri);

	ModuleRegistrar(Agent *ag) : Module(ag),mStaticRecordsTimer(NULL) {
		sRegistrarInstanceForSigAction=this;
		memset(&mSigaction, 0, sizeof(mSigaction));
	}

	~ModuleRegistrar() {
	}

	virtual void onDeclare(GenericStruct *mc) {
		ConfigItemDescriptor configs[] = {
			{ StringList, "reg-domains", "List of whitelist separated domain names to be managed by the registrar.", "localhost" },
			{ Boolean, "reg-on-response", "Register users based on response obtained from a back-end server. "
				"This mode is for using flexisip as a front-end server to hold client connections but register"
				"acceptance is deferred to backend server to which the REGISTER is routed.", "false" },
			{ Integer, "max-contacts-by-aor", "Maximum number of registered contacts of an address of record.", "12" }, /*used by registrardb*/
			{ StringList, "unique-id-parameters", "List of contact uri parameters that can be used to identify a user's device. "
					"The contact parameters are searched in the order of the list, the first matching parameter is used and the others ignored.", "+sip.instance pn-tok line" },

			{ Integer, "max-expires"	, "Maximum expire time for a REGISTER, in seconds.", "86400" },
			{ Integer, "min-expires"	, "Minimum expire time for a REGISTER, in seconds.", "60" },

			{ String, "static-records-file", "File containing the static records to add to database at startup. "
					"Format: one 'sip_uri contact_header' by line. Example:\n"
					"<sip:contact@domain> <sip:127.0.0.1:5460>,<sip:192.168.0.1:5160>", "" },
			{ Integer, "static-records-timeout", "Timeout in seconds after which the static records file is re-read and the contacts updated.", "600" },

			{ String , "db-implementation", "Implementation used for storing address of records contact uris. [redis, internal]","internal"},
			// Redis config support
			{ String , "redis-server-domain", "Domain of the redis server. ","localhost"},
			{ Integer , "redis-server-port", "Port of the redis server.","6379"},
			{ String , "redis-auth-password", "Authentication password for redis. Empty to disable.",""},
			{ Integer , "redis-server-timeout", "Timeout in milliseconds of the redis connection.","1500"},
			{ String , "redis-record-serializer", "Serialize contacts with: [C, protobuf]","protobuf"},
			{ String , "service-route", "Sequence of proxies (space-separated) where requests will be redirected through (RFC3608)",""},
			config_item_end
		};
		mc->addChildrenValues(configs);

		mStats.mCountClear = mc->createStats("count-clear", "Number of cleared registrations.");
		mStats.mCountBind = mc->createStats("count-bind", "Number of registers.");
		mStats.mCountLocalActives = mc->createStat("count-local-registered-users", "Number of users currently registered through this server.");
	}

	virtual void onLoad(const GenericStruct *mc) {
		mUpdateOnResponse = mc->get<ConfigBoolean>("reg-on-response")->read();
		mDomains = mc->get<ConfigStringList>("reg-domains")->read();
		for (auto it = mDomains.begin(); it != mDomains.end(); ++it) {
			LOGD("Found registrar domain: %s", (*it).c_str());
{'fraction_non_alphanumeric': 0.1162046908315565, 'fraction_numerical': 0.010467144795503005, 'mean_word_length': 5.048651817116061, 'pattern_counts': {'":': 0, '<': 39, '<?xml version=': 0, '>': 57, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 4, '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': '4085632', 'n_tokens_mistral': 3306, 'n_tokens_neox': 3039, 'n_words': 1049}