Passed
Push — develop ( 75d248...0e30e9 )
by Портнов
04:41
created

generateAdditionalModulesAllPeersContext()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 1
c 0
b 0
f 0
dl 0
loc 3
rs 10
cc 1
nc 1
nop 0
1
<?php
2
/*
3
 * MikoPBX - free phone system for small business
4
 * Copyright (C) 2017-2021 Alexey Portnov and Nikolay Beketov
5
 *
6
 * This program is free software: you can redistribute it and/or modify
7
 * it under the terms of the GNU General Public License as published by
8
 * the Free Software Foundation; either version 3 of the License, or
9
 * (at your option) any later version.
10
 *
11
 * This program is distributed in the hope that it will be useful,
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
 * GNU General Public License for more details.
15
 *
16
 * You should have received a copy of the GNU General Public License along with this program.
17
 * If not, see <https://www.gnu.org/licenses/>.
18
 */
19
20
namespace MikoPBX\Core\Asterisk\Configs\Generators\Extensions;
21
22
23
use MikoPBX\Core\Asterisk\Configs\ExtensionsConf;
24
use MikoPBX\Core\Asterisk\Configs\SIPConf;
25
use MikoPBX\Core\Asterisk\Configs\CoreConfigClass;
26
27
class InternalContexts extends CoreConfigClass
28
{
29
30
    private string $technology;
31
    private string $extensionPattern = 'X!';
32
33
    /**
34
     * Генератор входящих контекстов. Точка входа.
35
     *
36
     * @return string
37
     */
38
    public static function generate(): string
39
    {
40
        $generator = new self();
41
        $generator->getSettings();
42
43
        return $generator->makeDialplan();
44
    }
45
46
    public function getSettings(): void
47
    {
48
        $this->technology = SIPConf::getTechnology();
49
    }
50
51
    /**
52
     * Генерация dialplan.
53
     *
54
     * @return string
55
     */
56
    public function makeDialplan(): string
57
    {
58
        $conf = $this->generateAdditionalModulesContext();
59
60
        $conf .= "[internal-num-undefined] \n";
61
        $conf .= 'exten => _' . $this->extensionPattern . ',1,ExecIf($["${ISTRANSFER}x" != "x"]?Goto(transfer_dial_hangup,${EXTEN},1))' . "\n\t";
62
        $conf .= 'same => n,ExecIf($["${BLINDTRANSFER}x" != "x"]?AGI(check_redirect.php,${BLINDTRANSFER}))' . "\n\t";
63
        $conf .= "same => n,Playback(pbx-invalid,noanswer) \n\n";
64
65
        $conf .= '[set-dial-contacts]'.PHP_EOL.
66
                 'exten => _X!,1,NoOp()'.PHP_EOL."\t";
67
68
        if($this->generalSettings['UseWebRTC'] === '1') {
69
            $conf .= 'same => n,Set(SIP_CONTACT=${PJSIP_DIAL_CONTACTS(${EXTEN})})'.PHP_EOL."\t".
70
                     'same => n,Set(WS_CONTACTS=${PJSIP_DIAL_CONTACTS(${EXTEN}-WS)})'.PHP_EOL."\t".
71
                     'same => n,Set(DST_CONTACT=${SIP_CONTACT}${IF($["${SIP_CONTACT}x" != "x" && "${WS_CONTACTS}x" != "x"]?&)}${WS_CONTACTS})'.PHP_EOL."\t";
72
        }else{
73
            $conf .= 'same => n,Set(DST_CONTACT=${PJSIP_DIAL_CONTACTS(${EXTEN})})'.PHP_EOL."\t";
74
        }
75
        $conf .= 'same => n,return'.PHP_EOL.PHP_EOL;
76
77
        $conf .= $this->generateInternalFW();
78
        $conf .= $this->generateAllPeers();
79
        $conf .= $this->generateInternal();
80
        $conf .= $this->generateInternalUsers();
81
82
        return $conf;
83
    }
84
85
    /**
86
     * Генератор dialplan дополнительных модулей.
87
     *
88
     * @return string
89
     */
90
    private function generateAdditionalModulesContext(): string
91
    {
92
        $conf = $this->hookModulesMethod(CoreConfigClass::EXTENSION_GEN_CONTEXTS);
93
        $conf .= "\n";
94
95
        return $conf;
96
    }
97
98
    /**
99
     * Генератор [internal-fw] dialplan.
100
     *
101
     * @return string
102
     */
103
    private function generateInternalFW(): string
104
    {
105
        $conf = "[internal-fw]\n";
106
        $conf .= 'exten => _' . $this->extensionPattern . ',1,NoOp(DIALSTATUS - ${DIALSTATUS})' . "\n\t";
107
        // CANCEL - вызов был отменен, к примеру *0, не нужно дальше искать адресат.
108
        $conf .= 'same => n,ExecIf($["${DIALSTATUS}" == "CANCEL"]?Hangup())' . "\n\t";
109
        // BUSY - занято. К примру абонент завершил вызов или DND.
110
        $conf .= 'same => n,ExecIf($["${DIALSTATUS}" == "BUSY"]?Set(dstatus=FW_BUSY))' . "\n\t";
111
        // CHANUNAVAIL - канал не доступен. К примеру телефон не зарегистрирован или не отвечает.
112
        $conf .= 'same => n,ExecIf($["${DIALSTATUS}" == "CHANUNAVAIL"]?Set(dstatus=FW_UNAV))' . "\n\t";
113
        // NOANSWER - не ответили по таймауту.
114
        $conf .= 'same => n,ExecIf($["${dstatus}x" == "x"]?Set(dstatus=FW))' . "\n\t";
115
        $conf .= 'same => n,Set(fw=${DB(${dstatus}/${EXTEN})})' . "\n\t";
116
        $conf .= 'same => n,ExecIf($["${fw}x" != "x"]?Set(__pt1c_UNIQUEID=${UNDEFINED})' . "\n\t";
117
        $conf .= 'same => n,ExecIf($["${fw}x" != "x"]?Goto(internal,${fw},1))' . "\n\t";
118
        $conf .= 'same => n,ExecIf($["${BLINDTRANSFER}x" != "x"]?AGI(check_redirect.php,${BLINDTRANSFER}))' . "\n\t";
119
        $conf .= 'same => n,ExecIf($["${DIALSTATUS}" == "BUSY"]?Busy(2):Hangup())' . "\n\n";
120
121
        return $conf;
122
    }
123
124
    /**
125
     * Генератор [all_peers] dialplan.
126
     *
127
     * @return string
128
     */
129
    private function generateAllPeers(): string
130
    {
131
        $conf = "[all_peers]\n";
132
        $conf .= 'include => internal-hints' . "\n";
133
        $conf .= 'exten => failed,1,Hangup()' . "\n";
134
        $conf .= 'exten => ' . ExtensionsConf::ALL_NUMBER_EXTENSION . ',1,ExecIf($[ "${ORIGINATE_SRC_CHANNEL}x" != "x" ]?Wait(0.2))' . PHP_EOL . "\t";
135
        $conf .= 'same => n,ExecIf($[ "${ORIGINATE_SRC_CHANNEL}x" != "x" ]?ChannelRedirect(${ORIGINATE_SRC_CHANNEL},${CONTEXT},${ORIGINATE_DST_EXTEN},1))' . PHP_EOL . "\t";
136
        $conf .= 'same => n,ExecIf($[ "${ORIGINATE_SRC_CHANNEL}x" != "x" ]?Hangup())' . PHP_EOL . "\t";
137
138
        // Фильтр спецсимволов. Разершаем только цифры.
139
        $conf .= 'same => n,Set(cleanNumber=${FILTER(\*\#\+1234567890,${EXTEN})})' . "\n\t";
140
        $conf .= 'same => n,ExecIf($["${EXTEN}" != "${cleanNumber}"]?Goto(${CONTEXT},${cleanNumber},$[${PRIORITY} + 1]))' . "\n\t";
141
142
        $conf .= $this->generateAdditionalModulesAllPeersContext();
143
        $conf .= 'same => n,Set(__FROM_CHAN=${CHANNEL})' . "\n\t";
144
        $conf .= 'same => n,Set(__M_CALLID=${CHANNEL(callid)})' . "\n\t";
145
        $conf .= 'same => n,ExecIf($["${OLD_LINKEDID}x" == "x"]?Set(__OLD_LINKEDID=${CHANNEL(linkedid)}))' . "\n\t";
146
        $conf .= 'same => n,ExecIf($["${CHANNEL(channeltype)}" != "Local"]?Gosub(set_from_peer,s,1))' . "\n\t";
147
        $conf .= 'same => n,ExecIf($["${CHANNEL(channeltype)}" == "Local"]?Gosub(set_orign_chan,s,1))' . "\n\t";
148
149
        $conf .= 'same => n,ExecIf($["${CALLERID(num)}x" == "x"]?Set(CALLERID(num)=${FROM_PEER}))' . "\n\t";
150
        $conf .= 'same => n,ExecIf($["${CALLERID(num)}x" == "x"]?Set(CALLERID(name)=${FROM_PEER}))' . "\n\t";
151
152
        $conf .= 'same => n,ExecIf($["${CHANNEL(channeltype)}" == "Local" && "${FROM_PEER}x" == "x"]?Set(__FROM_PEER=${CALLERID(num)}))' . "\n\t";
153
        $conf .= 'same => n,Set(CHANNEL(hangup_handler_wipe)=hangup_handler,s,1)' . "\n\t";
154
        $conf .= 'same => n,Gosub(${ISTRANSFER}dial,${EXTEN},1)' . "\n\t";
155
156
        $conf          .= 'same => n,GosubIf($["${DIALPLAN_EXISTS(${CONTEXT}-custom,${EXTEN},1)}" == "1"]?${CONTEXT}-custom,${EXTEN},1)' . "\n\t";
157
        $dialplanNames = ['applications', 'internal', 'outgoing'];
158
        foreach ($dialplanNames as $name) {
159
            $conf .= 'same => n,GosubIf($["${DIALPLAN_EXISTS(' . $name . ',${EXTEN},1)}" == "1"]?' . $name . ',${EXTEN},1)' . " \n\t";
160
        }
161
        $conf .= 'same => n,Hangup()' . " \n";
162
163
        $pickupExtension = $this->generalSettings['PBXFeaturePickupExten'];
164
        if(!empty($pickupExtension)){
165
            $conf            .= 'exten => _' . $pickupExtension . $this->extensionPattern . ',1,Set(PICKUPEER=' . $this->technology . '/${FILTER(0-9,${EXTEN:2})})' . "\n\t";
166
            $conf            .= 'same => n,Set(pt1c_dnid=${EXTEN})' . "\n\t";
167
            $conf            .= 'same => n,PickupChan(${PICKUPEER})' . "\n\t";
168
            $conf            .= 'same => n,Hangup()' . "\n\n";
169
        }
170
        return $conf;
171
    }
172
173
    /**
174
     * Генератор [internal] dialplan.
175
     *
176
     * @return string
177
     */
178
    private function generateInternal(): string
179
    {
180
        // Контекст для внутренних вызовов.
181
        $conf = "[internal] \n";
182
        $conf .= $this->generateAdditionalModulesInternalContext();
183
184
        $conf .= 'exten => i,1,NoOp(-- INVALID NUMBER --)' . "\n\t";
185
        $conf .= 'same => n,Set(DIALSTATUS=INVALID_NUMBER)' . "\n\t";
186
        $conf .= 'same => n,Playback(privacy-incorrect,noanswer)' . "\n\t";
187
        $conf .= 'same => n,Hangup()' . "\n";
188
189
        $conf .= 'exten => h,1,ExecIf($["${ISTRANSFER}x" != "x"]?Goto(transfer_dial_hangup,${EXTEN},1))' . "\n\n";
190
191
        $conf .= 'exten => hangup,1,Set(MASTER_CHANNEL(M_DIALSTATUS)=ANSWER)'.PHP_EOL."\t";
192
        $conf .= 'same => n,Hangup()'.PHP_EOL;
193
        $conf .= 'exten => busy,1,Set(MASTER_CHANNEL(M_DIALSTATUS)=BUSY)'.PHP_EOL."\t";
194
        $conf .= 'same => n,Busy(2)'.PHP_EOL;
195
        $conf .= 'exten => did2user,1,ExecIf($["${FROM_DID}x" != "x" && ${DIALPLAN_EXISTS(internal,${FROM_DID},1)} ]?Goto(internal,${FROM_DID},1))'.PHP_EOL."\t";
196
        $conf .= 'same => n,Hangup()'.PHP_EOL.PHP_EOL;
197
198
199
        $conf .= "[internal-incoming]\n";
200
        $conf .= 'exten => ' . ExtensionsConf::ALL_NUMBER_EXTENSION . ',1,ExecIf($["${MASTER_CHANNEL(M_TIMEOUT)}x" != "x"]?Set(TIMEOUT(absolute)=${MASTER_CHANNEL(M_TIMEOUT)}))' . " \n\t";
201
        $conf .= 'same => n,Set(MASTER_CHANNEL(M_TIMEOUT_CHANNEL)=${CHANNEL})' . " \n\t";
202
        $conf .= 'same => n,Set(MASTER_CHANNEL(M_TIMEOUT)=${EMPTY_VAR})' . " \n\t";
203
        $conf .= 'same => n,Goto(internal,${EXTEN},1)' . " \n\n";
204
205
        return $conf;
206
    }
207
208
    /**
209
     * Переопределение dialplan [internal] из дополнительных модулей.
210
     *
211
     * @return string
212
     */
213
    private function generateAdditionalModulesInternalContext(): string
214
    {
215
        $conf = $this->hookModulesMethod(CoreConfigClass::GET_INCLUDE_INTERNAL);
216
        $conf .= $this->hookModulesMethod(CoreConfigClass::EXTENSION_GEN_INTERNAL);
217
218
        return $conf;
219
    }
220
221
    private function generateAdditionalModulesInternalUsersContext():string
222
    {
223
        return $this->hookModulesMethod(CoreConfigClass::EXTENSION_GEN_INTERNAL_USERS_PRE_DIAL);
224
    }
225
226
    private function generateAdditionalModulesAllPeersContext():string
227
    {
228
        return $this->hookModulesMethod(CoreConfigClass::EXTENSION_GEN_ALL_PEERS_CONTEXT);
229
    }
230
231
232
    /**
233
     * Генератор [internal-users] dialplan.
234
     *
235
     * @return string
236
     */
237
    private function generateInternalUsers(): string
238
    {
239
        $conf = "[internal-users] \n";
240
        $conf .= 'exten => _' . $this->extensionPattern . ',1,Set(CHANNEL(hangup_handler_wipe)=hangup_handler,s,1)' . " \n\t";
241
        $conf .= 'same => n,ExecIf($["${ISTRANSFER}x" != "x"]?Set(SIPADDHEADER01=${EMPTY_VAR})' . " \n\t";
242
        $conf .= 'same => n,ExecIf($["${CHANNEL(channeltype)}" == "Local"]?Gosub(set_orign_chan,s,1))' . " \n\t";
243
244
        $conf .= 'same => n,Gosub(${ISTRANSFER}dial,${EXTEN},1)' . "\n\t";
245
        // Проверим, существует ли такой пир.
246
        $conf .= 'same => n,ExecIf($["${PJSIP_ENDPOINT(${EXTEN},auth)}x" == "x"]?Goto(internal-num-undefined,${EXTEN},1))' . " \n\t";
247
248
        $conf .= $this->generateAdditionalModulesInternalUsersContext();
249
        $conf .= 'same => n,ExecIf($["${DEVICE_STATE(' . $this->technology . '/${EXTEN})}" == "BUSY" || "${DEVICE_STATE(' . $this->technology . '/${EXTEN}-WS)}" == "BUSY"]?Set(IS_BUSY=1))' . " \n\t";
250
        $conf .= 'same => n,ExecIf($["${IS_BUSY}" == "1"]?Set(DIALSTATUS=BUSY))' . " \n\t";
251
        $conf .= 'same => n,GotoIf($["${IS_BUSY}" == "1" && "${QUEUE_SRC_CHAN}x" == "x"]?fw_start)' . " \n\t";
252
253
        // Как долго звонить пиру.
254
        $conf .= 'same => n,Set(ringlength=${DB(FW_TIME/${EXTEN})})' . " \n\t";
255
        $conf .= 'same => n,ExecIf($["${ringlength}x" == "x"]?Set(ringlength=600))' . " \n\t";
256
        $conf .= 'same => n,ExecIf($["${QUEUE_SRC_CHAN}x" != "x" && "${ISTRANSFER}x" == "x"]?Set(ringlength=600))' . " \n\t";
257
258
        $conf .= 'same => n,GosubIf($["${DIALPLAN_EXISTS(${CONTEXT}-custom,${EXTEN},1)}" == "1"]?${CONTEXT}-custom,${EXTEN},1) ' . " \n\t";
259
        // Совершаем вызов пира.
260
        $conf .= 'same => n,Gosub(set-dial-contacts,${EXTEN},1)' . " \n\t";
261
        $conf .= 'same => n,ExecIf($["${FIELDQTY(DST_CONTACT,&)}" != "1"]?Set(__PT1C_SIP_HEADER=${EMPTY_VAR}))' . " \n\t";
262
        $conf .= 'same => n,ExecIf($["${TRANSFER_OPTIONS}x" == "x" || "${ISTRANSFER}x" != "x"]?Set(TRANSFER_OPTIONS=Tt))' . " \n\t";
263
        $conf .= 'same => n,ExecIf($["${DST_CONTACT}x" != "x"]?Dial(${DST_CONTACT},${ringlength},${TRANSFER_OPTIONS}ekKHhU(${ISTRANSFER}dial_answer)b(dial_create_chan,s,1)):Set(DIALSTATUS=CHANUNAVAIL))' . " \n\t";
264
        $conf .= 'same => n,ExecIf($["${DST_CONTACT}x" == "x"]?Gosub(dial_end,s,1))' . " \n\t";
265
        $conf .= 'same => n(fw_start),NoOp(start dial hangup)' . " \n\t";
266
267
        // QUEUE_SRC_CHAN - установлена, если вызов сервершен агенту очереди.
268
        // Проверяем нужна ли переадресация
269
        $conf .= 'same => n,ExecIf($["${DIALSTATUS}" != "ANSWER" && "${ISTRANSFER}x" != "x"]?Goto(internal-fw,${EXTEN},1))' . " \n\t";
270
        $conf .= 'same => n,ExecIf($["${DIALSTATUS}" != "ANSWER" && "${QUEUE_SRC_CHAN}x" == "x"]?Goto(internal-fw,${EXTEN},1))' . " \n\t";
271
        $conf .= 'same => n,ExecIf($["${BLINDTRANSFER}x" != "x"]?AGI(check_redirect.php,${BLINDTRANSFER}))' . " \n\t";
272
        $conf .= 'same => n,Hangup()' . "\n\n";
273
274
        $conf .= 'exten => h,1,ExecIf($["${ISTRANSFER}x" != "x"]?Goto(transfer_dial_hangup,${EXTEN},1))' . "\n\n";
275
276
        return $conf;
277
    }
278
279
}