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