00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038
00039
00040
00041
00042 #include "asterisk.h"
00043
00044 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 370642 $")
00045
00046 #include <sys/socket.h>
00047 #include <fcntl.h>
00048 #include <netdb.h>
00049 #include <netinet/in.h>
00050 #include <arpa/inet.h>
00051 #include <sys/signal.h>
00052 #include <iksemel.h>
00053 #include <pthread.h>
00054 #include <ctype.h>
00055
00056 #include "asterisk/lock.h"
00057 #include "asterisk/channel.h"
00058 #include "asterisk/config.h"
00059 #include "asterisk/module.h"
00060 #include "asterisk/pbx.h"
00061 #include "asterisk/sched.h"
00062 #include "asterisk/io.h"
00063 #include "asterisk/rtp_engine.h"
00064 #include "asterisk/stun.h"
00065 #include "asterisk/acl.h"
00066 #include "asterisk/callerid.h"
00067 #include "asterisk/file.h"
00068 #include "asterisk/cli.h"
00069 #include "asterisk/app.h"
00070 #include "asterisk/musiconhold.h"
00071 #include "asterisk/manager.h"
00072 #include "asterisk/stringfields.h"
00073 #include "asterisk/utils.h"
00074 #include "asterisk/causes.h"
00075 #include "asterisk/astobj.h"
00076 #include "asterisk/abstract_jb.h"
00077 #include "asterisk/jabber.h"
00078 #include "asterisk/jingle.h"
00079
00080 #define GOOGLE_CONFIG "gtalk.conf"
00081
00082
00083 static struct ast_jb_conf default_jbconf =
00084 {
00085 .flags = 0,
00086 .max_size = -1,
00087 .resync_threshold = -1,
00088 .impl = "",
00089 .target_extra = -1,
00090 };
00091 static struct ast_jb_conf global_jbconf;
00092
00093 enum gtalk_protocol {
00094 AJI_PROTOCOL_UDP = 1,
00095 AJI_PROTOCOL_SSLTCP = 2,
00096 };
00097
00098 enum gtalk_connect_type {
00099 AJI_CONNECT_STUN = 1,
00100 AJI_CONNECT_LOCAL = 2,
00101 AJI_CONNECT_RELAY = 3,
00102 };
00103
00104 struct gtalk_pvt {
00105 ast_mutex_t lock;
00106 time_t laststun;
00107 struct gtalk *parent;
00108 char sid[100];
00109 char us[AJI_MAX_JIDLEN];
00110 char them[AJI_MAX_JIDLEN];
00111 char ring[10];
00112 iksrule *ringrule;
00113 int initiator;
00114 int alreadygone;
00115 int capability;
00116 struct ast_codec_pref prefs;
00117 struct gtalk_candidate *theircandidates;
00118 struct gtalk_candidate *ourcandidates;
00119 char cid_num[80];
00120 char cid_name[80];
00121 char exten[80];
00122 struct ast_channel *owner;
00123 struct ast_rtp_instance *rtp;
00124 struct ast_rtp_instance *vrtp;
00125 format_t jointcapability;
00126 format_t peercapability;
00127 struct gtalk_pvt *next;
00128 };
00129
00130 struct gtalk_candidate {
00131 char name[100];
00132 enum gtalk_protocol protocol;
00133 double preference;
00134 char username[100];
00135 char password[100];
00136 enum gtalk_connect_type type;
00137 char network[6];
00138 int generation;
00139 char ip[16];
00140 int port;
00141 int receipt;
00142 struct gtalk_candidate *next;
00143 };
00144
00145 struct gtalk {
00146 ASTOBJ_COMPONENTS(struct gtalk);
00147 struct aji_client *connection;
00148 struct aji_buddy *buddy;
00149 struct gtalk_pvt *p;
00150 struct ast_codec_pref prefs;
00151 int amaflags;
00152 char user[AJI_MAX_JIDLEN];
00153 char context[AST_MAX_CONTEXT];
00154 char parkinglot[AST_MAX_CONTEXT];
00155 char accountcode[AST_MAX_ACCOUNT_CODE];
00156 format_t capability;
00157 ast_group_t callgroup;
00158 ast_group_t pickupgroup;
00159 int callingpres;
00160 int allowguest;
00161 char language[MAX_LANGUAGE];
00162 char musicclass[MAX_MUSICCLASS];
00163 };
00164
00165 struct gtalk_container {
00166 ASTOBJ_CONTAINER_COMPONENTS(struct gtalk);
00167 };
00168
00169 static const char desc[] = "Gtalk Channel";
00170
00171 static format_t global_capability = AST_FORMAT_ULAW | AST_FORMAT_ALAW | AST_FORMAT_GSM | AST_FORMAT_H263;
00172
00173 AST_MUTEX_DEFINE_STATIC(gtalklock);
00174
00175
00176 static struct ast_channel *gtalk_request(const char *type, format_t format, const struct ast_channel *requestor, void *data, int *cause);
00177
00178 static int gtalk_sendtext(struct ast_channel *ast, const char *text);
00179 static int gtalk_digit_begin(struct ast_channel *ast, char digit);
00180 static int gtalk_digit_end(struct ast_channel *ast, char digit, unsigned int duration);
00181 static int gtalk_call(struct ast_channel *ast, char *dest, int timeout);
00182 static int gtalk_hangup(struct ast_channel *ast);
00183 static int gtalk_answer(struct ast_channel *ast);
00184 static int gtalk_action(struct gtalk *client, struct gtalk_pvt *p, const char *action);
00185 static void gtalk_free_pvt(struct gtalk *client, struct gtalk_pvt *p);
00186 static int gtalk_newcall(struct gtalk *client, ikspak *pak);
00187 static struct ast_frame *gtalk_read(struct ast_channel *ast);
00188 static int gtalk_write(struct ast_channel *ast, struct ast_frame *f);
00189 static int gtalk_indicate(struct ast_channel *ast, int condition, const void *data, size_t datalen);
00190 static int gtalk_fixup(struct ast_channel *oldchan, struct ast_channel *newchan);
00191 static int gtalk_sendhtml(struct ast_channel *ast, int subclass, const char *data, int datalen);
00192 static struct gtalk_pvt *gtalk_alloc(struct gtalk *client, const char *us, const char *them, const char *sid);
00193 static int gtalk_update_stun(struct gtalk *client, struct gtalk_pvt *p);
00194
00195 static char *gtalk_show_channels(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
00196 static int gtalk_update_externip(void);
00197 static int gtalk_parser(void *data, ikspak *pak);
00198 static int gtalk_create_candidates(struct gtalk *client, struct gtalk_pvt *p, char *sid, char *from, char *to);
00199
00200
00201 static const struct ast_channel_tech gtalk_tech = {
00202 .type = "Gtalk",
00203 .description = "Gtalk Channel Driver",
00204 .capabilities = AST_FORMAT_AUDIO_MASK,
00205 .requester = gtalk_request,
00206 .send_text = gtalk_sendtext,
00207 .send_digit_begin = gtalk_digit_begin,
00208 .send_digit_end = gtalk_digit_end,
00209
00210
00211
00212
00213 .call = gtalk_call,
00214 .hangup = gtalk_hangup,
00215 .answer = gtalk_answer,
00216 .read = gtalk_read,
00217 .write = gtalk_write,
00218 .exception = gtalk_read,
00219 .indicate = gtalk_indicate,
00220 .fixup = gtalk_fixup,
00221 .send_html = gtalk_sendhtml,
00222 .properties = AST_CHAN_TP_WANTSJITTER | AST_CHAN_TP_CREATESJITTER
00223 };
00224
00225 static struct sockaddr_in bindaddr = { 0, };
00226
00227 static struct sched_context *sched;
00228 static struct io_context *io;
00229 static struct in_addr __ourip;
00230
00231 static struct ast_cli_entry gtalk_cli[] = {
00232
00233 AST_CLI_DEFINE(gtalk_show_channels, "Show GoogleTalk channels"),
00234 };
00235
00236 static char externip[16];
00237 static struct sockaddr_in stunaddr;
00238
00239 static struct gtalk_container gtalk_list;
00240
00241 static void gtalk_member_destroy(struct gtalk *obj)
00242 {
00243 ast_free(obj);
00244 }
00245
00246 static struct gtalk *find_gtalk(char *name, char *connection)
00247 {
00248 struct gtalk *gtalk = NULL;
00249 char *domain = NULL , *s = NULL;
00250
00251 if (strchr(connection, '@')) {
00252 s = ast_strdupa(connection);
00253 domain = strsep(&s, "@");
00254 ast_verbose("OOOOH domain = %s\n", domain);
00255 }
00256 gtalk = ASTOBJ_CONTAINER_FIND(>alk_list, name);
00257 if (!gtalk && strchr(name, '@'))
00258 gtalk = ASTOBJ_CONTAINER_FIND_FULL(>alk_list, name, user,,, strcasecmp);
00259
00260 if (!gtalk) {
00261
00262 ASTOBJ_CONTAINER_TRAVERSE(>alk_list, 1, {
00263 ASTOBJ_RDLOCK(iterator);
00264 if (!strcasecmp(iterator->name, "guest")) {
00265 gtalk = iterator;
00266 }
00267 ASTOBJ_UNLOCK(iterator);
00268
00269 if (gtalk)
00270 break;
00271 });
00272
00273 }
00274 return gtalk;
00275 }
00276
00277
00278 static int add_codec_to_answer(const struct gtalk_pvt *p, int codec, iks *dcodecs)
00279 {
00280 int res = 0;
00281 char *format = ast_getformatname(codec);
00282
00283 if (!strcasecmp("ulaw", format)) {
00284 iks *payload_eg711u, *payload_pcmu;
00285 payload_pcmu = iks_new("payload-type");
00286 payload_eg711u = iks_new("payload-type");
00287
00288 if(!payload_eg711u || !payload_pcmu) {
00289 iks_delete(payload_pcmu);
00290 iks_delete(payload_eg711u);
00291 ast_log(LOG_WARNING,"Failed to allocate iks node\n");
00292 return -1;
00293 }
00294 iks_insert_attrib(payload_pcmu, "id", "0");
00295 iks_insert_attrib(payload_pcmu, "name", "PCMU");
00296 iks_insert_attrib(payload_pcmu, "clockrate","8000");
00297 iks_insert_attrib(payload_pcmu, "bitrate","64000");
00298 iks_insert_attrib(payload_eg711u, "id", "100");
00299 iks_insert_attrib(payload_eg711u, "name", "EG711U");
00300 iks_insert_attrib(payload_eg711u, "clockrate","8000");
00301 iks_insert_attrib(payload_eg711u, "bitrate","64000");
00302 iks_insert_node(dcodecs, payload_pcmu);
00303 iks_insert_node(dcodecs, payload_eg711u);
00304 res ++;
00305 }
00306 if (!strcasecmp("alaw", format)) {
00307 iks *payload_eg711a, *payload_pcma;
00308 payload_pcma = iks_new("payload-type");
00309 payload_eg711a = iks_new("payload-type");
00310 if(!payload_eg711a || !payload_pcma) {
00311 iks_delete(payload_eg711a);
00312 iks_delete(payload_pcma);
00313 ast_log(LOG_WARNING,"Failed to allocate iks node\n");
00314 return -1;
00315 }
00316 iks_insert_attrib(payload_pcma, "id", "8");
00317 iks_insert_attrib(payload_pcma, "name", "PCMA");
00318 iks_insert_attrib(payload_pcma, "clockrate","8000");
00319 iks_insert_attrib(payload_pcma, "bitrate","64000");
00320 payload_eg711a = iks_new("payload-type");
00321 iks_insert_attrib(payload_eg711a, "id", "101");
00322 iks_insert_attrib(payload_eg711a, "name", "EG711A");
00323 iks_insert_attrib(payload_eg711a, "clockrate","8000");
00324 iks_insert_attrib(payload_eg711a, "bitrate","64000");
00325 iks_insert_node(dcodecs, payload_pcma);
00326 iks_insert_node(dcodecs, payload_eg711a);
00327 res ++;
00328 }
00329 if (!strcasecmp("ilbc", format)) {
00330 iks *payload_ilbc = iks_new("payload-type");
00331 if(!payload_ilbc) {
00332 ast_log(LOG_WARNING,"Failed to allocate iks node\n");
00333 return -1;
00334 }
00335 iks_insert_attrib(payload_ilbc, "id", "97");
00336 iks_insert_attrib(payload_ilbc, "name", "iLBC");
00337 iks_insert_attrib(payload_ilbc, "clockrate","8000");
00338 iks_insert_attrib(payload_ilbc, "bitrate","13300");
00339 iks_insert_node(dcodecs, payload_ilbc);
00340 res ++;
00341 }
00342 if (!strcasecmp("g723", format)) {
00343 iks *payload_g723 = iks_new("payload-type");
00344 if(!payload_g723) {
00345 ast_log(LOG_WARNING,"Failed to allocate iks node\n");
00346 return -1;
00347 }
00348 iks_insert_attrib(payload_g723, "id", "4");
00349 iks_insert_attrib(payload_g723, "name", "G723");
00350 iks_insert_attrib(payload_g723, "clockrate","8000");
00351 iks_insert_attrib(payload_g723, "bitrate","6300");
00352 iks_insert_node(dcodecs, payload_g723);
00353 res ++;
00354 }
00355 if (!strcasecmp("speex", format)) {
00356 iks *payload_speex = iks_new("payload-type");
00357 if(!payload_speex) {
00358 ast_log(LOG_WARNING,"Failed to allocate iks node\n");
00359 return -1;
00360 }
00361 iks_insert_attrib(payload_speex, "id", "110");
00362 iks_insert_attrib(payload_speex, "name", "speex");
00363 iks_insert_attrib(payload_speex, "clockrate","8000");
00364 iks_insert_attrib(payload_speex, "bitrate","11000");
00365 iks_insert_node(dcodecs, payload_speex);
00366 res++;
00367 }
00368 if (!strcasecmp("gsm", format)) {
00369 iks *payload_gsm = iks_new("payload-type");
00370 if(!payload_gsm) {
00371 ast_log(LOG_WARNING,"Failed to allocate iks node\n");
00372 return -1;
00373 }
00374 iks_insert_attrib(payload_gsm, "id", "103");
00375 iks_insert_attrib(payload_gsm, "name", "gsm");
00376 iks_insert_node(dcodecs, payload_gsm);
00377 res++;
00378 }
00379
00380 return res;
00381 }
00382
00383 static int gtalk_invite(struct gtalk_pvt *p, char *to, char *from, char *sid, int initiator)
00384 {
00385 struct gtalk *client = p->parent;
00386 iks *iq, *gtalk, *dcodecs, *payload_telephone, *transport;
00387 int x;
00388 int pref_codec = 0;
00389 int alreadysent = 0;
00390 int codecs_num = 0;
00391 char *lowerto = NULL;
00392
00393 iq = iks_new("iq");
00394 gtalk = iks_new("session");
00395 dcodecs = iks_new("description");
00396 transport = iks_new("transport");
00397 payload_telephone = iks_new("payload-type");
00398 if (!(iq && gtalk && dcodecs && transport && payload_telephone)){
00399 iks_delete(iq);
00400 iks_delete(gtalk);
00401 iks_delete(dcodecs);
00402 iks_delete(transport);
00403 iks_delete(payload_telephone);
00404
00405 ast_log(LOG_ERROR, "Could not allocate iksemel nodes\n");
00406 return 0;
00407 }
00408 iks_insert_attrib(dcodecs, "xmlns", GOOGLE_AUDIO_NS);
00409 iks_insert_attrib(dcodecs, "xml:lang", "en");
00410
00411 for (x = 0; x < 64; x++) {
00412 if (!(pref_codec = ast_codec_pref_index(&client->prefs, x)))
00413 break;
00414 if (!(client->capability & pref_codec))
00415 continue;
00416 if (alreadysent & pref_codec)
00417 continue;
00418 codecs_num = add_codec_to_answer(p, pref_codec, dcodecs);
00419 alreadysent |= pref_codec;
00420 }
00421
00422 if (codecs_num) {
00423
00424 iks_insert_attrib(payload_telephone, "id", "101");
00425 iks_insert_attrib(payload_telephone, "name", "telephone-event");
00426 iks_insert_attrib(payload_telephone, "clockrate", "8000");
00427 }
00428 iks_insert_attrib(transport,"xmlns",GOOGLE_TRANSPORT_NS);
00429
00430 iks_insert_attrib(iq, "type", "set");
00431 iks_insert_attrib(iq, "to", to);
00432 iks_insert_attrib(iq, "from", from);
00433 iks_insert_attrib(iq, "id", client->connection->mid);
00434 ast_aji_increment_mid(client->connection->mid);
00435
00436 iks_insert_attrib(gtalk, "xmlns", GOOGLE_NS);
00437 iks_insert_attrib(gtalk, "type",initiator ? "initiate": "accept");
00438
00439
00440 if (!initiator) {
00441 char c;
00442 char *t = lowerto = ast_strdupa(to);
00443 while (((c = *t) != '/') && (*t++ = tolower(c)));
00444 }
00445 iks_insert_attrib(gtalk, "initiator", initiator ? from : lowerto);
00446 iks_insert_attrib(gtalk, "id", sid);
00447 iks_insert_node(iq, gtalk);
00448 iks_insert_node(gtalk, dcodecs);
00449 iks_insert_node(dcodecs, payload_telephone);
00450
00451 ast_aji_send(client->connection, iq);
00452
00453 iks_delete(payload_telephone);
00454 iks_delete(transport);
00455 iks_delete(dcodecs);
00456 iks_delete(gtalk);
00457 iks_delete(iq);
00458 return 1;
00459 }
00460
00461 static int gtalk_ringing_ack(void *data, ikspak *pak)
00462 {
00463 struct gtalk_pvt *p = data;
00464 struct ast_channel *owner;
00465
00466 ast_mutex_lock(&p->lock);
00467
00468 if (p->ringrule) {
00469 iks_filter_remove_rule(p->parent->connection->f, p->ringrule);
00470 }
00471 p->ringrule = NULL;
00472
00473
00474 if (!strcmp(S_OR(iks_find_attrib(pak->x, "type"), ""), "error")) {
00475 char *name = NULL;
00476 char *redirect = NULL;
00477 iks *traversenodes = NULL;
00478 traversenodes = pak->query;
00479 while (traversenodes) {
00480 if (!(name = iks_name(traversenodes))) {
00481 break;
00482 }
00483 if (!strcasecmp(name, "error") &&
00484 ((redirect = iks_find_cdata(traversenodes, "redirect")) ||
00485 (redirect = iks_find_cdata(traversenodes, "sta:redirect"))) &&
00486 (redirect = strstr(redirect, "xmpp:"))) {
00487 redirect += 5;
00488 ast_log(LOG_DEBUG, "redirect %s\n", redirect);
00489 ast_copy_string(p->them, redirect, sizeof(p->them));
00490
00491 gtalk_invite(p, p->them, p->us, p->sid, 1);
00492 break;
00493 }
00494 traversenodes = iks_next_tag(traversenodes);
00495 }
00496 }
00497 gtalk_create_candidates(p->parent, p, p->sid, p->them, p->us);
00498 owner = p->owner;
00499 ast_mutex_unlock(&p->lock);
00500
00501 if (owner) {
00502 ast_queue_control(owner, AST_CONTROL_RINGING);
00503 }
00504
00505 return IKS_FILTER_EAT;
00506 }
00507
00508 static int gtalk_answer(struct ast_channel *ast)
00509 {
00510 struct gtalk_pvt *p = ast->tech_pvt;
00511 int res = 0;
00512
00513 ast_debug(1, "Answer!\n");
00514 ast_mutex_lock(&p->lock);
00515 gtalk_invite(p, p->them, p->us,p->sid, 0);
00516 manager_event(EVENT_FLAG_SYSTEM, "ChannelUpdate", "Channel: %s\r\nChanneltype: %s\r\nGtalk-SID: %s\r\n",
00517 ast->name, "GTALK", p->sid);
00518 ast_mutex_unlock(&p->lock);
00519 return res;
00520 }
00521
00522 static enum ast_rtp_glue_result gtalk_get_rtp_peer(struct ast_channel *chan, struct ast_rtp_instance **instance)
00523 {
00524 struct gtalk_pvt *p = chan->tech_pvt;
00525 enum ast_rtp_glue_result res = AST_RTP_GLUE_RESULT_FORBID;
00526
00527 if (!p)
00528 return res;
00529
00530 ast_mutex_lock(&p->lock);
00531 if (p->rtp){
00532 ao2_ref(p->rtp, +1);
00533 *instance = p->rtp;
00534 res = AST_RTP_GLUE_RESULT_LOCAL;
00535 }
00536 ast_mutex_unlock(&p->lock);
00537
00538 return res;
00539 }
00540
00541 static format_t gtalk_get_codec(struct ast_channel *chan)
00542 {
00543 struct gtalk_pvt *p = chan->tech_pvt;
00544 return p->peercapability;
00545 }
00546
00547 static int gtalk_set_rtp_peer(struct ast_channel *chan, struct ast_rtp_instance *rtp, struct ast_rtp_instance *vrtp, struct ast_rtp_instance *trtp, format_t codecs, int nat_active)
00548 {
00549 struct gtalk_pvt *p;
00550
00551 p = chan->tech_pvt;
00552 if (!p)
00553 return -1;
00554 ast_mutex_lock(&p->lock);
00555
00556
00557
00558
00559
00560
00561
00562
00563 ast_mutex_unlock(&p->lock);
00564 return 0;
00565 }
00566
00567 static struct ast_rtp_glue gtalk_rtp_glue = {
00568 .type = "Gtalk",
00569 .get_rtp_info = gtalk_get_rtp_peer,
00570 .get_codec = gtalk_get_codec,
00571 .update_peer = gtalk_set_rtp_peer,
00572 };
00573
00574 static int gtalk_response(struct gtalk *client, char *from, ikspak *pak, const char *reasonstr, const char *reasonstr2)
00575 {
00576 iks *response = NULL, *error = NULL, *reason = NULL;
00577 int res = -1;
00578
00579 response = iks_new("iq");
00580 if (response) {
00581 iks_insert_attrib(response, "type", "result");
00582 iks_insert_attrib(response, "from", from);
00583 iks_insert_attrib(response, "to", S_OR(iks_find_attrib(pak->x, "from"), ""));
00584 iks_insert_attrib(response, "id", S_OR(iks_find_attrib(pak->x, "id"), ""));
00585 if (reasonstr) {
00586 error = iks_new("error");
00587 if (error) {
00588 iks_insert_attrib(error, "type", "cancel");
00589 reason = iks_new(reasonstr);
00590 if (reason)
00591 iks_insert_node(error, reason);
00592 iks_insert_node(response, error);
00593 }
00594 }
00595 ast_aji_send(client->connection, response);
00596 res = 0;
00597 }
00598
00599 iks_delete(reason);
00600 iks_delete(error);
00601 iks_delete(response);
00602
00603 return res;
00604 }
00605
00606 static int gtalk_is_answered(struct gtalk *client, ikspak *pak)
00607 {
00608 struct gtalk_pvt *tmp = NULL;
00609 char *from;
00610 iks *codec;
00611 char s1[BUFSIZ], s2[BUFSIZ], s3[BUFSIZ];
00612 int peernoncodeccapability;
00613
00614 ast_log(LOG_DEBUG, "The client is %s\n", client->name);
00615
00616
00617 for (tmp = client->p; tmp; tmp = tmp->next) {
00618 if (iks_find_with_attrib(pak->x, "session", "id", tmp->sid)) {
00619 break;
00620 } else if (iks_find_with_attrib(pak->x, "ses:session", "id", tmp->sid)) {
00621 break;
00622 }
00623 }
00624
00625 if (!tmp) {
00626 ast_log(LOG_WARNING, "Could not find session in iq\n");
00627 return -1;
00628 }
00629
00630
00631 codec = iks_first_tag(iks_first_tag(iks_first_tag(pak->x)));
00632 while (codec) {
00633 char *codec_id = iks_find_attrib(codec, "id");
00634 char *codec_name = iks_find_attrib(codec, "name");
00635 if (!codec_id || !codec_name) {
00636 codec = iks_next_tag(codec);
00637 continue;
00638 }
00639
00640 ast_rtp_codecs_payloads_set_m_type(
00641 ast_rtp_instance_get_codecs(tmp->rtp),
00642 tmp->rtp,
00643 atoi(codec_id));
00644 ast_rtp_codecs_payloads_set_rtpmap_type(
00645 ast_rtp_instance_get_codecs(tmp->rtp),
00646 tmp->rtp,
00647 atoi(codec_id),
00648 "audio",
00649 codec_name,
00650 0);
00651 codec = iks_next_tag(codec);
00652 }
00653
00654
00655 ast_rtp_codecs_payload_formats(ast_rtp_instance_get_codecs(tmp->rtp), &tmp->peercapability, &peernoncodeccapability);
00656
00657
00658
00659 tmp->jointcapability = tmp->capability & tmp->peercapability;
00660 if (!tmp->jointcapability) {
00661 ast_log(LOG_WARNING, "Capabilities don't match : us - %s, peer - %s, combined - %s \n", ast_getformatname_multiple(s1, BUFSIZ, tmp->capability),
00662 ast_getformatname_multiple(s2, BUFSIZ, tmp->peercapability),
00663 ast_getformatname_multiple(s3, BUFSIZ, tmp->jointcapability));
00664
00665 ast_queue_hangup(tmp->owner);
00666
00667 return -1;
00668
00669 }
00670
00671 from = iks_find_attrib(pak->x, "to");
00672 if (!from) {
00673 from = client->connection->jid->full;
00674 }
00675
00676 if (tmp->owner) {
00677 ast_queue_control(tmp->owner, AST_CONTROL_ANSWER);
00678 }
00679 gtalk_update_stun(tmp->parent, tmp);
00680 gtalk_response(client, from, pak, NULL, NULL);
00681 return 1;
00682 }
00683
00684 static int gtalk_is_accepted(struct gtalk *client, ikspak *pak)
00685 {
00686 struct gtalk_pvt *tmp;
00687 char *from;
00688
00689 ast_log(LOG_DEBUG, "The client is %s\n", client->name);
00690
00691 for (tmp = client->p; tmp; tmp = tmp->next) {
00692 if (iks_find_with_attrib(pak->x, "session", "id", tmp->sid)) {
00693 break;
00694 }
00695 }
00696
00697 from = iks_find_attrib(pak->x, "to");
00698 if (!from) {
00699 from = client->connection->jid->full;
00700 }
00701
00702 if (tmp) {
00703 gtalk_update_stun(tmp->parent, tmp);
00704 } else {
00705 ast_log(LOG_NOTICE, "Whoa, didn't find call during accept?!\n");
00706 }
00707
00708
00709 gtalk_response(client, from, pak, NULL, NULL);
00710 return 1;
00711 }
00712
00713 static int gtalk_handle_dtmf(struct gtalk *client, ikspak *pak)
00714 {
00715 struct gtalk_pvt *tmp;
00716 iks *dtmfnode = NULL, *dtmfchild = NULL;
00717 char *dtmf;
00718 char *from;
00719
00720 for (tmp = client->p; tmp; tmp = tmp->next) {
00721 if (iks_find_with_attrib(pak->x, "session", "id", tmp->sid) || iks_find_with_attrib(pak->x, "gtalk", "sid", tmp->sid))
00722 break;
00723 }
00724 from = iks_find_attrib(pak->x, "to");
00725 if (!from) {
00726 from = client->connection->jid->full;
00727 }
00728
00729 if (tmp) {
00730 if(iks_find_with_attrib(pak->x, "dtmf-method", "method", "rtp")) {
00731 gtalk_response(client, from, pak,
00732 "feature-not-implemented xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'",
00733 "unsupported-dtmf-method xmlns='http://jabber.org/protocol/gtalk/info/dtmf#errors'");
00734 return -1;
00735 }
00736 if ((dtmfnode = iks_find(pak->x, "dtmf"))) {
00737 if((dtmf = iks_find_attrib(dtmfnode, "code"))) {
00738 if(iks_find_with_attrib(pak->x, "dtmf", "action", "button-up")) {
00739 struct ast_frame f = {AST_FRAME_DTMF_BEGIN, };
00740 f.subclass.integer = dtmf[0];
00741 ast_queue_frame(tmp->owner, &f);
00742 ast_verbose("GOOGLE! DTMF-relay event received: %c\n", (int) f.subclass.integer);
00743 } else if(iks_find_with_attrib(pak->x, "dtmf", "action", "button-down")) {
00744 struct ast_frame f = {AST_FRAME_DTMF_END, };
00745 f.subclass.integer = dtmf[0];
00746 ast_queue_frame(tmp->owner, &f);
00747 ast_verbose("GOOGLE! DTMF-relay event received: %c\n", (int) f.subclass.integer);
00748 } else if(iks_find_attrib(pak->x, "dtmf")) {
00749 struct ast_frame f = {AST_FRAME_DTMF, };
00750 f.subclass.integer = dtmf[0];
00751 ast_queue_frame(tmp->owner, &f);
00752 ast_verbose("GOOGLE! DTMF-relay event received: %c\n", (int) f.subclass.integer);
00753 }
00754 }
00755 } else if ((dtmfnode = iks_find_with_attrib(pak->x, "gtalk", "action", "session-info"))) {
00756 if((dtmfchild = iks_find(dtmfnode, "dtmf"))) {
00757 if((dtmf = iks_find_attrib(dtmfchild, "code"))) {
00758 if(iks_find_with_attrib(dtmfnode, "dtmf", "action", "button-up")) {
00759 struct ast_frame f = {AST_FRAME_DTMF_END, };
00760 f.subclass.integer = dtmf[0];
00761 ast_queue_frame(tmp->owner, &f);
00762 ast_verbose("GOOGLE! DTMF-relay event received: %c\n", (int) f.subclass.integer);
00763 } else if(iks_find_with_attrib(dtmfnode, "dtmf", "action", "button-down")) {
00764 struct ast_frame f = {AST_FRAME_DTMF_BEGIN, };
00765 f.subclass.integer = dtmf[0];
00766 ast_queue_frame(tmp->owner, &f);
00767 ast_verbose("GOOGLE! DTMF-relay event received: %c\n", (int) f.subclass.integer);
00768 }
00769 }
00770 }
00771 }
00772 gtalk_response(client, from, pak, NULL, NULL);
00773 return 1;
00774 } else {
00775 ast_log(LOG_NOTICE, "Whoa, didn't find call!\n");
00776 }
00777
00778 gtalk_response(client, from, pak, NULL, NULL);
00779 return 1;
00780 }
00781
00782 static int gtalk_hangup_farend(struct gtalk *client, ikspak *pak)
00783 {
00784 struct gtalk_pvt *tmp;
00785 char *from;
00786
00787 ast_debug(1, "The client is %s\n", client->name);
00788
00789 for (tmp = client->p; tmp; tmp = tmp->next) {
00790 if (iks_find_with_attrib(pak->x, "session", "id", tmp->sid) ||
00791 (iks_find_attrib(pak->query, "id") && !strcmp(iks_find_attrib(pak->query, "id"), tmp->sid))) {
00792 break;
00793 }
00794 }
00795 from = iks_find_attrib(pak->x, "to");
00796 if (!from) {
00797 from = client->connection->jid->full;
00798 }
00799
00800 if (tmp) {
00801 tmp->alreadygone = 1;
00802 if (tmp->owner) {
00803 ast_queue_hangup(tmp->owner);
00804 }
00805 } else {
00806 ast_log(LOG_NOTICE, "Whoa, didn't find call during hangup!\n");
00807 }
00808 gtalk_response(client, from, pak, NULL, NULL);
00809 return 1;
00810 }
00811
00812 static int gtalk_get_local_ip(struct ast_sockaddr *ourip)
00813 {
00814 struct ast_sockaddr root;
00815 struct ast_sockaddr bindaddr_tmp;
00816 struct ast_sockaddr *addrs;
00817 int addrs_cnt;
00818
00819
00820 ast_sockaddr_from_sin(&bindaddr_tmp, &bindaddr);
00821 if (!ast_sockaddr_is_any(&bindaddr_tmp)) {
00822 ast_sockaddr_copy(ourip, &bindaddr_tmp);
00823 return 0;
00824 }
00825
00826
00827
00828 if ((addrs_cnt = ast_sockaddr_resolve(&addrs, "google.com", PARSE_PORT_FORBID, AF_INET)) > 0) {
00829 ast_sockaddr_copy(&root, &addrs[0]);
00830 ast_free(addrs);
00831 if (!ast_ouraddrfor(&root, ourip)) {
00832 return 0;
00833 }
00834 }
00835
00836
00837 return ast_find_ourip(ourip, &bindaddr_tmp, AF_INET);
00838 }
00839
00840 static int gtalk_create_candidates(struct gtalk *client, struct gtalk_pvt *p, char *sid, char *from, char *to)
00841 {
00842 struct gtalk_candidate *tmp;
00843 struct aji_client *c = client->connection;
00844 struct gtalk_candidate *ours1 = NULL, *ours2 = NULL;
00845 struct sockaddr_in sin = { 0, };
00846 struct ast_sockaddr sin_tmp;
00847 struct ast_sockaddr us;
00848 iks *iq, *gtalk, *candidate, *transport;
00849 char user[17], pass[17], preference[5], port[7];
00850 char *lowerfrom = NULL;
00851
00852 iq = iks_new("iq");
00853 gtalk = iks_new("session");
00854 candidate = iks_new("candidate");
00855 transport = iks_new("transport");
00856 if (!iq || !gtalk || !candidate || !transport) {
00857 ast_log(LOG_ERROR, "Memory allocation error\n");
00858 goto safeout;
00859 }
00860 ours1 = ast_calloc(1, sizeof(*ours1));
00861 ours2 = ast_calloc(1, sizeof(*ours2));
00862 if (!ours1 || !ours2)
00863 goto safeout;
00864
00865 iks_insert_attrib(transport, "xmlns",GOOGLE_TRANSPORT_NS);
00866 iks_insert_node(iq, gtalk);
00867 iks_insert_node(gtalk,candidate);
00868 iks_insert_node(gtalk,transport);
00869
00870 for (; p; p = p->next) {
00871 if (!strcasecmp(p->sid, sid))
00872 break;
00873 }
00874
00875 if (!p) {
00876 ast_log(LOG_NOTICE, "No matching gtalk session - SID %s!\n", sid);
00877 goto safeout;
00878 }
00879
00880 ast_rtp_instance_get_local_address(p->rtp, &sin_tmp);
00881 ast_sockaddr_to_sin(&sin_tmp, &sin);
00882
00883 gtalk_get_local_ip(&us);
00884
00885 if (!strcmp(ast_sockaddr_stringify_addr(&us), "127.0.0.1")) {
00886 ast_log(LOG_WARNING, "Found a loopback IP on the system, check your network configuration or set the bindaddr attribute.\n");
00887 }
00888
00889
00890 ast_copy_string(ours1->name, "rtp", sizeof(ours1->name));
00891 ours1->port = ntohs(sin.sin_port);
00892 ours1->preference = 1;
00893 snprintf(user, sizeof(user), "%08lx%08lx", ast_random(), ast_random());
00894 snprintf(pass, sizeof(pass), "%08lx%08lx", ast_random(), ast_random());
00895 ast_copy_string(ours1->username, user, sizeof(ours1->username));
00896 ast_copy_string(ours1->password, pass, sizeof(ours1->password));
00897 ast_copy_string(ours1->ip, ast_sockaddr_stringify_addr(&us),
00898 sizeof(ours1->ip));
00899 ours1->protocol = AJI_PROTOCOL_UDP;
00900 ours1->type = AJI_CONNECT_LOCAL;
00901 ours1->generation = 0;
00902 p->ourcandidates = ours1;
00903
00904
00905
00906
00907 gtalk_update_externip();
00908 if (!ast_strlen_zero(externip)) {
00909 ast_copy_string(ours2->username, user, sizeof(ours2->username));
00910 ast_copy_string(ours2->password, pass, sizeof(ours2->password));
00911 ast_copy_string(ours2->ip, externip, sizeof(ours2->ip));
00912 ast_copy_string(ours2->name, "rtp", sizeof(ours1->name));
00913 ours2->port = ntohs(sin.sin_port);
00914 ours2->preference = 0.9;
00915 ours2->protocol = AJI_PROTOCOL_UDP;
00916 ours2->type = AJI_CONNECT_STUN;
00917 ours2->generation = 0;
00918 ours1->next = ours2;
00919 ours2 = NULL;
00920 }
00921 ours1 = NULL;
00922
00923 for (tmp = p->ourcandidates; tmp; tmp = tmp->next) {
00924 snprintf(port, sizeof(port), "%d", tmp->port);
00925 snprintf(preference, sizeof(preference), "%.2f", tmp->preference);
00926 iks_insert_attrib(iq, "from", to);
00927 iks_insert_attrib(iq, "to", from);
00928 iks_insert_attrib(iq, "type", "set");
00929 iks_insert_attrib(iq, "id", c->mid);
00930 ast_aji_increment_mid(c->mid);
00931 iks_insert_attrib(gtalk, "type", "candidates");
00932 iks_insert_attrib(gtalk, "id", sid);
00933
00934
00935 if (!p->initiator) {
00936 char c;
00937 char *t = lowerfrom = ast_strdupa(from);
00938 while (((c = *t) != '/') && (*t++ = tolower(c)));
00939 }
00940 iks_insert_attrib(gtalk, "initiator", (p->initiator) ? to : lowerfrom);
00941 iks_insert_attrib(gtalk, "xmlns", GOOGLE_NS);
00942 iks_insert_attrib(candidate, "name", tmp->name);
00943 iks_insert_attrib(candidate, "address", tmp->ip);
00944 iks_insert_attrib(candidate, "port", port);
00945 iks_insert_attrib(candidate, "username", tmp->username);
00946 iks_insert_attrib(candidate, "password", tmp->password);
00947 iks_insert_attrib(candidate, "preference", preference);
00948 if (tmp->protocol == AJI_PROTOCOL_UDP)
00949 iks_insert_attrib(candidate, "protocol", "udp");
00950 if (tmp->protocol == AJI_PROTOCOL_SSLTCP)
00951 iks_insert_attrib(candidate, "protocol", "ssltcp");
00952 if (tmp->type == AJI_CONNECT_STUN)
00953 iks_insert_attrib(candidate, "type", "stun");
00954 if (tmp->type == AJI_CONNECT_LOCAL)
00955 iks_insert_attrib(candidate, "type", "local");
00956 if (tmp->type == AJI_CONNECT_RELAY)
00957 iks_insert_attrib(candidate, "type", "relay");
00958 iks_insert_attrib(candidate, "network", "0");
00959 iks_insert_attrib(candidate, "generation", "0");
00960 ast_aji_send(c, iq);
00961 }
00962 p->laststun = 0;
00963
00964 safeout:
00965 if (ours1)
00966 ast_free(ours1);
00967 if (ours2)
00968 ast_free(ours2);
00969 iks_delete(iq);
00970 iks_delete(gtalk);
00971 iks_delete(candidate);
00972 iks_delete(transport);
00973
00974 return 1;
00975 }
00976
00977 static struct gtalk_pvt *gtalk_alloc(struct gtalk *client, const char *us, const char *them, const char *sid)
00978 {
00979 struct gtalk_pvt *tmp = NULL;
00980 struct aji_resource *resources = NULL;
00981 struct aji_buddy *buddy = NULL;
00982 char idroster[200] = "";
00983 char *data, *exten = NULL;
00984 struct ast_sockaddr bindaddr_tmp;
00985
00986 ast_debug(1, "The client is %s for alloc\n", client->name);
00987 if (!sid && !strchr(them, '/')) {
00988 if (!strcasecmp(client->name, "guest")) {
00989 buddy = ASTOBJ_CONTAINER_FIND(&client->connection->buddies, them);
00990 if (buddy) {
00991 resources = buddy->resources;
00992 }
00993 } else if (client->buddy) {
00994 resources = client->buddy->resources;
00995 }
00996
00997 while (resources) {
00998 if (resources->cap->jingle) {
00999 break;
01000 }
01001 resources = resources->next;
01002 }
01003 if (resources) {
01004 snprintf(idroster, sizeof(idroster), "%s/%s", them, resources->resource);
01005 } else if ((*them == '+') || (strstr(them, "@voice.google.com"))) {
01006 snprintf(idroster, sizeof(idroster), "%s", them);
01007 } else {
01008 ast_log(LOG_ERROR, "no gtalk capable clients to talk to.\n");
01009 if (buddy) {
01010 ASTOBJ_UNREF(buddy, ast_aji_buddy_destroy);
01011 }
01012 return NULL;
01013 }
01014 if (buddy) {
01015 ASTOBJ_UNREF(buddy, ast_aji_buddy_destroy);
01016 }
01017 }
01018 if (!(tmp = ast_calloc(1, sizeof(*tmp)))) {
01019 return NULL;
01020 }
01021
01022 memcpy(&tmp->prefs, &client->prefs, sizeof(struct ast_codec_pref));
01023
01024 if (sid) {
01025 ast_copy_string(tmp->sid, sid, sizeof(tmp->sid));
01026 ast_copy_string(tmp->them, them, sizeof(tmp->them));
01027 ast_copy_string(tmp->us, us, sizeof(tmp->us));
01028 } else {
01029 snprintf(tmp->sid, sizeof(tmp->sid), "%08lx%08lx", ast_random(), ast_random());
01030 ast_copy_string(tmp->them, idroster, sizeof(tmp->them));
01031 ast_copy_string(tmp->us, us, sizeof(tmp->us));
01032 tmp->initiator = 1;
01033 }
01034
01035 bindaddr.sin_family = AF_INET;
01036 ast_sockaddr_from_sin(&bindaddr_tmp, &bindaddr);
01037 if (!(tmp->rtp = ast_rtp_instance_new("asterisk", sched, &bindaddr_tmp, NULL))) {
01038 ast_log(LOG_ERROR, "Failed to create a new RTP instance (possibly an invalid bindaddr?)\n");
01039 ast_free(tmp);
01040 return NULL;
01041 }
01042 ast_rtp_instance_set_prop(tmp->rtp, AST_RTP_PROPERTY_RTCP, 1);
01043 ast_rtp_instance_set_prop(tmp->rtp, AST_RTP_PROPERTY_STUN, 1);
01044 ast_rtp_instance_set_prop(tmp->rtp, AST_RTP_PROPERTY_DTMF, 1);
01045 ast_rtp_instance_dtmf_mode_set(tmp->rtp, AST_RTP_DTMF_MODE_RFC2833);
01046 ast_rtp_codecs_payloads_clear(ast_rtp_instance_get_codecs(tmp->rtp), tmp->rtp);
01047
01048
01049 if (client->capability) {
01050 tmp->capability = client->capability;
01051 } else if (global_capability) {
01052 tmp->capability = global_capability;
01053 }
01054
01055 tmp->parent = client;
01056 if (!tmp->rtp) {
01057 ast_log(LOG_WARNING, "Out of RTP sessions?\n");
01058 ast_free(tmp);
01059 return NULL;
01060 }
01061
01062
01063 ast_copy_string(tmp->cid_name, tmp->them, sizeof(tmp->cid_name));
01064
01065 if(strchr(tmp->us, '/')) {
01066 data = ast_strdupa(tmp->us);
01067 exten = strsep(&data, "/");
01068 } else {
01069 exten = tmp->us;
01070 }
01071 ast_copy_string(tmp->exten, exten, sizeof(tmp->exten));
01072 ast_mutex_init(&tmp->lock);
01073 ast_mutex_lock(>alklock);
01074 tmp->next = client->p;
01075 client->p = tmp;
01076 ast_mutex_unlock(>alklock);
01077 return tmp;
01078 }
01079
01080
01081 static struct ast_channel *gtalk_new(struct gtalk *client, struct gtalk_pvt *i, int state, const char *title, const char *linkedid)
01082 {
01083 struct ast_channel *tmp;
01084 int fmt;
01085 int what;
01086 const char *n2;
01087
01088 if (title)
01089 n2 = title;
01090 else
01091 n2 = i->us;
01092 tmp = ast_channel_alloc(1, state, i->cid_num, i->cid_name, linkedid, client->accountcode, i->exten, client->context, client->amaflags, "Gtalk/%s-%04lx", n2, ast_random() & 0xffff);
01093 if (!tmp) {
01094 ast_log(LOG_WARNING, "Unable to allocate Gtalk channel structure!\n");
01095 return NULL;
01096 }
01097 tmp->tech = >alk_tech;
01098
01099
01100
01101 if (i->jointcapability)
01102 what = i->jointcapability;
01103 else if (i->capability)
01104 what = i->capability;
01105 else
01106 what = global_capability;
01107
01108
01109 if (i->rtp) {
01110 ast_rtp_codecs_packetization_set(ast_rtp_instance_get_codecs(i->rtp), i->rtp, &i->prefs);
01111 }
01112
01113 tmp->nativeformats = ast_codec_choose(&i->prefs, what, 1) | (i->jointcapability & AST_FORMAT_VIDEO_MASK);
01114 fmt = ast_best_codec(tmp->nativeformats);
01115
01116 if (i->rtp) {
01117 ast_channel_set_fd(tmp, 0, ast_rtp_instance_fd(i->rtp, 0));
01118 ast_channel_set_fd(tmp, 1, ast_rtp_instance_fd(i->rtp, 1));
01119 }
01120 if (i->vrtp) {
01121 ast_channel_set_fd(tmp, 2, ast_rtp_instance_fd(i->vrtp, 0));
01122 ast_channel_set_fd(tmp, 3, ast_rtp_instance_fd(i->vrtp, 1));
01123 }
01124 if (state == AST_STATE_RING)
01125 tmp->rings = 1;
01126 tmp->adsicpe = AST_ADSI_UNAVAILABLE;
01127 tmp->writeformat = fmt;
01128 tmp->rawwriteformat = fmt;
01129 tmp->readformat = fmt;
01130 tmp->rawreadformat = fmt;
01131 tmp->tech_pvt = i;
01132
01133 tmp->callgroup = client->callgroup;
01134 tmp->pickupgroup = client->pickupgroup;
01135 tmp->caller.id.name.presentation = client->callingpres;
01136 tmp->caller.id.number.presentation = client->callingpres;
01137 if (!ast_strlen_zero(client->accountcode))
01138 ast_string_field_set(tmp, accountcode, client->accountcode);
01139 if (client->amaflags)
01140 tmp->amaflags = client->amaflags;
01141 if (!ast_strlen_zero(client->language))
01142 ast_string_field_set(tmp, language, client->language);
01143 if (!ast_strlen_zero(client->musicclass))
01144 ast_string_field_set(tmp, musicclass, client->musicclass);
01145 if (!ast_strlen_zero(client->parkinglot))
01146 ast_string_field_set(tmp, parkinglot, client->parkinglot);
01147 i->owner = tmp;
01148 ast_module_ref(ast_module_info->self);
01149 ast_copy_string(tmp->context, client->context, sizeof(tmp->context));
01150 ast_copy_string(tmp->exten, i->exten, sizeof(tmp->exten));
01151
01152 if (!ast_strlen_zero(i->exten) && strcmp(i->exten, "s")) {
01153 tmp->dialed.number.str = ast_strdup(i->exten);
01154 }
01155 tmp->priority = 1;
01156 if (i->rtp)
01157 ast_jb_configure(tmp, &global_jbconf);
01158 if (state != AST_STATE_DOWN && ast_pbx_start(tmp)) {
01159 ast_log(LOG_WARNING, "Unable to start PBX on %s\n", tmp->name);
01160 tmp->hangupcause = AST_CAUSE_SWITCH_CONGESTION;
01161 ast_hangup(tmp);
01162 tmp = NULL;
01163 } else {
01164 manager_event(EVENT_FLAG_SYSTEM, "ChannelUpdate",
01165 "Channel: %s\r\nChanneltype: %s\r\nGtalk-SID: %s\r\n",
01166 i->owner ? i->owner->name : "", "Gtalk", i->sid);
01167 }
01168 return tmp;
01169 }
01170
01171 static int gtalk_action(struct gtalk *client, struct gtalk_pvt *p, const char *action)
01172 {
01173 iks *request, *session = NULL;
01174 int res = -1;
01175 char *lowerthem = NULL;
01176
01177 request = iks_new("iq");
01178 if (request) {
01179 iks_insert_attrib(request, "type", "set");
01180 iks_insert_attrib(request, "from", p->us);
01181 iks_insert_attrib(request, "to", p->them);
01182 iks_insert_attrib(request, "id", client->connection->mid);
01183 ast_aji_increment_mid(client->connection->mid);
01184 session = iks_new("session");
01185 if (session) {
01186 iks_insert_attrib(session, "type", action);
01187 iks_insert_attrib(session, "id", p->sid);
01188
01189
01190 if (!p->initiator) {
01191 char c;
01192 char *t = lowerthem = ast_strdupa(p->them);
01193 while (((c = *t) != '/') && (*t++ = tolower(c)));
01194 }
01195 iks_insert_attrib(session, "initiator", p->initiator ? p->us : lowerthem);
01196 iks_insert_attrib(session, "xmlns", GOOGLE_NS);
01197 iks_insert_node(request, session);
01198 ast_aji_send(client->connection, request);
01199 res = 0;
01200 }
01201 }
01202
01203 iks_delete(session);
01204 iks_delete(request);
01205
01206 return res;
01207 }
01208
01209 static void gtalk_free_candidates(struct gtalk_candidate *candidate)
01210 {
01211 struct gtalk_candidate *last;
01212 while (candidate) {
01213 last = candidate;
01214 candidate = candidate->next;
01215 ast_free(last);
01216 }
01217 }
01218
01219 static void gtalk_free_pvt(struct gtalk *client, struct gtalk_pvt *p)
01220 {
01221 struct gtalk_pvt *cur, *prev = NULL;
01222 cur = client->p;
01223 while (cur) {
01224 if (cur == p) {
01225 if (prev)
01226 prev->next = p->next;
01227 else
01228 client->p = p->next;
01229 break;
01230 }
01231 prev = cur;
01232 cur = cur->next;
01233 }
01234 if (p->ringrule)
01235 iks_filter_remove_rule(p->parent->connection->f, p->ringrule);
01236 if (p->owner)
01237 ast_log(LOG_WARNING, "Uh oh, there's an owner, this is going to be messy.\n");
01238 if (p->rtp)
01239 ast_rtp_instance_destroy(p->rtp);
01240 if (p->vrtp)
01241 ast_rtp_instance_destroy(p->vrtp);
01242 gtalk_free_candidates(p->theircandidates);
01243 ast_free(p);
01244 }
01245
01246
01247 static int gtalk_newcall(struct gtalk *client, ikspak *pak)
01248 {
01249 struct gtalk_pvt *p, *tmp = client->p;
01250 struct ast_channel *chan;
01251 int res;
01252 iks *codec;
01253 char *from = NULL;
01254 char s1[BUFSIZ], s2[BUFSIZ], s3[BUFSIZ];
01255 int peernoncodeccapability;
01256 char *sid;
01257
01258
01259 from = iks_find_attrib(pak->x,"to");
01260 if (!from) {
01261 from = client->connection->jid->full;
01262 }
01263
01264 while (tmp) {
01265 if (iks_find_with_attrib(pak->x, "session", "id", tmp->sid) ||
01266 (iks_find_attrib(pak->query, "id") && !strcmp(iks_find_attrib(pak->query, "id"), tmp->sid))) {
01267 ast_log(LOG_NOTICE, "Ignoring duplicate call setup on SID %s\n", tmp->sid);
01268 gtalk_response(client, from, pak, "out-of-order", NULL);
01269 return -1;
01270 }
01271 tmp = tmp->next;
01272 }
01273
01274 if (!strcasecmp(client->name, "guest")){
01275
01276
01277 if (client->connection) {
01278 ASTOBJ_UNREF(client->connection, ast_aji_client_destroy);
01279 }
01280 client->connection = ast_aji_get_client(from);
01281 if (!client->connection) {
01282 ast_log(LOG_ERROR, "No XMPP client to talk to, us (partial JID) : %s\n", from);
01283 return -1;
01284 }
01285 }
01286
01287 if (!(sid = iks_find_attrib(pak->query, "id"))) {
01288 ast_log(LOG_WARNING, "Received Initiate without id attribute. Can not start call.\n");
01289 return -1;
01290 }
01291
01292 p = gtalk_alloc(client, from, pak->from->full, sid);
01293 if (!p) {
01294 ast_log(LOG_WARNING, "Unable to allocate gtalk structure!\n");
01295 return -1;
01296 }
01297
01298 chan = gtalk_new(client, p, AST_STATE_DOWN, pak->from->user, NULL);
01299 if (!chan) {
01300 gtalk_free_pvt(client, p);
01301 return -1;
01302 }
01303
01304 ast_mutex_lock(&p->lock);
01305 ast_copy_string(p->them, pak->from->full, sizeof(p->them));
01306 ast_copy_string(p->sid, sid, sizeof(p->sid));
01307
01308
01309 codec = iks_first_tag(iks_first_tag(pak->query));
01310
01311 while (codec) {
01312 char *codec_id = iks_find_attrib(codec, "id");
01313 char *codec_name = iks_find_attrib(codec, "name");
01314 if (!codec_id || !codec_name) {
01315 codec = iks_next_tag(codec);
01316 continue;
01317 }
01318 if (!strcmp(S_OR(iks_name(codec), ""), "vid:payload-type") && p->vrtp) {
01319 ast_rtp_codecs_payloads_set_m_type(
01320 ast_rtp_instance_get_codecs(p->vrtp),
01321 p->vrtp,
01322 atoi(codec_id));
01323 ast_rtp_codecs_payloads_set_rtpmap_type(
01324 ast_rtp_instance_get_codecs(p->vrtp),
01325 p->vrtp,
01326 atoi(codec_id),
01327 "video",
01328 codec_name,
01329 0);
01330 } else {
01331 ast_rtp_codecs_payloads_set_m_type(
01332 ast_rtp_instance_get_codecs(p->rtp),
01333 p->rtp,
01334 atoi(codec_id));
01335 ast_rtp_codecs_payloads_set_rtpmap_type(
01336 ast_rtp_instance_get_codecs(p->rtp),
01337 p->rtp,
01338 atoi(codec_id),
01339 "audio",
01340 codec_name,
01341 0);
01342 }
01343 codec = iks_next_tag(codec);
01344 }
01345
01346
01347 ast_rtp_codecs_payload_formats(ast_rtp_instance_get_codecs(p->rtp), &p->peercapability, &peernoncodeccapability);
01348 p->jointcapability = p->capability & p->peercapability;
01349 ast_mutex_unlock(&p->lock);
01350
01351 ast_setstate(chan, AST_STATE_RING);
01352 if (!p->jointcapability) {
01353 ast_log(LOG_WARNING, "Capabilities don't match : us - %s, peer - %s, combined - %s \n", ast_getformatname_multiple(s1, BUFSIZ, p->capability),
01354 ast_getformatname_multiple(s2, BUFSIZ, p->peercapability),
01355 ast_getformatname_multiple(s3, BUFSIZ, p->jointcapability));
01356
01357 gtalk_action(client, p, "reject");
01358 p->alreadygone = 1;
01359 gtalk_hangup(chan);
01360 ast_channel_release(chan);
01361 return -1;
01362 }
01363
01364 res = ast_pbx_start(chan);
01365
01366 switch (res) {
01367 case AST_PBX_FAILED:
01368 ast_log(LOG_WARNING, "Failed to start PBX :(\n");
01369 gtalk_response(client, from, pak, "service-unavailable", NULL);
01370 break;
01371 case AST_PBX_CALL_LIMIT:
01372 ast_log(LOG_WARNING, "Failed to start PBX (call limit reached) \n");
01373 gtalk_response(client, from, pak, "service-unavailable", NULL);
01374 break;
01375 case AST_PBX_SUCCESS:
01376 gtalk_response(client, from, pak, NULL, NULL);
01377 gtalk_create_candidates(client, p, p->sid, p->them, p->us);
01378
01379 break;
01380 }
01381
01382 return 1;
01383 }
01384
01385 static int gtalk_update_externip(void)
01386 {
01387 int sock;
01388 char *newaddr;
01389 struct sockaddr_in answer = { 0, };
01390 struct sockaddr_in *dst;
01391 struct ast_sockaddr tmp_dst;
01392
01393 if (!stunaddr.sin_addr.s_addr) {
01394 return -1;
01395 }
01396 dst = &stunaddr;
01397
01398 sock = socket(AF_INET, SOCK_DGRAM, 0);
01399 if (sock < 0) {
01400 ast_log(LOG_WARNING, "Unable to create STUN socket: %s\n", strerror(errno));
01401 return -1;
01402 }
01403
01404 ast_sockaddr_from_sin(&tmp_dst, dst);
01405 if (ast_connect(sock, &tmp_dst) != 0) {
01406 ast_log(LOG_WARNING, "STUN Failed to connect to %s\n", ast_sockaddr_stringify(&tmp_dst));
01407 close(sock);
01408 return -1;
01409 }
01410
01411 if ((ast_stun_request(sock, &stunaddr, NULL, &answer))) {
01412 close(sock);
01413 return -1;
01414 }
01415
01416 newaddr = ast_strdupa(ast_inet_ntoa(answer.sin_addr));
01417 memcpy(externip, newaddr, sizeof(externip));
01418
01419 close(sock);
01420 return 0;
01421
01422 }
01423
01424 static int gtalk_update_stun(struct gtalk *client, struct gtalk_pvt *p)
01425 {
01426 struct gtalk_candidate *tmp;
01427 struct hostent *hp;
01428 struct ast_hostent ahp;
01429 struct sockaddr_in sin = { 0, };
01430 struct sockaddr_in aux = { 0, };
01431 struct ast_sockaddr sin_tmp;
01432 struct ast_sockaddr aux_tmp;
01433
01434 if (time(NULL) == p->laststun)
01435 return 0;
01436
01437 tmp = p->theircandidates;
01438 p->laststun = time(NULL);
01439 while (tmp) {
01440 char username[256];
01441
01442
01443 if (!(hp = ast_gethostbyname(tmp->ip, &ahp))) {
01444 ast_log(LOG_WARNING, "Could not get host by name for %s\n", tmp->ip);
01445 tmp = tmp->next;
01446 continue;
01447 }
01448 sin.sin_family = AF_INET;
01449 memcpy(&sin.sin_addr, hp->h_addr, sizeof(sin.sin_addr));
01450 sin.sin_port = htons(tmp->port);
01451 snprintf(username, sizeof(username), "%s%s", tmp->username, p->ourcandidates->username);
01452
01453
01454 ast_rtp_instance_get_remote_address(p->rtp, &aux_tmp);
01455 ast_sockaddr_to_sin(&aux_tmp, &aux);
01456
01457
01458
01459
01460 if (aux.sin_addr.s_addr && (aux.sin_addr.s_addr != sin.sin_addr.s_addr)) {
01461 ast_rtp_instance_stun_request(p->rtp, &aux_tmp, username);
01462 } else {
01463 ast_sockaddr_from_sin(&sin_tmp, &sin);
01464 ast_rtp_instance_stun_request(p->rtp, &sin_tmp, username);
01465 }
01466 if (aux.sin_addr.s_addr) {
01467 ast_debug(4, "Receiving RTP traffic from IP %s, matches with remote candidate's IP %s\n", ast_inet_ntoa(aux.sin_addr), tmp->ip);
01468 ast_debug(4, "Sending STUN request to %s\n", tmp->ip);
01469 }
01470
01471 tmp = tmp->next;
01472 }
01473 return 1;
01474 }
01475
01476 static int gtalk_add_candidate(struct gtalk *client, ikspak *pak)
01477 {
01478 struct gtalk_pvt *p = NULL, *tmp = NULL;
01479 struct aji_client *c = client->connection;
01480 struct gtalk_candidate *newcandidate = NULL;
01481 iks *traversenodes = NULL, *receipt = NULL;
01482 char *from;
01483
01484 from = iks_find_attrib(pak->x,"to");
01485 if (!from) {
01486 from = c->jid->full;
01487 }
01488
01489 for (tmp = client->p; tmp; tmp = tmp->next) {
01490 if (iks_find_with_attrib(pak->x, "session", "id", tmp->sid) ||
01491 (iks_find_attrib(pak->query, "id") && !strcmp(iks_find_attrib(pak->query, "id"), tmp->sid))) {
01492 p = tmp;
01493 break;
01494 }
01495 }
01496
01497 if (!p) {
01498 return -1;
01499 }
01500 traversenodes = pak->query;
01501 while(traversenodes) {
01502 if(!strcasecmp(S_OR(iks_name(traversenodes), ""), "session")) {
01503 traversenodes = iks_first_tag(traversenodes);
01504 continue;
01505 }
01506 if(!strcasecmp(S_OR(iks_name(traversenodes), ""), "ses:session")) {
01507 traversenodes = iks_child(traversenodes);
01508 continue;
01509 }
01510 if(!strcasecmp(S_OR(iks_name(traversenodes), ""), "candidate") || !strcasecmp(S_OR(iks_name(traversenodes), ""), "ses:candidate")) {
01511 newcandidate = ast_calloc(1, sizeof(*newcandidate));
01512 if (!newcandidate)
01513 return 0;
01514 ast_copy_string(newcandidate->name,
01515 S_OR(iks_find_attrib(traversenodes, "name"), ""),
01516 sizeof(newcandidate->name));
01517 ast_copy_string(newcandidate->ip,
01518 S_OR(iks_find_attrib(traversenodes, "address"), ""),
01519 sizeof(newcandidate->ip));
01520 newcandidate->port = atoi(iks_find_attrib(traversenodes, "port"));
01521 ast_copy_string(newcandidate->username,
01522 S_OR(iks_find_attrib(traversenodes, "username"), ""),
01523 sizeof(newcandidate->username));
01524 ast_copy_string(newcandidate->password,
01525 S_OR(iks_find_attrib(traversenodes, "password"), ""),
01526 sizeof(newcandidate->password));
01527 newcandidate->preference = atof(iks_find_attrib(traversenodes, "preference"));
01528 if (!strcasecmp(S_OR(iks_find_attrib(traversenodes, "protocol"), ""), "udp"))
01529 newcandidate->protocol = AJI_PROTOCOL_UDP;
01530 if (!strcasecmp(S_OR(iks_find_attrib(traversenodes, "protocol"), ""), "ssltcp"))
01531 newcandidate->protocol = AJI_PROTOCOL_SSLTCP;
01532
01533 if (!strcasecmp(S_OR(iks_find_attrib(traversenodes, "type"), ""), "stun"))
01534 newcandidate->type = AJI_CONNECT_STUN;
01535 if (!strcasecmp(S_OR(iks_find_attrib(traversenodes, "type"), ""), "local"))
01536 newcandidate->type = AJI_CONNECT_LOCAL;
01537 if (!strcasecmp(S_OR(iks_find_attrib(traversenodes, "type"), ""), "relay"))
01538 newcandidate->type = AJI_CONNECT_RELAY;
01539 ast_copy_string(newcandidate->network,
01540 S_OR(iks_find_attrib(traversenodes, "network"), ""),
01541 sizeof(newcandidate->network));
01542 newcandidate->generation = atoi(S_OR(iks_find_attrib(traversenodes, "generation"), "0"));
01543 newcandidate->next = NULL;
01544
01545 newcandidate->next = p->theircandidates;
01546 p->theircandidates = newcandidate;
01547 p->laststun = 0;
01548 gtalk_update_stun(p->parent, p);
01549 newcandidate = NULL;
01550 }
01551 traversenodes = iks_next_tag(traversenodes);
01552 }
01553
01554 receipt = iks_new("iq");
01555 iks_insert_attrib(receipt, "type", "result");
01556 iks_insert_attrib(receipt, "from", from);
01557 iks_insert_attrib(receipt, "to", S_OR(iks_find_attrib(pak->x, "from"), ""));
01558 iks_insert_attrib(receipt, "id", S_OR(iks_find_attrib(pak->x, "id"), ""));
01559 ast_aji_send(c, receipt);
01560
01561 iks_delete(receipt);
01562
01563 return 1;
01564 }
01565
01566 static struct ast_frame *gtalk_rtp_read(struct ast_channel *ast, struct gtalk_pvt *p)
01567 {
01568 struct ast_frame *f;
01569
01570 if (!p->rtp) {
01571 return &ast_null_frame;
01572 }
01573 f = ast_rtp_instance_read(p->rtp, 0);
01574 gtalk_update_stun(p->parent, p);
01575 if (p->owner) {
01576
01577 if (f->frametype == AST_FRAME_VOICE) {
01578 if (f->subclass.codec != (p->owner->nativeformats & AST_FORMAT_AUDIO_MASK)) {
01579 ast_debug(1, "Oooh, format changed to %s\n", ast_getformatname(f->subclass.codec));
01580 p->owner->nativeformats =
01581 (p->owner->nativeformats & AST_FORMAT_VIDEO_MASK) | f->subclass.codec;
01582 ast_set_read_format(p->owner, p->owner->readformat);
01583 ast_set_write_format(p->owner, p->owner->writeformat);
01584 }
01585
01586
01587
01588
01589
01590 }
01591 }
01592 return f;
01593 }
01594
01595 static struct ast_frame *gtalk_read(struct ast_channel *ast)
01596 {
01597 struct ast_frame *fr;
01598 struct gtalk_pvt *p = ast->tech_pvt;
01599
01600 ast_mutex_lock(&p->lock);
01601 fr = gtalk_rtp_read(ast, p);
01602 ast_mutex_unlock(&p->lock);
01603 return fr;
01604 }
01605
01606
01607 static int gtalk_write(struct ast_channel *ast, struct ast_frame *frame)
01608 {
01609 struct gtalk_pvt *p = ast->tech_pvt;
01610 int res = 0;
01611 char buf[256];
01612
01613 switch (frame->frametype) {
01614 case AST_FRAME_VOICE:
01615 if (!(frame->subclass.codec & ast->nativeformats)) {
01616 ast_log(LOG_WARNING,
01617 "Asked to transmit frame type %s, while native formats is %s (read/write = %s/%s)\n",
01618 ast_getformatname(frame->subclass.codec),
01619 ast_getformatname_multiple(buf, sizeof(buf), ast->nativeformats),
01620 ast_getformatname(ast->readformat),
01621 ast_getformatname(ast->writeformat));
01622 return 0;
01623 }
01624 if (p) {
01625 ast_mutex_lock(&p->lock);
01626 if (p->rtp) {
01627 res = ast_rtp_instance_write(p->rtp, frame);
01628 }
01629 ast_mutex_unlock(&p->lock);
01630 }
01631 break;
01632 case AST_FRAME_VIDEO:
01633 if (p) {
01634 ast_mutex_lock(&p->lock);
01635 if (p->vrtp) {
01636 res = ast_rtp_instance_write(p->vrtp, frame);
01637 }
01638 ast_mutex_unlock(&p->lock);
01639 }
01640 break;
01641 case AST_FRAME_IMAGE:
01642 return 0;
01643 break;
01644 default:
01645 ast_log(LOG_WARNING, "Can't send %d type frames with Gtalk write\n",
01646 frame->frametype);
01647 return 0;
01648 }
01649
01650 return res;
01651 }
01652
01653 static int gtalk_fixup(struct ast_channel *oldchan, struct ast_channel *newchan)
01654 {
01655 struct gtalk_pvt *p = newchan->tech_pvt;
01656 ast_mutex_lock(&p->lock);
01657
01658 if ((p->owner != oldchan)) {
01659 ast_mutex_unlock(&p->lock);
01660 return -1;
01661 }
01662 if (p->owner == oldchan)
01663 p->owner = newchan;
01664 ast_mutex_unlock(&p->lock);
01665 return 0;
01666 }
01667
01668 static int gtalk_indicate(struct ast_channel *ast, int condition, const void *data, size_t datalen)
01669 {
01670 int res = 0;
01671
01672 switch (condition) {
01673 case AST_CONTROL_HOLD:
01674 ast_moh_start(ast, data, NULL);
01675 break;
01676 case AST_CONTROL_UNHOLD:
01677 ast_moh_stop(ast);
01678 break;
01679 default:
01680 ast_debug(3, "Don't know how to indicate condition '%d'\n", condition);
01681 res = -1;
01682 }
01683
01684 return res;
01685 }
01686
01687 static int gtalk_sendtext(struct ast_channel *chan, const char *text)
01688 {
01689 int res = 0;
01690 struct aji_client *client = NULL;
01691 struct gtalk_pvt *p = chan->tech_pvt;
01692
01693 if (!p->parent) {
01694 ast_log(LOG_ERROR, "Parent channel not found\n");
01695 return -1;
01696 }
01697 if (!p->parent->connection) {
01698 ast_log(LOG_ERROR, "XMPP client not found\n");
01699 return -1;
01700 }
01701 client = p->parent->connection;
01702 res = ast_aji_send_chat(client, p->them, text);
01703 return res;
01704 }
01705
01706 static int gtalk_digit_begin(struct ast_channel *chan, char digit)
01707 {
01708 struct gtalk_pvt *p = chan->tech_pvt;
01709 int res = 0;
01710
01711 ast_mutex_lock(&p->lock);
01712 if (p->rtp) {
01713 ast_rtp_instance_dtmf_begin(p->rtp, digit);
01714 } else {
01715 res = -1;
01716 }
01717 ast_mutex_unlock(&p->lock);
01718
01719 return res;
01720 }
01721
01722 static int gtalk_digit_end(struct ast_channel *chan, char digit, unsigned int duration)
01723 {
01724 struct gtalk_pvt *p = chan->tech_pvt;
01725 int res = 0;
01726
01727 ast_mutex_lock(&p->lock);
01728 if (p->rtp) {
01729 ast_rtp_instance_dtmf_end_with_duration(p->rtp, digit, duration);
01730 } else {
01731 res = -1;
01732 }
01733 ast_mutex_unlock(&p->lock);
01734
01735 return res;
01736 }
01737
01738
01739
01740
01741 #if 0
01742 static int gtalk_digit(struct ast_channel *ast, char digit, unsigned int duration)
01743 {
01744 struct gtalk_pvt *p = ast->tech_pvt;
01745 struct gtalk *client = p->parent;
01746 iks *iq, *gtalk, *dtmf;
01747 char buffer[2] = {digit, '\0'};
01748 char *lowerthem = NULL;
01749 iq = iks_new("iq");
01750 gtalk = iks_new("gtalk");
01751 dtmf = iks_new("dtmf");
01752 if(!iq || !gtalk || !dtmf) {
01753 iks_delete(iq);
01754 iks_delete(gtalk);
01755 iks_delete(dtmf);
01756 ast_log(LOG_ERROR, "Did not send dtmf do to memory issue\n");
01757 return -1;
01758 }
01759
01760 iks_insert_attrib(iq, "type", "set");
01761 iks_insert_attrib(iq, "to", p->them);
01762 iks_insert_attrib(iq, "from", p->us);
01763 iks_insert_attrib(iq, "id", client->connection->mid);
01764 ast_aji_increment_mid(client->connection->mid);
01765 iks_insert_attrib(gtalk, "xmlns", "http://jabber.org/protocol/gtalk");
01766 iks_insert_attrib(gtalk, "action", "session-info");
01767
01768
01769 if (!p->initiator) {
01770 char c;
01771 char *t = lowerthem = ast_strdupa(p->them);
01772 while (((c = *t) != '/') && (*t++ = tolower(c)));
01773 }
01774 iks_insert_attrib(gtalk, "initiator", p->initiator ? p->us: lowerthem);
01775 iks_insert_attrib(gtalk, "sid", p->sid);
01776 iks_insert_attrib(dtmf, "xmlns", "http://jabber.org/protocol/gtalk/info/dtmf");
01777 iks_insert_attrib(dtmf, "code", buffer);
01778 iks_insert_node(iq, gtalk);
01779 iks_insert_node(gtalk, dtmf);
01780
01781 ast_mutex_lock(&p->lock);
01782 if (ast->dtmff.frametype == AST_FRAME_DTMF_BEGIN || duration == 0) {
01783 iks_insert_attrib(dtmf, "action", "button-down");
01784 } else if (ast->dtmff.frametype == AST_FRAME_DTMF_END || duration != 0) {
01785 iks_insert_attrib(dtmf, "action", "button-up");
01786 }
01787 ast_aji_send(client->connection, iq);
01788
01789 iks_delete(iq);
01790 iks_delete(gtalk);
01791 iks_delete(dtmf);
01792 ast_mutex_unlock(&p->lock);
01793 return 0;
01794 }
01795 #endif
01796
01797 static int gtalk_sendhtml(struct ast_channel *ast, int subclass, const char *data, int datalen)
01798 {
01799 ast_log(LOG_NOTICE, "XXX Implement gtalk sendhtml XXX\n");
01800
01801 return -1;
01802 }
01803
01804
01805
01806 static int gtalk_call(struct ast_channel *ast, char *dest, int timeout)
01807 {
01808 struct gtalk_pvt *p = ast->tech_pvt;
01809
01810 if ((ast->_state != AST_STATE_DOWN) && (ast->_state != AST_STATE_RESERVED)) {
01811 ast_log(LOG_WARNING, "gtalk_call called on %s, neither down nor reserved\n", ast->name);
01812 return -1;
01813 }
01814
01815 ast_setstate(ast, AST_STATE_RING);
01816 if (!p->ringrule) {
01817 ast_copy_string(p->ring, p->parent->connection->mid, sizeof(p->ring));
01818 p->ringrule = iks_filter_add_rule(p->parent->connection->f, gtalk_ringing_ack, p,
01819 IKS_RULE_ID, p->ring, IKS_RULE_DONE);
01820 } else {
01821 ast_log(LOG_WARNING, "Whoa, already have a ring rule!\n");
01822 }
01823
01824 gtalk_invite(p, p->them, p->us, p->sid, 1);
01825
01826 return 0;
01827 }
01828
01829
01830 static int gtalk_hangup(struct ast_channel *ast)
01831 {
01832 struct gtalk_pvt *p = ast->tech_pvt;
01833 struct gtalk *client;
01834
01835 ast_mutex_lock(&p->lock);
01836 client = p->parent;
01837 p->owner = NULL;
01838 ast->tech_pvt = NULL;
01839 if (!p->alreadygone) {
01840 gtalk_action(client, p, "terminate");
01841 }
01842 ast_mutex_unlock(&p->lock);
01843
01844 gtalk_free_pvt(client, p);
01845 ast_module_unref(ast_module_info->self);
01846
01847 return 0;
01848 }
01849
01850
01851 static struct ast_channel *gtalk_request(const char *type, format_t format, const struct ast_channel *requestor, void *data, int *cause)
01852 {
01853 struct gtalk_pvt *p = NULL;
01854 struct gtalk *client = NULL;
01855 char *sender = NULL, *to = NULL, *s = NULL;
01856 struct ast_channel *chan = NULL;
01857
01858 if (data) {
01859 s = ast_strdupa(data);
01860 sender = strsep(&s, "/");
01861 if (sender && (sender[0] != '\0')) {
01862 to = strsep(&s, "/");
01863 }
01864 if (!to) {
01865 ast_log(LOG_ERROR, "Bad arguments in Gtalk Dialstring: %s\n", (char*) data);
01866 return NULL;
01867 }
01868 }
01869
01870 client = find_gtalk(to, sender);
01871 if (!client) {
01872 ast_log(LOG_WARNING, "Could not find recipient.\n");
01873 return NULL;
01874 }
01875 if (!strcasecmp(client->name, "guest")){
01876
01877
01878 if (client->connection) {
01879 ASTOBJ_UNREF(client->connection, ast_aji_client_destroy);
01880 }
01881 client->connection = ast_aji_get_client(sender);
01882 if (!client->connection) {
01883 ast_log(LOG_ERROR, "No XMPP client to talk to, us (partial JID) : %s\n", sender);
01884 ASTOBJ_UNREF(client, gtalk_member_destroy);
01885 return NULL;
01886 }
01887 }
01888
01889 ASTOBJ_WRLOCK(client);
01890 p = gtalk_alloc(client, strchr(sender, '@') ? sender : client->connection->jid->full, strchr(to, '@') ? to : client->user, NULL);
01891 if (p) {
01892 chan = gtalk_new(client, p, AST_STATE_DOWN, to, requestor ? requestor->linkedid : NULL);
01893 }
01894 ASTOBJ_UNLOCK(client);
01895 return chan;
01896 }
01897
01898
01899 static char *gtalk_show_channels(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
01900 {
01901 #define FORMAT "%-30.30s %-30.30s %-15.15s %-5.5s %-5.5s \n"
01902 struct gtalk_pvt *p;
01903 struct ast_channel *chan;
01904 int numchans = 0;
01905 char them[AJI_MAX_JIDLEN];
01906 char *jid = NULL;
01907 char *resource = NULL;
01908
01909 switch (cmd) {
01910 case CLI_INIT:
01911 e->command = "gtalk show channels";
01912 e->usage =
01913 "Usage: gtalk show channels\n"
01914 " Shows current state of the Gtalk channels.\n";
01915 return NULL;
01916 case CLI_GENERATE:
01917 return NULL;
01918 }
01919
01920 if (a->argc != 3)
01921 return CLI_SHOWUSAGE;
01922
01923 ast_mutex_lock(>alklock);
01924 ast_cli(a->fd, FORMAT, "Channel", "Jabber ID", "Resource", "Read", "Write");
01925 ASTOBJ_CONTAINER_TRAVERSE(>alk_list, 1, {
01926 ASTOBJ_WRLOCK(iterator);
01927 p = iterator->p;
01928 while(p) {
01929 chan = p->owner;
01930 ast_copy_string(them, p->them, sizeof(them));
01931 jid = them;
01932 resource = strchr(them, '/');
01933 if (!resource)
01934 resource = "None";
01935 else {
01936 *resource = '\0';
01937 resource ++;
01938 }
01939 if (chan)
01940 ast_cli(a->fd, FORMAT,
01941 chan->name,
01942 jid,
01943 resource,
01944 ast_getformatname(chan->readformat),
01945 ast_getformatname(chan->writeformat)
01946 );
01947 else
01948 ast_log(LOG_WARNING, "No available channel\n");
01949 numchans ++;
01950 p = p->next;
01951 }
01952 ASTOBJ_UNLOCK(iterator);
01953 });
01954
01955 ast_mutex_unlock(>alklock);
01956
01957 ast_cli(a->fd, "%d active gtalk channel%s\n", numchans, (numchans != 1) ? "s" : "");
01958 return CLI_SUCCESS;
01959 #undef FORMAT
01960 }
01961
01962
01963
01964 #if 0
01965 static char *gtalk_do_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
01966 {
01967 switch (cmd) {
01968 case CLI_INIT:
01969 e->command = "gtalk reload";
01970 e->usage =
01971 "Usage: gtalk reload\n"
01972 " Reload gtalk channel driver.\n";
01973 return NULL;
01974 case CLI_GENERATE:
01975 return NULL;
01976 }
01977
01978 ast_verbose("IT DOES WORK!\n");
01979 return CLI_SUCCESS;
01980 }
01981 #endif
01982
01983 static int gtalk_parser(void *data, ikspak *pak)
01984 {
01985 struct gtalk *client = ASTOBJ_REF((struct gtalk *) data);
01986 int res;
01987 iks *tmp;
01988
01989 if (!strcasecmp(iks_name(pak->query), "jin:jingle") && (tmp = iks_next(pak->query)) && !strcasecmp(iks_name(tmp), "ses:session")) {
01990 ast_debug(1, "New method detected. Skipping jingle offer and using old gtalk method.\n");
01991 pak->query = tmp;
01992 }
01993
01994 if (!strcmp(S_OR(iks_find_attrib(pak->x, "type"), ""), "error")) {
01995 ast_log(LOG_NOTICE, "Remote peer reported an error, trying to establish the call anyway\n");
01996 }
01997
01998 if (ast_strlen_zero(iks_find_attrib(pak->query, "type"))) {
01999 ast_log(LOG_NOTICE, "No attribute \"type\" found. Ignoring message.\n");
02000 } else if (!strcmp(iks_find_attrib(pak->query, "type"), "initiate")) {
02001
02002 gtalk_newcall(client, pak);
02003 } else if (!strcmp(iks_find_attrib(pak->query, "type"), "candidates") || !strcmp(iks_find_attrib(pak->query, "type"), "transport-info")) {
02004 ast_debug(3, "About to add candidate!\n");
02005 res = gtalk_add_candidate(client, pak);
02006 if (!res) {
02007 ast_log(LOG_WARNING, "Could not add any candidate\n");
02008 } else {
02009 ast_debug(3, "Candidate Added!\n");
02010 }
02011 } else if (!strcmp(iks_find_attrib(pak->query, "type"), "accept")) {
02012 gtalk_is_answered(client, pak);
02013 } else if (!strcmp(iks_find_attrib(pak->query, "type"), "transport-accept")) {
02014 gtalk_is_accepted(client, pak);
02015 } else if (!strcmp(iks_find_attrib(pak->query, "type"), "content-info") || iks_find_with_attrib(pak->x, "gtalk", "action", "session-info")) {
02016 gtalk_handle_dtmf(client, pak);
02017 } else if (!strcmp(iks_find_attrib(pak->query, "type"), "terminate")) {
02018 gtalk_hangup_farend(client, pak);
02019 } else if (!strcmp(iks_find_attrib(pak->query, "type"), "reject")) {
02020 gtalk_hangup_farend(client, pak);
02021 }
02022 ASTOBJ_UNREF(client, gtalk_member_destroy);
02023 return IKS_FILTER_EAT;
02024 }
02025
02026 static int gtalk_create_member(char *label, struct ast_variable *var, int allowguest,
02027 struct ast_codec_pref prefs, char *context,
02028 struct gtalk *member)
02029 {
02030 struct aji_client *client;
02031
02032 if (!member)
02033 ast_log(LOG_WARNING, "Out of memory.\n");
02034
02035 ast_copy_string(member->name, label, sizeof(member->name));
02036 ast_copy_string(member->user, label, sizeof(member->user));
02037 ast_copy_string(member->context, context, sizeof(member->context));
02038 member->allowguest = allowguest;
02039 member->prefs = prefs;
02040 while (var) {
02041 if (!strcasecmp(var->name, "username"))
02042 ast_copy_string(member->user, var->value, sizeof(member->user));
02043 else if (!strcasecmp(var->name, "disallow"))
02044 ast_parse_allow_disallow(&member->prefs, &member->capability, var->value, 0);
02045 else if (!strcasecmp(var->name, "allow"))
02046 ast_parse_allow_disallow(&member->prefs, &member->capability, var->value, 1);
02047 else if (!strcasecmp(var->name, "context"))
02048 ast_copy_string(member->context, var->value, sizeof(member->context));
02049 else if (!strcasecmp(var->name, "parkinglot"))
02050 ast_copy_string(member->parkinglot, var->value, sizeof(member->parkinglot));
02051 else if (!strcasecmp(var->name, "connection")) {
02052 if ((client = ast_aji_get_client(var->value))) {
02053 member->connection = client;
02054 iks_filter_add_rule(client->f, gtalk_parser, member,
02055 IKS_RULE_TYPE, IKS_PAK_IQ,
02056 IKS_RULE_FROM_PARTIAL, member->user,
02057 IKS_RULE_NS, GOOGLE_NS,
02058 IKS_RULE_DONE);
02059 } else {
02060 ast_log(LOG_ERROR, "connection referenced not found!\n");
02061 return 0;
02062 }
02063 }
02064 var = var->next;
02065 }
02066 if (member->connection && member->user)
02067 member->buddy = ASTOBJ_CONTAINER_FIND(&member->connection->buddies, member->user);
02068 else {
02069 ast_log(LOG_ERROR, "No Connection or Username!\n");
02070 }
02071 return 1;
02072 }
02073
02074 static int gtalk_load_config(void)
02075 {
02076 char *cat = NULL;
02077 struct ast_config *cfg = NULL;
02078 char context[AST_MAX_CONTEXT] = "";
02079 char parkinglot[AST_MAX_CONTEXT] = "";
02080 int allowguest = 1;
02081 struct ast_variable *var;
02082 struct gtalk *member;
02083 struct ast_codec_pref prefs = { "", };
02084 struct aji_client_container *clients;
02085 struct gtalk_candidate *global_candidates = NULL;
02086 struct hostent *hp;
02087 struct ast_hostent ahp;
02088 struct ast_flags config_flags = { 0 };
02089
02090 cfg = ast_config_load(GOOGLE_CONFIG, config_flags);
02091 if (!cfg) {
02092 return 0;
02093 } else if (cfg == CONFIG_STATUS_FILEINVALID) {
02094 ast_log(LOG_ERROR, "Config file %s is in an invalid format. Aborting.\n", GOOGLE_CONFIG);
02095 return 0;
02096 }
02097
02098
02099 memcpy(&global_jbconf, &default_jbconf, sizeof(struct ast_jb_conf));
02100
02101
02102 memset(&stunaddr, 0, sizeof(stunaddr));
02103
02104 cat = ast_category_browse(cfg, NULL);
02105 for (var = ast_variable_browse(cfg, "general"); var; var = var->next) {
02106
02107 if (!ast_jb_read_conf(&global_jbconf, var->name, var->value))
02108 continue;
02109
02110 if (!strcasecmp(var->name, "allowguest")) {
02111 allowguest = (ast_true(ast_variable_retrieve(cfg, "general", "allowguest"))) ? 1 : 0;
02112 } else if (!strcasecmp(var->name, "disallow")) {
02113 ast_parse_allow_disallow(&prefs, &global_capability, var->value, 0);
02114 } else if (!strcasecmp(var->name, "allow")) {
02115 ast_parse_allow_disallow(&prefs, &global_capability, var->value, 1);
02116 } else if (!strcasecmp(var->name, "context")) {
02117 ast_copy_string(context, var->value, sizeof(context));
02118 } else if (!strcasecmp(var->name, "externip")) {
02119 ast_copy_string(externip, var->value, sizeof(externip));
02120 } else if (!strcasecmp(var->name, "parkinglot")) {
02121 ast_copy_string(parkinglot, var->value, sizeof(parkinglot));
02122 } else if (!strcasecmp(var->name, "bindaddr")) {
02123 if (!(hp = ast_gethostbyname(var->value, &ahp))) {
02124 ast_log(LOG_WARNING, "Invalid address: %s\n", var->value);
02125 } else {
02126 memcpy(&bindaddr.sin_addr, hp->h_addr, sizeof(bindaddr.sin_addr));
02127 }
02128 } else if (!strcasecmp(var->name, "stunaddr")) {
02129 stunaddr.sin_port = htons(STANDARD_STUN_PORT);
02130 if (ast_parse_arg(var->value, PARSE_INADDR, &stunaddr)) {
02131 ast_log(LOG_WARNING, "Invalid STUN server address: %s\n", var->value);
02132 }
02133 }
02134 }
02135 while (cat) {
02136 if (strcasecmp(cat, "general")) {
02137 var = ast_variable_browse(cfg, cat);
02138 member = ast_calloc(1, sizeof(*member));
02139 ASTOBJ_INIT(member);
02140 ASTOBJ_WRLOCK(member);
02141 if (!strcasecmp(cat, "guest")) {
02142 ast_copy_string(member->name, "guest", sizeof(member->name));
02143 ast_copy_string(member->user, "guest", sizeof(member->user));
02144 ast_copy_string(member->context, context, sizeof(member->context));
02145 ast_copy_string(member->parkinglot, parkinglot, sizeof(member->parkinglot));
02146 member->allowguest = allowguest;
02147 member->prefs = prefs;
02148 while (var) {
02149 if (!strcasecmp(var->name, "disallow")) {
02150 ast_parse_allow_disallow(&member->prefs, &member->capability,
02151 var->value, 0);
02152 } else if (!strcasecmp(var->name, "allow")) {
02153 ast_parse_allow_disallow(&member->prefs, &member->capability,
02154 var->value, 1);
02155 } else if (!strcasecmp(var->name, "context")) {
02156 ast_copy_string(member->context, var->value,
02157 sizeof(member->context));
02158 } else if (!strcasecmp(var->name, "parkinglot")) {
02159 ast_copy_string(member->parkinglot, var->value,
02160 sizeof(member->parkinglot));
02161 }
02162 var = var->next;
02163 }
02164 ASTOBJ_UNLOCK(member);
02165 clients = ast_aji_get_clients();
02166 if (clients) {
02167 ASTOBJ_CONTAINER_TRAVERSE(clients, 1, {
02168 ASTOBJ_WRLOCK(iterator);
02169 ASTOBJ_WRLOCK(member);
02170 if (member->connection) {
02171 ASTOBJ_UNREF(member->connection, ast_aji_client_destroy);
02172 }
02173 member->connection = NULL;
02174 iks_filter_add_rule(iterator->f, gtalk_parser, member, IKS_RULE_TYPE, IKS_PAK_IQ, IKS_RULE_NS, GOOGLE_NS, IKS_RULE_DONE);
02175 iks_filter_add_rule(iterator->f, gtalk_parser, member, IKS_RULE_TYPE, IKS_PAK_IQ, IKS_RULE_NS, GOOGLE_JINGLE_NS, IKS_RULE_DONE);
02176 iks_filter_add_rule(iterator->f, gtalk_parser, member, IKS_RULE_TYPE, IKS_PAK_IQ, IKS_RULE_NS, "http://jabber.org/protocol/gtalk", IKS_RULE_DONE);
02177 ASTOBJ_UNLOCK(member);
02178 ASTOBJ_UNLOCK(iterator);
02179 });
02180 ASTOBJ_CONTAINER_LINK(>alk_list, member);
02181 ASTOBJ_UNREF(member, gtalk_member_destroy);
02182 } else {
02183 ASTOBJ_UNLOCK(member);
02184 ASTOBJ_UNREF(member, gtalk_member_destroy);
02185 }
02186 } else {
02187 ASTOBJ_UNLOCK(member);
02188 if (gtalk_create_member(cat, var, allowguest, prefs, context, member))
02189 ASTOBJ_CONTAINER_LINK(>alk_list, member);
02190 ASTOBJ_UNREF(member, gtalk_member_destroy);
02191 }
02192 }
02193 cat = ast_category_browse(cfg, cat);
02194 }
02195
02196 ast_config_destroy(cfg);
02197 gtalk_update_externip();
02198 gtalk_free_candidates(global_candidates);
02199 return 1;
02200 }
02201
02202
02203 static int load_module(void)
02204 {
02205 struct ast_sockaddr bindaddr_tmp;
02206 struct ast_sockaddr ourip_tmp;
02207
02208 char *jabber_loaded = ast_module_helper("", "res_jabber.so", 0, 0, 0, 0);
02209 free(jabber_loaded);
02210 if (!jabber_loaded) {
02211
02212 jabber_loaded = ast_module_helper("", "res_jabber", 0, 0, 0, 0);
02213 free(jabber_loaded);
02214 if (!jabber_loaded) {
02215 ast_log(LOG_ERROR, "chan_gtalk.so depends upon res_jabber.so\n");
02216 return AST_MODULE_LOAD_DECLINE;
02217 }
02218 }
02219
02220 ASTOBJ_CONTAINER_INIT(>alk_list);
02221 if (!gtalk_load_config()) {
02222 ast_log(LOG_ERROR, "Unable to read config file %s. Not loading module.\n", GOOGLE_CONFIG);
02223 return 0;
02224 }
02225
02226 sched = sched_context_create();
02227 if (!sched) {
02228 ast_log(LOG_WARNING, "Unable to create schedule context\n");
02229 }
02230
02231 io = io_context_create();
02232 if (!io) {
02233 ast_log(LOG_WARNING, "Unable to create I/O context\n");
02234 }
02235
02236 ast_sockaddr_from_sin(&bindaddr_tmp, &bindaddr);
02237 if (gtalk_get_local_ip(&ourip_tmp)) {
02238 ast_log(LOG_WARNING, "Unable to get own IP address, Gtalk disabled\n");
02239 return 0;
02240 }
02241 __ourip.s_addr = htonl(ast_sockaddr_ipv4(&ourip_tmp));
02242
02243 ast_rtp_glue_register(>alk_rtp_glue);
02244 ast_cli_register_multiple(gtalk_cli, ARRAY_LEN(gtalk_cli));
02245
02246
02247 if (ast_channel_register(>alk_tech)) {
02248 ast_log(LOG_ERROR, "Unable to register channel class %s\n", gtalk_tech.type);
02249 return -1;
02250 }
02251 return 0;
02252 }
02253
02254
02255
02256 #if 0
02257 static int reload(void)
02258 {
02259 return 0;
02260 }
02261 #endif
02262
02263 static int unload_module(void)
02264 {
02265 struct gtalk_pvt *privates = NULL;
02266 ast_cli_unregister_multiple(gtalk_cli, ARRAY_LEN(gtalk_cli));
02267
02268 ast_channel_unregister(>alk_tech);
02269 ast_rtp_glue_unregister(>alk_rtp_glue);
02270
02271 if (!ast_mutex_lock(>alklock)) {
02272
02273 ASTOBJ_CONTAINER_TRAVERSE(>alk_list, 1, {
02274 ASTOBJ_WRLOCK(iterator);
02275 privates = iterator->p;
02276 while(privates) {
02277 if (privates->owner)
02278 ast_softhangup(privates->owner, AST_SOFTHANGUP_APPUNLOAD);
02279 privates = privates->next;
02280 }
02281 iterator->p = NULL;
02282 ASTOBJ_UNLOCK(iterator);
02283 });
02284 ast_mutex_unlock(>alklock);
02285 } else {
02286 ast_log(LOG_WARNING, "Unable to lock the monitor\n");
02287 return -1;
02288 }
02289 ASTOBJ_CONTAINER_DESTROYALL(>alk_list, gtalk_member_destroy);
02290 ASTOBJ_CONTAINER_DESTROY(>alk_list);
02291 return 0;
02292 }
02293
02294 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "Gtalk Channel Driver",
02295 .load = load_module,
02296 .unload = unload_module,
02297
02298 .load_pri = AST_MODPRI_CHANNEL_DRIVER,
02299 );