13444a95c2ef5a806d5f3879c5e59fec910a4f08
[asterisk/asterisk.git] / contrib / scripts / sip_to_res_sip / sip_to_res_sip.py
1 #!/usr/bin/python
2
3 import astconfigparser
4
5 # configuration parser for sip.conf
6 sip = astconfigparser.MultiOrderedConfigParser()
7
8 # configuration writer for res_sip.conf
9 res_sip = astconfigparser.MultiOrderedConfigParser()
10
11 ###############################################################################
12 ### some utility functions
13 ###############################################################################
14 def section_by_type(section, type='endpoint'):
15     """Finds a section based upon the given type, adding it if not found."""
16     try:
17         return astconfigparser.find_dict(
18             res_sip.section(section), 'type', type)
19     except LookupError:
20         # section for type doesn't exist, so add
21         sect = res_sip.add_section(section)
22         sect['type'] = type
23         return sect
24
25 def set_value(key=None, val=None, section=None, type='endpoint'):
26     """Sets the key to the value within the section in res_sip.conf"""
27     def _set_value(k, v, s):
28         set_value(key if key else k, v, s, type)
29
30     # if no value or section return the set_value
31     # function with the enclosed key and type
32     if not val and not section:
33         return _set_value
34
35     # otherwise try to set the value
36     section_by_type(section, type)[key] = val
37
38 def merge_value(key=None, val=None, section=None,
39                 type='endpoint', section_to=None):
40     """Merge values from the given section with those from the default."""
41     def _merge_value(k, v, s):
42         merge_value(key if key else k, v, s, type, section_to)
43
44     # if no value or section return the merge_value
45     # function with the enclosed key and type
46     if not val and not section:
47         return _merge_value
48
49     # should return single section
50     sect = sip.section(section)
51     # for each merged value add it to res_sip.conf
52     for i in sect.get_merged(key):
53         set_value(key, i, section_to if section_to else section, type)
54
55 def is_in(s, sub):
56     """Returns true if 'sub' is in 's'"""
57     return s.find(sub) != -1
58
59 ###############################################################################
60 ### mapping functions -
61 ###      define f(key, val, section) where key/val are the key/value pair to
62 ###      write to given section in res_sip.conf
63 ###############################################################################
64
65 def set_dtmfmode(key, val, section):
66     """Sets the dtmfmode value.  If value matches allowable option in res_sip
67        then map it, otherwise set it to none.
68     """
69     # available res_sip.conf values: frc4733, inband, info, none
70     if val != 'inband' or val != 'info':
71         print "sip.conf: dtmfmode = %s did not fully map into " \
72               "res_sip.conf - setting to 'none'" % val
73         val = 'none'
74     set_value(key, val, section)
75
76 def from_nat(key, val, section):
77     """Sets values from nat into the appropriate res_sip.conf options."""
78     # nat from sip.conf can be comma separated list of values:
79     # yes/no, [auto_]force_rport, [auto_]comedia
80     if is_in(val, 'yes'):
81         set_value('rtp_symmetric', 'yes', section)
82         set_value('rewrite_contact', 'yes', section)
83     if is_in(val, 'comedia'):
84         set_value('rtp_symmetric', 'yes', section)
85     if is_in(val, 'force_rport'):
86         set_value('force_rport', 'yes', section)
87         set_value('rewrite_contact', 'yes', section)
88
89 def set_timers(key, val, section):
90     """Sets the timers in res_sip.conf from the session-timers option
91        found in sip.conf.
92     """
93     # res_sip.conf values can be yes/no, required, always
94     if val == 'originate':
95         set_value('timers', 'always', section)
96     elif val == 'accept':
97         set_value('timers', 'required', section)
98     elif val == 'never':
99         set_value('timers', 'no', section)
100     else:
101         set_value('timers', 'yes', section)
102
103 def set_direct_media(key, val, section):
104     """Maps values from the sip.conf comma separated direct_media option
105        into res_sip.conf direct_media options.
106     """
107     if is_in(val, 'yes'):
108         set_value('direct_media', 'yes', section)
109     if is_in(val, 'update'):
110         set_value('direct_media_method', 'update', section)
111     if is_in(val, 'outgoing'):
112         set_value('directed_media_glare_mitigation', 'outgoing', section)
113     if is_in(val, 'nonat'):
114         set_value('disable_directed_media_on_nat', 'yes', section)
115
116 def from_sendrpid(key, val, section):
117     """Sets the send_rpid/pai values in res_sip.conf."""
118     if val == 'yes' or val == 'rpid':
119         set_value('send_rpid', 'yes', section)
120     elif val == 'pai':
121         set_value('send_pai', 'yes', section)
122
123 def set_media_encryption(key, val, section):
124     """Sets the media_encryption value in res_sip.conf"""
125     if val == 'yes':
126         set_value('media_encryption', 'sdes', section)
127
128 def from_recordfeature(key, val, section):
129     """If record on/off feature is set to automixmon then set
130        one_touch_recording, otherwise it can't be mapped.
131     """
132     if val == 'automixmon':
133         set_value('one_touch_recording', 'yes', section)
134     else:
135         print "sip.conf: %s = %s could not be fully map " \
136               "one_touch_recording not set in res_sip.conf" % (key, val)
137
138 def from_progressinband(key, val, section):
139     """Sets the inband_progress value in res_sip.conf"""
140     # progressinband can = yes/no/never
141     if val == 'never':
142         val = 'no'
143     set_value('inband_progress', val, section)
144
145 def from_host(key, val, section):
146     """Sets contact info in an AOR section in in res_sip.conf using 'host'
147        data from sip.conf
148     """
149     # all aors have the same name as the endpoint so makes
150     # it easy to endpoint's 'aors' value
151     set_value('aors', section, section)
152     if val != 'dynamic':
153         set_value('contact', val, section, 'aor')
154     else:
155         set_value('max_contacts', 1, section, 'aor')
156
157 def from_subscribemwi(key, val, section):
158     """Checks the subscribemwi value in sip.conf.  If yes places the
159        mailbox value in mailboxes within the endpoint, otherwise puts
160        it in the aor.
161     """
162     mailboxes = sip.get('mailbox', section)
163     type = 'endpoint' if val == 'yes' else 'aor'
164     set_value('mailboxes', mailboxes, section, type)
165
166 ###############################################################################
167
168 # options in res_sip.conf on an endpoint that have no sip.conf equivalent:
169 # type, rtp_ipv6, 100rel, trust_id_outbound, aggregate_mwi,
170 # connected_line_method
171
172 # known sip.conf peer keys that can be mapped to a res_sip.conf section/key
173 peer_map = {
174     # sip.conf option      mapping function     res_sip.conf option(s)
175     ###########################################################################
176     'context':            set_value,
177     'dtmfmode':           set_dtmfmode,
178     'disallow':           merge_value,
179     'allow':              merge_value,
180     'nat':                from_nat,            # rtp_symmetric, force_rport,
181                                                # rewrite_contact
182     'icesupport':         set_value('ice_support'),
183     'autoframing':        set_value('use_ptime'),
184     'outboundproxy':      set_value('outbound_proxy'),
185     'mohsuggest':         set_value,
186     'session-timers':     set_timers,          # timers
187     'session-minse':      set_value('timers_min_se'),
188     'session-expires':    set_value('timers_sess_expires'),
189     'externip':           set_value('external_media_address'),
190     'externhost':         set_value('external_media_address'),
191     # identify_by ?
192     'direct_media':       set_direct_media,    # direct_media
193                                                # direct_media_method
194                                                # directed_media_glare_mitigation
195                                                # disable_directed_media_on_nat
196     'callerid':           set_value,           # callerid
197     'callingpres':        set_value('callerid_privacy'),
198     'cid_tag':            set_value('callerid_tag'),
199     'trustpid':           set_value('trust_id_inbound'),
200     'sendrpid':           from_sendrpid,       # send_pai, send_rpid
201     'send_diversion':     set_value,
202     'encrpytion':         set_media_encryption,
203     'use_avpf':           set_value,
204     'recordonfeature':    from_recordfeature,  # automixon
205     'recordofffeature':   from_recordfeature,  # automixon
206     'progressinband':     from_progressinband, # in_band_progress
207     'callgroup':          set_value,
208     'pickupgroup':        set_value,
209     'namedcallgroup':     set_value,
210     'namedpickupgroup':   set_value,
211     'busylevel':          set_value('devicestate_busy_at'),
212
213 ############################ maps to an aor ###################################
214
215     'host':               from_host,           # contact, max_contacts
216     'subscribemwi':       from_subscribemwi,   # mailboxes
217     'qualifyfreq':        set_value('qualify_frequency', type='aor'),
218
219 ############################# maps to auth#####################################
220 #        type = auth
221 #        username
222 #        password
223 #        md5_cred
224 #        realm
225 #        nonce_lifetime
226 #        auth_type
227 ######################### maps to acl/security ################################
228
229     'permit':             merge_value(type='security', section_to='acl'),
230     'deny':               merge_value(type='security', section_to='acl'),
231     'acl':                merge_value(type='security', section_to='acl'),
232     'contactpermit':      merge_value(type='security', section_to='acl'),
233     'contactdeny':        merge_value(type='security', section_to='acl'),
234     'contactacl':         merge_value(type='security', section_to='acl'),
235
236 ########################### maps to transport #################################
237 #        type = transport
238 #        protocol
239 #        bind
240 #        async_operations
241 #        ca_list_file
242 #        cert_file
243 #        privkey_file
244 #        password
245 #        external_signaling_address - externip & externhost
246 #        external_signaling_port
247 #        external_media_address
248 #        domain
249 #        verify_server
250 #        verify_client
251 #        require_client_cert
252 #        method
253 #        cipher
254 #        localnet
255 ######################### maps to domain_alias ################################
256 #        type = domain_alias
257 #        domain
258 ######################### maps to registration ################################
259 #        type = registration
260 #        server_uri
261 #        client_uri
262 #        contact_user
263 #        transport
264 #        outbound_proxy
265 #        expiration
266 #        retry_interval
267 #        max_retries
268 #        auth_rejection_permanent
269 #        outbound_auth
270 ########################### maps to identify ##################################
271 #        type = identify
272 #        endpoint
273 #        match
274 }
275
276 def map_peer(section):
277     for key, fun in peer_map.iteritems():
278         try:
279             fun(key, sip.get(key, section), section)
280         except LookupError:
281             pass
282 #            print "%s not found for section %s - putting nothing in res_sip.conf" % (key, section)
283
284     # since we are pulling from sip.conf this should always return
285     # a single peer value and never a list of peers
286     peer = sip.section(section)
287     # loop through the peer and print out any that can't be mapped
288     # for key in peer.keys():
289     #     if key not in peer_map:
290     #         print "Peer: [{}] {} could not be mapped".format(section, key)
291
292 def convert():
293     for section in sip.sections():
294         if section == 'authentication':
295             pass
296         elif section != 'general':
297             map_peer(section)
298
299 ###############################################################################
300
301 if __name__ == "__main__":
302     sip.read('sip.conf')
303     convert()
304     res_sip.write('res_sip.conf')