Asterisk - The Open Source Telephony Project  GIT-master-a1fa8df
sip_to_pjsip.py
Go to the documentation of this file.
1 #!/usr/bin/env python
2 
3 from __future__ import print_function
4 
5 import sys
6 import optparse
7 import socket
8 try:
9  from urllib.parse import urlparse
10 except ImportError:
11  from urlparse import urlparse # Python 2.7 required for Literal IPv6 Addresses
12 import astdicts
13 import astconfigparser
14 
15 PREFIX = 'pjsip_'
16 QUIET = False
17 
18 ###############################################################################
19 ### some utility functions
20 ###############################################################################
21 
22 
23 def section_by_type(section, pjsip, type):
24  """Finds a section based upon the given type, adding it if not found."""
25  def __find_dict(mdicts, key, val):
26  """Given a list of multi-dicts, return the multi-dict that contains
27  the given key/value pair."""
28 
29  def found(d):
30  return key in d and val in d[key]
31 
32  try:
33  return [d for d in mdicts if found(d)][0]
34  except IndexError:
35  raise LookupError("Dictionary not located for key = %s, value = %s"
36  % (key, val))
37 
38  try:
39  return __find_dict(pjsip.section(section), 'type', type)
40  except LookupError:
41  # section for type doesn't exist, so add
42  sect = pjsip.add_section(section)
43  sect['type'] = type
44  return sect
45 
46 
47 def ignore(key=None, val=None, section=None, pjsip=None,
48  nmapped=None, type='endpoint'):
49  """Ignore a key and mark it as mapped"""
50 
51 
52 def set_value(key=None, val=None, section=None, pjsip=None,
53  nmapped=None, type='endpoint'):
54  """Sets the key to the value within the section in pjsip.conf"""
55  def _set_value(k, v, s, r, n):
56  set_value(key if key else k, v, s, r, n, type)
57 
58  # if no value or section return the set_value
59  # function with the enclosed key and type
60  if not val and not section:
61  return _set_value
62 
63  # otherwise try to set the value
64  section_by_type(section, pjsip, type)[key] = \
65  val[0] if isinstance(val, list) else val
66 
67 
68 def merge_value(key=None, val=None, section=None, pjsip=None,
69  nmapped=None, type='endpoint', section_to=None,
70  key_to=None):
71  """Merge values from the given section with those from the default."""
72  def _merge_value(k, v, s, r, n):
73  merge_value(key if key else k, v, s, r, n, type, section_to, key_to)
74 
75  # if no value or section return the merge_value
76  # function with the enclosed key and type
77  if not val and not section:
78  return _merge_value
79 
80  # should return a single value section list
81  try:
82  sect = sip.section(section)[0]
83  except LookupError:
84  sect = sip.default(section)[0]
85  # for each merged value add it to pjsip.conf
86  for i in sect.get_merged(key):
87  set_value(key_to if key_to else key, i,
88  section_to if section_to else section,
89  pjsip, nmapped, type)
90 
91 def merge_codec_value(key=None, val=None, section=None, pjsip=None,
92  nmapped=None, type='endpoint', section_to=None,
93  key_to=None):
94  """Merge values from allow/deny with those from the default. Special treatment for all"""
95  def _merge_codec_value(k, v, s, r, n):
96  merge_codec_value(key if key else k, v, s, r, n, type, section_to, key_to)
97 
98  # if no value or section return the merge_codec_value
99  # function with the enclosed key and type
100  if not val and not section:
101  return _merge_codec_value
102 
103  if key == 'allow':
104  try:
105  disallow = sip.get(section, 'disallow')[0]
106  if disallow == 'all':
107  #don't inherit
108  for i in sip.get(section, 'allow'):
109  set_value(key, i, section, pjsip, nmapped, type)
110  else:
111  merge_value(key, val, section, pjsip, nmapped, type, section_to, key_to)
112  except LookupError:
113  print("lookup error", file=sys.stderr)
114  merge_value(key, val, section, pjsip, nmapped, type, section_to, key_to)
115  return
116  elif key == 'disallow':
117  try:
118  allow = sip.get(section, 'allow')[0]
119  if allow == 'all':
120  #don't inherit
121  for i in sip.get(section, 'disallow'):
122  set_value(key, i, section, pjsip, nmapped, type)
123  else:
124  merge_value(key, val, section, pjsip, nmapped, type, section_to, key_to)
125  except LookupError:
126  merge_value(key, val, section, pjsip, nmapped, type, section_to, key_to)
127  return
128  else:
129  merge_value(key, val, section, pjsip, nmapped, type, section_to, key_to)
130 
131 
132 def non_mapped(nmapped):
133  """Write non-mapped sip.conf values to the non-mapped object"""
134  def _non_mapped(section, key, val):
135  """Writes a non-mapped value from sip.conf to the non-mapped object."""
136  if section not in nmapped:
137  nmapped[section] = astconfigparser.Section()
138  if isinstance(val, list):
139  for v in val:
140  # since coming from sip.conf we can assume
141  # single section lists
142  nmapped[section][0][key] = v
143  else:
144  nmapped[section][0][key] = val
145  return _non_mapped
146 
147 ###############################################################################
148 ### mapping functions -
149 ### define f(key, val, section) where key/val are the key/value pair to
150 ### write to given section in pjsip.conf
151 ###############################################################################
152 
153 
154 def set_dtmfmode(key, val, section, pjsip, nmapped):
155  """
156  Sets the dtmfmode value. If value matches allowable option in pjsip
157  then map it, otherwise set it to none.
158  """
159  key = 'dtmf_mode'
160  # available pjsip.conf values: rfc4733, inband, info, none
161  if val == 'inband' or val == 'info' or val == 'auto':
162  set_value(key, val, section, pjsip, nmapped)
163  elif val == 'rfc2833':
164  set_value(key, 'rfc4733', section, pjsip, nmapped)
165  else:
166  nmapped(section, key, val + " ; did not fully map - set to none")
167  set_value(key, 'none', section, pjsip, nmapped)
168 
169 
170 def setup_udptl(section, pjsip, nmapped):
171  """Sets values from udptl into the appropriate pjsip.conf options."""
172  try:
173  val = sip.get(section, 't38pt_udptl')[0]
174  except LookupError:
175  try:
176  val = sip.get('general', 't38pt_udptl')[0]
177  except LookupError:
178  return
179 
180  ec = 'none'
181  if 'yes' in val:
182  set_value('t38_udptl', 'yes', section, pjsip, nmapped)
183  if 'no' in val:
184  set_value('t38_udptl', 'no', section, pjsip, nmapped)
185  if 'redundancy' in val:
186  ec = 'redundancy'
187  if 'fec' in val:
188  ec = 'fec'
189  set_value('t38_udptl_ec', ec, section, pjsip, nmapped)
190 
191 def from_nat(key, val, section, pjsip, nmapped):
192  """Sets values from nat into the appropriate pjsip.conf options."""
193  # nat from sip.conf can be comma separated list of values:
194  # yes/no, [auto_]force_rport, [auto_]comedia
195  if 'yes' in val:
196  set_value('rtp_symmetric', 'yes', section, pjsip, nmapped)
197  set_value('rewrite_contact', 'yes', section, pjsip, nmapped)
198  if 'comedia' in val:
199  set_value('rtp_symmetric', 'yes', section, pjsip, nmapped)
200  if 'force_rport' in val:
201  set_value('force_rport', 'yes', section, pjsip, nmapped)
202  set_value('rewrite_contact', 'yes', section, pjsip, nmapped)
203 
204 
205 def set_timers(key, val, section, pjsip, nmapped):
206  """
207  Sets the timers in pjsip.conf from the session-timers option
208  found in sip.conf.
209  """
210  # pjsip.conf values can be yes/no, required, always
211  # 'required' is a new feature of chan_pjsip, which rejects
212  # all SIP clients not supporting Session Timers
213  # 'Accept' is the default value of chan_sip and maps to 'yes'
214  # chan_sip ignores the case, for example 'session-timers=Refuse'
215  val = val.lower()
216  if val == 'originate':
217  set_value('timers', 'always', section, pjsip, nmapped)
218  elif val == 'refuse':
219  set_value('timers', 'no', section, pjsip, nmapped)
220  else:
221  set_value('timers', 'yes', section, pjsip, nmapped)
222 
223 
224 def set_direct_media(key, val, section, pjsip, nmapped):
225  """
226  Maps values from the sip.conf comma separated direct_media option
227  into pjsip.conf direct_media options.
228  """
229  if 'yes' in val:
230  set_value('direct_media', 'yes', section, pjsip, nmapped)
231  if 'update' in val:
232  set_value('direct_media_method', 'update', section, pjsip, nmapped)
233  if 'outgoing' in val:
234  set_value('directed_media_glare_mitigation', 'outgoing', section,
235  pjsip, nmapped)
236  if 'nonat' in val:
237  set_value('disable_directed_media_on_nat', 'yes', section, pjsip,
238  nmapped)
239  if 'no' in val:
240  set_value('direct_media', 'no', section, pjsip, nmapped)
241 
242 
243 def from_sendrpid(key, val, section, pjsip, nmapped):
244  """Sets the send_rpid/pai values in pjsip.conf."""
245  if val == 'yes' or val == 'rpid':
246  set_value('send_rpid', 'yes', section, pjsip, nmapped)
247  elif val == 'pai':
248  set_value('send_pai', 'yes', section, pjsip, nmapped)
249 
250 
251 def set_media_encryption(key, val, section, pjsip, nmapped):
252  """Sets the media_encryption value in pjsip.conf"""
253  try:
254  dtls = sip.get(section, 'dtlsenable')[0]
255  if dtls == 'yes':
256  # If DTLS is enabled, then that overrides SDES encryption.
257  return
258  except LookupError:
259  pass
260 
261  if val == 'yes':
262  set_value('media_encryption', 'sdes', section, pjsip, nmapped)
263 
264 
265 def from_recordfeature(key, val, section, pjsip, nmapped):
266  """
267  If record on/off feature is set to automixmon then set
268  one_touch_recording, otherwise it can't be mapped.
269  """
270  set_value('one_touch_recording', 'yes', section, pjsip, nmapped)
271  set_value(key, val, section, pjsip, nmapped)
272 
273 def set_record_on_feature(key, val, section, pjsip, nmapped):
274  """Sets the record_on_feature in pjsip.conf"""
275  from_recordfeature('record_on_feature', val, section, pjsip, nmapped)
276 
277 def set_record_off_feature(key, val, section, pjsip, nmapped):
278  """Sets the record_off_feature in pjsip.conf"""
279  from_recordfeature('record_off_feature', val, section, pjsip, nmapped)
280 
281 def from_progressinband(key, val, section, pjsip, nmapped):
282  """Sets the inband_progress value in pjsip.conf"""
283  # progressinband can = yes/no/never
284  if val == 'never':
285  val = 'no'
286  set_value('inband_progress', val, section, pjsip, nmapped)
287 
288 
289 def build_host(config, host, section='general', port_key=None):
290  """
291  Returns a string composed of a host:port. This assumes that the host
292  may have a port as part of the initial value. The port_key overrides
293  a port in host, see parameter 'bindport' in chan_sip.
294  """
295  try:
296  socket.inet_pton(socket.AF_INET6, host)
297  if not host.startswith('['):
298  # SIP URI will need brackets.
299  host = '[' + host + ']'
300  except socket.error:
301  pass
302 
303  # Literal IPv6 (like [::]), IPv4, or hostname
304  # does not work for IPv6 without brackets; case catched above
305  url = urlparse('sip://' + host)
306 
307  if port_key:
308  try:
309  port = config.get(section, port_key)[0]
310  host = url.hostname # no port, but perhaps no brackets
311  try:
312  socket.inet_pton(socket.AF_INET6, host)
313  if not host.startswith('['):
314  # SIP URI will need brackets.
315  host = '[' + host + ']'
316  except socket.error:
317  pass
318  return host + ':' + port
319  except LookupError:
320  pass
321 
322  # Returns host:port, in brackets if required
323  # TODO Does not compress IPv6, for example 0:0:0:0:0:0:0:0 should get [::]
324  return url.netloc
325 
326 
327 def from_host(key, val, section, pjsip, nmapped):
328  """
329  Sets contact info in an AOR section in pjsip.conf using 'host'
330  and 'port' data from sip.conf
331  """
332  # all aors have the same name as the endpoint so makes
333  # it easy to set endpoint's 'aors' value
334  set_value('aors', section, section, pjsip, nmapped)
335  if val == 'dynamic':
336  # Easy case. Just set the max_contacts on the aor and we're done
337  set_value('max_contacts', 1, section, pjsip, nmapped, 'aor')
338  return
339 
340  result = 'sip:'
341 
342  # More difficult case. The host will be either a hostname or
343  # IP address and may or may not have a port specified. pjsip.conf
344  # expects the contact to be a SIP URI.
345 
346  user = None
347 
348  try:
349  user = sip.multi_get(section, ['defaultuser', 'username'])[0]
350  result += user + '@'
351  except LookupError:
352  # It's fine if there's no user name
353  pass
354 
355  result += build_host(sip, val, section, 'port')
356 
357  set_value('contact', result, section, pjsip, nmapped, 'aor')
358 
359 
360 def from_mailbox(key, val, section, pjsip, nmapped):
361  """
362  Determines whether a mailbox configured in sip.conf should map to
363  an endpoint or aor in pjsip.conf. If subscribemwi is true, then the
364  mailboxes are set on an aor. Otherwise the mailboxes are set on the
365  endpoint.
366  """
367 
368  try:
369  subscribemwi = sip.get(section, 'subscribemwi')[0]
370  except LookupError:
371  # No subscribemwi option means default it to 'no'
372  subscribemwi = 'no'
373 
374  set_value('mailboxes', val, section, pjsip, nmapped, 'aor'
375  if subscribemwi == 'yes' else 'endpoint')
376 
377 
378 def setup_auth(key, val, section, pjsip, nmapped):
379  """
380  Sets up authentication information for a specific endpoint based on the
381  'secret' setting on a peer in sip.conf
382  """
383  set_value('username', section, section, pjsip, nmapped, 'auth')
384  # In chan_sip, if a secret and an md5secret are both specified on a peer,
385  # then in practice, only the md5secret is used. If both are encountered
386  # then we build an auth section that has both an md5_cred and password.
387  # However, the auth_type will indicate to authenticators to use the
388  # md5_cred, so like with sip.conf, the password will be there but have
389  # no purpose.
390  if key == 'secret':
391  set_value('password', val, section, pjsip, nmapped, 'auth')
392  else:
393  set_value('md5_cred', val, section, pjsip, nmapped, 'auth')
394  set_value('auth_type', 'md5', section, pjsip, nmapped, 'auth')
395 
396  realms = [section]
397  try:
398  auths = sip.get('authentication', 'auth')
399  for i in auths:
400  user, at, realm = i.partition('@')
401  realms.append(realm)
402  except LookupError:
403  pass
404 
405  realm_str = ','.join(realms)
406 
407  set_value('auth', section, section, pjsip, nmapped)
408  set_value('outbound_auth', realm_str, section, pjsip, nmapped)
409 
410 
411 def setup_ident(key, val, section, pjsip, nmapped):
412  """
413  Examines the 'type' field for a sip.conf peer and creates an identify
414  section if the type is either 'peer' or 'friend'. The identify section uses
415  either the host or defaultip field of the sip.conf peer.
416  """
417  if val != 'peer' and val != 'friend':
418  return
419 
420  try:
421  ip = sip.get(section, 'host')[0]
422  except LookupError:
423  return
424 
425  if ip == 'dynamic':
426  try:
427  ip = sip.get(section, 'defaultip')[0]
428  except LookupError:
429  return
430 
431  set_value('endpoint', section, section, pjsip, nmapped, 'identify')
432  set_value('match', ip, section, pjsip, nmapped, 'identify')
433 
434 
435 def from_encryption_taglen(key, val, section, pjsip, nmapped):
436  """Sets the srtp_tag32 option based on sip.conf encryption_taglen"""
437  if val == '32':
438  set_value('srtp_tag_32', 'yes', section, pjsip, nmapped)
439 
440 
441 def from_dtlsenable(key, val, section, pjsip, nmapped):
442  """Optionally sets media_encryption=dtls based on sip.conf dtlsenable"""
443  if val == 'yes':
444  set_value('media_encryption', 'dtls', section, pjsip, nmapped)
445 
446 ###############################################################################
447 
448 # options in pjsip.conf on an endpoint that have no sip.conf equivalent:
449 # type, 100rel, trust_id_outbound, aggregate_mwi, connected_line_method
450 
451 # known sip.conf peer keys that can be mapped to a pjsip.conf section/key
452 peer_map = [
453  # sip.conf option mapping function pjsip.conf option(s)
454  ###########################################################################
455  ['context', set_value],
456  ['dtmfmode', set_dtmfmode],
457  ['disallow', merge_codec_value],
458  ['allow', merge_codec_value],
459  ['nat', from_nat], # rtp_symmetric, force_rport,
460  # rewrite_contact
461  ['rtptimeout', set_value('rtp_timeout')],
462  ['icesupport', set_value('ice_support')],
463  ['autoframing', set_value('use_ptime')],
464  ['outboundproxy', set_value('outbound_proxy')],
465  ['mohsuggest', set_value('moh_suggest')],
466  ['session-timers', set_timers], # timers
467  ['session-minse', set_value('timers_min_se')],
468  ['session-expires', set_value('timers_sess_expires')],
469  # identify_by ?
470  ['canreinvite', set_direct_media], # direct_media alias
471  ['directmedia', set_direct_media], # direct_media
472  # direct_media_method
473  # directed_media_glare_mitigation
474  # disable_directed_media_on_nat
475  ['callerid', set_value], # callerid
476  ['callingpres', set_value('callerid_privacy')],
477  ['cid_tag', set_value('callerid_tag')],
478  ['trustrpid', set_value('trust_id_inbound')],
479  ['sendrpid', from_sendrpid], # send_pai, send_rpid
480  ['send_diversion', set_value],
481  ['encryption', set_media_encryption],
482  ['avpf', set_value('use_avpf')],
483  ['recordonfeature', set_record_on_feature], # automixon
484  ['recordofffeature', set_record_off_feature], # automixon
485  ['progressinband', from_progressinband], # in_band_progress
486  ['callgroup', set_value('call_group')],
487  ['pickupgroup', set_value('pickup_group')],
488  ['namedcallgroup', set_value('named_call_group')],
489  ['namedpickupgroup', set_value('named_pickup_group')],
490  ['allowtransfer', set_value('allow_transfer')],
491  ['fromuser', set_value('from_user')],
492  ['fromdomain', set_value('from_domain')],
493  ['mwifrom', set_value('mwi_from_user')],
494  ['tos_audio', set_value],
495  ['tos_video', set_value],
496  ['cos_audio', set_value],
497  ['cos_video', set_value],
498  ['sdpowner', set_value('sdp_owner')],
499  ['sdpsession', set_value('sdp_session')],
500  ['tonezone', set_value('tone_zone')],
501  ['language', set_value],
502  ['allowsubscribe', set_value('allow_subscribe')],
503  ['subscribecontext', set_value('subscribe_context')],
504  ['subminexpiry', set_value('sub_min_expiry')],
505  ['rtp_engine', set_value],
506  ['mailbox', from_mailbox],
507  ['busylevel', set_value('device_state_busy_at')],
508  ['secret', setup_auth],
509  ['md5secret', setup_auth],
510  ['type', setup_ident],
511  ['dtlsenable', from_dtlsenable],
512  ['dtlsverify', set_value('dtls_verify')],
513  ['dtlsrekey', set_value('dtls_rekey')],
514  ['dtlscertfile', set_value('dtls_cert_file')],
515  ['dtlsprivatekey', set_value('dtls_private_key')],
516  ['dtlscipher', set_value('dtls_cipher')],
517  ['dtlscafile', set_value('dtls_ca_file')],
518  ['dtlscapath', set_value('dtls_ca_path')],
519  ['dtlssetup', set_value('dtls_setup')],
520  ['encryption_taglen', from_encryption_taglen],
521  ['setvar', ignore],
522 
523 ############################ maps to an aor ###################################
524 
525  ['host', from_host], # contact, max_contacts
526  ['qualifyfreq', set_value('qualify_frequency', type='aor')],
527  ['maxexpiry', set_value('maximum_expiration', type='aor')],
528  ['minexpiry', set_value('minimum_expiration', type='aor')],
529  ['defaultexpiry', set_value('default_expiration', type='aor')],
530 
531 ############################# maps to auth#####################################
532 # type = auth
533 # username
534 # password
535 # md5_cred
536 # realm
537 # nonce_lifetime
538 # auth_type
539 ######################### maps to acl/security ################################
540 
541  ['permit', merge_value(type='acl', section_to='acl')],
542  ['deny', merge_value(type='acl', section_to='acl')],
543  ['acl', merge_value(type='acl', section_to='acl')],
544  ['contactpermit', merge_value(type='acl', section_to='acl', key_to='contact_permit')],
545  ['contactdeny', merge_value(type='acl', section_to='acl', key_to='contact_deny')],
546  ['contactacl', merge_value(type='acl', section_to='acl', key_to='contact_acl')],
547 
548 ########################### maps to transport #################################
549 # type = transport
550 # protocol
551 # bind
552 # async_operations
553 # ca_list_file
554 # ca_list_path
555 # cert_file
556 # privkey_file
557 # password
558 # external_signaling_address - externip & externhost
559 # external_signaling_port
560 # external_media_address
561 # domain
562 # verify_server
563 # verify_client
564 # require_client_cert
565 # method
566 # cipher
567 # localnet
568 ######################### maps to domain_alias ################################
569 # type = domain_alias
570 # domain
571 ######################### maps to registration ################################
572 # type = registration
573 # server_uri
574 # client_uri
575 # contact_user
576 # transport
577 # outbound_proxy
578 # expiration
579 # retry_interval
580 # max_retries
581 # auth_rejection_permanent
582 # outbound_auth
583 ########################### maps to identify ##################################
584 # type = identify
585 # endpoint
586 # match
587 ]
588 
589 
590 def split_hostport(addr):
591  """
592  Given an address in the form 'host:port' separate the host and port
593  components.
594  Returns a two-tuple of strings, (host, port). If no port is present in the
595  string, then the port section of the tuple is None.
596  """
597  try:
598  socket.inet_pton(socket.AF_INET6, addr)
599  if not addr.startswith('['):
600  return (addr, None)
601  except socket.error:
602  pass
603 
604  # Literal IPv6 (like [::]), IPv4, or hostname
605  # does not work for IPv6 without brackets; case catched above
606  url = urlparse('sip://' + addr)
607  # TODO Does not compress IPv6, for example 0:0:0:0:0:0:0:0 should get [::]
608  return (url.hostname, url.port)
609 
610 
611 def set_transport_common(section, sip, pjsip, protocol, nmapped):
612  """
613  sip.conf has several global settings that in pjsip.conf apply to individual
614  transports. This function adds these global settings to each individual
615  transport.
616 
617  The settings included are:
618  externaddr (or externip)
619  externhost
620  externtcpport for TCP
621  externtlsport for TLS
622  localnet
623  tos_sip
624  cos_sip
625  """
626  try:
627  extern_addr = sip.multi_get('general', ['externaddr', 'externip',
628  'externhost'])[0]
629  host, port = split_hostport(extern_addr)
630  try:
631  port = sip.get('general', 'extern' + protocol + 'port')[0]
632  except LookupError:
633  pass
634  set_value('external_media_address', host, section, pjsip,
635  nmapped, 'transport')
636  set_value('external_signaling_address', host, section, pjsip,
637  nmapped, 'transport')
638  if port:
639  set_value('external_signaling_port', port, section, pjsip,
640  nmapped, 'transport')
641  except LookupError:
642  pass
643 
644  try:
645  merge_value('localnet', sip.get('general', 'localnet')[0], 'general',
646  pjsip, nmapped, 'transport', section, "local_net")
647  except LookupError:
648  # No localnet options configured. Move on.
649  pass
650 
651  try:
652  set_value('tos', sip.get('general', 'tos_sip')[0], section, pjsip,
653  nmapped, 'transport')
654  except LookupError:
655  pass
656 
657  try:
658  set_value('cos', sip.get('general', 'cos_sip')[0], section, pjsip,
659  nmapped, 'transport')
660  except LookupError:
661  pass
662 
663 
664 def get_bind(sip, pjsip, protocol):
665  """
666  Given the protocol (udp, tcp, or tls), return
667  - the bind address, like [::] or 0.0.0.0
668  - name of the section to be created
669  """
670  section = 'transport-' + protocol
671 
672  # UDP cannot be disabled in chan_sip
673  if protocol != 'udp':
674  try:
675  enabled = sip.get('general', protocol + 'enable')[0]
676  except LookupError:
677  # No value means disabled by default. Don't create this transport
678  return (None, section)
679  if enabled != 'yes':
680  return (None, section)
681 
682  try:
683  bind = pjsip.get(section, 'bind')[0]
684  # The first run created an transport already but this
685  # server was not configured for IPv4/IPv6 Dual Stack
686  return (None, section)
687  except LookupError:
688  pass
689 
690  try:
691  bind = pjsip.get(section + '6', 'bind')[0]
692  # The first run created an IPv6 transport, because
693  # the server was configured with :: as bindaddr.
694  # Now, re-use its port and create the IPv4 transport
695  host, port = split_hostport(bind)
696  bind = '0.0.0.0'
697  if port:
698  bind += ':' + str(port)
699  except LookupError:
700  # This is the first run, no transport in pjsip exists.
701  try:
702  bind = sip.get('general', protocol + 'bindaddr')[0]
703  except LookupError:
704  if protocol == 'udp':
705  try:
706  bind = sip.get('general', 'bindaddr')[0]
707  except LookupError:
708  bind = '0.0.0.0'
709  else:
710  try:
711  bind = pjsip.get('transport-udp6', 'bind')[0]
712  except LookupError:
713  bind = pjsip.get('transport-udp', 'bind')[0]
714  # Only TCP reuses host:port of UDP, others reuse just host
715  if protocol == 'tls':
716  bind, port = split_hostport(bind)
717  host, port = split_hostport(bind)
718  if host == '::':
719  section += '6'
720 
721  if protocol == 'udp':
722  host = build_host(sip, bind, 'general', 'bindport')
723  else:
724  host = build_host(sip, bind)
725 
726  return (host, section)
727 
728 
729 def create_udp(sip, pjsip, nmapped):
730  """
731  Creates a 'transport-udp' section in the pjsip.conf file based
732  on the following settings from sip.conf:
733 
734  bindaddr (or udpbindaddr)
735  bindport
736  """
737  protocol = 'udp'
738  bind, section = get_bind(sip, pjsip, protocol)
739 
740  set_value('protocol', protocol, section, pjsip, nmapped, 'transport')
741  set_value('bind', bind, section, pjsip, nmapped, 'transport')
742  set_transport_common(section, sip, pjsip, protocol, nmapped)
743 
744 
745 def create_tcp(sip, pjsip, nmapped):
746  """
747  Creates a 'transport-tcp' section in the pjsip.conf file based
748  on the following settings from sip.conf:
749 
750  tcpenable
751  tcpbindaddr (or bindaddr)
752  """
753  protocol = 'tcp'
754  bind, section = get_bind(sip, pjsip, protocol)
755  if not bind:
756  return
757 
758  set_value('protocol', protocol, section, pjsip, nmapped, 'transport')
759  set_value('bind', bind, section, pjsip, nmapped, 'transport')
760  set_transport_common(section, sip, pjsip, protocol, nmapped)
761 
762 
763 def set_tls_cert_file(val, pjsip, section, nmapped):
764  """Sets cert_file based on sip.conf tlscertfile"""
765  set_value('cert_file', val, section, pjsip, nmapped,
766  'transport')
767 
768 
769 def set_tls_private_key(val, pjsip, section, nmapped):
770  """Sets privkey_file based on sip.conf tlsprivatekey or sslprivatekey"""
771  set_value('priv_key_file', val, section, pjsip, nmapped,
772  'transport')
773 
774 
775 def set_tls_cipher(val, pjsip, section, nmapped):
776  """Sets cipher based on sip.conf tlscipher or sslcipher"""
777  if val == 'ALL':
778  return
779  print('chan_sip ciphers do not match 1:1 with PJSIP ciphers.' \
780  ' You should manually review and adjust this.', file=sys.stderr)
781 
782  set_value('cipher', val, section, pjsip, nmapped, 'transport')
783 
784 
785 def set_tls_cafile(val, pjsip, section, nmapped):
786  """Sets ca_list_file based on sip.conf tlscafile"""
787  set_value('ca_list_file', val, section, pjsip, nmapped,
788  'transport')
789 
790 
791 def set_tls_capath(val, pjsip, section, nmapped):
792  """Sets ca_list_path based on sip.conf tlscapath"""
793  set_value('ca_list_path', val, section, pjsip, nmapped,
794  'transport')
795 
796 
797 def set_tls_verifyclient(val, pjsip, section, nmapped):
798  """Sets verify_client based on sip.conf tlsverifyclient"""
799  set_value('verify_client', val, section, pjsip, nmapped,
800  'transport')
801 
802 
803 def set_tls_verifyserver(val, pjsip, section, nmapped):
804  """Sets verify_server based on sip.conf tlsdontverifyserver"""
805 
806  if val == 'no':
807  set_value('verify_server', 'yes', section, pjsip, nmapped,
808  'transport')
809  else:
810  set_value('verify_server', 'no', section, pjsip, nmapped,
811  'transport')
812 
813 
814 def create_tls(sip, pjsip, nmapped):
815  """
816  Creates a 'transport-tls' section in pjsip.conf based on the following
817  settings from sip.conf:
818 
819  tlsenable (or sslenable)
820  tlsbindaddr (or sslbindaddr or bindaddr)
821  tlsprivatekey (or sslprivatekey)
822  tlscipher (or sslcipher)
823  tlscafile
824  tlscapath (or tlscadir)
825  tlscertfile (or sslcert or tlscert)
826  tlsverifyclient
827  tlsdontverifyserver
828  tlsclientmethod (or sslclientmethod)
829  """
830  protocol = 'tls'
831  bind, section = get_bind(sip, pjsip, protocol)
832  if not bind:
833  return
834 
835  set_value('protocol', protocol, section, pjsip, nmapped, 'transport')
836  set_value('bind', bind, section, pjsip, nmapped, 'transport')
837  set_transport_common(section, sip, pjsip, protocol, nmapped)
838 
839  tls_map = [
840  (['tlscertfile', 'sslcert', 'tlscert'], set_tls_cert_file),
841  (['tlsprivatekey', 'sslprivatekey'], set_tls_private_key),
842  (['tlscipher', 'sslcipher'], set_tls_cipher),
843  (['tlscafile'], set_tls_cafile),
844  (['tlscapath', 'tlscadir'], set_tls_capath),
845  (['tlsverifyclient'], set_tls_verifyclient),
846  (['tlsdontverifyserver'], set_tls_verifyserver)
847  ]
848 
849  for i in tls_map:
850  try:
851  i[1](sip.multi_get('general', i[0])[0], pjsip, section, nmapped)
852  except LookupError:
853  pass
854 
855  try:
856  method = sip.multi_get('general', ['tlsclientmethod',
857  'sslclientmethod'])[0]
858  if section != 'transport-' + protocol + '6': # print only once
859  print('In chan_sip, you specified the TLS version. With chan_sip,' \
860  ' this was just for outbound client connections. In' \
861  ' chan_pjsip, this value is for client and server. Instead,' \
862  ' consider not to specify \'tlsclientmethod\' for chan_sip' \
863  ' and \'method = sslv23\' for chan_pjsip.', file=sys.stderr)
864  except LookupError:
865  """
866  OpenSSL emerged during the 90s. SSLv2 and SSLv3 were the only
867  existing methods at that time. The OpenSSL project continued. And as
868  of today (OpenSSL 1.0.2) this does not start SSLv2 and SSLv3 anymore
869  but TLSv1.0 and v1.2. Or stated differently: This method should
870  have been called 'method = secure' or 'method = automatic' back in
871  the 90s. The PJProject did not realize this and uses 'tlsv1' as
872  default when unspecified, which disables TLSv1.2. chan_sip used
873  'sslv23' as default when unspecified, which gives TLSv1.0 and v1.2.
874  """
875  method = 'sslv23'
876  set_value('method', method, section, pjsip, nmapped, 'transport')
877 
878 
879 def map_transports(sip, pjsip, nmapped):
880  """
881  Finds options in sip.conf general section pertaining to
882  transport configuration and creates appropriate transport
883  configuration sections in pjsip.conf.
884 
885  sip.conf only allows a single UDP transport, TCP transport,
886  and TLS transport for each IP version. As such, the mapping
887  into PJSIP can be made consistent by defining six sections:
888 
889  transport-udp6
890  transport-udp
891  transport-tcp6
892  transport-tcp
893  transport-tls6
894  transport-tls
895 
896  To accommodate the default behaviors in sip.conf, we'll need to
897  create the UDP transports first, followed by the TCP and TLS transports.
898  """
899 
900  # First create a UDP transport. Even if no bind parameters were provided
901  # in sip.conf, chan_sip would always bind to UDP 0.0.0.0:5060
902  create_udp(sip, pjsip, nmapped)
903  create_udp(sip, pjsip, nmapped)
904 
905  # TCP settings may be dependent on UDP settings, so do it second.
906  create_tcp(sip, pjsip, nmapped)
907  create_tcp(sip, pjsip, nmapped)
908  create_tls(sip, pjsip, nmapped)
909  create_tls(sip, pjsip, nmapped)
910 
911 
912 def map_auth(sip, pjsip, nmapped):
913  """
914  Creates auth sections based on entries in the authentication section of
915  sip.conf. pjsip.conf section names consist of "auth_" followed by the name
916  of the realm.
917  """
918  try:
919  auths = sip.get('authentication', 'auth')
920  except LookupError:
921  return
922 
923  for i in auths:
924  creds, at, realm = i.partition('@')
925  if not at and not realm:
926  # Invalid. Move on
927  continue
928  user, colon, secret = creds.partition(':')
929  if not secret:
930  user, sharp, md5 = creds.partition('#')
931  if not md5:
932  #Invalid. move on
933  continue
934  section = "auth_" + realm
935 
936  set_value('realm', realm, section, pjsip, nmapped, 'auth')
937  set_value('username', user, section, pjsip, nmapped, 'auth')
938  if secret:
939  set_value('password', secret, section, pjsip, nmapped, 'auth')
940  else:
941  set_value('md5_cred', md5, section, pjsip, nmapped, 'auth')
942  set_value('auth_type', 'md5', section, pjsip, nmapped, 'auth')
943 
944 
946  """
947  Class for parsing and storing information in a register line in sip.conf.
948  """
949  def __init__(self, line, retry_interval, max_attempts, outbound_proxy):
950  self.retry_interval = retry_interval
951  self.max_attempts = max_attempts
952  self.outbound_proxy = outbound_proxy
953  self.parse(line)
954 
955  def parse(self, line):
956  """
957  Initial parsing routine for register lines in sip.conf.
958 
959  This splits the line into the part before the host, and the part
960  after the '@' symbol. These two parts are then passed to their
961  own parsing routines
962  """
963 
964  # register =>
965  # [peer?][transport://]user[@domain][:secret[:authuser]]@host[:port][/extension][~expiry]
966 
967  prehost, at, host_part = line.rpartition('@')
968  if not prehost:
969  raise
970 
971  self.parse_host_part(host_part)
972  self.parse_user_part(prehost)
973 
974  def parse_host_part(self, host_part):
975  """
976  Parsing routine for the part after the final '@' in a register line.
977  The strategy is to use partition calls to peel away the data starting
978  from the right and working to the left.
979  """
980  pre_expiry, sep, expiry = host_part.partition('~')
981  pre_extension, sep, self.extension = pre_expiry.partition('/')
982  self.host, sep, self.port = pre_extension.partition(':')
983 
984  self.expiry = expiry if expiry else '120'
985 
986  def parse_user_part(self, user_part):
987  """
988  Parsing routine for the part before the final '@' in a register line.
989  The only mandatory part of this line is the user portion. The strategy
990  here is to start by using partition calls to remove everything to
991  the right of the user, then finish by using rpartition calls to remove
992  everything to the left of the user.
993  """
994  self.peer = ''
995  self.protocol = 'udp'
996  protocols = ['udp', 'tcp', 'tls']
997  for protocol in protocols:
998  position = user_part.find(protocol + '://')
999  if -1 < position:
1000  post_transport = user_part[position + 6:]
1001  self.peer, sep, self.protocol = user_part[:position + 3].rpartition('?')
1002  user_part = post_transport
1003  break
1004 
1005  colons = user_part.count(':')
1006  if (colons == 3):
1007  # :domainport:secret:authuser
1008  pre_auth, sep, port_auth = user_part.partition(':')
1009  self.domainport, sep, auth = port_auth.partition(':')
1010  self.secret, sep, self.authuser = auth.partition(':')
1011  elif (colons == 2):
1012  # :secret:authuser
1013  pre_auth, sep, auth = user_part.partition(':')
1014  self.secret, sep, self.authuser = auth.partition(':')
1015  elif (colons == 1):
1016  # :secret
1017  pre_auth, sep, self.secret = user_part.partition(':')
1018  elif (colons == 0):
1019  # No port, secret, or authuser
1020  pre_auth = user_part
1021  else:
1022  # Invalid setting
1023  raise
1024 
1025  self.user, sep, self.domain = pre_auth.partition('@')
1026 
1027  def write(self, pjsip, nmapped):
1028  """
1029  Write parsed registration data into a section in pjsip.conf
1030 
1031  Most of the data in self will get written to a registration section.
1032  However, there will also need to be an auth section created if a
1033  secret or authuser is present.
1034 
1035  General mapping of values:
1036  A combination of self.host and self.port is server_uri
1037  A combination of self.user, self.domain, and self.domainport is
1038  client_uri
1039  self.expiry is expiration
1040  self.extension is contact_user
1041  self.protocol will map to one of the mapped transports
1042  self.secret and self.authuser will result in a new auth section, and
1043  outbound_auth will point to that section.
1044  XXX self.peer really doesn't map to anything :(
1045  """
1046 
1047  section = 'reg_' + self.host
1048 
1049  set_value('retry_interval', self.retry_interval, section, pjsip,
1050  nmapped, 'registration')
1051  set_value('max_retries', self.max_attempts, section, pjsip, nmapped,
1052  'registration')
1053  if self.extension:
1054  set_value('contact_user', self.extension, section, pjsip, nmapped,
1055  'registration')
1056 
1057  set_value('expiration', self.expiry, section, pjsip, nmapped,
1058  'registration')
1059 
1060  if self.protocol == 'udp':
1061  set_value('transport', 'transport-udp', section, pjsip, nmapped,
1062  'registration')
1063  elif self.protocol == 'tcp':
1064  set_value('transport', 'transport-tcp', section, pjsip, nmapped,
1065  'registration')
1066  elif self.protocol == 'tls':
1067  set_value('transport', 'transport-tls', section, pjsip, nmapped,
1068  'registration')
1069 
1070  auth_section = 'auth_reg_' + self.host
1071 
1072  if hasattr(self, 'secret') and self.secret:
1073  set_value('password', self.secret, auth_section, pjsip, nmapped,
1074  'auth')
1075  set_value('username', self.authuser if hasattr(self, 'authuser')
1076  else self.user, auth_section, pjsip, nmapped, 'auth')
1077  set_value('outbound_auth', auth_section, section, pjsip, nmapped,
1078  'registration')
1079 
1080  client_uri = "sip:%s@" % self.user
1081  if self.domain:
1082  client_uri += self.domain
1083  else:
1084  client_uri += self.host
1085 
1086  if hasattr(self, 'domainport') and self.domainport:
1087  client_uri += ":" + self.domainport
1088  elif self.port:
1089  client_uri += ":" + self.port
1090 
1091  set_value('client_uri', client_uri, section, pjsip, nmapped,
1092  'registration')
1093 
1094  server_uri = "sip:%s" % self.host
1095  if self.port:
1096  server_uri += ":" + self.port
1097 
1098  set_value('server_uri', server_uri, section, pjsip, nmapped,
1099  'registration')
1100 
1101  if self.outbound_proxy:
1102  set_value('outboundproxy', self.outbound_proxy, section, pjsip,
1103  nmapped, 'registration')
1104 
1105 
1106 def map_registrations(sip, pjsip, nmapped):
1107  """
1108  Gathers all necessary outbound registration data in sip.conf and creates
1109  corresponding registration sections in pjsip.conf
1110  """
1111  try:
1112  regs = sip.get('general', 'register')
1113  except LookupError:
1114  return
1115 
1116  try:
1117  retry_interval = sip.get('general', 'registertimeout')[0]
1118  except LookupError:
1119  retry_interval = '20'
1120 
1121  try:
1122  max_attempts = sip.get('general', 'registerattempts')[0]
1123  except LookupError:
1124  max_attempts = '10'
1125 
1126  try:
1127  outbound_proxy = sip.get('general', 'outboundproxy')[0]
1128  except LookupError:
1129  outbound_proxy = ''
1130 
1131  for i in regs:
1132  reg = Registration(i, retry_interval, max_attempts, outbound_proxy)
1133  reg.write(pjsip, nmapped)
1134 
1135 
1136 def map_setvars(sip, section, pjsip, nmapped):
1137  """
1138  Map all setvar in peer section to the appropriate endpoint set_var
1139  """
1140  try:
1141  setvars = sip.section(section)[0].get('setvar')
1142  except LookupError:
1143  return
1144 
1145  for setvar in setvars:
1146  set_value('set_var', setvar, section, pjsip, nmapped)
1147 
1148 
1149 def map_peer(sip, section, pjsip, nmapped):
1150  """
1151  Map the options from a peer section in sip.conf into the appropriate
1152  sections in pjsip.conf
1153  """
1154  for i in peer_map:
1155  try:
1156  # coming from sip.conf the values should mostly be a list with a
1157  # single value. In the few cases that they are not a specialized
1158  # function (see merge_value) is used to retrieve the values.
1159  i[1](i[0], sip.get(section, i[0])[0], section, pjsip, nmapped)
1160  except LookupError:
1161  pass # key not found in sip.conf
1162 
1163  setup_udptl(section, pjsip, nmapped)
1164 
1165 def find_non_mapped(sections, nmapped):
1166  """
1167  Determine sip.conf options that were not properly mapped to pjsip.conf
1168  options.
1169  """
1170  for section, sect in sections.iteritems():
1171  try:
1172  # since we are pulling from sip.conf this should always
1173  # be a single value list
1174  sect = sect[0]
1175  # loop through the section and store any values that were not
1176  # mapped
1177  for key in sect.keys(True):
1178  for i in peer_map:
1179  if i[0] == key:
1180  break
1181  else:
1182  nmapped(section, key, sect[key])
1183  except LookupError:
1184  pass
1185 
1186 
1187 def map_system(sip, pjsip, nmapped):
1188  section = 'system' # Just a label; you as user can change that
1189  type = 'system' # Not a label, therefore not the same as section
1190 
1191  try:
1192  user_agent = sip.get('general', 'useragent')[0]
1193  set_value('user_agent', user_agent, 'global', pjsip, nmapped, 'global')
1194  except LookupError:
1195  pass
1196 
1197 
1198  try:
1199  sipdebug = sip.get('general', 'sipdebug')[0]
1200  set_value('debug', sipdebug, 'global', pjsip, nmapped, 'global')
1201  except LookupError:
1202  pass
1203 
1204  try:
1205  useroption_parsing = sip.get('general', 'legacy_useroption_parsing')[0]
1206  set_value('ignore_uri_user_options', useroption_parsing, 'global', pjsip, nmapped, 'global')
1207  except LookupError:
1208  pass
1209 
1210  try:
1211  timer_t1 = sip.get('general', 'timert1')[0]
1212  set_value('timer_t1', timer_t1, section, pjsip, nmapped, type)
1213  except LookupError:
1214  pass
1215 
1216  try:
1217  timer_b = sip.get('general', 'timerb')[0]
1218  set_value('timer_b', timer_b, section, pjsip, nmapped, type)
1219  except LookupError:
1220  pass
1221 
1222  try:
1223  compact_headers = sip.get('general', 'compactheaders')[0]
1224  set_value('compact_headers', compact_headers, section, pjsip, nmapped, type)
1225  except LookupError:
1226  pass
1227 
1228 
1229 def convert(sip, filename, non_mappings, include):
1230  """
1231  Entry point for configuration file conversion. This
1232  function will create a pjsip.conf object and begin to
1233  map specific sections from sip.conf into it.
1234  Returns the new pjsip.conf object once completed
1235  """
1236  pjsip = sip.__class__()
1237  non_mappings[filename] = astdicts.MultiOrderedDict()
1238  nmapped = non_mapped(non_mappings[filename])
1239  if not include:
1240  # Don't duplicate transport and registration configs
1241  map_system(sip, pjsip, nmapped)
1242  map_transports(sip, pjsip, nmapped)
1243  map_registrations(sip, pjsip, nmapped)
1244  map_auth(sip, pjsip, nmapped)
1245  for section in sip.sections():
1246  if section == 'authentication':
1247  pass
1248  else:
1249  map_peer(sip, section, pjsip, nmapped)
1250  map_setvars(sip, section, pjsip, nmapped)
1251 
1252  find_non_mapped(sip.defaults(), nmapped)
1253  find_non_mapped(sip.sections(), nmapped)
1254 
1255  for key, val in sip.includes().iteritems():
1256  pjsip.add_include(PREFIX + key, convert(val, PREFIX + key,
1257  non_mappings, True)[0])
1258  return pjsip, non_mappings
1259 
1260 
1261 def write_pjsip(filename, pjsip, non_mappings):
1262  """
1263  Write pjsip.conf file to disk
1264  """
1265  try:
1266  with open(filename, 'wt') as fp:
1267  fp.write(';--\n')
1268  fp.write(';;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n')
1269  fp.write('Non mapped elements start\n')
1270  fp.write(';;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n\n')
1271  astconfigparser.write_dicts(fp, non_mappings[filename])
1272  fp.write(';;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n')
1273  fp.write('Non mapped elements end\n')
1274  fp.write(';;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n')
1275  fp.write('--;\n\n')
1276  # write out include file(s)
1277  pjsip.write(fp)
1278 
1279  except IOError:
1280  print("Could not open file " + filename + " for writing", file=sys.stderr)
1281 
1282 ###############################################################################
1283 
1284 
1286  """
1287  Parse command line options and apply them. If invalid input is given,
1288  print usage information
1289  """
1290  global PREFIX
1291  global QUIET
1292  usage = "usage: %prog [options] [input-file [output-file]]\n\n" \
1293  "Converts the chan_sip configuration input-file to the chan_pjsip output-file.\n" \
1294  "The input-file defaults to 'sip.conf'.\n" \
1295  "The output-file defaults to 'pjsip.conf'."
1296  parser = optparse.OptionParser(usage=usage)
1297  parser.add_option('-p', '--prefix', dest='prefix', default=PREFIX,
1298  help='output prefix for include files')
1299  parser.add_option('-q', '--quiet', dest='quiet', default=False, action='store_true',
1300  help="don't print messages to stdout")
1301 
1302  options, args = parser.parse_args()
1303  PREFIX = options.prefix
1304  if options.quiet:
1305  QUIET = True
1306 
1307  sip_filename = args[0] if len(args) else 'sip.conf'
1308  pjsip_filename = args[1] if len(args) == 2 else 'pjsip.conf'
1309 
1310  return sip_filename, pjsip_filename
1311 
1312 
1313 def info(msg):
1314  if QUIET:
1315  return
1316  print(msg)
1317 
1318 
1319 if __name__ == "__main__":
1320  sip_filename, pjsip_filename = cli_options()
1321  # configuration parser for sip.conf
1323  info('Please, report any issue at:')
1324  info(' https://issues.asterisk.org/')
1325  info('Reading ' + sip_filename)
1326  sip.read(sip_filename)
1327  info('Converting to PJSIP...')
1328  pjsip, non_mappings = convert(sip, pjsip_filename, dict(), False)
1329  info('Writing ' + pjsip_filename)
1330  write_pjsip(pjsip_filename, pjsip, non_mappings)
def __init__(self, line, retry_interval, max_attempts, outbound_proxy)
def set_dtmfmode(key, val, section, pjsip, nmapped)
mapping functions - define f(key, val, section) where key/val are the key/value pair to write to give...
def set_tls_cipher(val, pjsip, section, nmapped)
def section_by_type(section, pjsip, type)
some utility functions
Definition: sip_to_pjsip.py:23
def from_progressinband(key, val, section, pjsip, nmapped)
def from_recordfeature(key, val, section, pjsip, nmapped)
def set_tls_private_key(val, pjsip, section, nmapped)
def set_value(key=None, val=None, section=None, pjsip=None, nmapped=None, type='endpoint')
Definition: sip_to_pjsip.py:53
def set_tls_capath(val, pjsip, section, nmapped)
def split_hostport(addr)
def from_sendrpid(key, val, section, pjsip, nmapped)
def parse_host_part(self, host_part)
def set_timers(key, val, section, pjsip, nmapped)
const char * str
Definition: app_jack.c:147
def set_record_off_feature(key, val, section, pjsip, nmapped)
def create_tls(sip, pjsip, nmapped)
def map_peer(sip, section, pjsip, nmapped)
def from_mailbox(key, val, section, pjsip, nmapped)
def write(self, pjsip, nmapped)
def map_auth(sip, pjsip, nmapped)
def convert(sip, filename, non_mappings, include)
def map_system(sip, pjsip, nmapped)
def set_tls_cert_file(val, pjsip, section, nmapped)
def map_transports(sip, pjsip, nmapped)
def merge_value(key=None, val=None, section=None, pjsip=None, nmapped=None, type='endpoint', section_to=None, key_to=None)
Definition: sip_to_pjsip.py:70
def parse_user_part(self, user_part)
def write_pjsip(filename, pjsip, non_mappings)
def setup_auth(key, val, section, pjsip, nmapped)
def set_media_encryption(key, val, section, pjsip, nmapped)
def merge_codec_value(key=None, val=None, section=None, pjsip=None, nmapped=None, type='endpoint', section_to=None, key_to=None)
Definition: sip_to_pjsip.py:93
def info(msg)
static int len(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t buflen)
def find_non_mapped(sections, nmapped)
def ignore(key=None, val=None, section=None, pjsip=None, nmapped=None, type='endpoint')
Definition: sip_to_pjsip.py:48
def map_setvars(sip, section, pjsip, nmapped)
def create_udp(sip, pjsip, nmapped)
def build_host(config, host, section='general', port_key=None)
def from_host(key, val, section, pjsip, nmapped)
def non_mapped(nmapped)
def from_encryption_taglen(key, val, section, pjsip, nmapped)
def from_dtlsenable(key, val, section, pjsip, nmapped)
def set_direct_media(key, val, section, pjsip, nmapped)
def set_record_on_feature(key, val, section, pjsip, nmapped)
def map_registrations(sip, pjsip, nmapped)
def write_dicts(config_file, mdicts)
def setup_ident(key, val, section, pjsip, nmapped)
def set_tls_cafile(val, pjsip, section, nmapped)
def from_nat(key, val, section, pjsip, nmapped)
def set_tls_verifyclient(val, pjsip, section, nmapped)
def set_transport_common(section, sip, pjsip, protocol, nmapped)
def set_tls_verifyserver(val, pjsip, section, nmapped)
def setup_udptl(section, pjsip, nmapped)
def get_bind(sip, pjsip, protocol)
def create_tcp(sip, pjsip, nmapped)