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: 332876 $")
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");
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");
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");
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");
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");
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");
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 = strstr(redirect, "xmpp:"))) {
00486 redirect += 5;
00487 ast_log(LOG_DEBUG, "redirect %s\n", redirect);
00488 ast_copy_string(p->them, redirect, sizeof(p->them));
00489
00490 gtalk_invite(p, p->them, p->us, p->sid, 1);
00491 break;
00492 }
00493 traversenodes = iks_next_tag(traversenodes);
00494 }
00495 }
00496 gtalk_create_candidates(p->parent, p, p->sid, p->them, p->us);
00497 owner = p->owner;
00498 ast_mutex_unlock(&p->lock);
00499
00500 if (owner) {
00501 ast_queue_control(owner, AST_CONTROL_RINGING);
00502 }
00503
00504 return IKS_FILTER_EAT;
00505 }
00506
00507 static int gtalk_answer(struct ast_channel *ast)
00508 {
00509 struct gtalk_pvt *p = ast->tech_pvt;
00510 int res = 0;
00511
00512 ast_debug(1, "Answer!\n");
00513 ast_mutex_lock(&p->lock);
00514 gtalk_invite(p, p->them, p->us,p->sid, 0);
00515 manager_event(EVENT_FLAG_SYSTEM, "ChannelUpdate", "Channel: %s\r\nChanneltype: %s\r\nGtalk-SID: %s\r\n",
00516 ast->name, "GTALK", p->sid);
00517 ast_mutex_unlock(&p->lock);
00518 return res;
00519 }
00520
00521 static enum ast_rtp_glue_result gtalk_get_rtp_peer(struct ast_channel *chan, struct ast_rtp_instance **instance)
00522 {
00523 struct gtalk_pvt *p = chan->tech_pvt;
00524 enum ast_rtp_glue_result res = AST_RTP_GLUE_RESULT_FORBID;
00525
00526 if (!p)
00527 return res;
00528
00529 ast_mutex_lock(&p->lock);
00530 if (p->rtp){
00531 ao2_ref(p->rtp, +1);
00532 *instance = p->rtp;
00533 res = AST_RTP_GLUE_RESULT_LOCAL;
00534 }
00535 ast_mutex_unlock(&p->lock);
00536
00537 return res;
00538 }
00539
00540 static format_t gtalk_get_codec(struct ast_channel *chan)
00541 {
00542 struct gtalk_pvt *p = chan->tech_pvt;
00543 return p->peercapability;
00544 }
00545
00546 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)
00547 {
00548 struct gtalk_pvt *p;
00549
00550 p = chan->tech_pvt;
00551 if (!p)
00552 return -1;
00553 ast_mutex_lock(&p->lock);
00554
00555
00556
00557
00558
00559
00560
00561
00562 ast_mutex_unlock(&p->lock);
00563 return 0;
00564 }
00565
00566 static struct ast_rtp_glue gtalk_rtp_glue = {
00567 .type = "Gtalk",
00568 .get_rtp_info = gtalk_get_rtp_peer,
00569 .get_codec = gtalk_get_codec,
00570 .update_peer = gtalk_set_rtp_peer,
00571 };
00572
00573 static int gtalk_response(struct gtalk *client, char *from, ikspak *pak, const char *reasonstr, const char *reasonstr2)
00574 {
00575 iks *response = NULL, *error = NULL, *reason = NULL;
00576 int res = -1;
00577
00578 response = iks_new("iq");
00579 if (response) {
00580 iks_insert_attrib(response, "type", "result");
00581 iks_insert_attrib(response, "from", from);
00582 iks_insert_attrib(response, "to", S_OR(iks_find_attrib(pak->x, "from"), ""));
00583 iks_insert_attrib(response, "id", S_OR(iks_find_attrib(pak->x, "id"), ""));
00584 if (reasonstr) {
00585 error = iks_new("error");
00586 if (error) {
00587 iks_insert_attrib(error, "type", "cancel");
00588 reason = iks_new(reasonstr);
00589 if (reason)
00590 iks_insert_node(error, reason);
00591 iks_insert_node(response, error);
00592 }
00593 }
00594 ast_aji_send(client->connection, response);
00595 res = 0;
00596 }
00597
00598 iks_delete(reason);
00599 iks_delete(error);
00600 iks_delete(response);
00601
00602 return res;
00603 }
00604
00605 static int gtalk_is_answered(struct gtalk *client, ikspak *pak)
00606 {
00607 struct gtalk_pvt *tmp = NULL;
00608 char *from;
00609 iks *codec;
00610 char s1[BUFSIZ], s2[BUFSIZ], s3[BUFSIZ];
00611 int peernoncodeccapability;
00612
00613 ast_log(LOG_DEBUG, "The client is %s\n", client->name);
00614
00615
00616 for (tmp = client->p; tmp; tmp = tmp->next) {
00617 if (iks_find_with_attrib(pak->x, "session", "id", tmp->sid)) {
00618 break;
00619 } else if (iks_find_with_attrib(pak->x, "ses:session", "id", tmp->sid)) {
00620 break;
00621 }
00622 }
00623
00624 if (!tmp) {
00625 ast_log(LOG_WARNING, "Could not find session in iq\n");
00626 return -1;
00627 }
00628
00629
00630 codec = iks_first_tag(iks_first_tag(iks_first_tag(pak->x)));
00631 while (codec) {
00632 char *codec_id = iks_find_attrib(codec, "id");
00633 char *codec_name = iks_find_attrib(codec, "name");
00634 if (!codec_id || !codec_name) {
00635 codec = iks_next_tag(codec);
00636 continue;
00637 }
00638
00639 ast_rtp_codecs_payloads_set_m_type(
00640 ast_rtp_instance_get_codecs(tmp->rtp),
00641 tmp->rtp,
00642 atoi(codec_id));
00643 ast_rtp_codecs_payloads_set_rtpmap_type(
00644 ast_rtp_instance_get_codecs(tmp->rtp),
00645 tmp->rtp,
00646 atoi(codec_id),
00647 "audio",
00648 codec_name,
00649 0);
00650 codec = iks_next_tag(codec);
00651 }
00652
00653
00654 ast_rtp_codecs_payload_formats(ast_rtp_instance_get_codecs(tmp->rtp), &tmp->peercapability, &peernoncodeccapability);
00655
00656
00657
00658 tmp->jointcapability = tmp->capability & tmp->peercapability;
00659 if (!tmp->jointcapability) {
00660 ast_log(LOG_WARNING, "Capabilities don't match : us - %s, peer - %s, combined - %s \n", ast_getformatname_multiple(s1, BUFSIZ, tmp->capability),
00661 ast_getformatname_multiple(s2, BUFSIZ, tmp->peercapability),
00662 ast_getformatname_multiple(s3, BUFSIZ, tmp->jointcapability));
00663
00664 ast_queue_hangup(tmp->owner);
00665
00666 return -1;
00667
00668 }
00669
00670 from = iks_find_attrib(pak->x, "to");
00671 if (!from) {
00672 from = client->connection->jid->full;
00673 }
00674
00675 if (tmp->owner) {
00676 ast_queue_control(tmp->owner, AST_CONTROL_ANSWER);
00677 }
00678 gtalk_update_stun(tmp->parent, tmp);
00679 gtalk_response(client, from, pak, NULL, NULL);
00680 return 1;
00681 }
00682
00683 static int gtalk_is_accepted(struct gtalk *client, ikspak *pak)
00684 {
00685 struct gtalk_pvt *tmp;
00686 char *from;
00687
00688 ast_log(LOG_DEBUG, "The client is %s\n", client->name);
00689
00690 for (tmp = client->p; tmp; tmp = tmp->next) {
00691 if (iks_find_with_attrib(pak->x, "session", "id", tmp->sid)) {
00692 break;
00693 }
00694 }
00695
00696 from = iks_find_attrib(pak->x, "to");
00697 if (!from) {
00698 from = client->connection->jid->full;
00699 }
00700
00701 if (tmp) {
00702 gtalk_update_stun(tmp->parent, tmp);
00703 } else {
00704 ast_log(LOG_NOTICE, "Whoa, didn't find call during accept?!\n");
00705 }
00706
00707
00708 gtalk_response(client, from, pak, NULL, NULL);
00709 return 1;
00710 }
00711
00712 static int gtalk_handle_dtmf(struct gtalk *client, ikspak *pak)
00713 {
00714 struct gtalk_pvt *tmp;
00715 iks *dtmfnode = NULL, *dtmfchild = NULL;
00716 char *dtmf;
00717 char *from;
00718
00719 for (tmp = client->p; tmp; tmp = tmp->next) {
00720 if (iks_find_with_attrib(pak->x, "session", "id", tmp->sid) || iks_find_with_attrib(pak->x, "gtalk", "sid", tmp->sid))
00721 break;
00722 }
00723 from = iks_find_attrib(pak->x, "to");
00724 if (!from) {
00725 from = client->connection->jid->full;
00726 }
00727
00728 if (tmp) {
00729 if(iks_find_with_attrib(pak->x, "dtmf-method", "method", "rtp")) {
00730 gtalk_response(client, from, pak,
00731 "feature-not-implemented xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'",
00732 "unsupported-dtmf-method xmlns='http://jabber.org/protocol/gtalk/info/dtmf#errors'");
00733 return -1;
00734 }
00735 if ((dtmfnode = iks_find(pak->x, "dtmf"))) {
00736 if((dtmf = iks_find_attrib(dtmfnode, "code"))) {
00737 if(iks_find_with_attrib(pak->x, "dtmf", "action", "button-up")) {
00738 struct ast_frame f = {AST_FRAME_DTMF_BEGIN, };
00739 f.subclass.integer = dtmf[0];
00740 ast_queue_frame(tmp->owner, &f);
00741 ast_verbose("GOOGLE! DTMF-relay event received: %c\n", (int) f.subclass.integer);
00742 } else if(iks_find_with_attrib(pak->x, "dtmf", "action", "button-down")) {
00743 struct ast_frame f = {AST_FRAME_DTMF_END, };
00744 f.subclass.integer = dtmf[0];
00745 ast_queue_frame(tmp->owner, &f);
00746 ast_verbose("GOOGLE! DTMF-relay event received: %c\n", (int) f.subclass.integer);
00747 } else if(iks_find_attrib(pak->x, "dtmf")) {
00748 struct ast_frame f = {AST_FRAME_DTMF, };
00749 f.subclass.integer = dtmf[0];
00750 ast_queue_frame(tmp->owner, &f);
00751 ast_verbose("GOOGLE! DTMF-relay event received: %c\n", (int) f.subclass.integer);
00752 }
00753 }
00754 } else if ((dtmfnode = iks_find_with_attrib(pak->x, "gtalk", "action", "session-info"))) {
00755 if((dtmfchild = iks_find(dtmfnode, "dtmf"))) {
00756 if((dtmf = iks_find_attrib(dtmfchild, "code"))) {
00757 if(iks_find_with_attrib(dtmfnode, "dtmf", "action", "button-up")) {
00758 struct ast_frame f = {AST_FRAME_DTMF_END, };
00759 f.subclass.integer = dtmf[0];
00760 ast_queue_frame(tmp->owner, &f);
00761 ast_verbose("GOOGLE! DTMF-relay event received: %c\n", (int) f.subclass.integer);
00762 } else if(iks_find_with_attrib(dtmfnode, "dtmf", "action", "button-down")) {
00763 struct ast_frame f = {AST_FRAME_DTMF_BEGIN, };
00764 f.subclass.integer = dtmf[0];
00765 ast_queue_frame(tmp->owner, &f);
00766 ast_verbose("GOOGLE! DTMF-relay event received: %c\n", (int) f.subclass.integer);
00767 }
00768 }
00769 }
00770 }
00771 gtalk_response(client, from, pak, NULL, NULL);
00772 return 1;
00773 } else {
00774 ast_log(LOG_NOTICE, "Whoa, didn't find call!\n");
00775 }
00776
00777 gtalk_response(client, from, pak, NULL, NULL);
00778 return 1;
00779 }
00780
00781 static int gtalk_hangup_farend(struct gtalk *client, ikspak *pak)
00782 {
00783 struct gtalk_pvt *tmp;
00784 char *from;
00785
00786 ast_debug(1, "The client is %s\n", client->name);
00787
00788 for (tmp = client->p; tmp; tmp = tmp->next) {
00789 if (iks_find_with_attrib(pak->x, "session", "id", tmp->sid) ||
00790 (iks_find_attrib(pak->query, "id") && !strcmp(iks_find_attrib(pak->query, "id"), tmp->sid))) {
00791 break;
00792 }
00793 }
00794 from = iks_find_attrib(pak->x, "to");
00795 if (!from) {
00796 from = client->connection->jid->full;
00797 }
00798
00799 if (tmp) {
00800 tmp->alreadygone = 1;
00801 if (tmp->owner) {
00802 ast_queue_hangup(tmp->owner);
00803 }
00804 } else {
00805 ast_log(LOG_NOTICE, "Whoa, didn't find call during hangup!\n");
00806 }
00807 gtalk_response(client, from, pak, NULL, NULL);
00808 return 1;
00809 }
00810
00811 static int gtalk_get_local_ip(struct ast_sockaddr *ourip)
00812 {
00813 struct ast_sockaddr root;
00814 struct ast_sockaddr bindaddr_tmp;
00815 struct ast_sockaddr *addrs;
00816 int addrs_cnt;
00817
00818
00819 ast_sockaddr_from_sin(&bindaddr_tmp, &bindaddr);
00820 if (!ast_sockaddr_is_any(&bindaddr_tmp)) {
00821 ast_sockaddr_copy(ourip, &bindaddr_tmp);
00822 return 0;
00823 }
00824
00825
00826
00827 if ((addrs_cnt = ast_sockaddr_resolve(&addrs, "google.com", PARSE_PORT_FORBID, AF_INET)) > 0) {
00828 ast_sockaddr_copy(&root, &addrs[0]);
00829 ast_free(addrs);
00830 if (!ast_ouraddrfor(&root, ourip)) {
00831 return 0;
00832 }
00833 }
00834
00835
00836 return ast_find_ourip(ourip, &bindaddr_tmp, AF_INET);
00837 }
00838
00839 static int gtalk_create_candidates(struct gtalk *client, struct gtalk_pvt *p, char *sid, char *from, char *to)
00840 {
00841 struct gtalk_candidate *tmp;
00842 struct aji_client *c = client->connection;
00843 struct gtalk_candidate *ours1 = NULL, *ours2 = NULL;
00844 struct sockaddr_in sin = { 0, };
00845 struct ast_sockaddr sin_tmp;
00846 struct ast_sockaddr us;
00847 iks *iq, *gtalk, *candidate, *transport;
00848 char user[17], pass[17], preference[5], port[7];
00849 char *lowerfrom = NULL;
00850
00851 iq = iks_new("iq");
00852 gtalk = iks_new("session");
00853 candidate = iks_new("candidate");
00854 transport = iks_new("transport");
00855 if (!iq || !gtalk || !candidate || !transport) {
00856 ast_log(LOG_ERROR, "Memory allocation error\n");
00857 goto safeout;
00858 }
00859 ours1 = ast_calloc(1, sizeof(*ours1));
00860 ours2 = ast_calloc(1, sizeof(*ours2));
00861 if (!ours1 || !ours2)
00862 goto safeout;
00863
00864 iks_insert_attrib(transport, "xmlns",GOOGLE_TRANSPORT_NS);
00865 iks_insert_node(iq, gtalk);
00866 iks_insert_node(gtalk,candidate);
00867 iks_insert_node(gtalk,transport);
00868
00869 for (; p; p = p->next) {
00870 if (!strcasecmp(p->sid, sid))
00871 break;
00872 }
00873
00874 if (!p) {
00875 ast_log(LOG_NOTICE, "No matching gtalk session - SID %s!\n", sid);
00876 goto safeout;
00877 }
00878
00879 ast_rtp_instance_get_local_address(p->rtp, &sin_tmp);
00880 ast_sockaddr_to_sin(&sin_tmp, &sin);
00881
00882 gtalk_get_local_ip(&us);
00883
00884 if (!strcmp(ast_sockaddr_stringify_addr(&us), "127.0.0.1")) {
00885 ast_log(LOG_WARNING, "Found a loopback IP on the system, check your network configuration or set the bindaddr attribute.");
00886 }
00887
00888
00889 ast_copy_string(ours1->name, "rtp", sizeof(ours1->name));
00890 ours1->port = ntohs(sin.sin_port);
00891 ours1->preference = 1;
00892 snprintf(user, sizeof(user), "%08lx%08lx", ast_random(), ast_random());
00893 snprintf(pass, sizeof(pass), "%08lx%08lx", ast_random(), ast_random());
00894 ast_copy_string(ours1->username, user, sizeof(ours1->username));
00895 ast_copy_string(ours1->password, pass, sizeof(ours1->password));
00896 ast_copy_string(ours1->ip, ast_sockaddr_stringify_addr(&us),
00897 sizeof(ours1->ip));
00898 ours1->protocol = AJI_PROTOCOL_UDP;
00899 ours1->type = AJI_CONNECT_LOCAL;
00900 ours1->generation = 0;
00901 p->ourcandidates = ours1;
00902
00903
00904
00905
00906 gtalk_update_externip();
00907 if (!ast_strlen_zero(externip)) {
00908 ast_copy_string(ours2->username, user, sizeof(ours2->username));
00909 ast_copy_string(ours2->password, pass, sizeof(ours2->password));
00910 ast_copy_string(ours2->ip, externip, sizeof(ours2->ip));
00911 ast_copy_string(ours2->name, "rtp", sizeof(ours1->name));
00912 ours2->port = ntohs(sin.sin_port);
00913 ours2->preference = 0.9;
00914 ours2->protocol = AJI_PROTOCOL_UDP;
00915 ours2->type = AJI_CONNECT_STUN;
00916 ours2->generation = 0;
00917 ours1->next = ours2;
00918 ours2 = NULL;
00919 }
00920 ours1 = NULL;
00921
00922 for (tmp = p->ourcandidates; tmp; tmp = tmp->next) {
00923 snprintf(port, sizeof(port), "%d", tmp->port);
00924 snprintf(preference, sizeof(preference), "%.2f", tmp->preference);
00925 iks_insert_attrib(iq, "from", to);
00926 iks_insert_attrib(iq, "to", from);
00927 iks_insert_attrib(iq, "type", "set");
00928 iks_insert_attrib(iq, "id", c->mid);
00929 ast_aji_increment_mid(c->mid);
00930 iks_insert_attrib(gtalk, "type", "candidates");
00931 iks_insert_attrib(gtalk, "id", sid);
00932
00933
00934 if (!p->initiator) {
00935 char c;
00936 char *t = lowerfrom = ast_strdupa(from);
00937 while (((c = *t) != '/') && (*t++ = tolower(c)));
00938 }
00939 iks_insert_attrib(gtalk, "initiator", (p->initiator) ? to : lowerfrom);
00940 iks_insert_attrib(gtalk, "xmlns", GOOGLE_NS);
00941 iks_insert_attrib(candidate, "name", tmp->name);
00942 iks_insert_attrib(candidate, "address", tmp->ip);
00943 iks_insert_attrib(candidate, "port", port);
00944 iks_insert_attrib(candidate, "username", tmp->username);
00945 iks_insert_attrib(candidate, "password", tmp->password);
00946 iks_insert_attrib(candidate, "preference", preference);
00947 if (tmp->protocol == AJI_PROTOCOL_UDP)
00948 iks_insert_attrib(candidate, "protocol", "udp");
00949 if (tmp->protocol == AJI_PROTOCOL_SSLTCP)
00950 iks_insert_attrib(candidate, "protocol", "ssltcp");
00951 if (tmp->type == AJI_CONNECT_STUN)
00952 iks_insert_attrib(candidate, "type", "stun");
00953 if (tmp->type == AJI_CONNECT_LOCAL)
00954 iks_insert_attrib(candidate, "type", "local");
00955 if (tmp->type == AJI_CONNECT_RELAY)
00956 iks_insert_attrib(candidate, "type", "relay");
00957 iks_insert_attrib(candidate, "network", "0");
00958 iks_insert_attrib(candidate, "generation", "0");
00959 ast_aji_send(c, iq);
00960 }
00961 p->laststun = 0;
00962
00963 safeout:
00964 if (ours1)
00965 ast_free(ours1);
00966 if (ours2)
00967 ast_free(ours2);
00968 iks_delete(iq);
00969 iks_delete(gtalk);
00970 iks_delete(candidate);
00971 iks_delete(transport);
00972
00973 return 1;
00974 }
00975
00976 static struct gtalk_pvt *gtalk_alloc(struct gtalk *client, const char *us, const char *them, const char *sid)
00977 {
00978 struct gtalk_pvt *tmp = NULL;
00979 struct aji_resource *resources = NULL;
00980 struct aji_buddy *buddy;
00981 char idroster[200];
00982 char *data, *exten = NULL;
00983 struct ast_sockaddr bindaddr_tmp;
00984
00985 ast_debug(1, "The client is %s for alloc\n", client->name);
00986 if (!sid && !strchr(them, '/')) {
00987 if (!strcasecmp(client->name, "guest")) {
00988 buddy = ASTOBJ_CONTAINER_FIND(&client->connection->buddies, them);
00989 if (buddy) {
00990 resources = buddy->resources;
00991 }
00992 } else if (client->buddy) {
00993 resources = client->buddy->resources;
00994 }
00995
00996 while (resources) {
00997 if (resources->cap->jingle) {
00998 break;
00999 }
01000 resources = resources->next;
01001 }
01002 if (resources) {
01003 snprintf(idroster, sizeof(idroster), "%s/%s", them, resources->resource);
01004 } else if ((*them == '+') || (strstr(them, "@voice.google.com"))) {
01005 snprintf(idroster, sizeof(idroster), "%s", them);
01006 } else {
01007 ast_log(LOG_ERROR, "no gtalk capable clients to talk to.\n");
01008 return NULL;
01009 }
01010 }
01011 if (!(tmp = ast_calloc(1, sizeof(*tmp)))) {
01012 return NULL;
01013 }
01014
01015 memcpy(&tmp->prefs, &client->prefs, sizeof(struct ast_codec_pref));
01016
01017 if (sid) {
01018 ast_copy_string(tmp->sid, sid, sizeof(tmp->sid));
01019 ast_copy_string(tmp->them, them, sizeof(tmp->them));
01020 ast_copy_string(tmp->us, us, sizeof(tmp->us));
01021 } else {
01022 snprintf(tmp->sid, sizeof(tmp->sid), "%08lx%08lx", ast_random(), ast_random());
01023 ast_copy_string(tmp->them, idroster, sizeof(tmp->them));
01024 ast_copy_string(tmp->us, us, sizeof(tmp->us));
01025 tmp->initiator = 1;
01026 }
01027
01028 bindaddr.sin_family = AF_INET;
01029 ast_sockaddr_from_sin(&bindaddr_tmp, &bindaddr);
01030 if (!(tmp->rtp = ast_rtp_instance_new("asterisk", sched, &bindaddr_tmp, NULL))) {
01031 ast_log(LOG_ERROR, "Failed to create a new RTP instance (possibly an invalid bindaddr?)\n");
01032 ast_free(tmp);
01033 return NULL;
01034 }
01035 ast_rtp_instance_set_prop(tmp->rtp, AST_RTP_PROPERTY_RTCP, 1);
01036 ast_rtp_instance_set_prop(tmp->rtp, AST_RTP_PROPERTY_STUN, 1);
01037 ast_rtp_instance_set_prop(tmp->rtp, AST_RTP_PROPERTY_DTMF, 1);
01038 ast_rtp_instance_dtmf_mode_set(tmp->rtp, AST_RTP_DTMF_MODE_RFC2833);
01039 ast_rtp_codecs_payloads_clear(ast_rtp_instance_get_codecs(tmp->rtp), tmp->rtp);
01040
01041
01042 if (client->capability) {
01043 tmp->capability = client->capability;
01044 } else if (global_capability) {
01045 tmp->capability = global_capability;
01046 }
01047
01048 tmp->parent = client;
01049 if (!tmp->rtp) {
01050 ast_log(LOG_WARNING, "Out of RTP sessions?\n");
01051 ast_free(tmp);
01052 return NULL;
01053 }
01054
01055
01056 ast_copy_string(tmp->cid_name, tmp->them, sizeof(tmp->cid_name));
01057
01058 if(strchr(tmp->us, '/')) {
01059 data = ast_strdupa(tmp->us);
01060 exten = strsep(&data, "/");
01061 } else {
01062 exten = tmp->us;
01063 }
01064 ast_copy_string(tmp->exten, exten, sizeof(tmp->exten));
01065 ast_mutex_init(&tmp->lock);
01066 ast_mutex_lock(>alklock);
01067 tmp->next = client->p;
01068 client->p = tmp;
01069 ast_mutex_unlock(>alklock);
01070 return tmp;
01071 }
01072
01073
01074 static struct ast_channel *gtalk_new(struct gtalk *client, struct gtalk_pvt *i, int state, const char *title, const char *linkedid)
01075 {
01076 struct ast_channel *tmp;
01077 int fmt;
01078 int what;
01079 const char *n2;
01080
01081 if (title)
01082 n2 = title;
01083 else
01084 n2 = i->us;
01085 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);
01086 if (!tmp) {
01087 ast_log(LOG_WARNING, "Unable to allocate Gtalk channel structure!\n");
01088 return NULL;
01089 }
01090 tmp->tech = >alk_tech;
01091
01092
01093
01094 if (i->jointcapability)
01095 what = i->jointcapability;
01096 else if (i->capability)
01097 what = i->capability;
01098 else
01099 what = global_capability;
01100
01101
01102 if (i->rtp) {
01103 ast_rtp_codecs_packetization_set(ast_rtp_instance_get_codecs(i->rtp), i->rtp, &i->prefs);
01104 }
01105
01106 tmp->nativeformats = ast_codec_choose(&i->prefs, what, 1) | (i->jointcapability & AST_FORMAT_VIDEO_MASK);
01107 fmt = ast_best_codec(tmp->nativeformats);
01108
01109 if (i->rtp) {
01110 ast_channel_set_fd(tmp, 0, ast_rtp_instance_fd(i->rtp, 0));
01111 ast_channel_set_fd(tmp, 1, ast_rtp_instance_fd(i->rtp, 1));
01112 }
01113 if (i->vrtp) {
01114 ast_channel_set_fd(tmp, 2, ast_rtp_instance_fd(i->vrtp, 0));
01115 ast_channel_set_fd(tmp, 3, ast_rtp_instance_fd(i->vrtp, 1));
01116 }
01117 if (state == AST_STATE_RING)
01118 tmp->rings = 1;
01119 tmp->adsicpe = AST_ADSI_UNAVAILABLE;
01120 tmp->writeformat = fmt;
01121 tmp->rawwriteformat = fmt;
01122 tmp->readformat = fmt;
01123 tmp->rawreadformat = fmt;
01124 tmp->tech_pvt = i;
01125
01126 tmp->callgroup = client->callgroup;
01127 tmp->pickupgroup = client->pickupgroup;
01128 tmp->caller.id.name.presentation = client->callingpres;
01129 tmp->caller.id.number.presentation = client->callingpres;
01130 if (!ast_strlen_zero(client->accountcode))
01131 ast_string_field_set(tmp, accountcode, client->accountcode);
01132 if (client->amaflags)
01133 tmp->amaflags = client->amaflags;
01134 if (!ast_strlen_zero(client->language))
01135 ast_string_field_set(tmp, language, client->language);
01136 if (!ast_strlen_zero(client->musicclass))
01137 ast_string_field_set(tmp, musicclass, client->musicclass);
01138 if (!ast_strlen_zero(client->parkinglot))
01139 ast_string_field_set(tmp, parkinglot, client->parkinglot);
01140 i->owner = tmp;
01141 ast_module_ref(ast_module_info->self);
01142 ast_copy_string(tmp->context, client->context, sizeof(tmp->context));
01143 ast_copy_string(tmp->exten, i->exten, sizeof(tmp->exten));
01144
01145 if (!ast_strlen_zero(i->exten) && strcmp(i->exten, "s")) {
01146 tmp->dialed.number.str = ast_strdup(i->exten);
01147 }
01148 tmp->priority = 1;
01149 if (i->rtp)
01150 ast_jb_configure(tmp, &global_jbconf);
01151 if (state != AST_STATE_DOWN && ast_pbx_start(tmp)) {
01152 ast_log(LOG_WARNING, "Unable to start PBX on %s\n", tmp->name);
01153 tmp->hangupcause = AST_CAUSE_SWITCH_CONGESTION;
01154 ast_hangup(tmp);
01155 tmp = NULL;
01156 } else {
01157 manager_event(EVENT_FLAG_SYSTEM, "ChannelUpdate",
01158 "Channel: %s\r\nChanneltype: %s\r\nGtalk-SID: %s\r\n",
01159 i->owner ? i->owner->name : "", "Gtalk", i->sid);
01160 }
01161 return tmp;
01162 }
01163
01164 static int gtalk_action(struct gtalk *client, struct gtalk_pvt *p, const char *action)
01165 {
01166 iks *request, *session = NULL;
01167 int res = -1;
01168 char *lowerthem = NULL;
01169
01170 request = iks_new("iq");
01171 if (request) {
01172 iks_insert_attrib(request, "type", "set");
01173 iks_insert_attrib(request, "from", p->us);
01174 iks_insert_attrib(request, "to", p->them);
01175 iks_insert_attrib(request, "id", client->connection->mid);
01176 ast_aji_increment_mid(client->connection->mid);
01177 session = iks_new("session");
01178 if (session) {
01179 iks_insert_attrib(session, "type", action);
01180 iks_insert_attrib(session, "id", p->sid);
01181
01182
01183 if (!p->initiator) {
01184 char c;
01185 char *t = lowerthem = ast_strdupa(p->them);
01186 while (((c = *t) != '/') && (*t++ = tolower(c)));
01187 }
01188 iks_insert_attrib(session, "initiator", p->initiator ? p->us : lowerthem);
01189 iks_insert_attrib(session, "xmlns", GOOGLE_NS);
01190 iks_insert_node(request, session);
01191 ast_aji_send(client->connection, request);
01192 res = 0;
01193 }
01194 }
01195
01196 iks_delete(session);
01197 iks_delete(request);
01198
01199 return res;
01200 }
01201
01202 static void gtalk_free_candidates(struct gtalk_candidate *candidate)
01203 {
01204 struct gtalk_candidate *last;
01205 while (candidate) {
01206 last = candidate;
01207 candidate = candidate->next;
01208 ast_free(last);
01209 }
01210 }
01211
01212 static void gtalk_free_pvt(struct gtalk *client, struct gtalk_pvt *p)
01213 {
01214 struct gtalk_pvt *cur, *prev = NULL;
01215 cur = client->p;
01216 while (cur) {
01217 if (cur == p) {
01218 if (prev)
01219 prev->next = p->next;
01220 else
01221 client->p = p->next;
01222 break;
01223 }
01224 prev = cur;
01225 cur = cur->next;
01226 }
01227 if (p->ringrule)
01228 iks_filter_remove_rule(p->parent->connection->f, p->ringrule);
01229 if (p->owner)
01230 ast_log(LOG_WARNING, "Uh oh, there's an owner, this is going to be messy.\n");
01231 if (p->rtp)
01232 ast_rtp_instance_destroy(p->rtp);
01233 if (p->vrtp)
01234 ast_rtp_instance_destroy(p->vrtp);
01235 gtalk_free_candidates(p->theircandidates);
01236 ast_free(p);
01237 }
01238
01239
01240 static int gtalk_newcall(struct gtalk *client, ikspak *pak)
01241 {
01242 struct gtalk_pvt *p, *tmp = client->p;
01243 struct ast_channel *chan;
01244 int res;
01245 iks *codec;
01246 char *from = NULL;
01247 char s1[BUFSIZ], s2[BUFSIZ], s3[BUFSIZ];
01248 int peernoncodeccapability;
01249 char *sid;
01250
01251
01252 from = iks_find_attrib(pak->x,"to");
01253 if (!from) {
01254 from = client->connection->jid->full;
01255 }
01256
01257 while (tmp) {
01258 if (iks_find_with_attrib(pak->x, "session", "id", tmp->sid) ||
01259 (iks_find_attrib(pak->query, "id") && !strcmp(iks_find_attrib(pak->query, "id"), tmp->sid))) {
01260 ast_log(LOG_NOTICE, "Ignoring duplicate call setup on SID %s\n", tmp->sid);
01261 gtalk_response(client, from, pak, "out-of-order", NULL);
01262 return -1;
01263 }
01264 tmp = tmp->next;
01265 }
01266
01267 if (!strcasecmp(client->name, "guest")){
01268
01269
01270 client->connection = ast_aji_get_client(from);
01271 if (!client->connection) {
01272 ast_log(LOG_ERROR, "No XMPP client to talk to, us (partial JID) : %s\n", from);
01273 return -1;
01274 }
01275 }
01276
01277 if (!(sid = iks_find_attrib(pak->query, "id"))) {
01278 ast_log(LOG_WARNING, "Received Initiate without id attribute. Can not start call.\n");
01279 return -1;
01280 }
01281
01282 p = gtalk_alloc(client, from, pak->from->full, sid);
01283 if (!p) {
01284 ast_log(LOG_WARNING, "Unable to allocate gtalk structure!\n");
01285 return -1;
01286 }
01287
01288 chan = gtalk_new(client, p, AST_STATE_DOWN, pak->from->user, NULL);
01289 if (!chan) {
01290 gtalk_free_pvt(client, p);
01291 return -1;
01292 }
01293
01294 ast_mutex_lock(&p->lock);
01295 ast_copy_string(p->them, pak->from->full, sizeof(p->them));
01296 ast_copy_string(p->sid, sid, sizeof(p->sid));
01297
01298
01299 codec = iks_first_tag(iks_first_tag(pak->query));
01300
01301 while (codec) {
01302 char *codec_id = iks_find_attrib(codec, "id");
01303 char *codec_name = iks_find_attrib(codec, "name");
01304 if (!codec_id || !codec_name) {
01305 codec = iks_next_tag(codec);
01306 continue;
01307 }
01308 if (!strcmp(S_OR(iks_name(codec), ""), "vid:payload-type") && p->vrtp) {
01309 ast_rtp_codecs_payloads_set_m_type(
01310 ast_rtp_instance_get_codecs(p->vrtp),
01311 p->vrtp,
01312 atoi(codec_id));
01313 ast_rtp_codecs_payloads_set_rtpmap_type(
01314 ast_rtp_instance_get_codecs(p->vrtp),
01315 p->vrtp,
01316 atoi(codec_id),
01317 "video",
01318 codec_name,
01319 0);
01320 } else {
01321 ast_rtp_codecs_payloads_set_m_type(
01322 ast_rtp_instance_get_codecs(p->rtp),
01323 p->rtp,
01324 atoi(codec_id));
01325 ast_rtp_codecs_payloads_set_rtpmap_type(
01326 ast_rtp_instance_get_codecs(p->rtp),
01327 p->rtp,
01328 atoi(codec_id),
01329 "audio",
01330 codec_name,
01331 0);
01332 }
01333 codec = iks_next_tag(codec);
01334 }
01335
01336
01337 ast_rtp_codecs_payload_formats(ast_rtp_instance_get_codecs(p->rtp), &p->peercapability, &peernoncodeccapability);
01338 p->jointcapability = p->capability & p->peercapability;
01339 ast_mutex_unlock(&p->lock);
01340
01341 ast_setstate(chan, AST_STATE_RING);
01342 if (!p->jointcapability) {
01343 ast_log(LOG_WARNING, "Capabilities don't match : us - %s, peer - %s, combined - %s \n", ast_getformatname_multiple(s1, BUFSIZ, p->capability),
01344 ast_getformatname_multiple(s2, BUFSIZ, p->peercapability),
01345 ast_getformatname_multiple(s3, BUFSIZ, p->jointcapability));
01346
01347 gtalk_action(client, p, "reject");
01348 p->alreadygone = 1;
01349 gtalk_hangup(chan);
01350 ast_channel_release(chan);
01351 return -1;
01352 }
01353
01354 res = ast_pbx_start(chan);
01355
01356 switch (res) {
01357 case AST_PBX_FAILED:
01358 ast_log(LOG_WARNING, "Failed to start PBX :(\n");
01359 gtalk_response(client, from, pak, "service-unavailable", NULL);
01360 break;
01361 case AST_PBX_CALL_LIMIT:
01362 ast_log(LOG_WARNING, "Failed to start PBX (call limit reached) \n");
01363 gtalk_response(client, from, pak, "service-unavailable", NULL);
01364 break;
01365 case AST_PBX_SUCCESS:
01366 gtalk_response(client, from, pak, NULL, NULL);
01367 gtalk_create_candidates(client, p, p->sid, p->them, p->us);
01368
01369 break;
01370 }
01371
01372 return 1;
01373 }
01374
01375 static int gtalk_update_externip(void)
01376 {
01377 int sock;
01378 char *newaddr;
01379 struct sockaddr_in answer = { 0, };
01380 struct sockaddr_in *dst;
01381 struct ast_sockaddr tmp_dst;
01382
01383 if (!stunaddr.sin_addr.s_addr) {
01384 return -1;
01385 }
01386 dst = &stunaddr;
01387
01388 sock = socket(AF_INET, SOCK_DGRAM, 0);
01389 if (sock < 0) {
01390 ast_log(LOG_WARNING, "Unable to create STUN socket: %s\n", strerror(errno));
01391 return -1;
01392 }
01393
01394 ast_sockaddr_from_sin(&tmp_dst, dst);
01395 if (ast_connect(sock, &tmp_dst) != 0) {
01396 ast_log(LOG_WARNING, "STUN Failed to connect to %s\n", ast_sockaddr_stringify(&tmp_dst));
01397 close(sock);
01398 return -1;
01399 }
01400
01401 if ((ast_stun_request(sock, &stunaddr, NULL, &answer))) {
01402 close(sock);
01403 return -1;
01404 }
01405
01406 newaddr = ast_strdupa(ast_inet_ntoa(answer.sin_addr));
01407 memcpy(externip, newaddr, sizeof(externip));
01408
01409 close(sock);
01410 return 0;
01411
01412 }
01413
01414 static int gtalk_update_stun(struct gtalk *client, struct gtalk_pvt *p)
01415 {
01416 struct gtalk_candidate *tmp;
01417 struct hostent *hp;
01418 struct ast_hostent ahp;
01419 struct sockaddr_in sin = { 0, };
01420 struct sockaddr_in aux = { 0, };
01421 struct ast_sockaddr sin_tmp;
01422 struct ast_sockaddr aux_tmp;
01423
01424 if (time(NULL) == p->laststun)
01425 return 0;
01426
01427 tmp = p->theircandidates;
01428 p->laststun = time(NULL);
01429 while (tmp) {
01430 char username[256];
01431
01432
01433 if (!(hp = ast_gethostbyname(tmp->ip, &ahp))) {
01434 ast_log(LOG_WARNING, "Could not get host by name for %s\n", tmp->ip);
01435 tmp = tmp->next;
01436 continue;
01437 }
01438 sin.sin_family = AF_INET;
01439 memcpy(&sin.sin_addr, hp->h_addr, sizeof(sin.sin_addr));
01440 sin.sin_port = htons(tmp->port);
01441 snprintf(username, sizeof(username), "%s%s", tmp->username, p->ourcandidates->username);
01442
01443
01444 ast_rtp_instance_get_remote_address(p->rtp, &aux_tmp);
01445 ast_sockaddr_to_sin(&aux_tmp, &aux);
01446
01447
01448
01449
01450 if (aux.sin_addr.s_addr && (aux.sin_addr.s_addr != sin.sin_addr.s_addr)) {
01451 ast_rtp_instance_stun_request(p->rtp, &aux_tmp, username);
01452 } else {
01453 ast_sockaddr_from_sin(&sin_tmp, &sin);
01454 ast_rtp_instance_stun_request(p->rtp, &sin_tmp, username);
01455 }
01456 if (aux.sin_addr.s_addr) {
01457 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);
01458 ast_debug(4, "Sending STUN request to %s\n", tmp->ip);
01459 }
01460
01461 tmp = tmp->next;
01462 }
01463 return 1;
01464 }
01465
01466 static int gtalk_add_candidate(struct gtalk *client, ikspak *pak)
01467 {
01468 struct gtalk_pvt *p = NULL, *tmp = NULL;
01469 struct aji_client *c = client->connection;
01470 struct gtalk_candidate *newcandidate = NULL;
01471 iks *traversenodes = NULL, *receipt = NULL;
01472 char *from;
01473
01474 from = iks_find_attrib(pak->x,"to");
01475 if (!from) {
01476 from = c->jid->full;
01477 }
01478
01479 for (tmp = client->p; tmp; tmp = tmp->next) {
01480 if (iks_find_with_attrib(pak->x, "session", "id", tmp->sid) ||
01481 (iks_find_attrib(pak->query, "id") && !strcmp(iks_find_attrib(pak->query, "id"), tmp->sid))) {
01482 p = tmp;
01483 break;
01484 }
01485 }
01486
01487 if (!p) {
01488 return -1;
01489 }
01490 traversenodes = pak->query;
01491 while(traversenodes) {
01492 if(!strcasecmp(S_OR(iks_name(traversenodes), ""), "session")) {
01493 traversenodes = iks_first_tag(traversenodes);
01494 continue;
01495 }
01496 if(!strcasecmp(S_OR(iks_name(traversenodes), ""), "ses:session")) {
01497 traversenodes = iks_child(traversenodes);
01498 continue;
01499 }
01500 if(!strcasecmp(S_OR(iks_name(traversenodes), ""), "candidate") || !strcasecmp(S_OR(iks_name(traversenodes), ""), "ses:candidate")) {
01501 newcandidate = ast_calloc(1, sizeof(*newcandidate));
01502 if (!newcandidate)
01503 return 0;
01504 ast_copy_string(newcandidate->name,
01505 S_OR(iks_find_attrib(traversenodes, "name"), ""),
01506 sizeof(newcandidate->name));
01507 ast_copy_string(newcandidate->ip,
01508 S_OR(iks_find_attrib(traversenodes, "address"), ""),
01509 sizeof(newcandidate->ip));
01510 newcandidate->port = atoi(iks_find_attrib(traversenodes, "port"));
01511 ast_copy_string(newcandidate->username,
01512 S_OR(iks_find_attrib(traversenodes, "username"), ""),
01513 sizeof(newcandidate->username));
01514 ast_copy_string(newcandidate->password,
01515 S_OR(iks_find_attrib(traversenodes, "password"), ""),
01516 sizeof(newcandidate->password));
01517 newcandidate->preference = atof(iks_find_attrib(traversenodes, "preference"));
01518 if (!strcasecmp(S_OR(iks_find_attrib(traversenodes, "protocol"), ""), "udp"))
01519 newcandidate->protocol = AJI_PROTOCOL_UDP;
01520 if (!strcasecmp(S_OR(iks_find_attrib(traversenodes, "protocol"), ""), "ssltcp"))
01521 newcandidate->protocol = AJI_PROTOCOL_SSLTCP;
01522
01523 if (!strcasecmp(S_OR(iks_find_attrib(traversenodes, "type"), ""), "stun"))
01524 newcandidate->type = AJI_CONNECT_STUN;
01525 if (!strcasecmp(S_OR(iks_find_attrib(traversenodes, "type"), ""), "local"))
01526 newcandidate->type = AJI_CONNECT_LOCAL;
01527 if (!strcasecmp(S_OR(iks_find_attrib(traversenodes, "type"), ""), "relay"))
01528 newcandidate->type = AJI_CONNECT_RELAY;
01529 ast_copy_string(newcandidate->network,
01530 S_OR(iks_find_attrib(traversenodes, "network"), ""),
01531 sizeof(newcandidate->network));
01532 newcandidate->generation = atoi(S_OR(iks_find_attrib(traversenodes, "generation"), "0"));
01533 newcandidate->next = NULL;
01534
01535 newcandidate->next = p->theircandidates;
01536 p->theircandidates = newcandidate;
01537 p->laststun = 0;
01538 gtalk_update_stun(p->parent, p);
01539 newcandidate = NULL;
01540 }
01541 traversenodes = iks_next_tag(traversenodes);
01542 }
01543
01544 receipt = iks_new("iq");
01545 iks_insert_attrib(receipt, "type", "result");
01546 iks_insert_attrib(receipt, "from", from);
01547 iks_insert_attrib(receipt, "to", S_OR(iks_find_attrib(pak->x, "from"), ""));
01548 iks_insert_attrib(receipt, "id", S_OR(iks_find_attrib(pak->x, "id"), ""));
01549 ast_aji_send(c, receipt);
01550
01551 iks_delete(receipt);
01552
01553 return 1;
01554 }
01555
01556 static struct ast_frame *gtalk_rtp_read(struct ast_channel *ast, struct gtalk_pvt *p)
01557 {
01558 struct ast_frame *f;
01559
01560 if (!p->rtp) {
01561 return &ast_null_frame;
01562 }
01563 f = ast_rtp_instance_read(p->rtp, 0);
01564 gtalk_update_stun(p->parent, p);
01565 if (p->owner) {
01566
01567 if (f->frametype == AST_FRAME_VOICE) {
01568 if (f->subclass.codec != (p->owner->nativeformats & AST_FORMAT_AUDIO_MASK)) {
01569 ast_debug(1, "Oooh, format changed to %s\n", ast_getformatname(f->subclass.codec));
01570 p->owner->nativeformats =
01571 (p->owner->nativeformats & AST_FORMAT_VIDEO_MASK) | f->subclass.codec;
01572 ast_set_read_format(p->owner, p->owner->readformat);
01573 ast_set_write_format(p->owner, p->owner->writeformat);
01574 }
01575
01576
01577
01578
01579
01580 }
01581 }
01582 return f;
01583 }
01584
01585 static struct ast_frame *gtalk_read(struct ast_channel *ast)
01586 {
01587 struct ast_frame *fr;
01588 struct gtalk_pvt *p = ast->tech_pvt;
01589
01590 ast_mutex_lock(&p->lock);
01591 fr = gtalk_rtp_read(ast, p);
01592 ast_mutex_unlock(&p->lock);
01593 return fr;
01594 }
01595
01596
01597 static int gtalk_write(struct ast_channel *ast, struct ast_frame *frame)
01598 {
01599 struct gtalk_pvt *p = ast->tech_pvt;
01600 int res = 0;
01601 char buf[256];
01602
01603 switch (frame->frametype) {
01604 case AST_FRAME_VOICE:
01605 if (!(frame->subclass.codec & ast->nativeformats)) {
01606 ast_log(LOG_WARNING,
01607 "Asked to transmit frame type %s, while native formats is %s (read/write = %s/%s)\n",
01608 ast_getformatname(frame->subclass.codec),
01609 ast_getformatname_multiple(buf, sizeof(buf), ast->nativeformats),
01610 ast_getformatname(ast->readformat),
01611 ast_getformatname(ast->writeformat));
01612 return 0;
01613 }
01614 if (p) {
01615 ast_mutex_lock(&p->lock);
01616 if (p->rtp) {
01617 res = ast_rtp_instance_write(p->rtp, frame);
01618 }
01619 ast_mutex_unlock(&p->lock);
01620 }
01621 break;
01622 case AST_FRAME_VIDEO:
01623 if (p) {
01624 ast_mutex_lock(&p->lock);
01625 if (p->vrtp) {
01626 res = ast_rtp_instance_write(p->vrtp, frame);
01627 }
01628 ast_mutex_unlock(&p->lock);
01629 }
01630 break;
01631 case AST_FRAME_IMAGE:
01632 return 0;
01633 break;
01634 default:
01635 ast_log(LOG_WARNING, "Can't send %d type frames with Gtalk write\n",
01636 frame->frametype);
01637 return 0;
01638 }
01639
01640 return res;
01641 }
01642
01643 static int gtalk_fixup(struct ast_channel *oldchan, struct ast_channel *newchan)
01644 {
01645 struct gtalk_pvt *p = newchan->tech_pvt;
01646 ast_mutex_lock(&p->lock);
01647
01648 if ((p->owner != oldchan)) {
01649 ast_mutex_unlock(&p->lock);
01650 return -1;
01651 }
01652 if (p->owner == oldchan)
01653 p->owner = newchan;
01654 ast_mutex_unlock(&p->lock);
01655 return 0;
01656 }
01657
01658 static int gtalk_indicate(struct ast_channel *ast, int condition, const void *data, size_t datalen)
01659 {
01660 int res = 0;
01661
01662 switch (condition) {
01663 case AST_CONTROL_HOLD:
01664 ast_moh_start(ast, data, NULL);
01665 break;
01666 case AST_CONTROL_UNHOLD:
01667 ast_moh_stop(ast);
01668 break;
01669 default:
01670 ast_debug(3, "Don't know how to indicate condition '%d'\n", condition);
01671 res = -1;
01672 }
01673
01674 return res;
01675 }
01676
01677 static int gtalk_sendtext(struct ast_channel *chan, const char *text)
01678 {
01679 int res = 0;
01680 struct aji_client *client = NULL;
01681 struct gtalk_pvt *p = chan->tech_pvt;
01682
01683 if (!p->parent) {
01684 ast_log(LOG_ERROR, "Parent channel not found\n");
01685 return -1;
01686 }
01687 if (!p->parent->connection) {
01688 ast_log(LOG_ERROR, "XMPP client not found\n");
01689 return -1;
01690 }
01691 client = p->parent->connection;
01692 res = ast_aji_send_chat(client, p->them, text);
01693 return res;
01694 }
01695
01696 static int gtalk_digit_begin(struct ast_channel *chan, char digit)
01697 {
01698 struct gtalk_pvt *p = chan->tech_pvt;
01699 int res = 0;
01700
01701 ast_mutex_lock(&p->lock);
01702 if (p->rtp) {
01703 ast_rtp_instance_dtmf_begin(p->rtp, digit);
01704 } else {
01705 res = -1;
01706 }
01707 ast_mutex_unlock(&p->lock);
01708
01709 return res;
01710 }
01711
01712 static int gtalk_digit_end(struct ast_channel *chan, char digit, unsigned int duration)
01713 {
01714 struct gtalk_pvt *p = chan->tech_pvt;
01715 int res = 0;
01716
01717 ast_mutex_lock(&p->lock);
01718 if (p->rtp) {
01719 ast_rtp_instance_dtmf_end_with_duration(p->rtp, digit, duration);
01720 } else {
01721 res = -1;
01722 }
01723 ast_mutex_unlock(&p->lock);
01724
01725 return res;
01726 }
01727
01728
01729
01730
01731 #if 0
01732 static int gtalk_digit(struct ast_channel *ast, char digit, unsigned int duration)
01733 {
01734 struct gtalk_pvt *p = ast->tech_pvt;
01735 struct gtalk *client = p->parent;
01736 iks *iq, *gtalk, *dtmf;
01737 char buffer[2] = {digit, '\0'};
01738 char *lowerthem = NULL;
01739 iq = iks_new("iq");
01740 gtalk = iks_new("gtalk");
01741 dtmf = iks_new("dtmf");
01742 if(!iq || !gtalk || !dtmf) {
01743 iks_delete(iq);
01744 iks_delete(gtalk);
01745 iks_delete(dtmf);
01746 ast_log(LOG_ERROR, "Did not send dtmf do to memory issue\n");
01747 return -1;
01748 }
01749
01750 iks_insert_attrib(iq, "type", "set");
01751 iks_insert_attrib(iq, "to", p->them);
01752 iks_insert_attrib(iq, "from", p->us);
01753 iks_insert_attrib(iq, "id", client->connection->mid);
01754 ast_aji_increment_mid(client->connection->mid);
01755 iks_insert_attrib(gtalk, "xmlns", "http://jabber.org/protocol/gtalk");
01756 iks_insert_attrib(gtalk, "action", "session-info");
01757
01758
01759 if (!p->initiator) {
01760 char c;
01761 char *t = lowerthem = ast_strdupa(p->them);
01762 while (((c = *t) != '/') && (*t++ = tolower(c)));
01763 }
01764 iks_insert_attrib(gtalk, "initiator", p->initiator ? p->us: lowerthem);
01765 iks_insert_attrib(gtalk, "sid", p->sid);
01766 iks_insert_attrib(dtmf, "xmlns", "http://jabber.org/protocol/gtalk/info/dtmf");
01767 iks_insert_attrib(dtmf, "code", buffer);
01768 iks_insert_node(iq, gtalk);
01769 iks_insert_node(gtalk, dtmf);
01770
01771 ast_mutex_lock(&p->lock);
01772 if (ast->dtmff.frametype == AST_FRAME_DTMF_BEGIN || duration == 0) {
01773 iks_insert_attrib(dtmf, "action", "button-down");
01774 } else if (ast->dtmff.frametype == AST_FRAME_DTMF_END || duration != 0) {
01775 iks_insert_attrib(dtmf, "action", "button-up");
01776 }
01777 ast_aji_send(client->connection, iq);
01778
01779 iks_delete(iq);
01780 iks_delete(gtalk);
01781 iks_delete(dtmf);
01782 ast_mutex_unlock(&p->lock);
01783 return 0;
01784 }
01785 #endif
01786
01787 static int gtalk_sendhtml(struct ast_channel *ast, int subclass, const char *data, int datalen)
01788 {
01789 ast_log(LOG_NOTICE, "XXX Implement gtalk sendhtml XXX\n");
01790
01791 return -1;
01792 }
01793
01794
01795
01796 static int gtalk_call(struct ast_channel *ast, char *dest, int timeout)
01797 {
01798 struct gtalk_pvt *p = ast->tech_pvt;
01799
01800 if ((ast->_state != AST_STATE_DOWN) && (ast->_state != AST_STATE_RESERVED)) {
01801 ast_log(LOG_WARNING, "gtalk_call called on %s, neither down nor reserved\n", ast->name);
01802 return -1;
01803 }
01804
01805 ast_setstate(ast, AST_STATE_RING);
01806 if (!p->ringrule) {
01807 ast_copy_string(p->ring, p->parent->connection->mid, sizeof(p->ring));
01808 p->ringrule = iks_filter_add_rule(p->parent->connection->f, gtalk_ringing_ack, p,
01809 IKS_RULE_ID, p->ring, IKS_RULE_DONE);
01810 } else {
01811 ast_log(LOG_WARNING, "Whoa, already have a ring rule!\n");
01812 }
01813
01814 gtalk_invite(p, p->them, p->us, p->sid, 1);
01815
01816 return 0;
01817 }
01818
01819
01820 static int gtalk_hangup(struct ast_channel *ast)
01821 {
01822 struct gtalk_pvt *p = ast->tech_pvt;
01823 struct gtalk *client;
01824
01825 ast_mutex_lock(&p->lock);
01826 client = p->parent;
01827 p->owner = NULL;
01828 ast->tech_pvt = NULL;
01829 if (!p->alreadygone) {
01830 gtalk_action(client, p, "terminate");
01831 }
01832 ast_mutex_unlock(&p->lock);
01833
01834 gtalk_free_pvt(client, p);
01835 ast_module_unref(ast_module_info->self);
01836
01837 return 0;
01838 }
01839
01840
01841 static struct ast_channel *gtalk_request(const char *type, format_t format, const struct ast_channel *requestor, void *data, int *cause)
01842 {
01843 struct gtalk_pvt *p = NULL;
01844 struct gtalk *client = NULL;
01845 char *sender = NULL, *to = NULL, *s = NULL;
01846 struct ast_channel *chan = NULL;
01847
01848 if (data) {
01849 s = ast_strdupa(data);
01850 if (s) {
01851 sender = strsep(&s, "/");
01852 if (sender && (sender[0] != '\0')) {
01853 to = strsep(&s, "/");
01854 }
01855 if (!to) {
01856 ast_log(LOG_ERROR, "Bad arguments in Gtalk Dialstring: %s\n", (char*) data);
01857 return NULL;
01858 }
01859 }
01860 }
01861
01862 client = find_gtalk(to, sender);
01863 if (!client) {
01864 ast_log(LOG_WARNING, "Could not find recipient.\n");
01865 return NULL;
01866 }
01867 if (!strcasecmp(client->name, "guest")){
01868
01869
01870 client->connection = ast_aji_get_client(sender);
01871 if (!client->connection) {
01872 ast_log(LOG_ERROR, "No XMPP client to talk to, us (partial JID) : %s\n", sender);
01873 ASTOBJ_UNREF(client, gtalk_member_destroy);
01874 return NULL;
01875 }
01876 }
01877
01878 ASTOBJ_WRLOCK(client);
01879 p = gtalk_alloc(client, strchr(sender, '@') ? sender : client->connection->jid->full, strchr(to, '@') ? to : client->user, NULL);
01880 if (p) {
01881 chan = gtalk_new(client, p, AST_STATE_DOWN, to, requestor ? requestor->linkedid : NULL);
01882 }
01883 ASTOBJ_UNLOCK(client);
01884 return chan;
01885 }
01886
01887
01888 static char *gtalk_show_channels(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
01889 {
01890 #define FORMAT "%-30.30s %-30.30s %-15.15s %-5.5s %-5.5s \n"
01891 struct gtalk_pvt *p;
01892 struct ast_channel *chan;
01893 int numchans = 0;
01894 char them[AJI_MAX_JIDLEN];
01895 char *jid = NULL;
01896 char *resource = NULL;
01897
01898 switch (cmd) {
01899 case CLI_INIT:
01900 e->command = "gtalk show channels";
01901 e->usage =
01902 "Usage: gtalk show channels\n"
01903 " Shows current state of the Gtalk channels.\n";
01904 return NULL;
01905 case CLI_GENERATE:
01906 return NULL;
01907 }
01908
01909 if (a->argc != 3)
01910 return CLI_SHOWUSAGE;
01911
01912 ast_mutex_lock(>alklock);
01913 ast_cli(a->fd, FORMAT, "Channel", "Jabber ID", "Resource", "Read", "Write");
01914 ASTOBJ_CONTAINER_TRAVERSE(>alk_list, 1, {
01915 ASTOBJ_WRLOCK(iterator);
01916 p = iterator->p;
01917 while(p) {
01918 chan = p->owner;
01919 ast_copy_string(them, p->them, sizeof(them));
01920 jid = them;
01921 resource = strchr(them, '/');
01922 if (!resource)
01923 resource = "None";
01924 else {
01925 *resource = '\0';
01926 resource ++;
01927 }
01928 if (chan)
01929 ast_cli(a->fd, FORMAT,
01930 chan->name,
01931 jid,
01932 resource,
01933 ast_getformatname(chan->readformat),
01934 ast_getformatname(chan->writeformat)
01935 );
01936 else
01937 ast_log(LOG_WARNING, "No available channel\n");
01938 numchans ++;
01939 p = p->next;
01940 }
01941 ASTOBJ_UNLOCK(iterator);
01942 });
01943
01944 ast_mutex_unlock(>alklock);
01945
01946 ast_cli(a->fd, "%d active gtalk channel%s\n", numchans, (numchans != 1) ? "s" : "");
01947 return CLI_SUCCESS;
01948 #undef FORMAT
01949 }
01950
01951
01952
01953 #if 0
01954 static char *gtalk_do_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
01955 {
01956 switch (cmd) {
01957 case CLI_INIT:
01958 e->command = "gtalk reload";
01959 e->usage =
01960 "Usage: gtalk reload\n"
01961 " Reload gtalk channel driver.\n";
01962 return NULL;
01963 case CLI_GENERATE:
01964 return NULL;
01965 }
01966
01967 ast_verbose("IT DOES WORK!\n");
01968 return CLI_SUCCESS;
01969 }
01970 #endif
01971
01972 static int gtalk_parser(void *data, ikspak *pak)
01973 {
01974 struct gtalk *client = ASTOBJ_REF((struct gtalk *) data);
01975 int res;
01976 iks *tmp;
01977
01978 if (!strcasecmp(iks_name(pak->query), "jin:jingle") && (tmp = iks_next(pak->query)) && !strcasecmp(iks_name(tmp), "ses:session")) {
01979 ast_debug(1, "New method detected. Skipping jingle offer and using old gtalk method.\n");
01980 pak->query = tmp;
01981 }
01982
01983 if (!strcmp(S_OR(iks_find_attrib(pak->x, "type"), ""), "error")) {
01984 ast_log(LOG_NOTICE, "Remote peer reported an error, trying to establish the call anyway\n");
01985 }
01986
01987 if (ast_strlen_zero(iks_find_attrib(pak->query, "type"))) {
01988 ast_log(LOG_NOTICE, "No attribute \"type\" found. Ignoring message.\n");
01989 } else if (!strcmp(iks_find_attrib(pak->query, "type"), "initiate")) {
01990
01991 gtalk_newcall(client, pak);
01992 } else if (!strcmp(iks_find_attrib(pak->query, "type"), "candidates") || !strcmp(iks_find_attrib(pak->query, "type"), "transport-info")) {
01993 ast_debug(3, "About to add candidate!\n");
01994 res = gtalk_add_candidate(client, pak);
01995 if (!res) {
01996 ast_log(LOG_WARNING, "Could not add any candidate\n");
01997 } else {
01998 ast_debug(3, "Candidate Added!\n");
01999 }
02000 } else if (!strcmp(iks_find_attrib(pak->query, "type"), "accept")) {
02001 gtalk_is_answered(client, pak);
02002 } else if (!strcmp(iks_find_attrib(pak->query, "type"), "transport-accept")) {
02003 gtalk_is_accepted(client, pak);
02004 } else if (!strcmp(iks_find_attrib(pak->query, "type"), "content-info") || iks_find_with_attrib(pak->x, "gtalk", "action", "session-info")) {
02005 gtalk_handle_dtmf(client, pak);
02006 } else if (!strcmp(iks_find_attrib(pak->query, "type"), "terminate")) {
02007 gtalk_hangup_farend(client, pak);
02008 } else if (!strcmp(iks_find_attrib(pak->query, "type"), "reject")) {
02009 gtalk_hangup_farend(client, pak);
02010 }
02011 ASTOBJ_UNREF(client, gtalk_member_destroy);
02012 return IKS_FILTER_EAT;
02013 }
02014
02015 static int gtalk_create_member(char *label, struct ast_variable *var, int allowguest,
02016 struct ast_codec_pref prefs, char *context,
02017 struct gtalk *member)
02018 {
02019 struct aji_client *client;
02020
02021 if (!member)
02022 ast_log(LOG_WARNING, "Out of memory.\n");
02023
02024 ast_copy_string(member->name, label, sizeof(member->name));
02025 ast_copy_string(member->user, label, sizeof(member->user));
02026 ast_copy_string(member->context, context, sizeof(member->context));
02027 member->allowguest = allowguest;
02028 member->prefs = prefs;
02029 while (var) {
02030 if (!strcasecmp(var->name, "username"))
02031 ast_copy_string(member->user, var->value, sizeof(member->user));
02032 else if (!strcasecmp(var->name, "disallow"))
02033 ast_parse_allow_disallow(&member->prefs, &member->capability, var->value, 0);
02034 else if (!strcasecmp(var->name, "allow"))
02035 ast_parse_allow_disallow(&member->prefs, &member->capability, var->value, 1);
02036 else if (!strcasecmp(var->name, "context"))
02037 ast_copy_string(member->context, var->value, sizeof(member->context));
02038 else if (!strcasecmp(var->name, "parkinglot"))
02039 ast_copy_string(member->parkinglot, var->value, sizeof(member->parkinglot));
02040 else if (!strcasecmp(var->name, "connection")) {
02041 if ((client = ast_aji_get_client(var->value))) {
02042 member->connection = client;
02043 iks_filter_add_rule(client->f, gtalk_parser, member,
02044 IKS_RULE_TYPE, IKS_PAK_IQ,
02045 IKS_RULE_FROM_PARTIAL, member->user,
02046 IKS_RULE_NS, GOOGLE_NS,
02047 IKS_RULE_DONE);
02048 } else {
02049 ast_log(LOG_ERROR, "connection referenced not found!\n");
02050 return 0;
02051 }
02052 }
02053 var = var->next;
02054 }
02055 if (member->connection && member->user)
02056 member->buddy = ASTOBJ_CONTAINER_FIND(&member->connection->buddies, member->user);
02057 else {
02058 ast_log(LOG_ERROR, "No Connection or Username!\n");
02059 }
02060 return 1;
02061 }
02062
02063 static int gtalk_load_config(void)
02064 {
02065 char *cat = NULL;
02066 struct ast_config *cfg = NULL;
02067 char context[AST_MAX_CONTEXT];
02068 char parkinglot[AST_MAX_CONTEXT];
02069 int allowguest = 1;
02070 struct ast_variable *var;
02071 struct gtalk *member;
02072 struct ast_codec_pref prefs;
02073 struct aji_client_container *clients;
02074 struct gtalk_candidate *global_candidates = NULL;
02075 struct hostent *hp;
02076 struct ast_hostent ahp;
02077 struct ast_flags config_flags = { 0 };
02078
02079 cfg = ast_config_load(GOOGLE_CONFIG, config_flags);
02080 if (!cfg) {
02081 return 0;
02082 } else if (cfg == CONFIG_STATUS_FILEINVALID) {
02083 ast_log(LOG_ERROR, "Config file %s is in an invalid format. Aborting.\n", GOOGLE_CONFIG);
02084 return 0;
02085 }
02086
02087
02088 memcpy(&global_jbconf, &default_jbconf, sizeof(struct ast_jb_conf));
02089
02090
02091 memset(&stunaddr, 0, sizeof(stunaddr));
02092
02093 cat = ast_category_browse(cfg, NULL);
02094 for (var = ast_variable_browse(cfg, "general"); var; var = var->next) {
02095
02096 if (!ast_jb_read_conf(&global_jbconf, var->name, var->value))
02097 continue;
02098
02099 if (!strcasecmp(var->name, "allowguest")) {
02100 allowguest = (ast_true(ast_variable_retrieve(cfg, "general", "allowguest"))) ? 1 : 0;
02101 } else if (!strcasecmp(var->name, "disallow")) {
02102 ast_parse_allow_disallow(&prefs, &global_capability, var->value, 0);
02103 } else if (!strcasecmp(var->name, "allow")) {
02104 ast_parse_allow_disallow(&prefs, &global_capability, var->value, 1);
02105 } else if (!strcasecmp(var->name, "context")) {
02106 ast_copy_string(context, var->value, sizeof(context));
02107 } else if (!strcasecmp(var->name, "externip")) {
02108 ast_copy_string(externip, var->value, sizeof(externip));
02109 } else if (!strcasecmp(var->name, "parkinglot")) {
02110 ast_copy_string(parkinglot, var->value, sizeof(parkinglot));
02111 } else if (!strcasecmp(var->name, "bindaddr")) {
02112 if (!(hp = ast_gethostbyname(var->value, &ahp))) {
02113 ast_log(LOG_WARNING, "Invalid address: %s\n", var->value);
02114 } else {
02115 memcpy(&bindaddr.sin_addr, hp->h_addr, sizeof(bindaddr.sin_addr));
02116 }
02117 } else if (!strcasecmp(var->name, "stunaddr")) {
02118 stunaddr.sin_port = htons(STANDARD_STUN_PORT);
02119 if (ast_parse_arg(var->value, PARSE_INADDR, &stunaddr)) {
02120 ast_log(LOG_WARNING, "Invalid STUN server address: %s\n", var->value);
02121 }
02122 }
02123 }
02124 while (cat) {
02125 if (strcasecmp(cat, "general")) {
02126 var = ast_variable_browse(cfg, cat);
02127 member = ast_calloc(1, sizeof(*member));
02128 ASTOBJ_INIT(member);
02129 ASTOBJ_WRLOCK(member);
02130 if (!strcasecmp(cat, "guest")) {
02131 ast_copy_string(member->name, "guest", sizeof(member->name));
02132 ast_copy_string(member->user, "guest", sizeof(member->user));
02133 ast_copy_string(member->context, context, sizeof(member->context));
02134 ast_copy_string(member->parkinglot, parkinglot, sizeof(member->parkinglot));
02135 member->allowguest = allowguest;
02136 member->prefs = prefs;
02137 while (var) {
02138 if (!strcasecmp(var->name, "disallow")) {
02139 ast_parse_allow_disallow(&member->prefs, &member->capability,
02140 var->value, 0);
02141 } else if (!strcasecmp(var->name, "allow")) {
02142 ast_parse_allow_disallow(&member->prefs, &member->capability,
02143 var->value, 1);
02144 } else if (!strcasecmp(var->name, "context")) {
02145 ast_copy_string(member->context, var->value,
02146 sizeof(member->context));
02147 } else if (!strcasecmp(var->name, "parkinglot")) {
02148 ast_copy_string(member->parkinglot, var->value,
02149 sizeof(member->parkinglot));
02150 }
02151 var = var->next;
02152 }
02153 ASTOBJ_UNLOCK(member);
02154 clients = ast_aji_get_clients();
02155 if (clients) {
02156 ASTOBJ_CONTAINER_TRAVERSE(clients, 1, {
02157 ASTOBJ_WRLOCK(iterator);
02158 ASTOBJ_WRLOCK(member);
02159 member->connection = NULL;
02160 iks_filter_add_rule(iterator->f, gtalk_parser, member, IKS_RULE_TYPE, IKS_PAK_IQ, IKS_RULE_NS, GOOGLE_NS, IKS_RULE_DONE);
02161 iks_filter_add_rule(iterator->f, gtalk_parser, member, IKS_RULE_TYPE, IKS_PAK_IQ, IKS_RULE_NS, GOOGLE_JINGLE_NS, IKS_RULE_DONE);
02162 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);
02163 ASTOBJ_UNLOCK(member);
02164 ASTOBJ_UNLOCK(iterator);
02165 });
02166 ASTOBJ_CONTAINER_LINK(>alk_list, member);
02167 ASTOBJ_UNREF(member, gtalk_member_destroy);
02168 } else {
02169 ASTOBJ_UNLOCK(member);
02170 ASTOBJ_UNREF(member, gtalk_member_destroy);
02171 }
02172 } else {
02173 ASTOBJ_UNLOCK(member);
02174 if (gtalk_create_member(cat, var, allowguest, prefs, context, member))
02175 ASTOBJ_CONTAINER_LINK(>alk_list, member);
02176 ASTOBJ_UNREF(member, gtalk_member_destroy);
02177 }
02178 }
02179 cat = ast_category_browse(cfg, cat);
02180 }
02181
02182 gtalk_update_externip();
02183 gtalk_free_candidates(global_candidates);
02184 return 1;
02185 }
02186
02187
02188 static int load_module(void)
02189 {
02190 struct ast_sockaddr bindaddr_tmp;
02191 struct ast_sockaddr ourip_tmp;
02192
02193 char *jabber_loaded = ast_module_helper("", "res_jabber.so", 0, 0, 0, 0);
02194 free(jabber_loaded);
02195 if (!jabber_loaded) {
02196
02197 jabber_loaded = ast_module_helper("", "res_jabber", 0, 0, 0, 0);
02198 free(jabber_loaded);
02199 if (!jabber_loaded) {
02200 ast_log(LOG_ERROR, "chan_gtalk.so depends upon res_jabber.so\n");
02201 return AST_MODULE_LOAD_DECLINE;
02202 }
02203 }
02204
02205 ASTOBJ_CONTAINER_INIT(>alk_list);
02206 if (!gtalk_load_config()) {
02207 ast_log(LOG_ERROR, "Unable to read config file %s. Not loading module.\n", GOOGLE_CONFIG);
02208 return 0;
02209 }
02210
02211 sched = sched_context_create();
02212 if (!sched) {
02213 ast_log(LOG_WARNING, "Unable to create schedule context\n");
02214 }
02215
02216 io = io_context_create();
02217 if (!io) {
02218 ast_log(LOG_WARNING, "Unable to create I/O context\n");
02219 }
02220
02221 ast_sockaddr_from_sin(&bindaddr_tmp, &bindaddr);
02222 if (gtalk_get_local_ip(&ourip_tmp)) {
02223 ast_log(LOG_WARNING, "Unable to get own IP address, Gtalk disabled\n");
02224 return 0;
02225 }
02226 __ourip.s_addr = htonl(ast_sockaddr_ipv4(&ourip_tmp));
02227
02228 ast_rtp_glue_register(>alk_rtp_glue);
02229 ast_cli_register_multiple(gtalk_cli, ARRAY_LEN(gtalk_cli));
02230
02231
02232 if (ast_channel_register(>alk_tech)) {
02233 ast_log(LOG_ERROR, "Unable to register channel class %s\n", gtalk_tech.type);
02234 return -1;
02235 }
02236 return 0;
02237 }
02238
02239
02240
02241 #if 0
02242 static int reload(void)
02243 {
02244 return 0;
02245 }
02246 #endif
02247
02248 static int unload_module(void)
02249 {
02250 struct gtalk_pvt *privates = NULL;
02251 ast_cli_unregister_multiple(gtalk_cli, ARRAY_LEN(gtalk_cli));
02252
02253 ast_channel_unregister(>alk_tech);
02254 ast_rtp_glue_unregister(>alk_rtp_glue);
02255
02256 if (!ast_mutex_lock(>alklock)) {
02257
02258 ASTOBJ_CONTAINER_TRAVERSE(>alk_list, 1, {
02259 ASTOBJ_WRLOCK(iterator);
02260 privates = iterator->p;
02261 while(privates) {
02262 if (privates->owner)
02263 ast_softhangup(privates->owner, AST_SOFTHANGUP_APPUNLOAD);
02264 privates = privates->next;
02265 }
02266 iterator->p = NULL;
02267 ASTOBJ_UNLOCK(iterator);
02268 });
02269 ast_mutex_unlock(>alklock);
02270 } else {
02271 ast_log(LOG_WARNING, "Unable to lock the monitor\n");
02272 return -1;
02273 }
02274 ASTOBJ_CONTAINER_DESTROYALL(>alk_list, gtalk_member_destroy);
02275 ASTOBJ_CONTAINER_DESTROY(>alk_list);
02276 return 0;
02277 }
02278
02279 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "Gtalk Channel Driver",
02280 .load = load_module,
02281 .unload = unload_module,
02282
02283 .load_pri = AST_MODPRI_CHANNEL_DRIVER,
02284 );