/* * $Id: xmpp.c 1435 2006-12-20 09:44:19Z miconda $ * * XMPP Module * This file is part of openser, a free SIP server. * * Copyright (C) 2006 Voice Sistem S.R.L. * * openser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * openser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Author: Andreea Spirea * */ /* * An inbound SIP message: * from sip:user1@domain1 to sip:user2*domain2@gateway_domain * is translated to an XMPP message: * from user1*domain1@xmpp_domain to user2@domain2 * * An inbound XMPP message: * from user1@domain1 to user2*domain2@xmpp_domain * is translated to a SIP message: * from sip:user1*domain1@gateway_domain to sip:user2@domain2 * * Where '*' is the domain_separator, and gateway_domain and * xmpp_domain are defined below. */ /* * 2-way dialback sequence with xmppd2: * * Originating server (us) Receiving server (them) Authoritative server (us) * ----------------------- ----------------------- ------------------------- * | | | * | establish connection | | * |------------------------------>| | * | send stream header | | * |------------------------------>| | * | send stream header | | * |<------------------------------| | * | send db:result request | | * |------------------------------>| | * | establish connection | * |------------------------------>| * | send stream header | * |------------------------------>| * | send stream header | * |<------------------------------| * | send db:result request | * |------------------------------>| * | send db:verify request | * |------------------------------>| * | send db:verify response | * |<------------------------------| * | send db:result response | * |------------------------------>| * | send db:verify request | * |<------------------------------| * | send db:verify response | * |------------------------------>| * | send db:result response | * |<------------------------------| * : : : * : : : * | outgoing | : * |------------------------------>| : * | incoming | * |------------------------------>| */ #include #include #include #include #include "../../sr_module.h" #include "../../mem/mem.h" #include "../../data_lump_rpl.h" #include "../../parser/msg_parser.h" #include "../../parser/parse_content.h" #include "../../parser/parse_from.h" #include "../tm/tm_load.h" #include "xode.h" #include "xmpp.h" #include "network.h" #include "xmpp_api.h" #include /* XXX hack */ #define DB_KEY "this-be-a-random-key" MODULE_VERSION struct tm_binds tmb; static int mod_init(void); static int child_init(int rank); static int cmd_send_message(struct sip_msg* msg, char* _foo, char* _bar); void destroy(void); static int pipe_fd = -1; /* * Configuration */ char *backend = "component"; char *domain_sep_str = NULL; char domain_separator = '*'; char *gateway_domain = "sip2xmpp.example.net"; char *xmpp_domain = "xmpp2sip.example.net"; char *xmpp_host = "xmpp.example.com"; int xmpp_port = 0; char *xmpp_password = "secret"; #define DEFAULT_COMPONENT_PORT 5347 #define DEFAULT_SERVER_PORT 5269 /* * Exported functions */ static cmd_export_t cmds[] = { {"xmpp_send_message", (cmd_function)cmd_send_message, 0, 0, REQUEST_ROUTE}, {"bind_xmpp", (cmd_function)bind_xmpp, 0, 0, 0}, {0, 0, 0, 0} }; /* * Exported parameters */ static param_export_t params[] = { { "backend", STR_PARAM, &backend }, { "domain_separator", STR_PARAM, &domain_sep_str }, { "gateway_domain", STR_PARAM, &gateway_domain }, { "xmpp_domain", STR_PARAM, &xmpp_domain }, { "xmpp_host", STR_PARAM, &xmpp_host }, { "xmpp_port", INT_PARAM, &xmpp_port }, { "xmpp_password", STR_PARAM, &xmpp_password }, {0, 0, 0} }; /* * Module description */ struct module_exports exports = { "xmpp", /* Module name */ DEFAULT_DLFLAGS, /* dlopen flags */ cmds, /* Exported functions */ params, /* Exported parameters */ 0, /* exported statistics */ 0, /* exported MI functions */ 0, /* exported pseudo-variables */ mod_init, /* Initialization function */ 0, /* Response function */ destroy, /* Destroy function */ child_init, /* Child init function */ }; /* * initialize module */ static int mod_init(void) { DBG("xmpp: mod_init\n"); if (load_tm_api(&tmb)) { LOG(L_ERR, "xmpp: cannot load tm API\n"); return -1; } if (strcmp(backend, "component") && strcmp(backend, "server")) { LOG(L_ERR, "xmpp: invalid backend '%s'\n", backend); return -1; } if (!xmpp_port) { if (!strcmp(backend, "component")) xmpp_port = DEFAULT_COMPONENT_PORT; else if (!strcmp(backend, "server")) xmpp_port = DEFAULT_SERVER_PORT; } /* fix up the domain separator -- we only need 1 char */ if (domain_sep_str && *domain_sep_str) domain_separator = *domain_sep_str; return 0; } static int child_init(int rank) { int pid; int fds[2]; /* if this blasted server had a decent I/O loop, we'd * just add our socket to it and connect(). */ DBG("xmpp: child_init: initializing child <%d>\n", rank); if (rank == 1) { if (pipe(fds) < 0) { LOG(L_ERR, "xmpp: cannot pipe()\n"); return -1; } if ((pid = fork()) < 0) { LOG(L_ERR, "xmpp: cannot fork()\n"); return -1; } if (pid) { /* parent */ close(fds[0]); pipe_fd = fds[1]; } else { /* child */ close(fds[1]); DBG("xmpp: started child connection process\n"); if (!strcmp(backend, "component")) xmpp_component_child_process(fds[0]); else if (!strcmp(backend, "server")) xmpp_server_child_process(fds[0]); _exit(127); } } return 0; } /* to be done */ void destroy(void) { DBG("xmpp: destroy: cleaning up...\n"); } /*********************************************************************************/ /* Relay a MESSAGE to a SIP client */ int xmpp_send_sip_msg(char *from, char *to, char *msg) { str msg_type = { "MESSAGE", 7 }; str hdr, fromstr, tostr, msgstr; char buf[512]; hdr.s = buf; hdr.len = snprintf(buf, sizeof(buf), "Content-type: text/plain" CRLF "Contact: %s" CRLF, from); fromstr.s = from; fromstr.len = strlen(from); tostr.s = to; tostr.len = strlen(to); msgstr.s = msg; msgstr.len = strlen(msg); return tmb.t_request(&msg_type, 0, &tostr, &fromstr, &hdr, &msgstr, 0, 0); } /*********************************************************************************/ static char *shm_strdup(str *src) { char *res; if (!src || !src->s) return NULL; if (!(res = (char *) shm_malloc(src->len + 1))) return NULL; strncpy(res, src->s, src->len); res[src->len] = 0; return res; } void xmpp_free_pipe_cmd(struct xmpp_pipe_cmd *cmd) { if (cmd->from) shm_free(cmd->from); if (cmd->to) shm_free(cmd->to); if (cmd->body) shm_free(cmd->body); if (cmd->id) shm_free(cmd->id); shm_free(cmd); } static int xmpp_send_pipe_cmd(enum xmpp_pipe_cmd_type type, str *from, str *to, str *body, str *id) { struct xmpp_pipe_cmd *cmd; /* todo: make shm allocation for one big chunk to include all fields */ cmd = (struct xmpp_pipe_cmd *) shm_malloc(sizeof(struct xmpp_pipe_cmd)); memset(cmd, 0, sizeof(struct xmpp_pipe_cmd)); cmd->type = type; cmd->from = shm_strdup(from); cmd->to = shm_strdup(to); cmd->body = shm_strdup(body); cmd->id = shm_strdup(id); if (write(pipe_fd, &cmd, sizeof(cmd)) != sizeof(cmd)) { LOG(L_ERR, "xmpp: unable to write to command pipe: %s\n", strerror(errno)); xmpp_free_pipe_cmd(cmd); return -1; } return 0; } static int cmd_send_message(struct sip_msg* msg, char* _foo, char* _bar) { str body, from_uri, dst, tagid; int mime; DBG("xmpp: cmd_send_message\n"); /* extract body */ if (!(body.s = get_body(msg))) { LOG(L_ERR, "xmpp: cannot extract body\n"); return -1; } if (!msg->content_length) { LOG(L_ERR, "xmpp: no content-length found\n"); return -1; } body.len = get_content_length(msg); if ((mime = parse_content_type_hdr(msg)) < 1) { LOG(L_ERR, "xmpp: cannot parse content-type\n"); return -1; } if (mime != (TYPE_TEXT << 16) + SUBTYPE_PLAIN && mime != (TYPE_MESSAGE << 16) + SUBTYPE_CPIM) { LOG(L_ERR, "xmpp: invalid content-type 0x%x\n", mime); return -1; } /* extract sender */ if (parse_headers(msg, HDR_TO_F | HDR_FROM_F, 0) == -1 || !msg->to || !msg->from) { LOG(L_ERR, "xmpp: no To/From headers\n"); return -1; } if (parse_from_header(msg) < 0 || !msg->from->parsed) { LOG(L_ERR, "xmpp: cannot parse From header\n"); return -1; } from_uri = ((struct to_body *) msg->from->parsed)->uri; tagid = ((struct to_body *) msg->from->parsed)->tag_value; DBG("xmpp: message from <%.*s>\n", from_uri.len, from_uri.s); /* extract recipient */ dst.len = 0; if (msg->new_uri.len > 0) { DBG("xmpp: using new URI as destination\n"); dst = msg->new_uri; } else if (msg->first_line.u.request.uri.s && msg->first_line.u.request.uri.len > 0) { DBG("xmpp: using R-URI as destination\n"); dst = msg->first_line.u.request.uri; } else if (msg->to->parsed) { DBG("xmpp: using TO-URI as destination\n"); dst = ((struct to_body *) msg->to->parsed)->uri; } else { LOG(L_ERR, "xmpp: cannot find a valid destination\n"); return -1; } if (!xmpp_send_pipe_cmd(XMPP_PIPE_SEND_MESSAGE, &from_uri, &dst, &body, &tagid)) return 1; return -1; } /** * */ int xmpp_send_xpacket(str *from, str *to, str *msg, str *id) { if(from==NULL || to==NULL || msg==NULL || id==NULL) return -1; return xmpp_send_pipe_cmd(XMPP_PIPE_SEND_PACKET, from, to, msg, id); } /** * */ int xmpp_send_xmessage(str *from, str *to, str *msg, str *id) { if(from==NULL || to==NULL || msg==NULL || id==NULL) return -1; return xmpp_send_pipe_cmd(XMPP_PIPE_SEND_MESSAGE, from, to, msg, id); } /** * */ int xmpp_send_xsubscribe(str *from, str *to, str *msg, str *id) { if(from==NULL || to==NULL || msg==NULL || id==NULL) return -1; return xmpp_send_pipe_cmd(XMPP_PIPE_SEND_PSUBSCRIBE, from, to, msg, id); } /** * */ int xmpp_send_xnotify(str *from, str *to, str *msg, str *id) { if(from==NULL || to==NULL || msg==NULL || id==NULL) return -1; return xmpp_send_pipe_cmd(XMPP_PIPE_SEND_PNOTIFY, from, to, msg, id); }