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