def deregister_client_queue_all(self, queue): for event_type, event_queues in self.client_queues.items(): if id(queue) in event_queues: ldebug( "deregister event queue %d for all registered event types - event type %s" % (id(queue), event_type), self) del self.client_queues[event_type][id(queue)]
def listen(self): tcpsocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) tcpsocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) ldebug( 'binding server to %s:%d, timeout: %d' % (self.address, self.port, self.socket_timeout), self) try: tcpsocket.bind((self.address, self.port)) except ValueError as exception: lerror( 'server socket address error: %s - %s' % (exception, format_exc()), self) return False except socket.error as exception: lerror( 'server socket error (%d): %s - %s' % (exception[0], exception[1], format_exc()), self) return False except: lerror('general server socket error: %s' % format_exc(), self) return False tcpsocket.listen(self.SOCKET_BACKLOG) tcpsocket.settimeout(self.socket_timeout) return tcpsocket
def run(self): ldebug('starting MonAMI handler thread', self) # starting asterisk AMI thread self.ami = AsteriskAMIServer(self.socket, self.address, self.message_pipe) self.ami.start() self.ami.send_greeting() # register for events self.event_socket.register_client_queue(self.message_pipe, 'CHANNEL_CREATE') self.event_socket.register_client_queue(self.message_pipe, 'CHANNEL_DESTROY') self.event_socket.register_client_queue(self.message_pipe, 'CHANNEL_STATE') self.event_socket.register_client_queue(self.message_pipe, 'CHANNEL_ANSWER') self.event_socket.register_client_queue(self.message_pipe, 'CHANNEL_BRIDGE') while self.runthread and self.ami.isAlive(): if self.message_pipe: message = self.message_pipe.pop() message_type = sval(message, 'type') if message_type == 'freeswitch_event': self.handle_fs_event(message['body']) elif message_type == 'ami_client_message': self.handle_ami_client_message(message['body']) else: sleep(0.1) self.event_socket.deregister_client_queue_all(self.message_pipe) ldebug('exiting MonAMI handler thread', self) self.shutdown()
def stop(self): ldebug('thread stop', self) ldebug('client connections: %s' % len(self.handler_threads), self) for thread_id, handler_thread in self.handler_threads.items(): if handler_thread.isAlive(): handler_thread.stop() self.runthread = False
def stop(self): ldebug("thread stop", self) ldebug("client connections: %s" % len(self.handler_threads), self) for thread_id, handler_thread in self.handler_threads.items(): if handler_thread.isAlive(): handler_thread.stop() self.runthread = False
def register_client_queue(self, queue, event_type): if not event_type in self.client_queues: self.client_queues[event_type] = {} self.send_message("event plain all %s" % event_type) ldebug("we are listening now to events of type: %s" % event_type, self) self.client_queues[event_type][id(queue)] = queue ldebug("event queue %d registered for event type: %s" % (id(queue), event_type), self)
def user_password_authentication(user_name, password): global configuration_options if configuration_options.user_ignore_name and configuration_options.user_ignore_password: ldebug('user-password authentication credentials provided but ignored - user: %s, password: %s' % (user_name, '*' * len(str(password)))) return True if configuration_options.user_override_name != None and configuration_options.user_override_password != None: if user_name == configuration_options.user_override_name and password == configuration_options.user_override_password: return True return False db = SQLiteR(configuration_options.user_db_name) if not db.connect(): lerror('cound not connect to user database "%s"' % configuration_options.user_db_name) return False user = db.find(configuration_options.user_db_table, {configuration_options.user_db_name_row: user_name, configuration_options.user_db_password_row: password}) db.disconnect() if user: ldebug('user-password authentication accepted - user: %s, password: %s' % (user_name, '*' * len(str(password)))) return True linfo('user-password authentication failed - user: %s, password: %s' % (user_name, '*' * len(str(password)))) return False
def process_message(self, content_type, message_head, content_length, message_body): if content_type == 'auth/request': self.authenticate() if content_type == 'command/reply': if 'Reply-Text' in message_head: ldebug( 'FreeSWITCH command reply: %s' % message_head['Reply-Text'], self) elif content_type == 'text/event-plain': event = to_hash(message_body.split(self.LINE_SEPARATOR)) if 'Event-Name' in event and event[ 'Event-Name'] in self.client_queues: event_type = event['Event-Name'] for entry_id, message_pipe in self.client_queues[ event_type].items(): if type(message_pipe) == deque: if len(message_pipe) < self.MESSAGE_PIPE_MAX_LENGTH: message_pipe.appendleft({ 'type': 'freeswitch_event', 'body': event }) else: lwarn("event queue %d full" % entry_id) else: ldebug( "force-deregister event queue %d for event type %s" % (entry_id, event_type), self) del self.client_queues[event_type][entry_id]
def deregister_handler_thread(self, handler_thread): if id(handler_thread) in self.handler_threads: ldebug('deregistering handler thread %d ' % id(handler_thread), self) del self.handler_threads[id(handler_thread)] else: lwarn('handler thread %d not registered' % id(handler_thread), self)
def register_client_queue(self, queue, event_type): if not event_type in self.client_queues: self.client_queues[event_type] = {} self.send_message("event plain all %s" % event_type) ldebug("we are listening now to events of type: %s" % event_type, self) self.client_queues[event_type][id(queue)] = queue ldebug( "event queue %d registered for event type: %s" % (id(queue), event_type), self)
def originate(self, sip_account, extension, action_id=''): uuid = hashlib.md5('%s%f' % (sip_account, random() * 65534)).hexdigest() ldebug( 'originate call - from: %s, to: %s, uuid: %s' % (sip_account, extension, uuid), self) self.send_message( 'bgapi originate {origination_uuid=%s,origination_action=%s,origination_caller_id_number=%s}user/%s %s' % (uuid, action_id, sip_account, sip_account, extension)) return uuid
def event_channel_bridge(self, event): uuid = sval(event, 'Unique-ID') o_uuid = sval(event, 'Other-Leg-Unique-ID') ldebug('bridge channel: %s to %s' % (uuid, o_uuid), self) channel = sval(self.channels, uuid) o_channel = sval(self.channels, o_uuid) if sval(channel, 'owned') or sval(o_channel, 'owned'): ldebug('sending AMI bridge response: %s -> %s' % (uuid, o_uuid), self) self.ami.send_event_bridge(uuid, sval(channel, 'name'), sval(channel, 'caller_id_number'), o_uuid, sval(o_channel, 'name'), sval(o_channel, 'caller_id_number'))
def event_channel_state(self, event): uuid = sval(event, 'Unique-ID') channel_state = sval(event, 'Channel-State') call_state = sval(event, 'Channel-Call-State') answer_state = sval(event, 'Answer-State') if sval(self.channels, uuid) and False: ldebug('updating channel state - channel: %s, channel_state: %s, call_state %s, answer_state: %s' % (uuid, channel_state, call_state, answer_state), self) self.channels[uuid]['channel_state'] = channel_state self.channels[uuid]['call_state'] = call_state self.channels[uuid]['answer_state'] = answer_state return uuid
def event_channel_destroy(self, event): uuid = sval(event, 'Unique-ID') hangup_cause_code = int(sval(event, 'variable_hangup_cause_q850')) channel = sval(self.channels, uuid) if channel: channel['hangup_cause_code'] = hangup_cause_code if sval(channel, 'ami_start'): self.ami_send_outbound_end(channel) del self.channels[uuid] ldebug('channel removed from list: %s, cause %d' % (uuid, hangup_cause_code), self) return uuid
def event_channel_destroy(self, event): uuid = sval(event, 'Unique-ID') hangup_cause_code = int(sval(event, 'variable_hangup_cause_q850')) channel = sval(self.channels, uuid) if channel: channel['hangup_cause_code'] = hangup_cause_code if sval(channel, 'ami_start'): self.ami_send_outbound_end(channel) del self.channels[uuid] ldebug( 'channel removed from list: %s, cause %d' % (uuid, hangup_cause_code), self) return uuid
def event_channel_state(self, event): uuid = sval(event, 'Unique-ID') channel_state = sval(event, 'Channel-State') call_state = sval(event, 'Channel-Call-State') answer_state = sval(event, 'Answer-State') if sval(self.channels, uuid) and False: ldebug( 'updating channel state - channel: %s, channel_state: %s, call_state %s, answer_state: %s' % (uuid, channel_state, call_state, answer_state), self) self.channels[uuid]['channel_state'] = channel_state self.channels[uuid]['call_state'] = call_state self.channels[uuid]['answer_state'] = answer_state return uuid
def process_message(self, content_type, message_head, content_length, message_body): if content_type == 'auth/request': self.authenticate() if content_type == 'command/reply': if 'Reply-Text' in message_head: ldebug('FreeSWITCH command reply: %s' % message_head['Reply-Text'], self) elif content_type == 'text/event-plain': event = to_hash(message_body.split(self.LINE_SEPARATOR)) if 'Event-Name' in event and event['Event-Name'] in self.client_queues: event_type = event['Event-Name'] for entry_id, message_pipe in self.client_queues[event_type].items(): if type(message_pipe) == deque: if len(message_pipe) < self.MESSAGE_PIPE_MAX_LENGTH: message_pipe.appendleft({'type': 'freeswitch_event', 'body': event}) else: lwarn("event queue %d full" % entry_id) else: ldebug("force-deregister event queue %d for event type %s" % (entry_id, event_type), self) del self.client_queues[event_type][entry_id]
def run(self): ldebug('starting AMI server thread', self) data = '' while self.runthread and self.socket: try: recv_buffer = self.socket.recv(128) except socket.timeout as exception: # Socket timeout occured continue except: lerror(format_exc(), self) self.runthread = False break if not recv_buffer: ldebug('client connection lost', self) break data += recv_buffer messages = data.split(self.LINE_SEPARATOR * 2) data = messages.pop() for message_str in messages: if not message_str: continue message = to_hash(message_str.split(self.LINE_SEPARATOR)) self.message_queue.appendleft({'type': 'ami_client_message', 'body': message}) ldebug('exiting AMI server thread', self)
def main(): global event_socket global mon_ami_server global configuration_options option_parser = OptionParser() # Log options option_parser.add_option("--log-file", action="store", type="string", dest="log_file", default=None) option_parser.add_option("--log-level", action="store", type="int", dest="log_level", default=5) # FreeSWITCH event_socket option_parser.add_option("--freeswitch-address", action="store", type="string", dest="freeswitch_address", default='127.0.0.1') option_parser.add_option("--freeswitch-port", action="store", type="int", dest="freeswitch_port", default=8021) option_parser.add_option("--freeswitch-password", action="store", type="string", dest="freeswitch_password", default='ClueCon') # Asterisk Manager Interface option_parser.add_option("-a", "--address", "--ami-address", action="store", type="string", dest="ami_address", default='0.0.0.0') option_parser.add_option("-p", "--port", "--ami-port", action="store", type="int", dest="ami_port", default=5038) # User database option_parser.add_option("--user-db-name", action="store", type="string", dest="user_db_name", default='/opt/GS5/db/development.sqlite3') option_parser.add_option("--user-db-table", action="store", type="string", dest="user_db_table", default='sip_accounts') option_parser.add_option("--user-db-name-row", action="store", type="string", dest="user_db_name_row", default='auth_name') option_parser.add_option("--user-db-password-row", action="store", type="string", dest="user_db_password_row", default='password') # Define common User/Password options option_parser.add_option("--user-override-name", action="store", type="string", dest="user_override_name", default=None) option_parser.add_option("--user-override-password", action="store", type="string", dest="user_override_password", default=None) option_parser.add_option("--user-ignore-name", action="store_true", dest="user_ignore_name", default=False) option_parser.add_option("--user-ignore-password", action="store_true", dest="user_ignore_password", default=False) (configuration_options, args) = option_parser.parse_args() setup_log(configuration_options.log_file, configuration_options.log_level) ldebug('starting MonAMI main process') # Catch signals signal(SIGHUP, signal_handler) signal(SIGTERM, signal_handler) signal(SIGINT, signal_handler) # Starting FreeSWITCH event_socket thread event_socket = FreeswitchEventSocket(configuration_options.freeswitch_address, configuration_options.freeswitch_port, configuration_options.freeswitch_password) event_socket.start() if event_socket.isAlive(): # Starting Asterisk manager thread mon_ami_server = MonAMIServer(configuration_options.ami_address, configuration_options.ami_port, event_socket) mon_ami_server.user_password_authentication = user_password_authentication mon_ami_server.start() while mon_ami_server.isAlive(): sleep(1) if event_socket.isAlive(): ldebug('killing event_socket thread') event_socket.stop() ldebug('exiting MonAMI main process')
def event_channel_answer(self, event): uuid = sval(event, 'Unique-ID') o_uuid = sval(event, 'Other-Leg-Unique-ID') channel = sval(self.channels, uuid) if not o_uuid: o_uuid = sval(channel, 'o_uuid') o_channel = sval(self.channels, o_uuid) origination_action = sval(channel, 'origination_action') if channel: channel_state = sval(event, 'Channel-State') call_state = sval(event, 'Channel-Call-State') answer_state = sval(event, 'Answer-State') ldebug('channel answered - channel: %s, owned: %s, channel_state: %s, call_state %s, answer_state: %s, other leg: %s' % (uuid, sval(channel, 'owned'), channel_state, call_state, answer_state, o_uuid), self) self.ami.send_event_newstate(uuid, sval(channel, 'name'), 6, sval(channel, 'caller_id_number'), sval(channel, 'caller_id_name')) self.channels[uuid]['channel_state'] = channel_state self.channels[uuid]['call_state'] = call_state self.channels[uuid]['answer_state'] = answer_state if sval(channel, 'origination_action'): if sval(channel, 'owned'): ldebug('sending AMI originate response - success: %s' % uuid, self) self.ami.send_event_originate_response(sval(channel, 'uuid'), sval(channel, 'name'), sval(channel, 'caller_id_number'), sval(channel, 'caller_id_name'), '101', sval(channel, 'origination_action'), 4) elif not o_uuid: ldebug('sending AMI events for outbound call start on one legged call (this channel): %s' % uuid, self) self.ami_send_outbound_start(channel) self.ami.send_event_bridge(uuid, sval(channel, 'name'), sval(channel, 'caller_id_number'), o_uuid, sval(o_channel, 'name'), sval(o_channel, 'caller_id_number')) self.channels[uuid]['ami_start'] = True return uuid return False
def run(self): ldebug('starting MonAMI server thread', self) serversocket = TCPServer(self.address, self.port).listen() #serversocket.setblocking(0) if not serversocket: ldebug('server socket could not be bound', self) return 1 while self.runthread: try: client_socket, client_address = serversocket.accept() except socket.timeout as exception: # Socket timeout occured continue except socket.error as exception: lerror('socket error (%s): %s - ' % (exception, format_exc()), self) sleep(1) continue except: lerror('general error: %s - ' % format_exc(), self) sleep(1) continue ldebug('connected to %s:%d' % client_address, self) client_thread = MonAMIHandler(client_socket, client_address, self.event_socket) client_thread.deregister_at_server = self.deregister_handler_thread client_thread.user_password_authentication = self.user_password_authentication client_thread.start() if client_thread.isAlive(): self.register_handler_thread(client_thread) ldebug( 'registered handler threads: %d' % len(self.handler_threads), self) ldebug('exiting MonAMI server thread', self)
def listen(self): tcpsocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) tcpsocket.setsockopt( socket.SOL_SOCKET, socket.SO_REUSEADDR, 1 ) ldebug('binding server to %s:%d, timeout: %d' % (self.address, self.port, self.socket_timeout), self) try: tcpsocket.bind((self.address, self.port)) except ValueError as exception: lerror('server socket address error: %s - %s' % (exception, format_exc()), self) return False except socket.error as exception: lerror('server socket error (%d): %s - %s' % (exception[0], exception[1], format_exc()), self) return False except: lerror('general server socket error: %s' % format_exc(), self) return False tcpsocket.listen(self.SOCKET_BACKLOG) tcpsocket.settimeout(self.socket_timeout) return tcpsocket
def user_password_authentication(user_name, password): global configuration_options if configuration_options.user_ignore_name and configuration_options.user_ignore_password: ldebug( 'user-password authentication credentials provided but ignored - user: %s, password: %s' % (user_name, '*' * len(str(password)))) return True if configuration_options.user_override_name != None and configuration_options.user_override_password != None: if user_name == configuration_options.user_override_name and password == configuration_options.user_override_password: return True return False db = MySQLR(configuration_options.user_db_name, configuration_options.user_db_user, configuration_options.user_db_password) if not db.connect(): lerror('cound not connect to user database "%s"' % configuration_options.user_db_name) return False user = db.find( configuration_options.user_db_table, { configuration_options.user_db_name_row: user_name, configuration_options.user_db_password_row: password }) db.disconnect() if user: ldebug( 'user-password authentication accepted - user: %s, password: %s' % (user_name, '*' * len(str(password)))) return True linfo('user-password authentication failed - user: %s, password: %s' % (user_name, '*' * len(str(password)))) return False
def signal_handler(signal_number, frame): global event_socket global mon_ami_server ldebug('signal %d received ' % signal_number, frame) if (signal_number == SIGTERM): ldebug('shutdown signal (%d) received ' % signal_number, frame) event_socket.stop() mon_ami_server.stop() elif (signal_number == SIGINT): ldebug('interrupt signal (%d) received ' % signal_number, frame) event_socket.stop() mon_ami_server.stop() elif (signal_number == SIGHUP): ldebug('hangup signal (%d) received - ignore' % signal_number, frame)
def shutdown(self): self.deregister_at_server(self) ldebug('closing connection to %s:%d' % self.address) try: self.socket.shutdown(SHUT_RDWR) self.socket.close() ldebug('connection closed ', self) except: ldebug('connection closed by foreign host', self)
def run(self): ldebug("starting MonAMI server thread", self) serversocket = TCPServer(self.address, self.port).listen() # serversocket.setblocking(0) if not serversocket: ldebug("server socket could not be bound", self) return 1 while self.runthread: try: client_socket, client_address = serversocket.accept() except socket.timeout as exception: # Socket timeout occured continue except socket.error as exception: lerror("socket error (%s): %s - " % (exception, format_exc()), self) sleep(1) continue except: lerror("general error: %s - " % format_exc(), self) sleep(1) continue ldebug("connected to %s:%d" % client_address, self) client_thread = MonAMIHandler(client_socket, client_address, self.event_socket) client_thread.deregister_at_server = self.deregister_handler_thread client_thread.user_password_authentication = self.user_password_authentication client_thread.start() if client_thread.isAlive(): self.register_handler_thread(client_thread) ldebug("registered handler threads: %d" % len(self.handler_threads), self) ldebug("exiting MonAMI server thread", self)
def event_channel_answer(self, event): uuid = sval(event, 'Unique-ID') o_uuid = sval(event, 'Other-Leg-Unique-ID') channel = sval(self.channels, uuid) if not o_uuid: o_uuid = sval(channel, 'o_uuid') o_channel = sval(self.channels, o_uuid) origination_action = sval(channel, 'origination_action') if channel: channel_state = sval(event, 'Channel-State') call_state = sval(event, 'Channel-Call-State') answer_state = sval(event, 'Answer-State') ldebug( 'channel answered - channel: %s, owned: %s, channel_state: %s, call_state %s, answer_state: %s, other leg: %s' % (uuid, sval(channel, 'owned'), channel_state, call_state, answer_state, o_uuid), self) self.ami.send_event_newstate(uuid, sval(channel, 'name'), 6, sval(channel, 'caller_id_number'), sval(channel, 'caller_id_name')) self.channels[uuid]['channel_state'] = channel_state self.channels[uuid]['call_state'] = call_state self.channels[uuid]['answer_state'] = answer_state if sval(channel, 'origination_action'): if sval(channel, 'owned'): ldebug( 'sending AMI originate response - success: %s' % uuid, self) self.ami.send_event_originate_response( sval(channel, 'uuid'), sval(channel, 'name'), sval(channel, 'caller_id_number'), sval(channel, 'caller_id_name'), '101', sval(channel, 'origination_action'), 4) elif not o_uuid: ldebug( 'sending AMI events for outbound call start on one legged call (this channel): %s' % uuid, self) self.ami_send_outbound_start(channel) self.ami.send_event_bridge(uuid, sval(channel, 'name'), sval(channel, 'caller_id_number'), o_uuid, sval(o_channel, 'name'), sval(o_channel, 'caller_id_number')) self.channels[uuid]['ami_start'] = True return uuid return False
def handle_ami_client_message(self, message): if 'Action' in message: action = message['Action'].lower() if action == 'login': if 'UserName' in message: self.account_name = message['UserName'] if 'Secret' in message and self.user_password_authentication and self.user_password_authentication( self.account_name, message['Secret']): self.ami.send_login_ack() ldebug( 'AMI connection authenticated - account: %s' % self.account_name, self) else: self.ami.send_login_nack() linfo( 'AMI authentication failed - account: %s' % sval(message, 'UserName'), self) self.ami.stop() self.stop() elif action == 'logoff': self.ami.send_logout_ack() ldebug('AMI logout', self) self.ami.stop() self.stop() elif action == 'ping': self.ami.send_pong(sval(message, 'ActionID')) elif action == 'status': self.ami.send_status_ack(sval(message, 'ActionID')) elif action == 'command' and sval( message, 'Command') == 'core show version': self.ami.send_asterisk_version(sval(message, 'ActionID')) elif action == 'hangup': account_name, separator, uuid = str(sval( message, 'Channel')).rpartition('-uuid-') if account_name != '': self.event_socket.hangup(uuid) self.ami.send_hangup_ack() elif action == 'originate': self.message_originate(message) elif action == 'extensionstate': self.ami.send_extension_state(sval(message, 'ActionID'), sval(message, 'Exten'), sval(message, 'Context')) else: ldebug('unknown asterisk message received: %s' % message, self) self.ami.send_message_unknown(message['Action'])
def handle_ami_client_message(self, message): if 'Action' in message: action = message['Action'].lower() if action == 'login': if 'UserName' in message: self.account_name = message['UserName'] if 'Secret' in message and self.user_password_authentication and self.user_password_authentication(self.account_name, message['Secret']): self.ami.send_login_ack() ldebug('AMI connection authenticated - account: %s' % self.account_name, self) else: self.ami.send_login_nack() linfo('AMI authentication failed - account: %s' % sval(message, 'UserName'), self) self.ami.stop() self.stop() elif action == 'logoff': self.ami.send_logout_ack() ldebug('AMI logout', self) self.ami.stop() self.stop() elif action == 'ping': self.ami.send_pong(sval(message, 'ActionID')) elif action == 'status': self.ami.send_status_ack(sval(message, 'ActionID')) elif action == 'command' and sval(message, 'Command') == 'core show version': self.ami.send_asterisk_version(sval(message, 'ActionID')) elif action == 'hangup': account_name, separator, uuid = str(sval(message, 'Channel')).rpartition('-uuid-') if account_name != '': self.event_socket.hangup(uuid) self.ami.send_hangup_ack() elif action == 'originate': self.message_originate(message) elif action == 'extensionstate': self.ami.send_extension_state(sval(message, 'ActionID'), sval(message, 'Exten'), sval(message, 'Context')) else: ldebug('unknown asterisk message received: %s' % message, self) self.ami.send_message_unknown(message['Action'])
def handle_fs_event(self, event): event_type = event['Event-Name'] #ldebug('event type received: %s' % event_type, self) event_types = { 'CHANNEL_CREATE': self.event_channel_create, 'CHANNEL_DESTROY': self.event_channel_destroy, 'CHANNEL_STATE': self.event_channel_state, 'CHANNEL_ANSWER': self.event_channel_answer, 'CHANNEL_BRIDGE': self.event_channel_bridge, } uuid = event_types[event_type](event) if not uuid: return False channel = sval(self.channels, uuid); if not channel: return False o_uuid = channel['o_uuid'] o_channel = sval(self.channels, o_uuid); if sval(channel, 'origination_action') or sval(o_channel, 'origination_action'): if not sval(channel, 'ami_start') and not sval(o_channel, 'ami_start'): if sval(channel, 'owned') and sval(channel, 'origination_action'): ldebug('sending AMI events for origitate call start (on this channel): %s' % uuid, self) self.ami_send_originate_start(channel) self.channels[uuid]['ami_start'] = True elif sval(o_channel, 'owned') and sval(o_channel, 'origination_action'): ldebug('sending AMI events for origitate call start (on other channel): %s' % uuid, self) self.ami_send_originate_start(o_channel) self.channels[o_uuid]['ami_start'] = True elif o_channel: if sval(channel, 'owned') and sval(channel, 'origination_action'): ldebug('sending AMI events for origitate call progress (on this channel): %s' % uuid, self) self.ami_send_originate_outbound(channel) self.channels[uuid]['origination_action'] = False elif sval(o_channel, 'owned') and sval(o_channel, 'origination_action'): ldebug('sending AMI events for origitate call progress (on other channel): %s' % uuid, self) self.ami_send_originate_outbound(o_channel) self.channels[o_uuid]['origination_action'] = False elif o_channel: if not sval(channel, 'ami_start') and not sval(o_channel, 'ami_start'): if sval(channel, 'owned') and sval(channel, 'direction') == 'inbound': ldebug('sending AMI events for outbound call start (on this channel): %s' % uuid, self) self.ami_send_outbound_start(channel) self.channels[uuid]['ami_start'] = True elif sval(o_channel, 'owned') and sval(channel, 'direction') == 'outbound': ldebug('sending AMI events for outbound call start (on other channel): %s' % uuid, self) self.ami_send_outbound_start(o_channel) self.channels[o_uuid]['ami_start'] = True if not sval(channel, 'ami_start')and not sval(o_channel, 'ami_start'): if sval(channel, 'owned') and sval(channel, 'direction') == 'outbound': ldebug('sending AMI events for inbound call start (on this channel): %s' % uuid, self) self.ami_send_inbound_start(channel) self.channels[uuid]['ami_start'] = True elif sval(o_channel, 'owned') and sval(channel, 'direction') == 'inbound': ldebug('sending AMI events for inbound call start (on other channel): %s' % uuid, self) self.ami_send_inbound_start(o_channel) self.channels[o_uuid]['ami_start'] = True
def authenticate(self): ldebug('send authentication to FreeSWITCH', self) self.send_message("auth %s" % self.password)
def deregister_handler_thread(self, handler_thread): if id(handler_thread) in self.handler_threads: ldebug("deregistering handler thread %d " % id(handler_thread), self) del self.handler_threads[id(handler_thread)] else: lwarn("handler thread %d not registered" % id(handler_thread), self)
def deregister_client_queue(self, queue, event_type): ldebug( "deregister event queue %d for event type %s" % (id(queue), event_type), self) del self.client_queues[event_type][id(queue)]
def hangup(self, uuid, hangup_cause = 'NORMAL_CLEARING'): ldebug('hangup channel: %s' % uuid, self) self.send_message('SendMsg %s' % uuid, 'call-command: hangup', 'hangup-cause: %s' % hangup_cause) return True
def event_channel_create(self, event): uuid = sval(event, 'Unique-ID') o_uuid = sval(event, 'Other-Leg-Unique-ID') if uuid in self.channels: ldebug('channel already listed: %s' % uuid, self) return false channel_name = self.to_unique_channel_name( uuid, unquote(str(sval(event, 'Channel-Name')))) o_channel_name = self.to_unique_channel_name( o_uuid, unquote(str(sval(event, 'Other-Leg-Channel-Name')))) if self.account_name in channel_name: channel_owned = True else: channel_owned = False if self.account_name in o_channel_name: channel_related = True else: channel_related = False if not channel_owned and not channel_related: ldebug('channel neither owned nor reladed to account: %s' % uuid, self) return False channel = { 'uuid': uuid, 'name': channel_name, 'direction': sval(event, 'Call-Direction'), 'channel_state': sval(event, 'Channel-State'), 'call_state': sval(event, 'Channel-Call-State'), 'answer_state': sval(event, 'Answer-State'), 'owned': channel_owned, 'related': channel_related, 'caller_id_name': unquote(str(sval(event, 'Caller-Caller-ID-Name'))), 'caller_id_number': unquote(str(sval(event, 'Caller-Caller-ID-Number'))), 'callee_id_name': unquote(str(sval(event, 'Caller-Callee-ID-Name'))), 'callee_id_number': unquote(str(sval(event, 'Caller-Callee-ID-Number'))), 'destination_number': str(sval(event, 'Caller-Destination-Number')), 'origination_action': sval(event, 'variable_origination_action'), 'o_uuid': o_uuid, 'o_name': o_channel_name, } if channel['answer_state'] == 'ringing': if channel['direction'] == 'inbound': asterisk_channel_state = 4 else: asterisk_channel_state = 5 else: asterisk_channel_state = 0 if not o_uuid: ldebug('one legged call, channel: %s' % uuid, self) elif o_uuid not in self.channels: o_channel = { 'uuid': o_uuid, 'name': o_channel_name, 'direction': sval(event, 'Other-Leg-Direction'), 'channel_state': sval(event, 'Channel-State'), 'call_state': sval(event, 'Channel-Call-State'), 'answer_state': sval(event, 'Answer-State'), 'owned': channel_related, 'related': channel_owned, 'caller_id_name': unquote(str(sval(event, 'Caller-Caller-ID-Name'))), 'caller_id_number': unquote(str(sval(event, 'Caller-Caller-ID-Number'))), 'callee_id_name': unquote(str(sval(event, 'Caller-Callee-ID-Name'))), 'callee_id_number': unquote(str(sval(event, 'Caller-Callee-ID-Number'))), 'destination_number': str(sval(event, 'Other-Leg-Destination-Number')), 'o_uuid': uuid, 'o_name': channel_name, } if o_channel['answer_state'] == 'ringing': if o_channel['direction'] == 'inbound': asterisk_o_channel_state = 4 else: asterisk_o_channel_state = 5 else: asterisk_o_channel_state = 0 ldebug( 'create channel list entry for related channel: %s, name: %s' % (o_uuid, o_channel_name), self) self.channels[o_uuid] = o_channel else: ldebug( 'updating channel: %s, name: %s, o_uuid: %s, o_name %s' % (o_uuid, o_channel_name, uuid, channel_name), self) self.channels[o_uuid]['o_uuid'] = uuid self.channels[o_uuid]['o_name'] = channel_name o_channel = self.channels[o_uuid] if channel_owned: ldebug( 'create channel list entry for own channel: %s, name: %s' % (uuid, channel_name), self) elif channel_related: ldebug( 'create channel list entry for related channel: %s, name: %s' % (uuid, channel_name), self) self.channels[uuid] = channel return uuid
def run(self): ldebug('starting FreeSWITCH event_socket thread', self) while self.runthread: if not self.connect(): ldebug('could not connect to FreeSWITCH - retry', self) sleep(self.SOCKET_TIMEOUT) continue ldebug('opening event_socket connection', self) data = '' while self.runthread and self.fs: try: recv_buffer = self.fs.recv(128) except socket.timeout as exception: # Socket timeout occured continue except: lerror(format_exc(), self) self.runthread = False break if not recv_buffer: ldebug('event_socket connection lost', self) break data += recv_buffer messages = data.split(self.LINE_SEPARATOR * 2) data = messages.pop() for message_str in messages: if not message_str: continue message_body = None message = to_hash(message_str.split(self.LINE_SEPARATOR)) if not 'Content-Type' in message: ldebug('message without Content-Type', self) continue if 'Content-Length' in message and int(message['Content-Length']) > 0: content_length = int(message['Content-Length']) while len(data) < int(message['Content-Length']): try: data += self.fs.recv(content_length - len(data)) except socket.timeout as exception: ldebug('Socket timeout in message body', self) continue except: lerror(format_exc(), self) break message_body = data.strip() data = '' else: content_length = 0 self.process_message(message['Content-Type'], message, content_length, message_body) ldebug('closing event_socket connection', self) if self.fs: self.fs.close()
def handle_fs_event(self, event): event_type = event['Event-Name'] #ldebug('event type received: %s' % event_type, self) event_types = { 'CHANNEL_CREATE': self.event_channel_create, 'CHANNEL_DESTROY': self.event_channel_destroy, 'CHANNEL_STATE': self.event_channel_state, 'CHANNEL_ANSWER': self.event_channel_answer, 'CHANNEL_BRIDGE': self.event_channel_bridge, } uuid = event_types[event_type](event) if not uuid: return False channel = sval(self.channels, uuid) if not channel: return False o_uuid = channel['o_uuid'] o_channel = sval(self.channels, o_uuid) if sval(channel, 'origination_action') or sval(o_channel, 'origination_action'): if not sval(channel, 'ami_start') and not sval( o_channel, 'ami_start'): if sval(channel, 'owned') and sval(channel, 'origination_action'): ldebug( 'sending AMI events for origitate call start (on this channel): %s' % uuid, self) self.ami_send_originate_start(channel) self.channels[uuid]['ami_start'] = True elif sval(o_channel, 'owned') and sval(o_channel, 'origination_action'): ldebug( 'sending AMI events for origitate call start (on other channel): %s' % uuid, self) self.ami_send_originate_start(o_channel) self.channels[o_uuid]['ami_start'] = True elif o_channel: if sval(channel, 'owned') and sval(channel, 'origination_action'): ldebug( 'sending AMI events for origitate call progress (on this channel): %s' % uuid, self) self.ami_send_originate_outbound(channel) self.channels[uuid]['origination_action'] = False elif sval(o_channel, 'owned') and sval(o_channel, 'origination_action'): ldebug( 'sending AMI events for origitate call progress (on other channel): %s' % uuid, self) self.ami_send_originate_outbound(o_channel) self.channels[o_uuid]['origination_action'] = False elif o_channel: if not sval(channel, 'ami_start') and not sval( o_channel, 'ami_start'): if sval(channel, 'owned') and sval(channel, 'direction') == 'inbound': ldebug( 'sending AMI events for outbound call start (on this channel): %s' % uuid, self) self.ami_send_outbound_start(channel) self.channels[uuid]['ami_start'] = True elif sval(o_channel, 'owned') and sval( channel, 'direction') == 'outbound': ldebug( 'sending AMI events for outbound call start (on other channel): %s' % uuid, self) self.ami_send_outbound_start(o_channel) self.channels[o_uuid]['ami_start'] = True if not sval(channel, 'ami_start') and not sval( o_channel, 'ami_start'): if sval(channel, 'owned') and sval(channel, 'direction') == 'outbound': ldebug( 'sending AMI events for inbound call start (on this channel): %s' % uuid, self) self.ami_send_inbound_start(channel) self.channels[uuid]['ami_start'] = True elif sval(o_channel, 'owned') and sval( channel, 'direction') == 'inbound': ldebug( 'sending AMI events for inbound call start (on other channel): %s' % uuid, self) self.ami_send_inbound_start(o_channel) self.channels[o_uuid]['ami_start'] = True
def register_handler_thread(self, handler_thread): if handler_thread.isAlive(): ldebug('registering handler thread %d ' % id(handler_thread), self) self.handler_threads[id(handler_thread)] = handler_thread else: lwarn('handler thread passed away: %d' % id(handler_thread), self)
def deregister_client_queue(self, queue, event_type): ldebug("deregister event queue %d for event type %s" % (id(queue), event_type), self) del self.client_queues[event_type][id(queue)]
def run(self): ldebug('starting FreeSWITCH event_socket thread', self) while self.runthread: if not self.connect(): ldebug('could not connect to FreeSWITCH - retry', self) sleep(self.SOCKET_TIMEOUT) continue ldebug('opening event_socket connection', self) data = '' while self.runthread and self.fs: try: recv_buffer = self.fs.recv(128) except socket.timeout as exception: # Socket timeout occured continue except: lerror(format_exc(), self) self.runthread = False break if not recv_buffer: ldebug('event_socket connection lost', self) break data += recv_buffer messages = data.split(self.LINE_SEPARATOR * 2) data = messages.pop() for message_str in messages: if not message_str: continue message_body = None message = to_hash(message_str.split(self.LINE_SEPARATOR)) if not 'Content-Type' in message: ldebug('message without Content-Type', self) continue if 'Content-Length' in message and int( message['Content-Length']) > 0: content_length = int(message['Content-Length']) while len(data) < int(message['Content-Length']): try: data += self.fs.recv(content_length - len(data)) except socket.timeout as exception: ldebug('Socket timeout in message body', self) continue except: lerror(format_exc(), self) break message_body = data.strip() data = '' else: content_length = 0 self.process_message(message['Content-Type'], message, content_length, message_body) ldebug('closing event_socket connection', self) if self.fs: self.fs.close()
def deregister_client_queue_all(self, queue): for event_type, event_queues in self.client_queues.items(): if id(queue) in event_queues: ldebug("deregister event queue %d for all registered event types - event type %s" % (id(queue), event_type), self) del self.client_queues[event_type][id(queue)]
def register_handler_thread(self, handler_thread): if handler_thread.isAlive(): ldebug("registering handler thread %d " % id(handler_thread), self) self.handler_threads[id(handler_thread)] = handler_thread else: lwarn("handler thread passed away: %d" % id(handler_thread), self)
def originate(self, sip_account, extension, action_id = ''): uuid = hashlib.md5('%s%f' % (sip_account, random() * 65534)).hexdigest() ldebug('originate call - from: %s, to: %s, uuid: %s' % (sip_account, extension, uuid), self) self.send_message('bgapi originate {origination_uuid=%s,origination_action=%s,origination_caller_id_number=%s}user/%s %s' % (uuid, action_id, sip_account, sip_account, extension)) return uuid
def stop(self): ldebug('thread stop', self) self.runthread = False
def main(): global event_socket global mon_ami_server global configuration_options option_parser = OptionParser() # Log options option_parser.add_option("--log-file", action="store", type="string", dest="log_file", default=None) option_parser.add_option("--log-level", action="store", type="int", dest="log_level", default=5) # FreeSWITCH event_socket option_parser.add_option("--freeswitch-address", action="store", type="string", dest="freeswitch_address", default='127.0.0.1') option_parser.add_option("--freeswitch-port", action="store", type="int", dest="freeswitch_port", default=8021) option_parser.add_option("--freeswitch-password", action="store", type="string", dest="freeswitch_password", default='ClueCon') # Asterisk Manager Interface option_parser.add_option("-a", "--address", "--ami-address", action="store", type="string", dest="ami_address", default='0.0.0.0') option_parser.add_option("-p", "--port", "--ami-port", action="store", type="int", dest="ami_port", default=5038) # User database option_parser.add_option("--user-db-name", action="store", type="string", dest="user_db_name", default='gemeinschaft') option_parser.add_option("--user-db-user", action="store", type="string", dest="user_db_user", default='gemeinschaft') option_parser.add_option("--user-db-password", action="store", type="string", dest="user_db_password", default='gemeinschaft') option_parser.add_option("--user-db-table", action="store", type="string", dest="user_db_table", default='sip_accounts') option_parser.add_option("--user-db-name-row", action="store", type="string", dest="user_db_name_row", default='auth_name') option_parser.add_option("--user-db-password-row", action="store", type="string", dest="user_db_password_row", default='password') # Define common User/Password options option_parser.add_option("--user-override-name", action="store", type="string", dest="user_override_name", default=None) option_parser.add_option("--user-override-password", action="store", type="string", dest="user_override_password", default=None) option_parser.add_option("--user-ignore-name", action="store_true", dest="user_ignore_name", default=False) option_parser.add_option("--user-ignore-password", action="store_true", dest="user_ignore_password", default=False) (configuration_options, args) = option_parser.parse_args() setup_log(configuration_options.log_file, configuration_options.log_level) ldebug('starting MonAMI main process') # Catch signals signal(SIGHUP, signal_handler) signal(SIGTERM, signal_handler) signal(SIGINT, signal_handler) # Starting FreeSWITCH event_socket thread event_socket = FreeswitchEventSocket( configuration_options.freeswitch_address, configuration_options.freeswitch_port, configuration_options.freeswitch_password) event_socket.start() if event_socket.isAlive(): # Starting Asterisk manager thread mon_ami_server = MonAMIServer(configuration_options.ami_address, configuration_options.ami_port, event_socket) mon_ami_server.user_password_authentication = user_password_authentication mon_ami_server.start() while mon_ami_server.isAlive(): sleep(1) if event_socket.isAlive(): ldebug('killing event_socket thread') event_socket.stop() ldebug('exiting MonAMI main process')
def event_channel_create(self, event): uuid = sval(event, 'Unique-ID') o_uuid = sval(event, 'Other-Leg-Unique-ID') if uuid in self.channels: ldebug('channel already listed: %s' % uuid, self) return false channel_name = self.to_unique_channel_name(uuid, unquote(str(sval(event, 'Channel-Name')))) o_channel_name = self.to_unique_channel_name(o_uuid, unquote(str(sval(event, 'Other-Leg-Channel-Name')))) if self.account_name in channel_name: channel_owned = True else: channel_owned = False if self.account_name in o_channel_name: channel_related = True else: channel_related = False if not channel_owned and not channel_related: ldebug('channel neither owned nor reladed to account: %s' % uuid, self) return False channel = { 'uuid': uuid, 'name': channel_name, 'direction': sval(event, 'Call-Direction'), 'channel_state': sval(event, 'Channel-State'), 'call_state': sval(event, 'Channel-Call-State'), 'answer_state': sval(event, 'Answer-State'), 'owned': channel_owned, 'related': channel_related, 'caller_id_name': unquote(str(sval(event, 'Caller-Caller-ID-Name'))), 'caller_id_number': unquote(str(sval(event, 'Caller-Caller-ID-Number'))), 'callee_id_name': unquote(str(sval(event, 'Caller-Callee-ID-Name'))), 'callee_id_number': unquote(str(sval(event, 'Caller-Callee-ID-Number'))), 'destination_number': str(sval(event, 'Caller-Destination-Number')), 'origination_action': sval(event, 'variable_origination_action'), 'o_uuid': o_uuid, 'o_name': o_channel_name, } if channel['answer_state'] == 'ringing': if channel['direction'] == 'inbound': asterisk_channel_state = 4 else: asterisk_channel_state = 5 else: asterisk_channel_state = 0 if not o_uuid: ldebug('one legged call, channel: %s' % uuid, self) elif o_uuid not in self.channels: o_channel = { 'uuid': o_uuid, 'name': o_channel_name, 'direction': sval(event, 'Other-Leg-Direction'), 'channel_state': sval(event, 'Channel-State'), 'call_state': sval(event, 'Channel-Call-State'), 'answer_state': sval(event, 'Answer-State'), 'owned': channel_related, 'related': channel_owned, 'caller_id_name': unquote(str(sval(event, 'Caller-Caller-ID-Name'))), 'caller_id_number': unquote(str(sval(event, 'Caller-Caller-ID-Number'))), 'callee_id_name': unquote(str(sval(event, 'Caller-Callee-ID-Name'))), 'callee_id_number': unquote(str(sval(event, 'Caller-Callee-ID-Number'))), 'destination_number': str(sval(event, 'Other-Leg-Destination-Number')), 'o_uuid': uuid, 'o_name': channel_name, } if o_channel['answer_state'] == 'ringing': if o_channel['direction'] == 'inbound': asterisk_o_channel_state = 4 else: asterisk_o_channel_state = 5 else: asterisk_o_channel_state = 0 ldebug('create channel list entry for related channel: %s, name: %s' % (o_uuid, o_channel_name), self) self.channels[o_uuid] = o_channel else: ldebug('updating channel: %s, name: %s, o_uuid: %s, o_name %s' % (o_uuid, o_channel_name, uuid, channel_name), self) self.channels[o_uuid]['o_uuid'] = uuid self.channels[o_uuid]['o_name'] = channel_name o_channel = self.channels[o_uuid] if channel_owned: ldebug('create channel list entry for own channel: %s, name: %s' % (uuid, channel_name), self) elif channel_related: ldebug('create channel list entry for related channel: %s, name: %s' % (uuid, channel_name), self) self.channels[uuid] = channel return uuid
def hangup(self, uuid, hangup_cause='NORMAL_CLEARING'): ldebug('hangup channel: %s' % uuid, self) self.send_message('SendMsg %s' % uuid, 'call-command: hangup', 'hangup-cause: %s' % hangup_cause) return True