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: 413586 $")
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", (long unsigned)ast_random(), (long unsigned)ast_random());
00894 snprintf(pass, sizeof(pass), "%08lx%08lx", (long unsigned)ast_random(), (long unsigned)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", (long unsigned)ast_random(), (long unsigned)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,
01093 client->accountcode, i->exten, client->context, client->amaflags,
01094 "Gtalk/%s-%04lx", n2, (long unsigned)(ast_random() & 0xffff));
01095 if (!tmp) {
01096 ast_log(LOG_WARNING, "Unable to allocate Gtalk channel structure!\n");
01097 return NULL;
01098 }
01099 tmp->tech = >alk_tech;
01100
01101
01102
01103 if (i->jointcapability)
01104 what = i->jointcapability;
01105 else if (i->capability)
01106 what = i->capability;
01107 else
01108 what = global_capability;
01109
01110
01111 if (i->rtp) {
01112 ast_rtp_codecs_packetization_set(ast_rtp_instance_get_codecs(i->rtp), i->rtp, &i->prefs);
01113 }
01114
01115 tmp->nativeformats = ast_codec_choose(&i->prefs, what, 1) | (i->jointcapability & AST_FORMAT_VIDEO_MASK);
01116 fmt = ast_best_codec(tmp->nativeformats);
01117
01118 if (i->rtp) {
01119 ast_channel_set_fd(tmp, 0, ast_rtp_instance_fd(i->rtp, 0));
01120 ast_channel_set_fd(tmp, 1, ast_rtp_instance_fd(i->rtp, 1));
01121 }
01122 if (i->vrtp) {
01123 ast_channel_set_fd(tmp, 2, ast_rtp_instance_fd(i->vrtp, 0));
01124 ast_channel_set_fd(tmp, 3, ast_rtp_instance_fd(i->vrtp, 1));
01125 }
01126 if (state == AST_STATE_RING)
01127 tmp->rings = 1;
01128 tmp->adsicpe = AST_ADSI_UNAVAILABLE;
01129 tmp->writeformat = fmt;
01130 tmp->rawwriteformat = fmt;
01131 tmp->readformat = fmt;
01132 tmp->rawreadformat = fmt;
01133 tmp->tech_pvt = i;
01134
01135 tmp->callgroup = client->callgroup;
01136 tmp->pickupgroup = client->pickupgroup;
01137 tmp->caller.id.name.presentation = client->callingpres;
01138 tmp->caller.id.number.presentation = client->callingpres;
01139 if (!ast_strlen_zero(client->accountcode))
01140 ast_string_field_set(tmp, accountcode, client->accountcode);
01141 if (client->amaflags)
01142 tmp->amaflags = client->amaflags;
01143 if (!ast_strlen_zero(client->language))
01144 ast_string_field_set(tmp, language, client->language);
01145 if (!ast_strlen_zero(client->musicclass))
01146 ast_string_field_set(tmp, musicclass, client->musicclass);
01147 if (!ast_strlen_zero(client->parkinglot))
01148 ast_string_field_set(tmp, parkinglot, client->parkinglot);
01149 i->owner = tmp;
01150 ast_module_ref(ast_module_info->self);
01151 ast_copy_string(tmp->context, client->context, sizeof(tmp->context));
01152 ast_copy_string(tmp->exten, i->exten, sizeof(tmp->exten));
01153
01154 if (!ast_strlen_zero(i->exten) && strcmp(i->exten, "s")) {
01155 tmp->dialed.number.str = ast_strdup(i->exten);
01156 }
01157 tmp->priority = 1;
01158 if (i->rtp)
01159 ast_jb_configure(tmp, &global_jbconf);
01160 if (state != AST_STATE_DOWN && ast_pbx_start(tmp)) {
01161 ast_log(LOG_WARNING, "Unable to start PBX on %s\n", tmp->name);
01162 tmp->hangupcause = AST_CAUSE_SWITCH_CONGESTION;
01163 ast_hangup(tmp);
01164 tmp = NULL;
01165 } else {
01166 manager_event(EVENT_FLAG_SYSTEM, "ChannelUpdate",
01167 "Channel: %s\r\nChanneltype: %s\r\nGtalk-SID: %s\r\n",
01168 i->owner ? i->owner->name : "", "Gtalk", i->sid);
01169 }
01170 return tmp;
01171 }
01172
01173 static int gtalk_action(struct gtalk *client, struct gtalk_pvt *p, const char *action)
01174 {
01175 iks *request, *session = NULL;
01176 int res = -1;
01177 char *lowerthem = NULL;
01178
01179 request = iks_new("iq");
01180 if (request) {
01181 iks_insert_attrib(request, "type", "set");
01182 iks_insert_attrib(request, "from", p->us);
01183 iks_insert_attrib(request, "to", p->them);
01184 iks_insert_attrib(request, "id", client->connection->mid);
01185 ast_aji_increment_mid(client->connection->mid);
01186 session = iks_new("session");
01187 if (session) {
01188 iks_insert_attrib(session, "type", action);
01189 iks_insert_attrib(session, "id", p->sid);
01190
01191
01192 if (!p->initiator) {
01193 char c;
01194 char *t = lowerthem = ast_strdupa(p->them);
01195 while (((c = *t) != '/') && (*t++ = tolower(c)));
01196 }
01197 iks_insert_attrib(session, "initiator", p->initiator ? p->us : lowerthem);
01198 iks_insert_attrib(session, "xmlns", GOOGLE_NS);
01199 iks_insert_node(request, session);
01200 ast_aji_send(client->connection, request);
01201 res = 0;
01202 }
01203 }
01204
01205 iks_delete(session);
01206 iks_delete(request);
01207
01208 return res;
01209 }
01210
01211 static void gtalk_free_candidates(struct gtalk_candidate *candidate)
01212 {
01213 struct gtalk_candidate *last;
01214 while (candidate) {
01215 last = candidate;
01216 candidate = candidate->next;
01217 ast_free(last);
01218 }
01219 }
01220
01221 static void gtalk_free_pvt(struct gtalk *client, struct gtalk_pvt *p)
01222 {
01223 struct gtalk_pvt *cur, *prev = NULL;
01224 cur = client->p;
01225 while (cur) {
01226 if (cur == p) {
01227 if (prev)
01228 prev->next = p->next;
01229 else
01230 client->p = p->next;
01231 break;
01232 }
01233 prev = cur;
01234 cur = cur->next;
01235 }
01236 if (p->ringrule)
01237 iks_filter_remove_rule(p->parent->connection->f, p->ringrule);
01238 if (p->owner)
01239 ast_log(LOG_WARNING, "Uh oh, there's an owner, this is going to be messy.\n");
01240 if (p->rtp)
01241 ast_rtp_instance_destroy(p->rtp);
01242 if (p->vrtp)
01243 ast_rtp_instance_destroy(p->vrtp);
01244 gtalk_free_candidates(p->theircandidates);
01245 ast_free(p);
01246 }
01247
01248
01249 static int gtalk_newcall(struct gtalk *client, ikspak *pak)
01250 {
01251 struct gtalk_pvt *p, *tmp = client->p;
01252 struct ast_channel *chan;
01253 int res;
01254 iks *codec;
01255 char *from = NULL;
01256 char s1[BUFSIZ], s2[BUFSIZ], s3[BUFSIZ];
01257 int peernoncodeccapability;
01258 char *sid;
01259
01260
01261 from = iks_find_attrib(pak->x,"to");
01262 if (!from) {
01263 from = client->connection->jid->full;
01264 }
01265
01266 while (tmp) {
01267 if (iks_find_with_attrib(pak->x, "session", "id", tmp->sid) ||
01268 (iks_find_attrib(pak->query, "id") && !strcmp(iks_find_attrib(pak->query, "id"), tmp->sid))) {
01269 ast_log(LOG_NOTICE, "Ignoring duplicate call setup on SID %s\n", tmp->sid);
01270 gtalk_response(client, from, pak, "out-of-order", NULL);
01271 return -1;
01272 }
01273 tmp = tmp->next;
01274 }
01275
01276 if (!strcasecmp(client->name, "guest")){
01277
01278
01279 if (client->connection) {
01280 ASTOBJ_UNREF(client->connection, ast_aji_client_destroy);
01281 }
01282 client->connection = ast_aji_get_client(from);
01283 if (!client->connection) {
01284 ast_log(LOG_ERROR, "No XMPP client to talk to, us (partial JID) : %s\n", from);
01285 return -1;
01286 }
01287 }
01288
01289 if (!(sid = iks_find_attrib(pak->query, "id"))) {
01290 ast_log(LOG_WARNING, "Received Initiate without id attribute. Can not start call.\n");
01291 return -1;
01292 }
01293
01294 p = gtalk_alloc(client, from, pak->from->full, sid);
01295 if (!p) {
01296 ast_log(LOG_WARNING, "Unable to allocate gtalk structure!\n");
01297 return -1;
01298 }
01299
01300 chan = gtalk_new(client, p, AST_STATE_DOWN, pak->from->user, NULL);
01301 if (!chan) {
01302 gtalk_free_pvt(client, p);
01303 return -1;
01304 }
01305
01306 ast_mutex_lock(&p->lock);
01307 ast_copy_string(p->them, pak->from->full, sizeof(p->them));
01308 ast_copy_string(p->sid, sid, sizeof(p->sid));
01309
01310
01311 codec = iks_first_tag(iks_first_tag(pak->query));
01312
01313 while (codec) {
01314 char *codec_id = iks_find_attrib(codec, "id");
01315 char *codec_name = iks_find_attrib(codec, "name");
01316 if (!codec_id || !codec_name) {
01317 codec = iks_next_tag(codec);
01318 continue;
01319 }
01320 if (!strcmp(S_OR(iks_name(codec), ""), "vid:payload-type") && p->vrtp) {
01321 ast_rtp_codecs_payloads_set_m_type(
01322 ast_rtp_instance_get_codecs(p->vrtp),
01323 p->vrtp,
01324 atoi(codec_id));
01325 ast_rtp_codecs_payloads_set_rtpmap_type(
01326 ast_rtp_instance_get_codecs(p->vrtp),
01327 p->vrtp,
01328 atoi(codec_id),
01329 "video",
01330 codec_name,
01331 0);
01332 } else {
01333 ast_rtp_codecs_payloads_set_m_type(
01334 ast_rtp_instance_get_codecs(p->rtp),
01335 p->rtp,
01336 atoi(codec_id));
01337 ast_rtp_codecs_payloads_set_rtpmap_type(
01338 ast_rtp_instance_get_codecs(p->rtp),
01339 p->rtp,
01340 atoi(codec_id),
01341 "audio",
01342 codec_name,
01343 0);
01344 }
01345 codec = iks_next_tag(codec);
01346 }
01347
01348
01349 ast_rtp_codecs_payload_formats(ast_rtp_instance_get_codecs(p->rtp), &p->peercapability, &peernoncodeccapability);
01350 p->jointcapability = p->capability & p->peercapability;
01351 ast_mutex_unlock(&p->lock);
01352
01353 ast_setstate(chan, AST_STATE_RING);
01354 if (!p->jointcapability) {
01355 ast_log(LOG_WARNING, "Capabilities don't match : us - %s, peer - %s, combined - %s \n", ast_getformatname_multiple(s1, BUFSIZ, p->capability),
01356 ast_getformatname_multiple(s2, BUFSIZ, p->peercapability),
01357 ast_getformatname_multiple(s3, BUFSIZ, p->jointcapability));
01358
01359 gtalk_action(client, p, "reject");
01360 p->alreadygone = 1;
01361 gtalk_hangup(chan);
01362 ast_channel_release(chan);
01363 return -1;
01364 }
01365
01366 res = ast_pbx_start(chan);
01367
01368 switch (res) {
01369 case AST_PBX_FAILED:
01370 ast_log(LOG_WARNING, "Failed to start PBX :(\n");
01371 gtalk_response(client, from, pak, "service-unavailable", NULL);
01372 break;
01373 case AST_PBX_CALL_LIMIT:
01374 ast_log(LOG_WARNING, "Failed to start PBX (call limit reached) \n");
01375 gtalk_response(client, from, pak, "service-unavailable", NULL);
01376 break;
01377 case AST_PBX_SUCCESS:
01378 gtalk_response(client, from, pak, NULL, NULL);
01379 gtalk_create_candidates(client, p, p->sid, p->them, p->us);
01380
01381 break;
01382 }
01383
01384 return 1;
01385 }
01386
01387 static int gtalk_update_externip(void)
01388 {
01389 int sock;
01390 char *newaddr;
01391 struct sockaddr_in answer = { 0, };
01392 struct sockaddr_in *dst;
01393 struct ast_sockaddr tmp_dst;
01394
01395 if (!stunaddr.sin_addr.s_addr) {
01396 return -1;
01397 }
01398 dst = &stunaddr;
01399
01400 sock = socket(AF_INET, SOCK_DGRAM, 0);
01401 if (sock < 0) {
01402 ast_log(LOG_WARNING, "Unable to create STUN socket: %s\n", strerror(errno));
01403 return -1;
01404 }
01405
01406 ast_sockaddr_from_sin(&tmp_dst, dst);
01407 if (ast_connect(sock, &tmp_dst) != 0) {
01408 ast_log(LOG_WARNING, "STUN Failed to connect to %s\n", ast_sockaddr_stringify(&tmp_dst));
01409 close(sock);
01410 return -1;
01411 }
01412
01413 if ((ast_stun_request(sock, &stunaddr, NULL, &answer))) {
01414 close(sock);
01415 return -1;
01416 }
01417
01418 newaddr = ast_strdupa(ast_inet_ntoa(answer.sin_addr));
01419 memcpy(externip, newaddr, sizeof(externip));
01420
01421 close(sock);
01422 return 0;
01423
01424 }
01425
01426 static int gtalk_update_stun(struct gtalk *client, struct gtalk_pvt *p)
01427 {
01428 struct gtalk_candidate *tmp;
01429 struct hostent *hp;
01430 struct ast_hostent ahp;
01431 struct sockaddr_in sin = { 0, };
01432 struct sockaddr_in aux = { 0, };
01433 struct ast_sockaddr sin_tmp;
01434 struct ast_sockaddr aux_tmp;
01435
01436 if (time(NULL) == p->laststun)
01437 return 0;
01438
01439 tmp = p->theircandidates;
01440 p->laststun = time(NULL);
01441 while (tmp) {
01442 char username[256];
01443
01444
01445 if (!(hp = ast_gethostbyname(tmp->ip, &ahp))) {
01446 ast_log(LOG_WARNING, "Could not get host by name for %s\n", tmp->ip);
01447 tmp = tmp->next;
01448 continue;
01449 }
01450 sin.sin_family = AF_INET;
01451 memcpy(&sin.sin_addr, hp->h_addr, sizeof(sin.sin_addr));
01452 sin.sin_port = htons(tmp->port);
01453 snprintf(username, sizeof(username), "%s%s", tmp->username, p->ourcandidates->username);
01454
01455
01456 ast_rtp_instance_get_remote_address(p->rtp, &aux_tmp);
01457 ast_sockaddr_to_sin(&aux_tmp, &aux);
01458
01459
01460
01461
01462 if (aux.sin_addr.s_addr && (aux.sin_addr.s_addr != sin.sin_addr.s_addr)) {
01463 ast_rtp_instance_stun_request(p->rtp, &aux_tmp, username);
01464 } else {
01465 ast_sockaddr_from_sin(&sin_tmp, &sin);
01466 ast_rtp_instance_stun_request(p->rtp, &sin_tmp, username);
01467 }
01468 if (aux.sin_addr.s_addr) {
01469 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);
01470 ast_debug(4, "Sending STUN request to %s\n", tmp->ip);
01471 }
01472
01473 tmp = tmp->next;
01474 }
01475 return 1;
01476 }
01477
01478 static int gtalk_add_candidate(struct gtalk *client, ikspak *pak)
01479 {
01480 struct gtalk_pvt *p = NULL, *tmp = NULL;
01481 struct aji_client *c = client->connection;
01482 struct gtalk_candidate *newcandidate = NULL;
01483 iks *traversenodes = NULL, *receipt = NULL;
01484 char *from;
01485
01486 from = iks_find_attrib(pak->x,"to");
01487 if (!from) {
01488 from = c->jid->full;
01489 }
01490
01491 for (tmp = client->p; tmp; tmp = tmp->next) {
01492 if (iks_find_with_attrib(pak->x, "session", "id", tmp->sid) ||
01493 (iks_find_attrib(pak->query, "id") && !strcmp(iks_find_attrib(pak->query, "id"), tmp->sid))) {
01494 p = tmp;
01495 break;
01496 }
01497 }
01498
01499 if (!p) {
01500 return -1;
01501 }
01502 traversenodes = pak->query;
01503 while(traversenodes) {
01504 if(!strcasecmp(S_OR(iks_name(traversenodes), ""), "session")) {
01505 traversenodes = iks_first_tag(traversenodes);
01506 continue;
01507 }
01508 if(!strcasecmp(S_OR(iks_name(traversenodes), ""), "ses:session")) {
01509 traversenodes = iks_child(traversenodes);
01510 continue;
01511 }
01512 if(!strcasecmp(S_OR(iks_name(traversenodes), ""), "candidate") || !strcasecmp(S_OR(iks_name(traversenodes), ""), "ses:candidate")) {
01513 newcandidate = ast_calloc(1, sizeof(*newcandidate));
01514 if (!newcandidate)
01515 return 0;
01516 ast_copy_string(newcandidate->name,
01517 S_OR(iks_find_attrib(traversenodes, "name"), ""),
01518 sizeof(newcandidate->name));
01519 ast_copy_string(newcandidate->ip,
01520 S_OR(iks_find_attrib(traversenodes, "address"), ""),
01521 sizeof(newcandidate->ip));
01522 newcandidate->port = atoi(iks_find_attrib(traversenodes, "port"));
01523 ast_copy_string(newcandidate->username,
01524 S_OR(iks_find_attrib(traversenodes, "username"), ""),
01525 sizeof(newcandidate->username));
01526 ast_copy_string(newcandidate->password,
01527 S_OR(iks_find_attrib(traversenodes, "password"), ""),
01528 sizeof(newcandidate->password));
01529 newcandidate->preference = atof(iks_find_attrib(traversenodes, "preference"));
01530 if (!strcasecmp(S_OR(iks_find_attrib(traversenodes, "protocol"), ""), "udp"))
01531 newcandidate->protocol = AJI_PROTOCOL_UDP;
01532 if (!strcasecmp(S_OR(iks_find_attrib(traversenodes, "protocol"), ""), "ssltcp"))
01533 newcandidate->protocol = AJI_PROTOCOL_SSLTCP;
01534
01535 if (!strcasecmp(S_OR(iks_find_attrib(traversenodes, "type"), ""), "stun"))
01536 newcandidate->type = AJI_CONNECT_STUN;
01537 if (!strcasecmp(S_OR(iks_find_attrib(traversenodes, "type"), ""), "local"))
01538 newcandidate->type = AJI_CONNECT_LOCAL;
01539 if (!strcasecmp(S_OR(iks_find_attrib(traversenodes, "type"), ""), "relay"))
01540 newcandidate->type = AJI_CONNECT_RELAY;
01541 ast_copy_string(newcandidate->network,
01542 S_OR(iks_find_attrib(traversenodes, "network"), ""),
01543 sizeof(newcandidate->network));
01544 newcandidate->generation = atoi(S_OR(iks_find_attrib(traversenodes, "generation"), "0"));
01545 newcandidate->next = NULL;
01546
01547 newcandidate->next = p->theircandidates;
01548 p->theircandidates = newcandidate;
01549 p->laststun = 0;
01550 gtalk_update_stun(p->parent, p);
01551 newcandidate = NULL;
01552 }
01553 traversenodes = iks_next_tag(traversenodes);
01554 }
01555
01556 receipt = iks_new("iq");
01557 iks_insert_attrib(receipt, "type", "result");
01558 iks_insert_attrib(receipt, "from", from);
01559 iks_insert_attrib(receipt, "to", S_OR(iks_find_attrib(pak->x, "from"), ""));
01560 iks_insert_attrib(receipt, "id", S_OR(iks_find_attrib(pak->x, "id"), ""));
01561 ast_aji_send(c, receipt);
01562
01563 iks_delete(receipt);
01564
01565 return 1;
01566 }
01567
01568 static struct ast_frame *gtalk_rtp_read(struct ast_channel *ast, struct gtalk_pvt *p)
01569 {
01570 struct ast_frame *f;
01571
01572 if (!p->rtp) {
01573 return &ast_null_frame;
01574 }
01575 f = ast_rtp_instance_read(p->rtp, 0);
01576 gtalk_update_stun(p->parent, p);
01577 if (p->owner) {
01578
01579 if (f->frametype == AST_FRAME_VOICE) {
01580 if (f->subclass.codec != (p->owner->nativeformats & AST_FORMAT_AUDIO_MASK)) {
01581 ast_debug(1, "Oooh, format changed to %s\n", ast_getformatname(f->subclass.codec));
01582 p->owner->nativeformats =
01583 (p->owner->nativeformats & AST_FORMAT_VIDEO_MASK) | f->subclass.codec;
01584 ast_set_read_format(p->owner, p->owner->readformat);
01585 ast_set_write_format(p->owner, p->owner->writeformat);
01586 }
01587
01588
01589
01590
01591
01592 }
01593 }
01594 return f;
01595 }
01596
01597 static struct ast_frame *gtalk_read(struct ast_channel *ast)
01598 {
01599 struct ast_frame *fr;
01600 struct gtalk_pvt *p = ast->tech_pvt;
01601
01602 ast_mutex_lock(&p->lock);
01603 fr = gtalk_rtp_read(ast, p);
01604 ast_mutex_unlock(&p->lock);
01605 return fr;
01606 }
01607
01608
01609 static int gtalk_write(struct ast_channel *ast, struct ast_frame *frame)
01610 {
01611 struct gtalk_pvt *p = ast->tech_pvt;
01612 int res = 0;
01613 char buf[256];
01614
01615 switch (frame->frametype) {
01616 case AST_FRAME_VOICE:
01617 if (!(frame->subclass.codec & ast->nativeformats)) {
01618 ast_log(LOG_WARNING,
01619 "Asked to transmit frame type %s, while native formats is %s (read/write = %s/%s)\n",
01620 ast_getformatname(frame->subclass.codec),
01621 ast_getformatname_multiple(buf, sizeof(buf), ast->nativeformats),
01622 ast_getformatname(ast->readformat),
01623 ast_getformatname(ast->writeformat));
01624 return 0;
01625 }
01626 if (p) {
01627 ast_mutex_lock(&p->lock);
01628 if (p->rtp) {
01629 res = ast_rtp_instance_write(p->rtp, frame);
01630 }
01631 ast_mutex_unlock(&p->lock);
01632 }
01633 break;
01634 case AST_FRAME_VIDEO:
01635 if (p) {
01636 ast_mutex_lock(&p->lock);
01637 if (p->vrtp) {
01638 res = ast_rtp_instance_write(p->vrtp, frame);
01639 }
01640 ast_mutex_unlock(&p->lock);
01641 }
01642 break;
01643 case AST_FRAME_IMAGE:
01644 return 0;
01645 break;
01646 default:
01647 ast_log(LOG_WARNING, "Can't send %u type frames with Gtalk write\n",
01648 frame->frametype);
01649 return 0;
01650 }
01651
01652 return res;
01653 }
01654
01655 static int gtalk_fixup(struct ast_channel *oldchan, struct ast_channel *newchan)
01656 {
01657 struct gtalk_pvt *p = newchan->tech_pvt;
01658 ast_mutex_lock(&p->lock);
01659
01660 if ((p->owner != oldchan)) {
01661 ast_mutex_unlock(&p->lock);
01662 return -1;
01663 }
01664 if (p->owner == oldchan)
01665 p->owner = newchan;
01666 ast_mutex_unlock(&p->lock);
01667 return 0;
01668 }
01669
01670 static int gtalk_indicate(struct ast_channel *ast, int condition, const void *data, size_t datalen)
01671 {
01672 int res = 0;
01673
01674 switch (condition) {
01675 case AST_CONTROL_HOLD:
01676 ast_moh_start(ast, data, NULL);
01677 break;
01678 case AST_CONTROL_UNHOLD:
01679 ast_moh_stop(ast);
01680 break;
01681 default:
01682 ast_debug(3, "Don't know how to indicate condition '%d'\n", condition);
01683 res = -1;
01684 }
01685
01686 return res;
01687 }
01688
01689 static int gtalk_sendtext(struct ast_channel *chan, const char *text)
01690 {
01691 int res = 0;
01692 struct aji_client *client = NULL;
01693 struct gtalk_pvt *p = chan->tech_pvt;
01694
01695 if (!p->parent) {
01696 ast_log(LOG_ERROR, "Parent channel not found\n");
01697 return -1;
01698 }
01699 if (!p->parent->connection) {
01700 ast_log(LOG_ERROR, "XMPP client not found\n");
01701 return -1;
01702 }
01703 client = p->parent->connection;
01704 res = ast_aji_send_chat(client, p->them, text);
01705 return res;
01706 }
01707
01708 static int gtalk_digit_begin(struct ast_channel *chan, char digit)
01709 {
01710 struct gtalk_pvt *p = chan->tech_pvt;
01711 int res = 0;
01712
01713 ast_mutex_lock(&p->lock);
01714 if (p->rtp) {
01715 ast_rtp_instance_dtmf_begin(p->rtp, digit);
01716 } else {
01717 res = -1;
01718 }
01719 ast_mutex_unlock(&p->lock);
01720
01721 return res;
01722 }
01723
01724 static int gtalk_digit_end(struct ast_channel *chan, char digit, unsigned int duration)
01725 {
01726 struct gtalk_pvt *p = chan->tech_pvt;
01727 int res = 0;
01728
01729 ast_mutex_lock(&p->lock);
01730 if (p->rtp) {
01731 ast_rtp_instance_dtmf_end_with_duration(p->rtp, digit, duration);
01732 } else {
01733 res = -1;
01734 }
01735 ast_mutex_unlock(&p->lock);
01736
01737 return res;
01738 }
01739
01740
01741
01742
01743 #if 0
01744 static int gtalk_digit(struct ast_channel *ast, char digit, unsigned int duration)
01745 {
01746 struct gtalk_pvt *p = ast->tech_pvt;
01747 struct gtalk *client = p->parent;
01748 iks *iq, *gtalk, *dtmf;
01749 char buffer[2] = {digit, '\0'};
01750 char *lowerthem = NULL;
01751 iq = iks_new("iq");
01752 gtalk = iks_new("gtalk");
01753 dtmf = iks_new("dtmf");
01754 if(!iq || !gtalk || !dtmf) {
01755 iks_delete(iq);
01756 iks_delete(gtalk);
01757 iks_delete(dtmf);
01758 ast_log(LOG_ERROR, "Did not send dtmf do to memory issue\n");
01759 return -1;
01760 }
01761
01762 iks_insert_attrib(iq, "type", "set");
01763 iks_insert_attrib(iq, "to", p->them);
01764 iks_insert_attrib(iq, "from", p->us);
01765 iks_insert_attrib(iq, "id", client->connection->mid);
01766 ast_aji_increment_mid(client->connection->mid);
01767 iks_insert_attrib(gtalk, "xmlns", "http://jabber.org/protocol/gtalk");
01768 iks_insert_attrib(gtalk, "action", "session-info");
01769
01770
01771 if (!p->initiator) {
01772 char c;
01773 char *t = lowerthem = ast_strdupa(p->them);
01774 while (((c = *t) != '/') && (*t++ = tolower(c)));
01775 }
01776 iks_insert_attrib(gtalk, "initiator", p->initiator ? p->us: lowerthem);
01777 iks_insert_attrib(gtalk, "sid", p->sid);
01778 iks_insert_attrib(dtmf, "xmlns", "http://jabber.org/protocol/gtalk/info/dtmf");
01779 iks_insert_attrib(dtmf, "code", buffer);
01780 iks_insert_node(iq, gtalk);
01781 iks_insert_node(gtalk, dtmf);
01782
01783 ast_mutex_lock(&p->lock);
01784 if (ast->dtmff.frametype == AST_FRAME_DTMF_BEGIN || duration == 0) {
01785 iks_insert_attrib(dtmf, "action", "button-down");
01786 } else if (ast->dtmff.frametype == AST_FRAME_DTMF_END || duration != 0) {
01787 iks_insert_attrib(dtmf, "action", "button-up");
01788 }
01789 ast_aji_send(client->connection, iq);
01790
01791 iks_delete(iq);
01792 iks_delete(gtalk);
01793 iks_delete(dtmf);
01794 ast_mutex_unlock(&p->lock);
01795 return 0;
01796 }
01797 #endif
01798
01799 static int gtalk_sendhtml(struct ast_channel *ast, int subclass, const char *data, int datalen)
01800 {
01801 ast_log(LOG_NOTICE, "XXX Implement gtalk sendhtml XXX\n");
01802
01803 return -1;
01804 }
01805
01806
01807
01808 static int gtalk_call(struct ast_channel *ast, char *dest, int timeout)
01809 {
01810 struct gtalk_pvt *p = ast->tech_pvt;
01811
01812 if ((ast->_state != AST_STATE_DOWN) && (ast->_state != AST_STATE_RESERVED)) {
01813 ast_log(LOG_WARNING, "gtalk_call called on %s, neither down nor reserved\n", ast->name);
01814 return -1;
01815 }
01816
01817 ast_setstate(ast, AST_STATE_RING);
01818 if (!p->ringrule) {
01819 ast_copy_string(p->ring, p->parent->connection->mid, sizeof(p->ring));
01820 p->ringrule = iks_filter_add_rule(p->parent->connection->f, gtalk_ringing_ack, p,
01821 IKS_RULE_ID, p->ring, IKS_RULE_DONE);
01822 } else {
01823 ast_log(LOG_WARNING, "Whoa, already have a ring rule!\n");
01824 }
01825
01826 gtalk_invite(p, p->them, p->us, p->sid, 1);
01827
01828 return 0;
01829 }
01830
01831
01832 static int gtalk_hangup(struct ast_channel *ast)
01833 {
01834 struct gtalk_pvt *p = ast->tech_pvt;
01835 struct gtalk *client;
01836
01837 ast_mutex_lock(&p->lock);
01838 client = p->parent;
01839 p->owner = NULL;
01840 ast->tech_pvt = NULL;
01841 if (!p->alreadygone) {
01842 gtalk_action(client, p, "terminate");
01843 }
01844 ast_mutex_unlock(&p->lock);
01845
01846 gtalk_free_pvt(client, p);
01847 ast_module_unref(ast_module_info->self);
01848
01849 return 0;
01850 }
01851
01852
01853 static struct ast_channel *gtalk_request(const char *type, format_t format, const struct ast_channel *requestor, void *data, int *cause)
01854 {
01855 struct gtalk_pvt *p = NULL;
01856 struct gtalk *client = NULL;
01857 char *sender = NULL, *to = NULL, *s = NULL;
01858 struct ast_channel *chan = NULL;
01859
01860 if (data) {
01861 s = ast_strdupa(data);
01862 sender = strsep(&s, "/");
01863 if (sender && (sender[0] != '\0')) {
01864 to = strsep(&s, "/");
01865 }
01866 if (!to) {
01867 ast_log(LOG_ERROR, "Bad arguments in Gtalk Dialstring: %s\n", (char*) data);
01868 return NULL;
01869 }
01870 }
01871
01872 client = find_gtalk(to, sender);
01873 if (!client) {
01874 ast_log(LOG_WARNING, "Could not find recipient.\n");
01875 return NULL;
01876 }
01877 if (!strcasecmp(client->name, "guest")){
01878
01879
01880 if (client->connection) {
01881 ASTOBJ_UNREF(client->connection, ast_aji_client_destroy);
01882 }
01883 client->connection = ast_aji_get_client(sender);
01884 if (!client->connection) {
01885 ast_log(LOG_ERROR, "No XMPP client to talk to, us (partial JID) : %s\n", sender);
01886 ASTOBJ_UNREF(client, gtalk_member_destroy);
01887 return NULL;
01888 }
01889 }
01890
01891 ASTOBJ_WRLOCK(client);
01892 p = gtalk_alloc(client, strchr(sender, '@') ? sender : client->connection->jid->full, strchr(to, '@') ? to : client->user, NULL);
01893 if (p) {
01894 chan = gtalk_new(client, p, AST_STATE_DOWN, to, requestor ? requestor->linkedid : NULL);
01895 }
01896 ASTOBJ_UNLOCK(client);
01897 return chan;
01898 }
01899
01900
01901 static char *gtalk_show_channels(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
01902 {
01903 #define FORMAT "%-30.30s %-30.30s %-15.15s %-5.5s %-5.5s \n"
01904 struct gtalk_pvt *p;
01905 struct ast_channel *chan;
01906 int numchans = 0;
01907 char them[AJI_MAX_JIDLEN];
01908 char *jid = NULL;
01909 char *resource = NULL;
01910
01911 switch (cmd) {
01912 case CLI_INIT:
01913 e->command = "gtalk show channels";
01914 e->usage =
01915 "Usage: gtalk show channels\n"
01916 " Shows current state of the Gtalk channels.\n";
01917 return NULL;
01918 case CLI_GENERATE:
01919 return NULL;
01920 }
01921
01922 if (a->argc != 3)
01923 return CLI_SHOWUSAGE;
01924
01925 ast_mutex_lock(>alklock);
01926 ast_cli(a->fd, FORMAT, "Channel", "Jabber ID", "Resource", "Read", "Write");
01927 ASTOBJ_CONTAINER_TRAVERSE(>alk_list, 1, {
01928 ASTOBJ_WRLOCK(iterator);
01929 p = iterator->p;
01930 while(p) {
01931 chan = p->owner;
01932 ast_copy_string(them, p->them, sizeof(them));
01933 jid = them;
01934 resource = strchr(them, '/');
01935 if (!resource)
01936 resource = "None";
01937 else {
01938 *resource = '\0';
01939 resource ++;
01940 }
01941 if (chan)
01942 ast_cli(a->fd, FORMAT,
01943 chan->name,
01944 jid,
01945 resource,
01946 ast_getformatname(chan->readformat),
01947 ast_getformatname(chan->writeformat)
01948 );
01949 else
01950 ast_log(LOG_WARNING, "No available channel\n");
01951 numchans ++;
01952 p = p->next;
01953 }
01954 ASTOBJ_UNLOCK(iterator);
01955 });
01956
01957 ast_mutex_unlock(>alklock);
01958
01959 ast_cli(a->fd, "%d active gtalk channel%s\n", numchans, (numchans != 1) ? "s" : "");
01960 return CLI_SUCCESS;
01961 #undef FORMAT
01962 }
01963
01964
01965
01966 #if 0
01967 static char *gtalk_do_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
01968 {
01969 switch (cmd) {
01970 case CLI_INIT:
01971 e->command = "gtalk reload";
01972 e->usage =
01973 "Usage: gtalk reload\n"
01974 " Reload gtalk channel driver.\n";
01975 return NULL;
01976 case CLI_GENERATE:
01977 return NULL;
01978 }
01979
01980 ast_verbose("IT DOES WORK!\n");
01981 return CLI_SUCCESS;
01982 }
01983 #endif
01984
01985 static int gtalk_parser(void *data, ikspak *pak)
01986 {
01987 struct gtalk *client = ASTOBJ_REF((struct gtalk *) data);
01988 int res;
01989 iks *tmp;
01990
01991 if (!strcasecmp(iks_name(pak->query), "jin:jingle") && (tmp = iks_next(pak->query)) && !strcasecmp(iks_name(tmp), "ses:session")) {
01992 ast_debug(1, "New method detected. Skipping jingle offer and using old gtalk method.\n");
01993 pak->query = tmp;
01994 }
01995
01996 if (!strcmp(S_OR(iks_find_attrib(pak->x, "type"), ""), "error")) {
01997 ast_log(LOG_NOTICE, "Remote peer reported an error, trying to establish the call anyway\n");
01998 }
01999
02000 if (ast_strlen_zero(iks_find_attrib(pak->query, "type"))) {
02001 ast_log(LOG_NOTICE, "No attribute \"type\" found. Ignoring message.\n");
02002 } else if (!strcmp(iks_find_attrib(pak->query, "type"), "initiate")) {
02003
02004 gtalk_newcall(client, pak);
02005 } else if (!strcmp(iks_find_attrib(pak->query, "type"), "candidates") || !strcmp(iks_find_attrib(pak->query, "type"), "transport-info")) {
02006 ast_debug(3, "About to add candidate!\n");
02007 res = gtalk_add_candidate(client, pak);
02008 if (!res) {
02009 ast_log(LOG_WARNING, "Could not add any candidate\n");
02010 } else {
02011 ast_debug(3, "Candidate Added!\n");
02012 }
02013 } else if (!strcmp(iks_find_attrib(pak->query, "type"), "accept")) {
02014 gtalk_is_answered(client, pak);
02015 } else if (!strcmp(iks_find_attrib(pak->query, "type"), "transport-accept")) {
02016 gtalk_is_accepted(client, pak);
02017 } else if (!strcmp(iks_find_attrib(pak->query, "type"), "content-info") || iks_find_with_attrib(pak->x, "gtalk", "action", "session-info")) {
02018 gtalk_handle_dtmf(client, pak);
02019 } else if (!strcmp(iks_find_attrib(pak->query, "type"), "terminate")) {
02020 gtalk_hangup_farend(client, pak);
02021 } else if (!strcmp(iks_find_attrib(pak->query, "type"), "reject")) {
02022 gtalk_hangup_farend(client, pak);
02023 }
02024 ASTOBJ_UNREF(client, gtalk_member_destroy);
02025 return IKS_FILTER_EAT;
02026 }
02027
02028 static int gtalk_create_member(char *label, struct ast_variable *var, int allowguest,
02029 struct ast_codec_pref prefs, char *context,
02030 struct gtalk *member)
02031 {
02032 struct aji_client *client;
02033
02034 if (!member)
02035 ast_log(LOG_WARNING, "Out of memory.\n");
02036
02037 ast_copy_string(member->name, label, sizeof(member->name));
02038 ast_copy_string(member->user, label, sizeof(member->user));
02039 ast_copy_string(member->context, context, sizeof(member->context));
02040 member->allowguest = allowguest;
02041 member->prefs = prefs;
02042 while (var) {
02043 if (!strcasecmp(var->name, "username"))
02044 ast_copy_string(member->user, var->value, sizeof(member->user));
02045 else if (!strcasecmp(var->name, "disallow"))
02046 ast_parse_allow_disallow(&member->prefs, &member->capability, var->value, 0);
02047 else if (!strcasecmp(var->name, "allow"))
02048 ast_parse_allow_disallow(&member->prefs, &member->capability, var->value, 1);
02049 else if (!strcasecmp(var->name, "context"))
02050 ast_copy_string(member->context, var->value, sizeof(member->context));
02051 else if (!strcasecmp(var->name, "parkinglot"))
02052 ast_copy_string(member->parkinglot, var->value, sizeof(member->parkinglot));
02053 else if (!strcasecmp(var->name, "connection")) {
02054 if ((client = ast_aji_get_client(var->value))) {
02055 member->connection = client;
02056 iks_filter_add_rule(client->f, gtalk_parser, member,
02057 IKS_RULE_TYPE, IKS_PAK_IQ,
02058 IKS_RULE_FROM_PARTIAL, member->user,
02059 IKS_RULE_NS, GOOGLE_NS,
02060 IKS_RULE_DONE);
02061 } else {
02062 ast_log(LOG_ERROR, "connection referenced not found!\n");
02063 return 0;
02064 }
02065 }
02066 var = var->next;
02067 }
02068 if (member->connection && member->user)
02069 member->buddy = ASTOBJ_CONTAINER_FIND(&member->connection->buddies, member->user);
02070 else {
02071 ast_log(LOG_ERROR, "No Connection or Username!\n");
02072 }
02073 return 1;
02074 }
02075
02076 static int gtalk_load_config(void)
02077 {
02078 char *cat = NULL;
02079 struct ast_config *cfg = NULL;
02080 char context[AST_MAX_CONTEXT] = "";
02081 char parkinglot[AST_MAX_CONTEXT] = "";
02082 int allowguest = 1;
02083 struct ast_variable *var;
02084 struct gtalk *member;
02085 struct ast_codec_pref prefs = { "", };
02086 struct aji_client_container *clients;
02087 struct gtalk_candidate *global_candidates = NULL;
02088 struct hostent *hp;
02089 struct ast_hostent ahp;
02090 struct ast_flags config_flags = { 0 };
02091
02092 cfg = ast_config_load(GOOGLE_CONFIG, config_flags);
02093 if (!cfg) {
02094 return 0;
02095 } else if (cfg == CONFIG_STATUS_FILEINVALID) {
02096 ast_log(LOG_ERROR, "Config file %s is in an invalid format. Aborting.\n", GOOGLE_CONFIG);
02097 return 0;
02098 }
02099
02100
02101 memcpy(&global_jbconf, &default_jbconf, sizeof(struct ast_jb_conf));
02102
02103
02104 memset(&stunaddr, 0, sizeof(stunaddr));
02105
02106 cat = ast_category_browse(cfg, NULL);
02107 for (var = ast_variable_browse(cfg, "general"); var; var = var->next) {
02108
02109 if (!ast_jb_read_conf(&global_jbconf, var->name, var->value))
02110 continue;
02111
02112 if (!strcasecmp(var->name, "allowguest")) {
02113 allowguest = (ast_true(ast_variable_retrieve(cfg, "general", "allowguest"))) ? 1 : 0;
02114 } else if (!strcasecmp(var->name, "disallow")) {
02115 ast_parse_allow_disallow(&prefs, &global_capability, var->value, 0);
02116 } else if (!strcasecmp(var->name, "allow")) {
02117 ast_parse_allow_disallow(&prefs, &global_capability, var->value, 1);
02118 } else if (!strcasecmp(var->name, "context")) {
02119 ast_copy_string(context, var->value, sizeof(context));
02120 } else if (!strcasecmp(var->name, "externip")) {
02121 ast_copy_string(externip, var->value, sizeof(externip));
02122 } else if (!strcasecmp(var->name, "parkinglot")) {
02123 ast_copy_string(parkinglot, var->value, sizeof(parkinglot));
02124 } else if (!strcasecmp(var->name, "bindaddr")) {
02125 if (!(hp = ast_gethostbyname(var->value, &ahp))) {
02126 ast_log(LOG_WARNING, "Invalid address: %s\n", var->value);
02127 } else {
02128 memcpy(&bindaddr.sin_addr, hp->h_addr, sizeof(bindaddr.sin_addr));
02129 }
02130 } else if (!strcasecmp(var->name, "stunaddr")) {
02131 stunaddr.sin_port = htons(STANDARD_STUN_PORT);
02132 if (ast_parse_arg(var->value, PARSE_INADDR, &stunaddr)) {
02133 ast_log(LOG_WARNING, "Invalid STUN server address: %s\n", var->value);
02134 }
02135 }
02136 }
02137 while (cat) {
02138 if (strcasecmp(cat, "general")) {
02139 var = ast_variable_browse(cfg, cat);
02140 member = ast_calloc(1, sizeof(*member));
02141 ASTOBJ_INIT(member);
02142 ASTOBJ_WRLOCK(member);
02143 if (!strcasecmp(cat, "guest")) {
02144 ast_copy_string(member->name, "guest", sizeof(member->name));
02145 ast_copy_string(member->user, "guest", sizeof(member->user));
02146 ast_copy_string(member->context, context, sizeof(member->context));
02147 ast_copy_string(member->parkinglot, parkinglot, sizeof(member->parkinglot));
02148 member->allowguest = allowguest;
02149 member->prefs = prefs;
02150 while (var) {
02151 if (!strcasecmp(var->name, "disallow")) {
02152 ast_parse_allow_disallow(&member->prefs, &member->capability,
02153 var->value, 0);
02154 } else if (!strcasecmp(var->name, "allow")) {
02155 ast_parse_allow_disallow(&member->prefs, &member->capability,
02156 var->value, 1);
02157 } else if (!strcasecmp(var->name, "context")) {
02158 ast_copy_string(member->context, var->value,
02159 sizeof(member->context));
02160 } else if (!strcasecmp(var->name, "parkinglot")) {
02161 ast_copy_string(member->parkinglot, var->value,
02162 sizeof(member->parkinglot));
02163 }
02164 var = var->next;
02165 }
02166 ASTOBJ_UNLOCK(member);
02167 clients = ast_aji_get_clients();
02168 if (clients) {
02169 ASTOBJ_CONTAINER_TRAVERSE(clients, 1, {
02170 ASTOBJ_WRLOCK(iterator);
02171 ASTOBJ_WRLOCK(member);
02172 if (member->connection) {
02173 ASTOBJ_UNREF(member->connection, ast_aji_client_destroy);
02174 }
02175 member->connection = NULL;
02176 iks_filter_add_rule(iterator->f, gtalk_parser, member, IKS_RULE_TYPE, IKS_PAK_IQ, IKS_RULE_NS, GOOGLE_NS, IKS_RULE_DONE);
02177 iks_filter_add_rule(iterator->f, gtalk_parser, member, IKS_RULE_TYPE, IKS_PAK_IQ, IKS_RULE_NS, GOOGLE_JINGLE_NS, IKS_RULE_DONE);
02178 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);
02179 ASTOBJ_UNLOCK(member);
02180 ASTOBJ_UNLOCK(iterator);
02181 });
02182 ASTOBJ_CONTAINER_LINK(>alk_list, member);
02183 ASTOBJ_UNREF(member, gtalk_member_destroy);
02184 } else {
02185 ASTOBJ_UNLOCK(member);
02186 ASTOBJ_UNREF(member, gtalk_member_destroy);
02187 }
02188 } else {
02189 ASTOBJ_UNLOCK(member);
02190 if (gtalk_create_member(cat, var, allowguest, prefs, context, member))
02191 ASTOBJ_CONTAINER_LINK(>alk_list, member);
02192 ASTOBJ_UNREF(member, gtalk_member_destroy);
02193 }
02194 }
02195 cat = ast_category_browse(cfg, cat);
02196 }
02197
02198 ast_config_destroy(cfg);
02199 gtalk_update_externip();
02200 gtalk_free_candidates(global_candidates);
02201 return 1;
02202 }
02203
02204
02205 static int load_module(void)
02206 {
02207 struct ast_sockaddr bindaddr_tmp;
02208 struct ast_sockaddr ourip_tmp;
02209
02210 char *jabber_loaded = ast_module_helper("", "res_jabber.so", 0, 0, 0, 0);
02211 free(jabber_loaded);
02212 if (!jabber_loaded) {
02213
02214 jabber_loaded = ast_module_helper("", "res_jabber", 0, 0, 0, 0);
02215 free(jabber_loaded);
02216 if (!jabber_loaded) {
02217 ast_log(LOG_ERROR, "chan_gtalk.so depends upon res_jabber.so\n");
02218 return AST_MODULE_LOAD_DECLINE;
02219 }
02220 }
02221
02222 ASTOBJ_CONTAINER_INIT(>alk_list);
02223 if (!gtalk_load_config()) {
02224 ast_log(LOG_ERROR, "Unable to read config file %s. Not loading module.\n", GOOGLE_CONFIG);
02225 return 0;
02226 }
02227
02228 sched = sched_context_create();
02229 if (!sched) {
02230 ast_log(LOG_WARNING, "Unable to create schedule context\n");
02231 }
02232
02233 io = io_context_create();
02234 if (!io) {
02235 ast_log(LOG_WARNING, "Unable to create I/O context\n");
02236 }
02237
02238 ast_sockaddr_from_sin(&bindaddr_tmp, &bindaddr);
02239 if (gtalk_get_local_ip(&ourip_tmp)) {
02240 ast_log(LOG_WARNING, "Unable to get own IP address, Gtalk disabled\n");
02241 return 0;
02242 }
02243 __ourip.s_addr = htonl(ast_sockaddr_ipv4(&ourip_tmp));
02244
02245 ast_rtp_glue_register(>alk_rtp_glue);
02246 ast_cli_register_multiple(gtalk_cli, ARRAY_LEN(gtalk_cli));
02247
02248
02249 if (ast_channel_register(>alk_tech)) {
02250 ast_log(LOG_ERROR, "Unable to register channel class %s\n", gtalk_tech.type);
02251 return -1;
02252 }
02253 return 0;
02254 }
02255
02256
02257
02258 #if 0
02259 static int reload(void)
02260 {
02261 return 0;
02262 }
02263 #endif
02264
02265 static int unload_module(void)
02266 {
02267 struct gtalk_pvt *privates = NULL;
02268 ast_cli_unregister_multiple(gtalk_cli, ARRAY_LEN(gtalk_cli));
02269
02270 ast_channel_unregister(>alk_tech);
02271 ast_rtp_glue_unregister(>alk_rtp_glue);
02272
02273 if (!ast_mutex_lock(>alklock)) {
02274
02275 ASTOBJ_CONTAINER_TRAVERSE(>alk_list, 1, {
02276 ASTOBJ_WRLOCK(iterator);
02277 privates = iterator->p;
02278 while(privates) {
02279 if (privates->owner)
02280 ast_softhangup(privates->owner, AST_SOFTHANGUP_APPUNLOAD);
02281 privates = privates->next;
02282 }
02283 iterator->p = NULL;
02284 ASTOBJ_UNLOCK(iterator);
02285 });
02286 ast_mutex_unlock(>alklock);
02287 } else {
02288 ast_log(LOG_WARNING, "Unable to lock the monitor\n");
02289 return -1;
02290 }
02291 ASTOBJ_CONTAINER_DESTROYALL(>alk_list, gtalk_member_destroy);
02292 ASTOBJ_CONTAINER_DESTROY(>alk_list);
02293 return 0;
02294 }
02295
02296 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "Gtalk Channel Driver",
02297 .load = load_module,
02298 .unload = unload_module,
02299
02300 .load_pri = AST_MODPRI_CHANNEL_DRIVER,
02301 );