]> git.ipfire.org Git - ipfire-2.x.git/blob - src/patches/asterisk-1.2.4-iax2-bristuff-0.3.0-PRE-1l.patch
HinzugefĆ¼gt:
[ipfire-2.x.git] / src / patches / asterisk-1.2.4-iax2-bristuff-0.3.0-PRE-1l.patch
1 diff -urN asterisk-1.2.4/channels/chan_iax2.c asterisk-1.2.4.carrier/channels/chan_iax2.c
2 --- asterisk-1.2.4/channels/chan_iax2.c 2006-01-31 09:41:43.000000000 +0100
3 +++ asterisk-1.2.4.carrier/channels/chan_iax2.c 2006-02-08 09:49:45.000000000 +0100
4 @@ -179,6 +179,8 @@
5 static struct ast_netsock_list *netsock;
6 static int defaultsockfd = -1;
7
8 +static char servername[80];
9 +
10 static int usecnt;
11 AST_MUTEX_DEFINE_STATIC(usecnt_lock);
12
13 @@ -232,6 +234,10 @@
14
15 static pthread_t netthreadid = AST_PTHREADT_NULL;
16
17 +static pthread_t regthreadid = AST_PTHREADT_NULL;
18 +
19 +static pthread_t auththreadid = AST_PTHREADT_NULL;
20 +
21 enum {
22 IAX_STATE_STARTED = (1 << 0),
23 IAX_STATE_AUTHENTICATED = (1 << 1),
24 @@ -606,6 +612,25 @@
25 ast_mutex_t lock;
26 } iaxq;
27
28 +typedef struct iax_auth_frame {
29 + struct iax_auth_frame *prev;
30 + struct iax_auth_frame *next;
31 + struct iax_frame *frame;
32 + struct sockaddr_in sin;
33 + int fd;
34 + int subclass;
35 + struct iax_ies ies;
36 + unsigned char iebuf[4096];
37 +} iax_auth_frame;
38 +
39 +static struct ast_iax2_auth_queue {
40 + struct iax_auth_frame *head;
41 + struct iax_auth_frame *tail;
42 + int count;
43 + ast_cond_t cond;
44 + ast_mutex_t lock;
45 +} callq, regq;
46 +
47 static struct ast_user_list {
48 struct iax2_user *users;
49 ast_mutex_t lock;
50 @@ -712,6 +737,8 @@
51 static struct chan_iax2_pvt *iaxs[IAX_MAX_CALLS];
52 static ast_mutex_t iaxsl[IAX_MAX_CALLS];
53 static struct timeval lastused[IAX_MAX_CALLS];
54 +/* some packets have been queued for asynchronous processing */
55 +static int iaxs_queued[IAX_MAX_CALLS];
56
57
58 static int send_command(struct chan_iax2_pvt *, char, int, unsigned int, const unsigned char *, int, int);
59 @@ -798,11 +825,12 @@
60 static int send_lagrq(void *data)
61 {
62 int callno = (long)data;
63 - /* Ping only if it's real not if it's bridged */
64 + /* Ping only if it's real not if it's bridged (and not if it's queued!)*/
65 if (iaxs[callno]) {
66 #ifdef BRIDGE_OPTIMIZATION
67 if (!iaxs[callno]->bridgecallno)
68 #endif
69 + if (!iaxs_queued[callno])
70 send_command(iaxs[callno], AST_FRAME_IAX, IAX_COMMAND_LAGRQ, 0, NULL, 0, -1);
71 return 1;
72 } else
73 @@ -1070,8 +1098,9 @@
74 }
75 }
76 if ((res < 1) && (new >= NEW_ALLOW)) {
77 - if (!iax2_getpeername(*sin, host, sizeof(host), lockpeer))
78 - snprintf(host, sizeof(host), "%s:%d", ast_inet_ntoa(iabuf, sizeof(iabuf), sin->sin_addr), ntohs(sin->sin_port));
79 + /* We are NOT calling iax2_getpeernme() here, because it WILL BLOCK when using realtime.
80 + Instead we will overwrite the host filed in the iax_pvt for each new call or registration. */
81 + snprintf(host, sizeof(host), "%s:%d", ast_inet_ntoa(iabuf, sizeof(iabuf), sin->sin_addr), ntohs(sin->sin_port));
82 gettimeofday(&now, NULL);
83 for (x=1;x<TRUNK_CALL_START;x++) {
84 /* Find first unused call number that hasn't been used in a while */
85 @@ -1141,6 +1170,58 @@
86 return 0;
87 }
88
89 +static int iax2_queue_auth_frame(struct ast_iax2_auth_queue *authq, int subclass, struct iax_frame *ifr, unsigned char *buf, unsigned char *iebuf, int iebuflen, struct sockaddr_in *sin, int fd, int maydrop)
90 +{
91 + struct iax_auth_frame *fr = NULL;
92 + /*
93 + ok, we get here with the iaxsl[callno] still locked.
94 + in any case we have to unlock it before returning.
95 + */
96 +
97 + if (!authq || !ifr || !ifr->callno) return 1;
98 +
99 + fr = malloc(sizeof(struct iax_auth_frame));
100 + if (!fr) {
101 + ast_log(LOG_ERROR, "Unable to malloc!\n");
102 + return 1;
103 + }
104 + memset(fr,0,sizeof(fr));
105 + fr->next = NULL;
106 + fr->prev = NULL;
107 + fr->frame = ifr;
108 + fr->subclass = subclass;
109 + fr->fd = fd;
110 + memcpy(fr->iebuf, iebuf, iebuflen);
111 + if (iebuflen)
112 + iax_parse_ies(&fr->ies, fr->iebuf, iebuflen);
113 + memcpy(&fr->sin, sin, sizeof(struct sockaddr_in));
114 + ast_mutex_lock(&authq->lock);
115 + if (maydrop && (authq->count > 5000)) {
116 + ast_mutex_unlock(&authq->lock);
117 + ast_mutex_unlock(&iaxsl[ifr->callno]);
118 + free(fr);
119 + if (option_verbose > 5)
120 + ast_log(LOG_WARNING, "Queue too long, not queueing frame.\n");
121 + return -1;
122 + }
123 + if (!authq->head) {
124 + /* Empty queue */
125 + authq->head = fr;
126 + authq->tail = fr;
127 + } else {
128 + /* Double link */
129 + authq->tail->next = fr;
130 + fr->prev = authq->tail;
131 + authq->tail = fr;
132 + }
133 + iaxs_queued[ifr->callno]++;
134 + authq->count++;
135 + ast_mutex_unlock(&iaxsl[ifr->callno]);
136 + ast_cond_signal(&authq->cond);
137 + ast_mutex_unlock(&authq->lock);
138 + return 0;
139 +}
140 +
141 static void destroy_firmware(struct iax_firmware *cur)
142 {
143 /* Close firmware */
144 @@ -1713,8 +1794,10 @@
145 /* Transfer timeout */
146 send_command(iaxs[f->callno], AST_FRAME_IAX, IAX_COMMAND_TXREJ, 0, NULL, 0, -1);
147 } else if (f->final) {
148 - if (f->final)
149 + if (!iaxs_queued[f->callno]) {
150 + if (f->final)
151 iax2_destroy_nolock(f->callno);
152 + }
153 } else {
154 if (iaxs[f->callno]->owner)
155 ast_log(LOG_WARNING, "Max retries exceeded to host %s on %s (type = %d, subclass = %d, ts=%d, seqno=%d)\n", ast_inet_ntoa(iabuf, sizeof(iabuf), iaxs[f->callno]->addr.sin_addr),iaxs[f->callno]->owner->name , f->af.frametype, f->af.subclass, f->ts, f->oseqno);
156 @@ -1729,12 +1812,14 @@
157 if (iaxs[f->callno]->owner)
158 iaxs[f->callno]->owner->hangupcause = AST_CAUSE_DESTINATION_OUT_OF_ORDER;
159 } else {
160 - if (iaxs[f->callno]->reg) {
161 + if (!iaxs_queued[f->callno]) {
162 + if (iaxs[f->callno]->reg) {
163 memset(&iaxs[f->callno]->reg->us, 0, sizeof(iaxs[f->callno]->reg->us));
164 iaxs[f->callno]->reg->regstate = REG_STATE_TIMEOUT;
165 iaxs[f->callno]->reg->refresh = IAX_DEFAULT_REG_EXPIRE;
166 + }
167 + iax2_destroy_nolock(f->callno);
168 }
169 - iax2_destroy_nolock(f->callno);
170 }
171 }
172
173 @@ -2706,6 +2791,8 @@
174 snprintf(regseconds, sizeof(regseconds), "%d", (int)nowtime);
175 ast_inet_ntoa(ipaddr, sizeof(ipaddr), sin->sin_addr);
176 snprintf(port, sizeof(port), "%d", ntohs(sin->sin_port));
177 +// the following line is commented out for compatibility
178 +// ast_update_realtime("iaxpeers", "name", peername, "ipaddr", ipaddr, "port", port, "regseconds", regseconds, "servername", servername, NULL);
179 ast_update_realtime("iaxpeers", "name", peername, "ipaddr", ipaddr, "port", port, "regseconds", regseconds, NULL);
180 }
181
182 @@ -5034,16 +5121,17 @@
183 }
184 /* We release the lock for the call to prevent a deadlock, but it's okay because
185 only the current thread could possibly make it go away or make changes */
186 - ast_mutex_unlock(&iaxsl[callno]);
187 +// ast_mutex_unlock(&iaxsl[callno]);
188 /* SLD: first call to lookup peer during registration */
189 p = find_peer(peer, 1);
190 - ast_mutex_lock(&iaxsl[callno]);
191 -
192 +// ast_mutex_lock(&iaxsl[callno]);
193 if (!p) {
194 if (authdebug)
195 ast_log(LOG_NOTICE, "No registration for peer '%s' (from %s)\n", peer, ast_inet_ntoa(iabuf, sizeof(iabuf), sin->sin_addr));
196 return -1;
197 }
198 + /* set the host name here. instead of doing it in find_callno() */
199 + ast_copy_string(iaxs[callno]->host, p->name, sizeof(iaxs[callno]->host));
200
201 if (!ast_test_flag(p, IAX_DYNAMIC)) {
202 if (authdebug)
203 @@ -5677,11 +5765,13 @@
204 if (!refresh)
205 refresh = min_reg_expire;
206 if (refresh > max_reg_expire) {
207 - ast_log(LOG_NOTICE, "Restricting registration for peer '%s' to %d seconds (requested %d)\n",
208 + if (option_verbose > 5)
209 + ast_log(LOG_NOTICE, "Restricting registration for peer '%s' to %d seconds (requested %d)\n",
210 p->name, max_reg_expire, refresh);
211 p->expiry = max_reg_expire;
212 } else if (refresh < min_reg_expire) {
213 - ast_log(LOG_NOTICE, "Restricting registration for peer '%s' to %d seconds (requested %d)\n",
214 + if (option_verbose > 5)
215 + ast_log(LOG_NOTICE, "Restricting registration for peer '%s' to %d seconds (requested %d)\n",
216 p->name, min_reg_expire, refresh);
217 p->expiry = min_reg_expire;
218 } else {
219 @@ -5720,6 +5810,7 @@
220 iax_ie_append_short(&ied, IAX_IE_FIRMWAREVER, version);
221 if (ast_test_flag(p, IAX_TEMPONLY))
222 destroy_peer(p);
223 + ast_mutex_lock(&iaxsl[callno]);
224 return send_command_final(iaxs[callno], AST_FRAME_IAX, IAX_COMMAND_REGACK, 0, ied.buf, ied.pos, -1);
225 }
226
227 @@ -6279,6 +6370,8 @@
228 struct timeval rxtrunktime;
229 struct iax_ies ies;
230 struct iax_ie_data ied0, ied1;
231 + unsigned char iebuf[4096];
232 + int iebuflen = 0;
233 int format;
234 int exists;
235 int minivid = 0;
236 @@ -6609,9 +6702,11 @@
237 cur->retries = -1;
238 /* Destroy call if this is the end */
239 if (cur->final) {
240 - if (iaxdebug && option_debug)
241 + if (!iaxs_queued[fr.callno]) {
242 + if (iaxdebug && option_debug)
243 ast_log(LOG_DEBUG, "Really destroying %d, having been acked on final message\n", fr.callno);
244 - iax2_destroy_nolock(fr.callno);
245 + iax2_destroy_nolock(fr.callno);
246 + }
247 }
248 }
249 }
250 @@ -6639,6 +6734,8 @@
251
252 if (f.datalen) {
253 if (f.frametype == AST_FRAME_IAX) {
254 + memcpy(iebuf, buf + sizeof(struct ast_iax2_full_hdr), f.datalen);
255 + iebuflen = f.datalen;
256 if (iax_parse_ies(&ies, buf + sizeof(struct ast_iax2_full_hdr), f.datalen)) {
257 ast_log(LOG_WARNING, "Undecodable frame received from '%s'\n", ast_inet_ntoa(iabuf, sizeof(iabuf), sin.sin_addr));
258 ast_mutex_unlock(&iaxsl[fr.callno]);
259 @@ -6772,166 +6869,11 @@
260 break;
261 if (ies.provverpres && ies.serviceident && sin.sin_addr.s_addr)
262 check_provisioning(&sin, fd, ies.serviceident, ies.provver);
263 - /* If we're in trunk mode, do it now, and update the trunk number in our frame before continuing */
264 - if (ast_test_flag(iaxs[fr.callno], IAX_TRUNK)) {
265 - fr.callno = make_trunk(fr.callno, 1);
266 - }
267 /* For security, always ack immediately */
268 if (delayreject)
269 send_command_immediate(iaxs[fr.callno], AST_FRAME_IAX, IAX_COMMAND_ACK, fr.ts, NULL, 0,fr.iseqno);
270 - if (check_access(fr.callno, &sin, &ies)) {
271 - /* They're not allowed on */
272 - auth_fail(fr.callno, IAX_COMMAND_REJECT);
273 - if (authdebug)
274 - ast_log(LOG_NOTICE, "Rejected connect attempt from %s, who was trying to reach '%s@%s'\n", ast_inet_ntoa(iabuf, sizeof(iabuf), sin.sin_addr), iaxs[fr.callno]->exten, iaxs[fr.callno]->context);
275 - break;
276 - }
277 - /* This might re-enter the IAX code and need the lock */
278 - if (strcasecmp(iaxs[fr.callno]->exten, "TBD")) {
279 - ast_mutex_unlock(&iaxsl[fr.callno]);
280 - exists = ast_exists_extension(NULL, iaxs[fr.callno]->context, iaxs[fr.callno]->exten, 1, iaxs[fr.callno]->cid_num);
281 - ast_mutex_lock(&iaxsl[fr.callno]);
282 - } else
283 - exists = 0;
284 - if (ast_strlen_zero(iaxs[fr.callno]->secret) && ast_strlen_zero(iaxs[fr.callno]->inkeys)) {
285 - if (strcmp(iaxs[fr.callno]->exten, "TBD") && !exists) {
286 - memset(&ied0, 0, sizeof(ied0));
287 - iax_ie_append_str(&ied0, IAX_IE_CAUSE, "No such context/extension");
288 - iax_ie_append_byte(&ied0, IAX_IE_CAUSECODE, AST_CAUSE_NO_ROUTE_DESTINATION);
289 - send_command_final(iaxs[fr.callno], AST_FRAME_IAX, IAX_COMMAND_REJECT, 0, ied0.buf, ied0.pos, -1);
290 - if (authdebug)
291 - ast_log(LOG_NOTICE, "Rejected connect attempt from %s, request '%s@%s' does not exist\n", ast_inet_ntoa(iabuf, sizeof(iabuf), sin.sin_addr), iaxs[fr.callno]->exten, iaxs[fr.callno]->context);
292 - } else {
293 - /* Select an appropriate format */
294 -
295 - if(ast_test_flag(iaxs[fr.callno], IAX_CODEC_NOPREFS)) {
296 - if(ast_test_flag(iaxs[fr.callno], IAX_CODEC_NOCAP)) {
297 - using_prefs = "reqonly";
298 - } else {
299 - using_prefs = "disabled";
300 - }
301 - format = iaxs[fr.callno]->peerformat & iaxs[fr.callno]->capability;
302 - memset(&pref, 0, sizeof(pref));
303 - strcpy(caller_pref_buf, "disabled");
304 - strcpy(host_pref_buf, "disabled");
305 - } else {
306 - using_prefs = "mine";
307 - if(ies.codec_prefs) {
308 - ast_codec_pref_convert(&rpref, ies.codec_prefs, 32, 0);
309 - /* If we are codec_first_choice we let the caller have the 1st shot at picking the codec.*/
310 - if (ast_test_flag(iaxs[fr.callno], IAX_CODEC_USER_FIRST)) {
311 - pref = rpref;
312 - using_prefs = "caller";
313 - } else {
314 - pref = iaxs[fr.callno]->prefs;
315 - }
316 - } else
317 - pref = iaxs[fr.callno]->prefs;
318 -
319 - format = ast_codec_choose(&pref, iaxs[fr.callno]->capability & iaxs[fr.callno]->peercapability, 0);
320 - ast_codec_pref_string(&rpref, caller_pref_buf, sizeof(caller_pref_buf) - 1);
321 - ast_codec_pref_string(&iaxs[fr.callno]->prefs, host_pref_buf, sizeof(host_pref_buf) - 1);
322 - }
323 - if (!format) {
324 - if(!ast_test_flag(iaxs[fr.callno], IAX_CODEC_NOCAP))
325 - format = iaxs[fr.callno]->peercapability & iaxs[fr.callno]->capability;
326 - if (!format) {
327 - memset(&ied0, 0, sizeof(ied0));
328 - iax_ie_append_str(&ied0, IAX_IE_CAUSE, "Unable to negotiate codec");
329 - iax_ie_append_byte(&ied0, IAX_IE_CAUSECODE, AST_CAUSE_BEARERCAPABILITY_NOTAVAIL);
330 - send_command_final(iaxs[fr.callno], AST_FRAME_IAX, IAX_COMMAND_REJECT, 0, ied0.buf, ied0.pos, -1);
331 - if (authdebug) {
332 - if(ast_test_flag(iaxs[fr.callno], IAX_CODEC_NOCAP))
333 - ast_log(LOG_NOTICE, "Rejected connect attempt from %s, requested 0x%x incompatible with our capability 0x%x.\n", ast_inet_ntoa(iabuf, sizeof(iabuf), sin.sin_addr), iaxs[fr.callno]->peerformat, iaxs[fr.callno]->capability);
334 - else
335 - ast_log(LOG_NOTICE, "Rejected connect attempt from %s, requested/capability 0x%x/0x%x incompatible with our capability 0x%x.\n", ast_inet_ntoa(iabuf, sizeof(iabuf), sin.sin_addr), iaxs[fr.callno]->peerformat, iaxs[fr.callno]->peercapability, iaxs[fr.callno]->capability);
336 - }
337 - } else {
338 - /* Pick one... */
339 - if(ast_test_flag(iaxs[fr.callno], IAX_CODEC_NOCAP)) {
340 - if(!(iaxs[fr.callno]->peerformat & iaxs[fr.callno]->capability))
341 - format = 0;
342 - } else {
343 - if(ast_test_flag(iaxs[fr.callno], IAX_CODEC_NOPREFS)) {
344 - using_prefs = ast_test_flag(iaxs[fr.callno], IAX_CODEC_NOCAP) ? "reqonly" : "disabled";
345 - memset(&pref, 0, sizeof(pref));
346 - format = ast_best_codec(iaxs[fr.callno]->peercapability & iaxs[fr.callno]->capability);
347 - strcpy(caller_pref_buf,"disabled");
348 - strcpy(host_pref_buf,"disabled");
349 - } else {
350 - using_prefs = "mine";
351 - if(ies.codec_prefs) {
352 - /* Do the opposite of what we tried above. */
353 - if (ast_test_flag(iaxs[fr.callno], IAX_CODEC_USER_FIRST)) {
354 - pref = iaxs[fr.callno]->prefs;
355 - } else {
356 - pref = rpref;
357 - using_prefs = "caller";
358 - }
359 - format = ast_codec_choose(&pref, iaxs[fr.callno]->peercapability & iaxs[fr.callno]->capability, 1);
360 -
361 - } else /* if no codec_prefs IE do it the old way */
362 - format = ast_best_codec(iaxs[fr.callno]->peercapability & iaxs[fr.callno]->capability);
363 - }
364 - }
365 -
366 - if (!format) {
367 - memset(&ied0, 0, sizeof(ied0));
368 - iax_ie_append_str(&ied0, IAX_IE_CAUSE, "Unable to negotiate codec");
369 - iax_ie_append_byte(&ied0, IAX_IE_CAUSECODE, AST_CAUSE_BEARERCAPABILITY_NOTAVAIL);
370 - ast_log(LOG_ERROR, "No best format in 0x%x???\n", iaxs[fr.callno]->peercapability & iaxs[fr.callno]->capability);
371 - send_command_final(iaxs[fr.callno], AST_FRAME_IAX, IAX_COMMAND_REJECT, 0, ied0.buf, ied0.pos, -1);
372 - if (authdebug)
373 - ast_log(LOG_NOTICE, "Rejected connect attempt from %s, requested/capability 0x%x/0x%x incompatible with our capability 0x%x.\n", ast_inet_ntoa(iabuf, sizeof(iabuf), sin.sin_addr), iaxs[fr.callno]->peerformat, iaxs[fr.callno]->peercapability, iaxs[fr.callno]->capability);
374 - ast_set_flag(iaxs[fr.callno], IAX_ALREADYGONE);
375 - break;
376 - }
377 - }
378 - }
379 - if (format) {
380 - /* No authentication required, let them in */
381 - memset(&ied1, 0, sizeof(ied1));
382 - iax_ie_append_int(&ied1, IAX_IE_FORMAT, format);
383 - send_command(iaxs[fr.callno], AST_FRAME_IAX, IAX_COMMAND_ACCEPT, 0, ied1.buf, ied1.pos, -1);
384 - if (strcmp(iaxs[fr.callno]->exten, "TBD")) {
385 - ast_set_flag(&iaxs[fr.callno]->state, IAX_STATE_STARTED);
386 - if (option_verbose > 2)
387 - ast_verbose(VERBOSE_PREFIX_3 "Accepting UNAUTHENTICATED call from %s:\n"
388 - "%srequested format = %s,\n"
389 - "%srequested prefs = %s,\n"
390 - "%sactual format = %s,\n"
391 - "%shost prefs = %s,\n"
392 - "%spriority = %s\n",
393 - ast_inet_ntoa(iabuf, sizeof(iabuf), sin.sin_addr),
394 - VERBOSE_PREFIX_4,
395 - ast_getformatname(iaxs[fr.callno]->peerformat),
396 - VERBOSE_PREFIX_4,
397 - caller_pref_buf,
398 - VERBOSE_PREFIX_4,
399 - ast_getformatname(format),
400 - VERBOSE_PREFIX_4,
401 - host_pref_buf,
402 - VERBOSE_PREFIX_4,
403 - using_prefs);
404 -
405 - if(!(c = ast_iax2_new(fr.callno, AST_STATE_RING, format)))
406 - iax2_destroy_nolock(fr.callno);
407 - } else {
408 - ast_set_flag(&iaxs[fr.callno]->state, IAX_STATE_TBD);
409 - /* If this is a TBD call, we're ready but now what... */
410 - if (option_verbose > 2)
411 - ast_verbose(VERBOSE_PREFIX_3 "Accepted unauthenticated TBD call from %s\n", ast_inet_ntoa(iabuf, sizeof(iabuf), sin.sin_addr));
412 - }
413 - }
414 - }
415 - break;
416 - }
417 - if (iaxs[fr.callno]->authmethods & IAX_AUTH_MD5)
418 - merge_encryption(iaxs[fr.callno],ies.encmethods);
419 - else
420 - iaxs[fr.callno]->encmethods = 0;
421 - authenticate_request(iaxs[fr.callno]);
422 - ast_set_flag(&iaxs[fr.callno]->state, IAX_STATE_AUTHENTICATED);
423 + fr.af.datalen = 0;
424 + iax2_queue_auth_frame(&callq, f.subclass, iaxfrdup2(&fr), buf, iebuf, iebuflen, &sin, fd, 0);
425 break;
426 case IAX_COMMAND_DPREQ:
427 /* Request status in the dialplan */
428 @@ -6947,14 +6889,22 @@
429 }
430 break;
431 case IAX_COMMAND_HANGUP:
432 - ast_set_flag(iaxs[fr.callno], IAX_ALREADYGONE);
433 - ast_log(LOG_DEBUG, "Immediately destroying %d, having received hangup\n", fr.callno);
434 - /* Set hangup cause according to remote */
435 - if (ies.causecode && iaxs[fr.callno]->owner)
436 - iaxs[fr.callno]->owner->hangupcause = ies.causecode;
437 - /* Send ack immediately, before we destroy */
438 - send_command_immediate(iaxs[fr.callno], AST_FRAME_IAX, IAX_COMMAND_ACK, fr.ts, NULL, 0,fr.iseqno);
439 - iax2_destroy_nolock(fr.callno);
440 + if (iaxs_queued[fr.callno]) {
441 + /* there is something queued, maybe the call has not been authorized yet. */
442 + /* Send ack immediately, before we destroy */
443 + send_command_immediate(iaxs[fr.callno], AST_FRAME_IAX, IAX_COMMAND_ACK, fr.ts, NULL, 0,fr.iseqno);
444 + fr.af.datalen = 0;
445 + iax2_queue_auth_frame(&callq, f.subclass, iaxfrdup2(&fr), buf, iebuf, iebuflen, &sin, fd, 0);
446 + } else {
447 + ast_set_flag(iaxs[fr.callno], IAX_ALREADYGONE);
448 + ast_log(LOG_DEBUG, "Immediately destroying %d, having received hangup\n", fr.callno);
449 + /* Set hangup cause according to remote */
450 + if (ies.causecode && iaxs[fr.callno]->owner)
451 + iaxs[fr.callno]->owner->hangupcause = ies.causecode;
452 + /* Send ack immediately, before we destroy */
453 + send_command_immediate(iaxs[fr.callno], AST_FRAME_IAX, IAX_COMMAND_ACK, fr.ts, NULL, 0,fr.iseqno);
454 + iax2_destroy_nolock(fr.callno);
455 + }
456 break;
457 case IAX_COMMAND_REJECT:
458 memset(&f, 0, sizeof(f));
459 @@ -7183,31 +7133,36 @@
460 /* For security, always ack immediately */
461 if (delayreject)
462 send_command_immediate(iaxs[fr.callno], AST_FRAME_IAX, IAX_COMMAND_ACK, fr.ts, NULL, 0,fr.iseqno);
463 - /* Ignore once we've started */
464 - if (ast_test_flag(&iaxs[fr.callno]->state, IAX_STATE_STARTED | IAX_STATE_TBD)) {
465 + if (iaxs_queued[fr.callno]) {
466 + /* there is something queued, take the same path */
467 + fr.af.datalen = 0;
468 + iax2_queue_auth_frame(&callq, f.subclass, iaxfrdup2(&fr), buf, iebuf, iebuflen, &sin, fd, 0);
469 + } else {
470 + /* Ignore once we've started */
471 + if (ast_test_flag(&iaxs[fr.callno]->state, IAX_STATE_STARTED | IAX_STATE_TBD)) {
472 ast_log(LOG_WARNING, "Call on %s is already up, can't start on it\n", iaxs[fr.callno]->owner ? iaxs[fr.callno]->owner->name : "<Unknown>");
473 break;
474 - }
475 - if (authenticate_verify(iaxs[fr.callno], &ies)) {
476 + }
477 + if (authenticate_verify(iaxs[fr.callno], &ies)) {
478 if (authdebug)
479 ast_log(LOG_NOTICE, "Host %s failed to authenticate as %s\n", ast_inet_ntoa(iabuf, sizeof(iabuf), iaxs[fr.callno]->addr.sin_addr), iaxs[fr.callno]->username);
480 memset(&ied0, 0, sizeof(ied0));
481 auth_fail(fr.callno, IAX_COMMAND_REJECT);
482 break;
483 - }
484 - if (strcasecmp(iaxs[fr.callno]->exten, "TBD")) {
485 + }
486 + if (strcasecmp(iaxs[fr.callno]->exten, "TBD")) {
487 /* This might re-enter the IAX code and need the lock */
488 exists = ast_exists_extension(NULL, iaxs[fr.callno]->context, iaxs[fr.callno]->exten, 1, iaxs[fr.callno]->cid_num);
489 - } else
490 + } else
491 exists = 0;
492 - if (strcmp(iaxs[fr.callno]->exten, "TBD") && !exists) {
493 + if (strcmp(iaxs[fr.callno]->exten, "TBD") && !exists) {
494 if (authdebug)
495 ast_log(LOG_NOTICE, "Rejected connect attempt from %s, request '%s@%s' does not exist\n", ast_inet_ntoa(iabuf, sizeof(iabuf), sin.sin_addr), iaxs[fr.callno]->exten, iaxs[fr.callno]->context);
496 memset(&ied0, 0, sizeof(ied0));
497 iax_ie_append_str(&ied0, IAX_IE_CAUSE, "No such context/extension");
498 iax_ie_append_byte(&ied0, IAX_IE_CAUSECODE, AST_CAUSE_NO_ROUTE_DESTINATION);
499 send_command_final(iaxs[fr.callno], AST_FRAME_IAX, IAX_COMMAND_REJECT, 0, ied0.buf, ied0.pos, -1);
500 - } else {
501 + } else {
502 /* Select an appropriate format */
503 if(ast_test_flag(iaxs[fr.callno], IAX_CODEC_NOPREFS)) {
504 if(ast_test_flag(iaxs[fr.callno], IAX_CODEC_NOCAP)) {
505 @@ -7332,6 +7287,7 @@
506 ast_verbose(VERBOSE_PREFIX_3 "Accepted AUTHENTICATED TBD call from %s\n", ast_inet_ntoa(iabuf, sizeof(iabuf), sin.sin_addr));
507 }
508 }
509 + }
510 }
511 break;
512 case IAX_COMMAND_DIAL:
513 @@ -7357,11 +7313,16 @@
514 }
515 break;
516 case IAX_COMMAND_INVAL:
517 - iaxs[fr.callno]->error = ENOTCONN;
518 - ast_log(LOG_DEBUG, "Immediately destroying %d, having received INVAL\n", fr.callno);
519 - iax2_destroy_nolock(fr.callno);
520 - if (option_debug)
521 + if (iaxs_queued[fr.callno]) {
522 + fr.af.datalen = 0;
523 + iax2_queue_auth_frame(&callq, f.subclass, iaxfrdup2(&fr), buf, iebuf, iebuflen, &sin, fd, 0);
524 + } else {
525 + iaxs[fr.callno]->error = ENOTCONN;
526 + ast_log(LOG_DEBUG, "Immediately destroying %d, having received INVAL\n", fr.callno);
527 + iax2_destroy_nolock(fr.callno);
528 + if (option_debug)
529 ast_log(LOG_DEBUG, "Destroying call %d\n", fr.callno);
530 + }
531 break;
532 case IAX_COMMAND_VNAK:
533 ast_log(LOG_DEBUG, "Received VNAK: resending outstanding frames\n");
534 @@ -7373,21 +7334,11 @@
535 /* For security, always ack immediately */
536 if (delayreject)
537 send_command_immediate(iaxs[fr.callno], AST_FRAME_IAX, IAX_COMMAND_ACK, fr.ts, NULL, 0,fr.iseqno);
538 - if (register_verify(fr.callno, &sin, &ies)) {
539 - /* Send delayed failure */
540 - auth_fail(fr.callno, IAX_COMMAND_REGREJ);
541 - break;
542 - }
543 - if ((ast_strlen_zero(iaxs[fr.callno]->secret) && ast_strlen_zero(iaxs[fr.callno]->inkeys)) || ast_test_flag(&iaxs[fr.callno]->state, IAX_STATE_AUTHENTICATED)) {
544 - if (f.subclass == IAX_COMMAND_REGREL)
545 - memset(&sin, 0, sizeof(sin));
546 - if (update_registry(iaxs[fr.callno]->peer, &sin, fr.callno, ies.devicetype, fd, ies.refresh))
547 - ast_log(LOG_WARNING, "Registry error\n");
548 - if (ies.provverpres && ies.serviceident && sin.sin_addr.s_addr)
549 - check_provisioning(&sin, fd, ies.serviceident, ies.provver);
550 - break;
551 + fr.af.datalen = 0;
552 + if (iax2_queue_auth_frame(&regq, f.subclass, iaxfrdup2(&fr), buf, iebuf, iebuflen, &sin, fd, 1)) {
553 + /* queueing failed due to a full queue */
554 + iax2_destroy_nolock(fr.callno);
555 }
556 - registry_authrequest(iaxs[fr.callno]->peer, fr.callno);
557 break;
558 case IAX_COMMAND_REGACK:
559 if (iax2_ack_registry(&ies, &sin, fr.callno))
560 @@ -7989,6 +7940,478 @@
561 return ast_pthread_create(&netthreadid, NULL, network_thread, NULL);
562 }
563
564 +struct iax_auth_frame *iax2_get_next_auth_frame(struct ast_iax2_auth_queue *authq) {
565 + struct iax_auth_frame *frame = NULL;
566 +
567 + /* sleep until the network_thread queues us a frame, then grab it and release the lock A.S.A.P. */
568 + ast_mutex_lock(&authq->lock);
569 + if (authq->count == 0)
570 + ast_cond_wait(&authq->cond, &authq->lock);
571 + frame = authq->head;
572 + if (frame) {
573 + if (frame->next) {
574 +// frame->next->prev = NULL;
575 + authq->head = frame->next;
576 + } else {
577 + authq->head = NULL;
578 + authq->tail = NULL;
579 + }
580 + authq->count--;
581 + } else {
582 + ast_log(LOG_ERROR, "I SHOULD NEVER HAPPEN! EXPECT SOME MAJOR KABOOM! DUCK AND COVER!\n");
583 + }
584 + ast_mutex_unlock(&authq->lock);
585 + return frame;
586 +}
587 +
588 +
589 +void *auth_thread(void *data)
590 +{
591 + /*
592 + The network_thread queues iax_frames into our queue and wakes us up.
593 + We will authenticate IAX_COMMAND_NEWs and IAX_COMMAND_REGREQs.
594 +
595 + We also have to process IAX_COMMAND_HANGUP when somebody hangs up a call
596 + before it has been authorized!
597 +
598 + If ever possible we shall not lock any iaxsl[...].
599 + */
600 + struct ast_iax2_auth_queue *authq = (struct ast_iax2_auth_queue *)data;
601 + struct iax_frame *ifr;
602 + struct iax_auth_frame *fr;
603 + struct iax_ies *ies;
604 + struct iax_ie_data ied0, ied1;
605 + struct ast_channel *c;
606 + char iabuf[INET_ADDRSTRLEN];
607 + int exists;
608 + int format;
609 + int dequeued = 0;
610 + char host_pref_buf[128];
611 + char caller_pref_buf[128];
612 + struct ast_codec_pref pref,rpref;
613 + char *using_prefs = "mine";
614 +
615 + if (!authq) {
616 + ast_log(LOG_ERROR, "no queue!\n");
617 + return NULL;
618 + }
619 +
620 + for(;;) {
621 + fr = iax2_get_next_auth_frame(authq);
622 + ifr = fr->frame;
623 + ies = &fr->ies;
624 + if (ifr) {
625 + if (ifr->callno > 0) {
626 + /* we will not lock iaxsl[ifr->callno], instead we will haveset a flag in iaxs_queued[ifr->callno].
627 + if this flag is set the socket_read thread may not touch iaxs[ifr->callno]. instead of
628 + processing the incoming packets there it has to queue them to us!
629 + This should only happen with IAX_COMMAND_HANGUP when hanging up a not-yet-established call.
630 +
631 + However we will try to deliver audio frames in socket_read. If we get audio data for call
632 + whos signalling frames have been queued we should be safe to just drop them.
633 +
634 + When we really want to destroy something we will aquire the lock first.
635 + */
636 + switch(fr->subclass) {
637 + case IAX_COMMAND_NEW:
638 + iax2_getpeername(fr->sin, iaxs[ifr->callno]->host, sizeof(iaxs[ifr->callno]->host), 1);
639 + if (check_access(ifr->callno, &fr->sin, ies)) {
640 + /* They're not allowed on */
641 + if (authdebug)
642 + ast_log(LOG_NOTICE, "Rejected connect attempt from %s, who was trying to reach '%s@%s'\n", ast_inet_ntoa(iabuf, sizeof(iabuf), fr->sin.sin_addr), iaxs[ifr->callno]->exten, iaxs[ifr->callno]->context);
643 + auth_fail(ifr->callno, IAX_COMMAND_REJECT);
644 + break;
645 + }
646 + /* If we're in trunk mode, do it now, and update the trunk number in our frame before continuing */
647 + if (ast_test_flag(iaxs[ifr->callno], IAX_TRUNK)) {
648 + ifr->callno = make_trunk(ifr->callno, 1);
649 + }
650 + /* This might re-enter the IAX code and need the lock */
651 + if (strcasecmp(iaxs[ifr->callno]->exten, "TBD")) {
652 + // ast_mutex_unlock(&iaxsl[ifr->callno]);
653 + exists = ast_exists_extension(NULL, iaxs[ifr->callno]->context, iaxs[ifr->callno]->exten, 1, iaxs[ifr->callno]->cid_num);
654 + // ast_mutex_lock(&iaxsl[ifr->callno]);
655 + } else
656 + exists = 0;
657 + if (ast_strlen_zero(iaxs[ifr->callno]->secret) && ast_strlen_zero(iaxs[ifr->callno]->inkeys)) {
658 + if (strcmp(iaxs[ifr->callno]->exten, "TBD") && !exists) {
659 + memset(&ied0, 0, sizeof(ied0));
660 + iax_ie_append_str(&ied0, IAX_IE_CAUSE, "No such context/extension");
661 + iax_ie_append_byte(&ied0, IAX_IE_CAUSECODE, AST_CAUSE_NO_ROUTE_DESTINATION);
662 + send_command_final(iaxs[ifr->callno], AST_FRAME_IAX, IAX_COMMAND_REJECT, 0, ied0.buf, ied0.pos, -1);
663 + if (authdebug)
664 + ast_log(LOG_NOTICE, "Rejected connect attempt from %s, request '%s@%s' does not exist\n", ast_inet_ntoa(iabuf, sizeof(iabuf), fr->sin.sin_addr), iaxs[ifr->callno]->exten, iaxs[ifr->callno]->context);
665 + } else {
666 + /* Select an appropriate format */
667 + if(ast_test_flag(iaxs[ifr->callno], IAX_CODEC_NOPREFS)) {
668 + if(ast_test_flag(iaxs[ifr->callno], IAX_CODEC_NOCAP)) {
669 + using_prefs = "reqonly";
670 + } else {
671 + using_prefs = "disabled";
672 + }
673 + format = iaxs[ifr->callno]->peerformat & iaxs[ifr->callno]->capability;
674 + memset(&pref, 0, sizeof(pref));
675 + strcpy(caller_pref_buf, "disabled");
676 + strcpy(host_pref_buf, "disabled");
677 + } else {
678 + using_prefs = "mine";
679 + if(ies->codec_prefs) {
680 + ast_codec_pref_convert(&rpref, ies->codec_prefs, 32, 0);
681 + /* If we are codec_first_choice we let the caller have the 1st shot at picking the codec.*/
682 + if (ast_test_flag(iaxs[ifr->callno], IAX_CODEC_USER_FIRST)) {
683 + pref = rpref;
684 + using_prefs = "caller";
685 + } else {
686 + pref = iaxs[ifr->callno]->prefs;
687 + }
688 + } else
689 + pref = iaxs[ifr->callno]->prefs;
690 +
691 + format = ast_codec_choose(&pref, iaxs[ifr->callno]->capability & iaxs[ifr->callno]->peercapability, 0);
692 + ast_codec_pref_string(&rpref, caller_pref_buf, sizeof(caller_pref_buf) - 1);
693 + ast_codec_pref_string(&iaxs[ifr->callno]->prefs, host_pref_buf, sizeof(host_pref_buf) - 1);
694 + }
695 + if (!format) {
696 + if(!ast_test_flag(iaxs[ifr->callno], IAX_CODEC_NOCAP))
697 + format = iaxs[ifr->callno]->peercapability & iaxs[ifr->callno]->capability;
698 + if (!format) {
699 + memset(&ied0, 0, sizeof(ied0));
700 + iax_ie_append_str(&ied0, IAX_IE_CAUSE, "Unable to negotiate codec");
701 + iax_ie_append_byte(&ied0, IAX_IE_CAUSECODE, AST_CAUSE_BEARERCAPABILITY_NOTAVAIL);
702 + send_command_final(iaxs[ifr->callno], AST_FRAME_IAX, IAX_COMMAND_REJECT, 0, ied0.buf, ied0.pos, -1);
703 + if (authdebug) {
704 + if(ast_test_flag(iaxs[ifr->callno], IAX_CODEC_NOCAP))
705 + ast_log(LOG_NOTICE, "Rejected connect attempt from %s, requested 0x%x incompatible with our capability 0x%x.\n", ast_inet_ntoa(iabuf, sizeof(iabuf), fr->sin.sin_addr), iaxs[ifr->callno]->peerformat, iaxs[ifr->callno]->capability);
706 + else
707 + ast_log(LOG_NOTICE, "Rejected connect attempt from %s, requested/capability 0x%x/0x%x incompatible with our capability 0x%x.\n", ast_inet_ntoa(iabuf, sizeof(iabuf), fr->sin.sin_addr), iaxs[ifr->callno]->peerformat, iaxs[ifr->callno]->peercapability, iaxs[ifr->callno]->capability);
708 + }
709 + } else {
710 + /* Pick one... */
711 + if(ast_test_flag(iaxs[ifr->callno], IAX_CODEC_NOCAP)) {
712 + if(!(iaxs[ifr->callno]->peerformat & iaxs[ifr->callno]->capability))
713 + format = 0;
714 + } else {
715 + if(ast_test_flag(iaxs[ifr->callno], IAX_CODEC_NOPREFS)) {
716 + using_prefs = ast_test_flag(iaxs[ifr->callno], IAX_CODEC_NOCAP) ? "reqonly" : "disabled";
717 + memset(&pref, 0, sizeof(pref));
718 + format = ast_best_codec(iaxs[ifr->callno]->peercapability & iaxs[ifr->callno]->capability);
719 + strcpy(caller_pref_buf,"disabled");
720 + strcpy(host_pref_buf,"disabled");
721 + } else {
722 + using_prefs = "mine";
723 + if(ies->codec_prefs) {
724 + /* Do the opposite of what we tried above. */
725 + if (ast_test_flag(iaxs[ifr->callno], IAX_CODEC_USER_FIRST)) {
726 + pref = iaxs[ifr->callno]->prefs;
727 + } else {
728 + pref = rpref;
729 + using_prefs = "caller";
730 + }
731 + format = ast_codec_choose(&pref, iaxs[ifr->callno]->peercapability & iaxs[ifr->callno]->capability, 1);
732 +
733 + } else /* if no codec_prefs IE do it the old way */
734 + format = ast_best_codec(iaxs[ifr->callno]->peercapability & iaxs[ifr->callno]->capability);
735 + }
736 + }
737 +
738 + if (!format) {
739 + memset(&ied0, 0, sizeof(ied0));
740 + iax_ie_append_str(&ied0, IAX_IE_CAUSE, "Unable to negotiate codec");
741 + iax_ie_append_byte(&ied0, IAX_IE_CAUSECODE, AST_CAUSE_BEARERCAPABILITY_NOTAVAIL);
742 + ast_log(LOG_ERROR, "No best format in 0x%x???\n", iaxs[ifr->callno]->peercapability & iaxs[ifr->callno]->capability);
743 + send_command_final(iaxs[ifr->callno], AST_FRAME_IAX, IAX_COMMAND_REJECT, 0, ied0.buf, ied0.pos, -1);
744 + if (authdebug)
745 + ast_log(LOG_NOTICE, "Rejected connect attempt from %s, requested/capability 0x%x/0x%x incompatible with our capability 0x%x.\n", ast_inet_ntoa(iabuf, sizeof(iabuf), fr->sin.sin_addr), iaxs[ifr->callno]->peerformat, iaxs[ifr->callno]->peercapability, iaxs[ifr->callno]->capability);
746 + ast_set_flag(iaxs[ifr->callno], IAX_ALREADYGONE);
747 + break;
748 + }
749 + }
750 + }
751 + if (format) {
752 + /* No authentication required, let them in */
753 + memset(&ied1, 0, sizeof(ied1));
754 + iax_ie_append_int(&ied1, IAX_IE_FORMAT, format);
755 + send_command(iaxs[ifr->callno], AST_FRAME_IAX, IAX_COMMAND_ACCEPT, 0, ied1.buf, ied1.pos, -1);
756 + if (strcmp(iaxs[ifr->callno]->exten, "TBD")) {
757 + ast_set_flag(&iaxs[ifr->callno]->state, IAX_STATE_STARTED);
758 + if (option_verbose > 2)
759 + ast_verbose(VERBOSE_PREFIX_3 "Accepting UNAUTHENTICATED call from %s:\n"
760 + "%srequested format = %s,\n"
761 + "%srequested prefs = %s,\n"
762 + "%sactual format = %s,\n"
763 + "%shost prefs = %s,\n"
764 + "%spriority = %s\n",
765 + ast_inet_ntoa(iabuf, sizeof(iabuf), fr->sin.sin_addr),
766 + VERBOSE_PREFIX_4,
767 + ast_getformatname(iaxs[ifr->callno]->peerformat),
768 + VERBOSE_PREFIX_4,
769 + caller_pref_buf,
770 + VERBOSE_PREFIX_4,
771 + ast_getformatname(format),
772 + VERBOSE_PREFIX_4,
773 + host_pref_buf,
774 + VERBOSE_PREFIX_4,
775 + using_prefs);
776 +
777 + if(!(c = ast_iax2_new(ifr->callno, AST_STATE_RING, format))) {
778 + ast_mutex_lock(&iaxsl[ifr->callno]);
779 + iax2_destroy_nolock(ifr->callno);
780 + ast_mutex_unlock(&iaxsl[ifr->callno]);
781 + }
782 + } else {
783 + ast_set_flag(&iaxs[ifr->callno]->state, IAX_STATE_TBD);
784 + /* If this is a TBD call, we're ready but now what... */
785 + if (option_verbose > 2)
786 + ast_verbose(VERBOSE_PREFIX_3 "Accepted unauthenticated TBD call from %s\n", ast_inet_ntoa(iabuf, sizeof(iabuf), fr->sin.sin_addr));
787 + }
788 + }
789 + }
790 + break;
791 + }
792 + if (iaxs[ifr->callno]->authmethods & IAX_AUTH_MD5)
793 + merge_encryption(iaxs[ifr->callno],ies->encmethods);
794 + else
795 + iaxs[ifr->callno]->encmethods = 0;
796 + authenticate_request(iaxs[ifr->callno]);
797 + ast_set_flag(&iaxs[ifr->callno]->state, IAX_STATE_AUTHENTICATED);
798 + break;
799 + case IAX_COMMAND_REGREQ:
800 + case IAX_COMMAND_REGREL:
801 + // usleep(1000000);
802 + if (register_verify(ifr->callno, &fr->sin, ies)) {
803 + /* Send delayed failure */
804 + auth_fail(ifr->callno, IAX_COMMAND_REGREJ);
805 + break;
806 + }
807 + if ((ast_strlen_zero(iaxs[ifr->callno]->secret) && ast_strlen_zero(iaxs[ifr->callno]->inkeys)) || ast_test_flag(&iaxs[ifr->callno]->state, IAX_STATE_AUTHENTICATED)) {
808 + if (fr->subclass == IAX_COMMAND_REGREL)
809 + memset(&fr->sin, 0, sizeof(struct sockaddr_in));
810 + if (update_registry(iaxs[ifr->callno]->peer, &fr->sin, ifr->callno, ies->devicetype, fr->fd, ies->refresh))
811 + ast_log(LOG_WARNING, "Registry error\n");
812 + if (ies->provverpres && ies->serviceident && fr->sin.sin_addr.s_addr)
813 + check_provisioning(&fr->sin, fr->fd, ies->serviceident, ies->provver);
814 + /* update registry leaves us locked, so we have to unlock to not deadlock */
815 + iaxs_queued[ifr->callno]--;
816 + if (iaxs_queued[ifr->callno] < 0) iaxs_queued[ifr->callno] = 0;
817 + ast_mutex_unlock(&iaxsl[ifr->callno]);
818 + break;
819 + }
820 + registry_authrequest(iaxs[ifr->callno]->peer, ifr->callno);
821 + break;
822 + case IAX_COMMAND_HANGUP:
823 + /* Here we really have to lock */
824 + ast_mutex_lock(&iaxsl[ifr->callno]);
825 + if (iaxs[ifr->callno]) {
826 + ast_set_flag(iaxs[ifr->callno], IAX_ALREADYGONE);
827 + ast_log(LOG_DEBUG, "Asynchronously destroying %d, having received hangup\n", ifr->callno);
828 + /* Set hangup cause according to remote */
829 + if (ies->causecode && iaxs[ifr->callno]->owner)
830 + iaxs[ifr->callno]->owner->hangupcause = ies->causecode;
831 + iax2_destroy_nolock(ifr->callno);
832 + }
833 + iaxs_queued[ifr->callno]--;
834 + if (iaxs_queued[ifr->callno] < 0) iaxs_queued[ifr->callno] = 0;
835 + dequeued = 1;
836 + ast_mutex_unlock(&iaxsl[ifr->callno]);
837 + break;
838 + case IAX_COMMAND_INVAL:
839 + /* Here we really have to lock */
840 + ast_mutex_lock(&iaxsl[ifr->callno]);
841 + if (iaxs[ifr->callno]) {
842 + iaxs[ifr->callno]->error = ENOTCONN;
843 + ast_log(LOG_DEBUG, "Asynchronously destroying %d, having received INVAL\n", ifr->callno);
844 + iax2_destroy_nolock(ifr->callno);
845 + if (option_debug)
846 + ast_log(LOG_DEBUG, "Destroying call %d\n", ifr->callno);
847 + }
848 + iaxs_queued[ifr->callno]--;
849 + if (iaxs_queued[ifr->callno] < 0) iaxs_queued[ifr->callno] = 0;
850 + dequeued = 1;
851 + ast_mutex_unlock(&iaxsl[ifr->callno]);
852 + break;
853 + case IAX_COMMAND_AUTHREP:
854 + /* Ignore once we've started */
855 + if (ast_test_flag(&iaxs[ifr->callno]->state, IAX_STATE_STARTED | IAX_STATE_TBD)) {
856 + ast_log(LOG_WARNING, "Call on %s is already up, can't start on it\n", iaxs[ifr->callno]->owner ? iaxs[ifr->callno]->owner->name : "<Unknown>");
857 + break;
858 + }
859 + if (authenticate_verify(iaxs[ifr->callno], ies)) {
860 + if (authdebug)
861 + ast_log(LOG_NOTICE, "Host %s failed to authenticate as %s\n", ast_inet_ntoa(iabuf, sizeof(iabuf), iaxs[ifr->callno]->addr.sin_addr), iaxs[ifr->callno]->username);
862 + memset(&ied0, 0, sizeof(ied0));
863 + auth_fail(ifr->callno, IAX_COMMAND_REJECT);
864 + break;
865 + }
866 + ast_mutex_lock(&iaxsl[ifr->callno]);
867 + if (strcasecmp(iaxs[ifr->callno]->exten, "TBD")) {
868 + /* This might re-enter the IAX code and need the lock */
869 + exists = ast_exists_extension(NULL, iaxs[ifr->callno]->context, iaxs[ifr->callno]->exten, 1, iaxs[ifr->callno]->cid_num);
870 + } else
871 + exists = 0;
872 + if (strcmp(iaxs[ifr->callno]->exten, "TBD") && !exists) {
873 + if (authdebug)
874 + ast_log(LOG_NOTICE, "Rejected connect attempt from %s, request '%s@%s' does not exist\n", ast_inet_ntoa(iabuf, sizeof(iabuf), fr->sin.sin_addr), iaxs[ifr->callno]->exten, iaxs[ifr->callno]->context);
875 + memset(&ied0, 0, sizeof(ied0));
876 + iax_ie_append_str(&ied0, IAX_IE_CAUSE, "No such context/extension");
877 + iax_ie_append_byte(&ied0, IAX_IE_CAUSECODE, AST_CAUSE_NO_ROUTE_DESTINATION);
878 + send_command_final(iaxs[ifr->callno], AST_FRAME_IAX, IAX_COMMAND_REJECT, 0, ied0.buf, ied0.pos, -1);
879 + } else {
880 + /* Select an appropriate format */
881 + if(ast_test_flag(iaxs[ifr->callno], IAX_CODEC_NOPREFS)) {
882 + if(ast_test_flag(iaxs[ifr->callno], IAX_CODEC_NOCAP)) {
883 + using_prefs = "reqonly";
884 + } else {
885 + using_prefs = "disabled";
886 + }
887 + format = iaxs[ifr->callno]->peerformat & iaxs[ifr->callno]->capability;
888 + memset(&pref, 0, sizeof(pref));
889 + strcpy(caller_pref_buf, "disabled");
890 + strcpy(host_pref_buf, "disabled");
891 + } else {
892 + using_prefs = "mine";
893 + if(ies->codec_prefs) {
894 + /* If we are codec_first_choice we let the caller have the 1st shot at picking the codec.*/
895 + ast_codec_pref_convert(&rpref, ies->codec_prefs, 32, 0);
896 + if (ast_test_flag(iaxs[ifr->callno], IAX_CODEC_USER_FIRST)) {
897 + ast_codec_pref_convert(&pref, ies->codec_prefs, 32, 0);
898 + using_prefs = "caller";
899 + } else {
900 + pref = iaxs[ifr->callno]->prefs;
901 + }
902 + } else /* if no codec_prefs IE do it the old way */
903 + pref = iaxs[ifr->callno]->prefs;
904 +
905 + format = ast_codec_choose(&pref, iaxs[ifr->callno]->capability & iaxs[ifr->callno]->peercapability, 0);
906 + ast_codec_pref_string(&rpref, caller_pref_buf, sizeof(caller_pref_buf) - 1);
907 + ast_codec_pref_string(&iaxs[ifr->callno]->prefs, host_pref_buf, sizeof(host_pref_buf) - 1);
908 + }
909 + if (!format) {
910 + if(!ast_test_flag(iaxs[ifr->callno], IAX_CODEC_NOCAP)) {
911 + ast_log(LOG_DEBUG, "We don't do requested format %s, falling back to peer capability %d\n", ast_getformatname(iaxs[ifr->callno]->peerformat), iaxs[ifr->callno]->peercapability);
912 + format = iaxs[ifr->callno]->peercapability & iaxs[ifr->callno]->capability;
913 + }
914 + if (!format) {
915 + if (authdebug) {
916 + if(ast_test_flag(iaxs[ifr->callno], IAX_CODEC_NOCAP))
917 + ast_log(LOG_NOTICE, "Rejected connect attempt from %s, requested 0x%x incompatible with our capability 0x%x.\n", ast_inet_ntoa(iabuf, sizeof(iabuf), fr->sin.sin_addr), iaxs[ifr->callno]->peerformat, iaxs[ifr->callno]->capability);
918 + else
919 + ast_log(LOG_NOTICE, "Rejected connect attempt from %s, requested/capability 0x%x/0x%x incompatible with our capability 0x%x.\n", ast_inet_ntoa(iabuf, sizeof(iabuf), fr->sin.sin_addr), iaxs[ifr->callno]->peerformat, iaxs[ifr->callno]->peercapability, iaxs[ifr->callno]->capability);
920 + }
921 + memset(&ied0, 0, sizeof(ied0));
922 + iax_ie_append_str(&ied0, IAX_IE_CAUSE, "Unable to negotiate codec");
923 + iax_ie_append_byte(&ied0, IAX_IE_CAUSECODE, AST_CAUSE_BEARERCAPABILITY_NOTAVAIL);
924 + send_command_final(iaxs[ifr->callno], AST_FRAME_IAX, IAX_COMMAND_REJECT, 0, ied0.buf, ied0.pos, -1);
925 + } else {
926 + /* Pick one... */
927 + if(ast_test_flag(iaxs[ifr->callno], IAX_CODEC_NOCAP)) {
928 + if(!(iaxs[ifr->callno]->peerformat & iaxs[ifr->callno]->capability))
929 + format = 0;
930 + } else {
931 + if(ast_test_flag(iaxs[ifr->callno], IAX_CODEC_NOPREFS)) {
932 + using_prefs = ast_test_flag(iaxs[ifr->callno], IAX_CODEC_NOCAP) ? "reqonly" : "disabled";
933 + memset(&pref, 0, sizeof(pref));
934 + format = ast_test_flag(iaxs[ifr->callno], IAX_CODEC_NOCAP) ?
935 + iaxs[ifr->callno]->peerformat : ast_best_codec(iaxs[ifr->callno]->peercapability & iaxs[ifr->callno]->capability);
936 + strcpy(caller_pref_buf,"disabled");
937 + strcpy(host_pref_buf,"disabled");
938 + } else {
939 + using_prefs = "mine";
940 + if(ies->codec_prefs) {
941 + /* Do the opposite of what we tried above. */
942 + if (ast_test_flag(iaxs[ifr->callno], IAX_CODEC_USER_FIRST)) {
943 + pref = iaxs[ifr->callno]->prefs;
944 + } else {
945 + pref = rpref;
946 + using_prefs = "caller";
947 + }
948 + format = ast_codec_choose(&pref, iaxs[ifr->callno]->peercapability & iaxs[ifr->callno]->capability, 1);
949 + } else /* if no codec_prefs IE do it the old way */
950 + format = ast_best_codec(iaxs[ifr->callno]->peercapability & iaxs[ifr->callno]->capability);
951 + }
952 + }
953 + if (!format) {
954 + ast_log(LOG_ERROR, "No best format in 0x%x???\n", iaxs[ifr->callno]->peercapability & iaxs[ifr->callno]->capability);
955 + if (authdebug) {
956 + if(ast_test_flag(iaxs[ifr->callno], IAX_CODEC_NOCAP))
957 + ast_log(LOG_NOTICE, "Rejected connect attempt from %s, requested 0x%x incompatible with our capability 0x%x.\n", ast_inet_ntoa(iabuf, sizeof(iabuf), fr->sin.sin_addr), iaxs[ifr->callno]->peerformat, iaxs[ifr->callno]->capability);
958 + else
959 + ast_log(LOG_NOTICE, "Rejected connect attempt from %s, requested/capability 0x%x/0x%x incompatible with our capability 0x%x.\n", ast_inet_ntoa(iabuf, sizeof(iabuf), fr->sin.sin_addr), iaxs[ifr->callno]->peerformat, iaxs[ifr->callno]->peercapability, iaxs[ifr->callno]->capability);
960 + }
961 + memset(&ied0, 0, sizeof(ied0));
962 + iax_ie_append_str(&ied0, IAX_IE_CAUSE, "Unable to negotiate codec");
963 + iax_ie_append_byte(&ied0, IAX_IE_CAUSECODE, AST_CAUSE_BEARERCAPABILITY_NOTAVAIL);
964 + send_command_final(iaxs[ifr->callno], AST_FRAME_IAX, IAX_COMMAND_REJECT, 0, ied0.buf, ied0.pos, -1);
965 + }
966 + }
967 + }
968 + if (format) {
969 + /* Authentication received */
970 + memset(&ied1, 0, sizeof(ied1));
971 + iax_ie_append_int(&ied1, IAX_IE_FORMAT, format);
972 + send_command(iaxs[ifr->callno], AST_FRAME_IAX, IAX_COMMAND_ACCEPT, 0, ied1.buf, ied1.pos, -1);
973 + if (strcmp(iaxs[ifr->callno]->exten, "TBD")) {
974 + ast_set_flag(&iaxs[ifr->callno]->state, IAX_STATE_STARTED);
975 + if (option_verbose > 2)
976 + ast_verbose(VERBOSE_PREFIX_3 "Accepting AUTHENTICATED call from %s:\n"
977 + "%srequested format = %s,\n"
978 + "%srequested prefs = %s,\n"
979 + "%sactual format = %s,\n"
980 + "%shost prefs = %s,\n"
981 + "%spriority = %s\n",
982 + ast_inet_ntoa(iabuf, sizeof(iabuf), fr->sin.sin_addr),
983 + VERBOSE_PREFIX_4,
984 + ast_getformatname(iaxs[ifr->callno]->peerformat),
985 + VERBOSE_PREFIX_4,
986 + caller_pref_buf,
987 + VERBOSE_PREFIX_4,
988 + ast_getformatname(format),
989 + VERBOSE_PREFIX_4,
990 + host_pref_buf,
991 + VERBOSE_PREFIX_4,
992 + using_prefs);
993 +
994 + ast_set_flag(&iaxs[ifr->callno]->state, IAX_STATE_STARTED);
995 + if(!(c = ast_iax2_new(ifr->callno, AST_STATE_RING, format)))
996 + iax2_destroy_nolock(ifr->callno);
997 + } else {
998 + ast_set_flag(&iaxs[ifr->callno]->state, IAX_STATE_TBD);
999 + /* If this is a TBD call, we're ready but now what... */
1000 + if (option_verbose > 2)
1001 + ast_verbose(VERBOSE_PREFIX_3 "Accepted AUTHENTICATED TBD call from %s\n", ast_inet_ntoa(iabuf, sizeof(iabuf), fr->sin.sin_addr));
1002 + }
1003 + }
1004 + }
1005 + iaxs_queued[ifr->callno]--;
1006 + if (iaxs_queued[ifr->callno] < 0) iaxs_queued[ifr->callno] = 0;
1007 + ast_mutex_unlock(&iaxsl[ifr->callno]);
1008 + }
1009 + if (!dequeued) {
1010 + ast_mutex_lock(&iaxsl[ifr->callno]);
1011 + iaxs_queued[ifr->callno]--;
1012 + if (iaxs_queued[ifr->callno] < 0) iaxs_queued[ifr->callno] = 0;
1013 + ast_mutex_unlock(&iaxsl[ifr->callno]);
1014 + }
1015 + }
1016 + iax_frame_free(ifr);
1017 + free(fr);
1018 + fr = NULL;
1019 + }
1020 +
1021 + }
1022 + return NULL;
1023 +}
1024 +
1025 +
1026 +static int start_auth_thread(void)
1027 +{
1028 + return ast_pthread_create(&auththreadid, NULL, auth_thread, &callq);
1029 +}
1030 +
1031 +static int start_reg_thread(void)
1032 +{
1033 + return ast_pthread_create(&regthreadid, NULL, auth_thread, &regq);
1034 +}
1035 +
1036 static struct iax2_context *build_context(char *context)
1037 {
1038 struct iax2_context *con = malloc(sizeof(struct iax2_context));
1039 @@ -8882,6 +9305,7 @@
1040 tmpstr = ast_strdupa(data);
1041 parse_dial_string(tmpstr, &pds);
1042
1043 +ast_log(LOG_NOTICE, "calling create_addr here\n");
1044 /* Populate our address from the given */
1045 if (create_addr(pds.peer, &sin, &cai))
1046 return -1;
1047 @@ -9492,6 +9916,11 @@
1048 static int __unload_module(void)
1049 {
1050 int x;
1051 + /* Cancel the authentication thread */
1052 + if (auththreadid != AST_PTHREADT_NULL) {
1053 + pthread_cancel(auththreadid);
1054 + pthread_join(auththreadid, NULL);
1055 + }
1056 /* Cancel the network thread, close the net socket */
1057 if (netthreadid != AST_PTHREADT_NULL) {
1058 pthread_cancel(netthreadid);
1059 @@ -9514,6 +9943,8 @@
1060
1061 int unload_module()
1062 {
1063 + ast_mutex_destroy(&regq.lock);
1064 + ast_mutex_destroy(&callq.lock);
1065 ast_mutex_destroy(&iaxq.lock);
1066 ast_mutex_destroy(&userl.lock);
1067 ast_mutex_destroy(&peerl.lock);
1068 @@ -9535,6 +9966,9 @@
1069 struct ast_netsock *ns;
1070 struct sockaddr_in sin;
1071
1072 + /* you never can tell */
1073 + memset(iaxs_queued, 0, sizeof(iaxs_queued));
1074 +
1075 ast_custom_function_register(&iaxpeer_function);
1076
1077 iax_set_output(iax_debug_output);
1078 @@ -9577,7 +10011,14 @@
1079 }
1080 ast_netsock_init(netsock);
1081
1082 + gethostname(servername, sizeof(servername) - 1);
1083 + ast_log(LOG_NOTICE, "servername = %s\n", servername);
1084 +
1085 ast_mutex_init(&iaxq.lock);
1086 + ast_mutex_init(&regq.lock);
1087 + ast_cond_init(&regq.cond, NULL);
1088 + ast_mutex_init(&callq.lock);
1089 + ast_cond_init(&callq.cond, NULL);
1090 ast_mutex_init(&userl.lock);
1091 ast_mutex_init(&peerl.lock);
1092 ast_mutex_init(&waresl.lock);
1093 @@ -9621,6 +10062,16 @@
1094 ast_netsock_release(netsock);
1095 }
1096
1097 + res = start_reg_thread();
1098 + if (res) {
1099 + ast_log(LOG_ERROR, "Unable to start registration thread\n");
1100 + }
1101 +
1102 + res = start_auth_thread();
1103 + if (res) {
1104 + ast_log(LOG_ERROR, "Unable to start authentication thread\n");
1105 + }
1106 +
1107 for (reg = registrations; reg; reg = reg->next)
1108 iax2_do_register(reg);
1109 ast_mutex_lock(&peerl.lock);
1110 diff -urN asterisk-1.2.4/res/Makefile asterisk-1.2.4.carrier/res/Makefile
1111 --- asterisk-1.2.4/res/Makefile 2006-01-31 09:41:43.000000000 +0100
1112 +++ asterisk-1.2.4.carrier/res/Makefile 2006-01-31 15:04:30.000000000 +0100
1113 @@ -13,6 +13,21 @@
1114
1115 MODS=res_indications.so res_monitor.so res_adsi.so res_agi.so res_features.so res_watchdog.so
1116
1117 +#
1118 +# MySQL stuff... Autoconf anyone??
1119 +#
1120 +MODS+=$(shell if [ -d /usr/local/mysql/include ] || [ -d /usr/include/mysql ] || [ -d /usr/local/include/mysql ] || [ -d /opt/mysql/include ]; then echo "res_config_mysql.so"; fi)
1121 +CFLAGS+=$(shell if [ -d /usr/local/mysql/include ]; then echo "-I/usr/local/mysql/include"; fi)
1122 +CFLAGS+=$(shell if [ -d /usr/include/mysql ]; then echo "-I/usr/include/mysql"; fi)
1123 +CFLAGS+=$(shell if [ -d /usr/local/include/mysql ]; then echo "-I/usr/local/include/mysql"; fi)
1124 +CFLAGS+=$(shell if [ -d /opt/mysql/include/mysql ]; then echo "-I/opt/mysql/include/mysql"; fi)
1125 +MLFLAGS=
1126 +MLFLAGS+=$(shell if [ -d /usr/lib/mysql ]; then echo "-L/usr/lib/mysql"; fi)
1127 +MLFLAGS+=$(shell if [ -d /usr/lib64/mysql ]; then echo "-L/usr/lib64/mysql"; fi)
1128 +MLFLAGS+=$(shell if [ -d /usr/local/mysql/lib ]; then echo "-L/usr/local/mysql/lib"; fi)
1129 +MLFLAGS+=$(shell if [ -d /usr/local/lib/mysql ]; then echo "-L/usr/local/lib/mysql"; fi)
1130 +MLFLAGS+=$(shell if [ -d /opt/mysql/lib/mysql ]; then echo "-L/opt/mysql/lib/mysql"; fi)
1131 +
1132 ifneq ($(wildcard $(CROSS_COMPILE_TARGET)/usr/include/odbcinst.h)$(wildcard $(CROSS_COMPILE_TARGET)/usr/local/include/odbcinst.h),)
1133 ifneq (${OSARCH},FreeBSD)
1134 MODS+=res_config_odbc.so
1135 @@ -109,6 +124,9 @@
1136 res_config_odbc.so: res_config_odbc.o
1137 $(CC) $(SOLINK) -o $@ ${CYGSOLINK} $< ${CYGSOLIB} ${CYG_RES_CONFIG_ODBC_LIB}
1138
1139 +res_config_mysql.so: res_config_mysql.o
1140 + $(CC) $(SOLINK) -o $@ $< -lmysqlclient -lz $(MLFLAGS)
1141 +
1142 ifneq ($(wildcard .depend),)
1143 include .depend
1144 endif
1145 diff -urN asterisk-1.2.4/res/res_config_mysql.c asterisk-1.2.4.carrier/res/res_config_mysql.c
1146 --- asterisk-1.2.4/res/res_config_mysql.c 1970-01-01 01:00:00.000000000 +0100
1147 +++ asterisk-1.2.4.carrier/res/res_config_mysql.c 2006-01-31 14:54:49.000000000 +0100
1148 @@ -0,0 +1,680 @@
1149 +/*
1150 + * Asterisk -- A telephony toolkit for Linux.
1151 + *
1152 + * Copyright (C) 1999-2005, Digium, Inc.
1153 + *
1154 + * Mark Spencer <markster@digium.com> - Asterisk Author
1155 + * Matthew Boehm <mboehm@cytelcom.com> - MySQL RealTime Driver Author
1156 + *
1157 + * res_config_mysql.c <mysql plugin for RealTime configuration engine>
1158 + *
1159 + * v2.0 - (10-07-05) - mutex_lock fixes (bug #4973, comment #0034602)
1160 + *
1161 + * v1.9 - (08-19-05) - Added support to correctly honor the family database specified
1162 + * in extconfig.conf (bug #4973)
1163 + *
1164 + * v1.8 - (04-21-05) - Modified return values of update_mysql to better indicate
1165 + * what really happened.
1166 + *
1167 + * v1.7 - (01-28-05) - Fixed non-initialization of ast_category struct
1168 + * in realtime_multi_mysql function which caused segfault.
1169 + *
1170 + * v1.6 - (00-00-00) - Skipped to bring comments into sync with version number in CVS.
1171 + *
1172 + * v1.5.1 - (01-26-05) - Added better(?) locking stuff
1173 + *
1174 + * v1.5 - (01-26-05) - Brought up to date with new config.h changes (bug #3406)
1175 + * - Added in extra locking provided by georg (bug #3248)
1176 + *
1177 + * v1.4 - (12-02-04) - Added realtime_multi_mysql function
1178 + * This function will return an ast_config with categories,
1179 + * unlike standard realtime_mysql which only returns
1180 + * a linked list of ast_variables
1181 + *
1182 + * v1.3 - (12-01-04) - Added support other operators
1183 + * Ex: =, !=, LIKE, NOT LIKE, RLIKE, etc...
1184 + *
1185 + * v1.2 - (11-DD-04) - Added reload. Updated load and unload.
1186 + * Code beautification (doc/CODING-GUIDELINES)
1187 + */
1188 +
1189 +#include <asterisk/channel.h>
1190 +#include <asterisk/logger.h>
1191 +#include <asterisk/config.h>
1192 +#include <asterisk/module.h>
1193 +#include <asterisk/lock.h>
1194 +#include <asterisk/options.h>
1195 +#include <asterisk/cli.h>
1196 +#include <asterisk/utils.h>
1197 +#include <stdlib.h>
1198 +#include <string.h>
1199 +#include <mysql.h>
1200 +#include <mysql_version.h>
1201 +#include <errmsg.h>
1202 +
1203 +static char *res_config_mysql_desc = "MySQL RealTime Configuration Driver";
1204 +
1205 +AST_MUTEX_DEFINE_STATIC(mysql_lock);
1206 +#define RES_CONFIG_MYSQL_CONF "res_mysql.conf"
1207 +MYSQL mysql;
1208 +static char dbhost[50];
1209 +static char dbuser[50];
1210 +static char dbpass[50];
1211 +static char dbname[50];
1212 +static char dbsock[50];
1213 +static int dbport;
1214 +static int connected;
1215 +static time_t connect_time;
1216 +
1217 +static int parse_config(void);
1218 +static int mysql_reconnect(const char *database);
1219 +static int realtime_mysql_status(int fd, int argc, char **argv);
1220 +
1221 +STANDARD_LOCAL_USER;
1222 +
1223 +LOCAL_USER_DECL;
1224 +
1225 +static char cli_realtime_mysql_status_usage[] =
1226 +"Usage: realtime mysql status\n"
1227 +" Shows connection information for the MySQL RealTime driver\n";
1228 +
1229 +static struct ast_cli_entry cli_realtime_mysql_status = {
1230 + { "realtime", "mysql", "status", NULL }, realtime_mysql_status,
1231 + "Shows connection information for the MySQL RealTime driver", cli_realtime_mysql_status_usage, NULL };
1232 +
1233 +static struct ast_variable *realtime_mysql(const char *database, const char *table, va_list ap)
1234 +{
1235 + MYSQL_RES *result;
1236 + MYSQL_ROW row;
1237 + MYSQL_FIELD *fields;
1238 + int numFields, i;
1239 + char sql[256];
1240 + char *stringp;
1241 + char *chunk;
1242 + char *op;
1243 + const char *newparam, *newval;
1244 + struct ast_variable *var=NULL, *prev=NULL;
1245 +
1246 + if(!table) {
1247 + ast_log(LOG_WARNING, "MySQL RealTime: No table specified.\n");
1248 + return NULL;
1249 + }
1250 +
1251 + /* Get the first parameter and first value in our list of passed paramater/value pairs */
1252 + newparam = va_arg(ap, const char *);
1253 + newval = va_arg(ap, const char *);
1254 + if(!newparam || !newval) {
1255 + ast_log(LOG_WARNING, "MySQL RealTime: Realtime retrieval requires at least 1 parameter and 1 value to search on.\n");
1256 + mysql_close(&mysql);
1257 + return NULL;
1258 + }
1259 +
1260 + /* Create the first part of the query using the first parameter/value pairs we just extracted
1261 + If there is only 1 set, then we have our query. Otherwise, loop thru the list and concat */
1262 +
1263 + if(!strchr(newparam, ' ')) op = " ="; else op = "";
1264 +
1265 + snprintf(sql, sizeof(sql), "SELECT * FROM %s WHERE %s%s '%s'", table, newparam, op, newval);
1266 + while((newparam = va_arg(ap, const char *))) {
1267 + newval = va_arg(ap, const char *);
1268 + if(!strchr(newparam, ' ')) op = " ="; else op = "";
1269 + snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), " AND %s%s '%s'", newparam, op, newval);
1270 + }
1271 + va_end(ap);
1272 +
1273 + ast_log(LOG_DEBUG, "MySQL RealTime: Retrieve SQL: %s\n", sql);
1274 +
1275 + /* We now have our complete statement; Lets connect to the server and execute it. */
1276 + ast_mutex_lock(&mysql_lock);
1277 + if(!mysql_reconnect(database)) {
1278 + ast_mutex_unlock(&mysql_lock);
1279 + return NULL;
1280 + }
1281 +
1282 +// ast_log(LOG_NOTICE, "SQL: %s\m", sql);
1283 +
1284 + if(mysql_real_query(&mysql, sql, strlen(sql))) {
1285 + ast_log(LOG_WARNING, "MySQL RealTime: Failed to query database. Check debug for more info.\n");
1286 + ast_log(LOG_DEBUG, "MySQL RealTime: Query: %s\n", sql);
1287 + ast_log(LOG_DEBUG, "MySQL RealTime: Query Failed because: %s\n", mysql_error(&mysql));
1288 + ast_mutex_unlock(&mysql_lock);
1289 + return NULL;
1290 + }
1291 +
1292 + if((result = mysql_store_result(&mysql))) {
1293 + numFields = mysql_num_fields(result);
1294 + fields = mysql_fetch_fields(result);
1295 +
1296 + while((row = mysql_fetch_row(result))) {
1297 + for(i = 0; i < numFields; i++) {
1298 + stringp = row[i];
1299 + while(stringp) {
1300 + chunk = strsep(&stringp, ";");
1301 + if(chunk && !ast_strlen_zero(ast_strip(chunk))) {
1302 + if(prev) {
1303 + prev->next = ast_variable_new(fields[i].name, chunk);
1304 + if (prev->next) {
1305 + prev = prev->next;
1306 + }
1307 + } else {
1308 + prev = var = ast_variable_new(fields[i].name, chunk);
1309 + }
1310 + }
1311 + }
1312 + }
1313 + }
1314 + } else {
1315 + ast_log(LOG_WARNING, "MySQL RealTime: Could not find any rows in table %s.\n", table);
1316 + }
1317 +
1318 + mysql_free_result(result);
1319 + ast_mutex_unlock(&mysql_lock);
1320 +
1321 + return var;
1322 +}
1323 +
1324 +static struct ast_config *realtime_multi_mysql(const char *database, const char *table, va_list ap)
1325 +{
1326 + MYSQL_RES *result;
1327 + MYSQL_ROW row;
1328 + MYSQL_FIELD *fields;
1329 + int numFields, i;
1330 + char sql[256];
1331 + const char *initfield = NULL;
1332 + char *stringp;
1333 + char *chunk;
1334 + char *op;
1335 + const char *newparam, *newval;
1336 + struct ast_realloca ra;
1337 + struct ast_variable *var=NULL;
1338 + struct ast_config *cfg = NULL;
1339 + struct ast_category *cat = NULL;
1340 +
1341 + if(!table) {
1342 + ast_log(LOG_WARNING, "MySQL RealTime: No table specified.\n");
1343 + return NULL;
1344 + }
1345 +
1346 + memset(&ra, 0, sizeof(ra));
1347 +
1348 + cfg = ast_config_new();
1349 + if (!cfg) {
1350 + /* If I can't alloc memory at this point, why bother doing anything else? */
1351 + ast_log(LOG_WARNING, "Out of memory!\n");
1352 + return NULL;
1353 + }
1354 +
1355 + /* Get the first parameter and first value in our list of passed paramater/value pairs */
1356 + newparam = va_arg(ap, const char *);
1357 + newval = va_arg(ap, const char *);
1358 + if(!newparam || !newval) {
1359 + ast_log(LOG_WARNING, "MySQL RealTime: Realtime retrieval requires at least 1 parameter and 1 value to search on.\n");
1360 + mysql_close(&mysql);
1361 + return NULL;
1362 + }
1363 +
1364 + initfield = ast_strdupa(newparam);
1365 + if(initfield && (op = strchr(initfield, ' '))) {
1366 + *op = '\0';
1367 + }
1368 +
1369 + /* Create the first part of the query using the first parameter/value pairs we just extracted
1370 + If there is only 1 set, then we have our query. Otherwise, loop thru the list and concat */
1371 +
1372 + if(!strchr(newparam, ' ')) op = " ="; else op = "";
1373 +
1374 + snprintf(sql, sizeof(sql), "SELECT * FROM %s WHERE %s%s '%s'", table, newparam, op, newval);
1375 + while((newparam = va_arg(ap, const char *))) {
1376 + newval = va_arg(ap, const char *);
1377 + if(!strchr(newparam, ' ')) op = " ="; else op = "";
1378 + snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), " AND %s%s '%s'", newparam, op, newval);
1379 + }
1380 +
1381 + if(initfield) {
1382 + snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), " ORDER BY %s", initfield);
1383 + }
1384 +
1385 + va_end(ap);
1386 +
1387 + ast_log(LOG_DEBUG, "MySQL RealTime: Retrieve SQL: %s\n", sql);
1388 +
1389 + /* We now have our complete statement; Lets connect to the server and execute it. */
1390 + ast_mutex_lock(&mysql_lock);
1391 + if(!mysql_reconnect(database)) {
1392 + ast_mutex_unlock(&mysql_lock);
1393 + return NULL;
1394 + }
1395 +
1396 + if(mysql_real_query(&mysql, sql, strlen(sql))) {
1397 + ast_log(LOG_WARNING, "MySQL RealTime: Failed to query database. Check debug for more info.\n");
1398 + ast_log(LOG_DEBUG, "MySQL RealTime: Query: %s\n", sql);
1399 + ast_log(LOG_DEBUG, "MySQL RealTime: Query Failed because: %s\n", mysql_error(&mysql));
1400 + ast_mutex_unlock(&mysql_lock);
1401 + return NULL;
1402 + }
1403 +
1404 + if((result = mysql_store_result(&mysql))) {
1405 + numFields = mysql_num_fields(result);
1406 + fields = mysql_fetch_fields(result);
1407 +
1408 + while((row = mysql_fetch_row(result))) {
1409 + var = NULL;
1410 + cat = ast_category_new("");
1411 + if(!cat) {
1412 + ast_log(LOG_WARNING, "Out of memory!\n");
1413 + continue;
1414 + }
1415 + for(i = 0; i < numFields; i++) {
1416 + stringp = row[i];
1417 + while(stringp) {
1418 + chunk = strsep(&stringp, ";");
1419 + if(chunk && !ast_strlen_zero(ast_strip(chunk))) {
1420 + if(initfield && !strcmp(initfield, fields[i].name)) {
1421 + ast_category_rename(cat, chunk);
1422 + }
1423 + var = ast_variable_new(fields[i].name, chunk);
1424 + ast_variable_append(cat, var);
1425 + }
1426 + }
1427 + }
1428 + ast_category_append(cfg, cat);
1429 + }
1430 + } else {
1431 + ast_log(LOG_WARNING, "MySQL RealTime: Could not find any rows in table %s.\n", table);
1432 + }
1433 +
1434 + mysql_free_result(result);
1435 + ast_mutex_unlock(&mysql_lock);
1436 +
1437 + return cfg;
1438 +}
1439 +
1440 +static int update_mysql(const char *database, const char *table, const char *keyfield, const char *lookup, va_list ap)
1441 +{
1442 + my_ulonglong numrows;
1443 + char sql[256];
1444 + const char *newparam, *newval;
1445 +
1446 + if(!table) {
1447 + ast_log(LOG_WARNING, "MySQL RealTime: No table specified.\n");
1448 + return -1;
1449 + }
1450 +
1451 + /* Get the first parameter and first value in our list of passed paramater/value pairs */
1452 + newparam = va_arg(ap, const char *);
1453 + newval = va_arg(ap, const char *);
1454 + if(!newparam || !newval) {
1455 + ast_log(LOG_WARNING, "MySQL RealTime: Realtime retrieval requires at least 1 parameter and 1 value to search on.\n");
1456 + mysql_close(&mysql);
1457 + return -1;
1458 + }
1459 +
1460 + /* Create the first part of the query using the first parameter/value pairs we just extracted
1461 + If there is only 1 set, then we have our query. Otherwise, loop thru the list and concat */
1462 +
1463 + snprintf(sql, sizeof(sql), "UPDATE %s SET %s = '%s'", table, newparam, newval);
1464 + while((newparam = va_arg(ap, const char *))) {
1465 + newval = va_arg(ap, const char *);
1466 + snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), ", %s = '%s'", newparam, newval);
1467 + }
1468 + va_end(ap);
1469 + snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), " WHERE %s = '%s'", keyfield, lookup);
1470 +
1471 + ast_log(LOG_DEBUG,"MySQL RealTime: Update SQL: %s\n", sql);
1472 +
1473 + /* We now have our complete statement; Lets connect to the server and execute it. */
1474 + ast_mutex_lock(&mysql_lock);
1475 + if(!mysql_reconnect(database)) {
1476 + ast_mutex_unlock(&mysql_lock);
1477 + return -1;
1478 + }
1479 +
1480 + if(mysql_real_query(&mysql, sql, strlen(sql))) {
1481 + ast_log(LOG_WARNING, "MySQL RealTime: Failed to query database. Check debug for more info.\n");
1482 + ast_log(LOG_DEBUG, "MySQL RealTime: Query: %s\n", sql);
1483 + ast_log(LOG_DEBUG, "MySQL RealTime: Query Failed because: %s\n", mysql_error(&mysql));
1484 + ast_mutex_unlock(&mysql_lock);
1485 + return -1;
1486 + }
1487 +
1488 + numrows = mysql_affected_rows(&mysql);
1489 + ast_mutex_unlock(&mysql_lock);
1490 +
1491 + ast_log(LOG_DEBUG,"MySQL RealTime: Updated %llu rows on table: %s\n", numrows, table);
1492 +
1493 + /* From http://dev.mysql.com/doc/mysql/en/mysql-affected-rows.html
1494 + * An integer greater than zero indicates the number of rows affected
1495 + * Zero indicates that no records were updated
1496 + * -1 indicates that the query returned an error (although, if the query failed, it should have been caught above.)
1497 + */
1498 +
1499 + if(numrows >= 0)
1500 + return (int)numrows;
1501 +
1502 + return -1;
1503 +}
1504 +
1505 +static struct ast_config *config_mysql(const char *database, const char *table, const char *file, struct ast_config *cfg)
1506 +{
1507 + MYSQL_RES *result;
1508 + MYSQL_ROW row;
1509 + my_ulonglong num_rows;
1510 + struct ast_config *new;
1511 + struct ast_variable *cur_v, *new_v;
1512 + struct ast_category *cur_cat, *new_cat;
1513 + char sql[250] = "";
1514 + char last[80] = "";
1515 + int cat_started = 0;
1516 + int var_started = 0;
1517 + int last_cat_metric = 0;
1518 +
1519 + last[0] = '\0';
1520 +
1521 + if(!file || !strcmp(file, RES_CONFIG_MYSQL_CONF)) {
1522 + ast_log(LOG_WARNING, "MySQL RealTime: Cannot configure myself.\n");
1523 + return NULL;
1524 + }
1525 +
1526 + snprintf(sql, sizeof(sql), "SELECT category, var_name, var_val, cat_metric FROM %s WHERE filename='%s' and commented=0 ORDER BY filename, cat_metric desc, var_metric asc, category, var_name, var_val, id", table, file);
1527 +
1528 + ast_log(LOG_DEBUG, "MySQL RealTime: Static SQL: %s\n", sql);
1529 +
1530 + /* We now have our complete statement; Lets connect to the server and execute it. */
1531 + ast_mutex_lock(&mysql_lock);
1532 + if(!mysql_reconnect(database)) {
1533 + ast_mutex_unlock(&mysql_lock);
1534 + return NULL;
1535 + }
1536 +
1537 + if(mysql_real_query(&mysql, sql, strlen(sql))) {
1538 + ast_log(LOG_WARNING, "MySQL RealTime: Failed to query database. Check debug for more info.\n");
1539 + ast_log(LOG_DEBUG, "MySQL RealTime: Query: %s\n", sql);
1540 + ast_log(LOG_DEBUG, "MySQL RealTime: Query Failed because: %s\n", mysql_error(&mysql));
1541 + ast_mutex_unlock(&mysql_lock);
1542 + return NULL;
1543 + }
1544 +
1545 + if((result = mysql_store_result(&mysql))) {
1546 + num_rows = mysql_num_rows(result);
1547 + ast_log(LOG_DEBUG, "MySQL RealTime: Found %llu rows.\n", num_rows);
1548 +
1549 + /* There might exist a better way to access the column names other than counting,
1550 + but I believe that would require another loop that we don't need. */
1551 +
1552 + while((row = mysql_fetch_row(result))) {
1553 + if(!strcmp(row[1], "#include")) {
1554 + if (!ast_config_internal_load(row[2], cfg)) {
1555 + mysql_free_result(result);
1556 + ast_mutex_unlock(&mysql_lock);
1557 + return NULL;
1558 + }
1559 + continue;
1560 + }
1561 +
1562 + if(strcmp(last, row[0]) || last_cat_metric != atoi(row[3])) {
1563 + cur_cat = ast_category_new(row[0]);
1564 + if (!cur_cat) {
1565 + ast_log(LOG_WARNING, "Out of memory!\n");
1566 + break;
1567 + }
1568 + strcpy(last, row[0]);
1569 + last_cat_metric = atoi(row[3]);
1570 + ast_category_append(cfg, cur_cat);
1571 + }
1572 + new_v = ast_variable_new(row[1], row[2]);
1573 + ast_variable_append(cur_cat, new_v);
1574 + }
1575 + } else {
1576 + ast_log(LOG_WARNING, "MySQL RealTime: Could not find config '%s' in database.\n", file);
1577 + }
1578 +
1579 + mysql_free_result(result);
1580 + ast_mutex_unlock(&mysql_lock);
1581 +
1582 + return cfg;
1583 +}
1584 +
1585 +static struct ast_config_engine mysql_engine = {
1586 + .name = "mysql",
1587 + .load_func = config_mysql,
1588 + .realtime_func = realtime_mysql,
1589 + .realtime_multi_func = realtime_multi_mysql,
1590 + .update_func = update_mysql
1591 +};
1592 +
1593 +int load_module (void)
1594 +{
1595 + parse_config();
1596 +
1597 + ast_mutex_lock(&mysql_lock);
1598 +
1599 + if(!mysql_reconnect(NULL)) {
1600 + ast_log(LOG_WARNING, "MySQL RealTime: Couldn't establish connection. Check debug.\n");
1601 + ast_log(LOG_DEBUG, "MySQL RealTime: Cannot Connect: %s\n", mysql_error(&mysql));
1602 + }
1603 +
1604 + ast_config_engine_register(&mysql_engine);
1605 + if(option_verbose) {
1606 + ast_verbose("MySQL RealTime driver loaded.\n");
1607 + }
1608 + ast_cli_register(&cli_realtime_mysql_status);
1609 +
1610 + ast_mutex_unlock(&mysql_lock);
1611 +
1612 + return 0;
1613 +}
1614 +
1615 +int unload_module (void)
1616 +{
1617 + /* Aquire control before doing anything to the module itself. */
1618 + ast_mutex_lock(&mysql_lock);
1619 +
1620 + mysql_close(&mysql);
1621 + ast_cli_unregister(&cli_realtime_mysql_status);
1622 + ast_config_engine_deregister(&mysql_engine);
1623 + if(option_verbose) {
1624 + ast_verbose("MySQL RealTime unloaded.\n");
1625 + }
1626 +
1627 + STANDARD_HANGUP_LOCALUSERS;
1628 +
1629 + /* Unlock so something else can destroy the lock. */
1630 + ast_mutex_unlock(&mysql_lock);
1631 +
1632 + return 0;
1633 +}
1634 +
1635 +int reload (void)
1636 +{
1637 + /* Aquire control before doing anything to the module itself. */
1638 + ast_mutex_lock(&mysql_lock);
1639 +
1640 + mysql_close(&mysql);
1641 + connected = 0;
1642 + parse_config();
1643 +
1644 + if(!mysql_reconnect(NULL)) {
1645 + ast_log(LOG_WARNING, "MySQL RealTime: Couldn't establish connection. Check debug.\n");
1646 + ast_log(LOG_DEBUG, "MySQL RealTime: Cannot Connect: %s\n", mysql_error(&mysql));
1647 + }
1648 +
1649 + ast_verbose(VERBOSE_PREFIX_2 "MySQL RealTime reloaded.\n");
1650 +
1651 + /* Done reloading. Release lock so others can now use driver. */
1652 + ast_mutex_unlock(&mysql_lock);
1653 +
1654 + return 0;
1655 +}
1656 +
1657 +int parse_config (void)
1658 +{
1659 + struct ast_config *config;
1660 + char *s;
1661 +
1662 + config = ast_config_load(RES_CONFIG_MYSQL_CONF);
1663 +
1664 + if(config) {
1665 + if(!(s=ast_variable_retrieve(config, "general", "dbuser"))) {
1666 + ast_log(LOG_WARNING, "MySQL RealTime: No database user found, using 'asterisk' as default.\n");
1667 + strncpy(dbuser, "asterisk", sizeof(dbuser) - 1);
1668 + } else {
1669 + strncpy(dbuser, s, sizeof(dbuser) - 1);
1670 + }
1671 +
1672 + if(!(s=ast_variable_retrieve(config, "general", "dbpass"))) {
1673 + ast_log(LOG_WARNING, "MySQL RealTime: No database password found, using 'asterisk' as default.\n");
1674 + strncpy(dbpass, "asterisk", sizeof(dbpass) - 1);
1675 + } else {
1676 + strncpy(dbpass, s, sizeof(dbpass) - 1);
1677 + }
1678 +
1679 + if(!(s=ast_variable_retrieve(config, "general", "dbhost"))) {
1680 + ast_log(LOG_WARNING, "MySQL RealTime: No database host found, using localhost via socket.\n");
1681 + dbhost[0] = '\0';
1682 + } else {
1683 + strncpy(dbhost, s, sizeof(dbhost) - 1);
1684 + }
1685 +
1686 + if(!(s=ast_variable_retrieve(config, "general", "dbname"))) {
1687 + ast_log(LOG_WARNING, "MySQL RealTime: No database name found, using 'asterisk' as default.\n");
1688 + strncpy(dbname, "asterisk", sizeof(dbname) - 1);
1689 + } else {
1690 + strncpy(dbname, s, sizeof(dbname) - 1);
1691 + }
1692 +
1693 + if(!(s=ast_variable_retrieve(config, "general", "dbport"))) {
1694 + ast_log(LOG_WARNING, "MySQL RealTime: No database port found, using 3306 as default.\n");
1695 + dbport = 3306;
1696 + } else {
1697 + dbport = atoi(s);
1698 + }
1699 +
1700 + if(dbhost && !(s=ast_variable_retrieve(config, "general", "dbsock"))) {
1701 + ast_log(LOG_WARNING, "MySQL RealTime: No database socket found, using '/tmp/mysql.sock' as default.\n");
1702 + strncpy(dbsock, "/tmp/mysql.sock", sizeof(dbsock) - 1);
1703 + } else {
1704 + strncpy(dbsock, s, sizeof(dbsock) - 1);
1705 + }
1706 + }
1707 + ast_config_destroy(config);
1708 +
1709 + if(dbhost) {
1710 + ast_log(LOG_DEBUG, "MySQL RealTime Host: %s\n", dbhost);
1711 + ast_log(LOG_DEBUG, "MySQL RealTime Port: %i\n", dbport);
1712 + } else {
1713 + ast_log(LOG_DEBUG, "MySQL RealTime Socket: %s\n", dbsock);
1714 + }
1715 + ast_log(LOG_DEBUG, "MySQL RealTime User: %s\n", dbuser);
1716 + ast_log(LOG_DEBUG, "MySQL RealTime Password: %s\n", dbpass);
1717 +
1718 + return 1;
1719 +}
1720 +
1721 +char *description (void)
1722 +{
1723 + return res_config_mysql_desc;
1724 +}
1725 +
1726 +int usecount (void)
1727 +{
1728 + /* Try and get a lock. If unsuccessful, than that means another thread is using the mysql object. */
1729 + if(ast_mutex_trylock(&mysql_lock)) {
1730 + ast_log(LOG_DEBUG, "MySQL RealTime: Module usage count is 1.\n");
1731 + return 1;
1732 + }
1733 + ast_mutex_unlock(&mysql_lock);
1734 + return 0;
1735 +}
1736 +
1737 +char *key ()
1738 +{
1739 + return ASTERISK_GPL_KEY;
1740 +}
1741 +
1742 +static int mysql_reconnect(const char *database)
1743 +{
1744 + char my_database[50];
1745 +
1746 + if(!database || ast_strlen_zero(database))
1747 + ast_copy_string(my_database, dbname, sizeof(my_database));
1748 + else
1749 + ast_copy_string(my_database, database, sizeof(my_database));
1750 +
1751 + /* mutex lock should have been locked before calling this function. */
1752 +
1753 + if((!connected) && (dbhost || dbsock) && dbuser && dbpass && my_database) {
1754 + if(!mysql_init(&mysql)) {
1755 + ast_log(LOG_WARNING, "MySQL RealTime: Insufficient memory to allocate MySQL resource.\n");
1756 + connected = 0;
1757 + return 0;
1758 + }
1759 + if(mysql_real_connect(&mysql, dbhost, dbuser, dbpass, my_database, dbport, dbsock, 0)) {
1760 + ast_log(LOG_DEBUG, "MySQL RealTime: Successfully connected to database.\n");
1761 + connected = 1;
1762 + connect_time = time(NULL);
1763 + return 1;
1764 + } else {
1765 + ast_log(LOG_ERROR, "MySQL RealTime: Failed to connect database server %s on %s. Check debug for more info.\n", dbname, dbhost);
1766 + ast_log(LOG_DEBUG, "MySQL RealTime: Cannot Connect: %s\n", mysql_error(&mysql));
1767 + connected = 0;
1768 + return 0;
1769 + }
1770 + } else {
1771 + if(mysql_ping(&mysql) != 0) {
1772 + connected = 0;
1773 + ast_log(LOG_ERROR, "MySQL RealTime: Failed to reconnect. Check debug for more info.\n");
1774 + ast_log(LOG_DEBUG, "MySQL RealTime: Server Error: %s\n", mysql_error(&mysql));
1775 + return 0;
1776 + }
1777 +
1778 + connected = 1;
1779 +
1780 + if(mysql_select_db(&mysql, my_database) != 0) {
1781 + ast_log(LOG_WARNING, "MySQL RealTime: Unable to select database: %s. Still Connected.\n", my_database);
1782 + ast_log(LOG_DEBUG, "MySQL RealTime: Database Select Failed: %s\n", mysql_error(&mysql));
1783 + return 0;
1784 + }
1785 +
1786 + ast_log(LOG_DEBUG, "MySQL RealTime: Everything is fine.\n");
1787 + return 1;
1788 + }
1789 +}
1790 +
1791 +static int realtime_mysql_status(int fd, int argc, char **argv)
1792 +{
1793 + char status[256], status2[100] = "";
1794 + int ctime = time(NULL) - connect_time;
1795 +
1796 + ast_mutex_lock(&mysql_lock);
1797 + if(mysql_reconnect(NULL)) {
1798 + if(dbhost) {
1799 + snprintf(status, 255, "Connected to %s@%s, port %d", dbname, dbhost, dbport);
1800 + } else if(dbsock) {
1801 + snprintf(status, 255, "Connected to %s on socket file %s", dbname, dbsock);
1802 + } else {
1803 + snprintf(status, 255, "Connected to %s@%s", dbname, dbhost);
1804 + }
1805 +
1806 + if(dbuser && *dbuser) {
1807 + snprintf(status2, 99, " with username %s", dbuser);
1808 + }
1809 +
1810 + if (ctime > 31536000) {
1811 + ast_cli(fd, "%s%s for %d years, %d days, %d hours, %d minutes, %d seconds.\n", status, status2, ctime / 31536000, (ctime % 31536000) / 86400, (ctime % 86400) / 3600, (ctime % 3600) / 60, ctime % 60);
1812 + } else if (ctime > 86400) {
1813 + ast_cli(fd, "%s%s for %d days, %d hours, %d minutes, %d seconds.\n", status, status2, ctime / 86400, (ctime % 86400) / 3600, (ctime % 3600) / 60, ctime % 60);
1814 + } else if (ctime > 3600) {
1815 + ast_cli(fd, "%s%s for %d hours, %d minutes, %d seconds.\n", status, status2, ctime / 3600, (ctime % 3600) / 60, ctime % 60);
1816 + } else if (ctime > 60) {
1817 + ast_cli(fd, "%s%s for %d minutes, %d seconds.\n", status, status2, ctime / 60, ctime % 60);
1818 + } else {
1819 + ast_cli(fd, "%s%s for %d seconds.\n", status, status2, ctime);
1820 + }
1821 +
1822 + ast_mutex_unlock(&mysql_lock);
1823 + return RESULT_SUCCESS;
1824 + } else {
1825 + ast_mutex_unlock(&mysql_lock);
1826 + return RESULT_FAILURE;
1827 + }
1828 +}