]> git.ipfire.org Git - thirdparty/freeswitch.git/commitdiff
mod_rayo: add <sendfax> component
authorChris Rienzo <chris@rienzo.com>
Fri, 8 Nov 2013 01:31:58 +0000 (20:31 -0500)
committerChris Rienzo <chris@rienzo.com>
Fri, 8 Nov 2013 02:41:45 +0000 (21:41 -0500)
src/mod/event_handlers/mod_rayo/Makefile
src/mod/event_handlers/mod_rayo/conf/autoload_configs/rayo.conf.xml
src/mod/event_handlers/mod_rayo/mod_rayo.c
src/mod/event_handlers/mod_rayo/rayo_components.c
src/mod/event_handlers/mod_rayo/rayo_components.h
src/mod/event_handlers/mod_rayo/rayo_elements.c
src/mod/event_handlers/mod_rayo/rayo_elements.h
src/mod/event_handlers/mod_rayo/rayo_fax_components.c [moved from src/mod/event_handlers/mod_rayo/rayo_receivefax_component.c with 60% similarity]

index bfb38dbd6c0257ec4a510472688d51812f49aa3b..a3bfc45d476b861d2b213dbedc9a3cdd2d1f92af 100644 (file)
@@ -11,10 +11,10 @@ LOCAL_OBJS= $(IKS_LA) \
        nlsml.o \
        rayo_components.o \
        rayo_elements.o \
+       rayo_fax_components.o \
        rayo_input_component.o \
        rayo_output_component.o \
        rayo_prompt_component.o \
-       rayo_receivefax_component.o \
        rayo_record_component.o \
        sasl.o \
        srgs.o \
@@ -24,11 +24,11 @@ LOCAL_SOURCES=      \
        nlsml.c \
        rayo_components.c \
        rayo_elements.c \
+       rayo_fax_components.c \
        rayo_input_component.c \
        rayo_output_component.c \
        rayo_prompt_component.c \
        rayo_record_component.c \
-       rayo_receivefax_component.c \
        sasl.c \
        srgs.c \
        xmpp_streams.c
index c8171569aff89cddc2c39a82f39fe74aa09ef458..3fdc772dafbff3b0936405f79bb38877df56994f 100644 (file)
                <param name="default-recognizer" value="pocketsphinx"/>
        </input>
 
-       <!-- receivefax component params -->
-       <receivefax>
+       <!-- send/receivefax component params -->
+       <fax>
                <!-- where to store incoming faxes -->
-               <param name="file-prefix" value="/tmp/"/>
-       </receivefax>
+               <param name="receivefax-file-prefix" value="/tmp/"/>
+       </fax>
 
        <!-- XMPP server domain -->
        <domain name="$${rayo_domain_name}" shared-secret="ClueCon">
index 51d1614751870f368575a47582fdbce2c1fd5666..f852d2c5f8bfac195cf94a57f447607087b8d5cb 100644 (file)
@@ -952,6 +952,7 @@ static void rayo_call_cleanup(struct rayo_actor *actor)
 
        iks_delete(revent);
        switch_event_destroy(&event);
+       switch_core_hash_destroy(&call->pcps);
 }
 
 /**
@@ -1130,13 +1131,23 @@ static struct rayo_call *_rayo_call_create(const char *uuid, const char *file, i
        return rayo_call_init(call, pool, uuid, file, line);
 }
 
+/**
+ * Mixer destructor
+ */
+static void rayo_mixer_cleanup(struct rayo_actor *actor)
+{
+       struct rayo_mixer *mixer = RAYO_MIXER(actor);
+       switch_core_hash_destroy(&mixer->members);
+       switch_core_hash_destroy(&mixer->subscribers);
+}
+
 /**
  * Initialize mixer
  */
 static struct rayo_mixer *rayo_mixer_init(struct rayo_mixer *mixer, switch_memory_pool_t *pool, const char *name, const char *file, int line)
 {
        char *mixer_jid = switch_mprintf("%s@%s", name, RAYO_JID(globals.server));
-       rayo_actor_init(RAYO_ACTOR(mixer), pool, RAT_MIXER, "", name, mixer_jid, NULL, rayo_mixer_send, file, line);
+       rayo_actor_init(RAYO_ACTOR(mixer), pool, RAT_MIXER, "", name, mixer_jid, rayo_mixer_cleanup, rayo_mixer_send, file, line);
        switch_core_hash_init(&mixer->members, pool);
        switch_core_hash_init(&mixer->subscribers, pool);
        switch_safe_free(mixer_jid);
@@ -1307,6 +1318,7 @@ static void rayo_peer_server_cleanup(struct rayo_actor *actor)
                RAYO_UNLOCK(client);
                RAYO_DESTROY(client);
        }
+       switch_core_hash_destroy(&rserver->clients);
        switch_mutex_unlock(globals.clients_mutex);
 }
 
index 2448e166a8573fa39c75edffcdb189506aaaabcc..7ad96d2a7d3b60d243059d0eacbcd90d994220c4 100644 (file)
@@ -227,7 +227,7 @@ switch_status_t rayo_components_load(switch_loadable_module_interface_t **module
                rayo_output_component_load(module_interface, pool, config_file) != SWITCH_STATUS_SUCCESS ||
                rayo_prompt_component_load(module_interface, pool, config_file) != SWITCH_STATUS_SUCCESS ||
                rayo_record_component_load(module_interface, pool, config_file) != SWITCH_STATUS_SUCCESS ||
-               rayo_receivefax_component_load(module_interface, pool, config_file) != SWITCH_STATUS_SUCCESS) {
+               rayo_fax_components_load(module_interface, pool, config_file) != SWITCH_STATUS_SUCCESS) {
                return SWITCH_STATUS_TERM;
        }
        return SWITCH_STATUS_SUCCESS;
@@ -242,7 +242,7 @@ switch_status_t rayo_components_shutdown(void)
        rayo_output_component_shutdown();
        rayo_prompt_component_shutdown();
        rayo_record_component_shutdown();
-       rayo_receivefax_component_shutdown();
+       rayo_fax_components_shutdown();
 
        return SWITCH_STATUS_SUCCESS;
 }
index 8d3909dd4df19f82fca8904de651c032580d98d9..5d1367d645fcd57fc11aacabb1c9f91796b46789 100644 (file)
@@ -61,14 +61,14 @@ extern switch_status_t rayo_input_component_load(switch_loadable_module_interfac
 extern switch_status_t rayo_output_component_load(switch_loadable_module_interface_t **module_interface, switch_memory_pool_t *pool, const char *config_file);
 extern switch_status_t rayo_prompt_component_load(switch_loadable_module_interface_t **module_interface, switch_memory_pool_t *pool, const char *config_file);
 extern switch_status_t rayo_record_component_load(switch_loadable_module_interface_t **module_interface, switch_memory_pool_t *pool, const char *config_file);
-extern switch_status_t rayo_receivefax_component_load(switch_loadable_module_interface_t **module_interface, switch_memory_pool_t *pool, const char *config_file);
+extern switch_status_t rayo_fax_components_load(switch_loadable_module_interface_t **module_interface, switch_memory_pool_t *pool, const char *config_file);
 
 extern switch_status_t rayo_components_shutdown(void);
 extern switch_status_t rayo_input_component_shutdown(void);
 extern switch_status_t rayo_output_component_shutdown(void);
 extern switch_status_t rayo_prompt_component_shutdown(void);
 extern switch_status_t rayo_record_component_shutdown(void);
-extern switch_status_t rayo_receivefax_component_shutdown(void);
+extern switch_status_t rayo_fax_components_shutdown(void);
 
 extern void rayo_component_send_start(struct rayo_component *component, iks *iq);
 extern void rayo_component_send_iq_error(struct rayo_component *component, iks *iq, const char *error_name, const char *error_type);
index a6e4f09140201c16a979f847cb0247909053242e..78209c3bd65f7bac6a11ef89628f06963cff3b42 100644 (file)
@@ -49,6 +49,17 @@ ELEMENT(RAYO_INPUT)
        ATTRIB(start-timers, true, bool)
 ELEMENT_END
 
+/**
+ * <join> command validation
+ */
+ELEMENT(RAYO_JOIN)
+       ATTRIB(xmlns,, any)
+       STRING_ATTRIB(direction, duplex, "send,recv,duplex")
+       STRING_ATTRIB(media, bridge, "bridge,direct")
+       ATTRIB(call-uri,, any)
+       ATTRIB(mixer-name,, any)
+ELEMENT_END
+
 /**
  * <output> component validation
  */
@@ -80,6 +91,13 @@ ELEMENT(RAYO_PROMPT)
        ATTRIB(barge-in, true, bool)
 ELEMENT_END
 
+/**
+ * <receivefax> command validation
+ */
+ELEMENT(RAYO_RECEIVEFAX)
+       ATTRIB(xmlns,, any)
+ELEMENT_END
+
 /**
  * <record> component validation
  */
@@ -97,22 +115,12 @@ ELEMENT(RAYO_RECORD)
 ELEMENT_END
 
 /**
- * <join> command validation
+ * <sendfax> command validation
  */
-ELEMENT(RAYO_JOIN)
+ELEMENT(RAYO_SENDFAX)
        ATTRIB(xmlns,, any)
-       STRING_ATTRIB(direction, duplex, "send,recv,duplex")
-       STRING_ATTRIB(media, bridge, "bridge,direct")
-       ATTRIB(call-uri,, any)
-       ATTRIB(mixer-name,, any)
 ELEMENT_END
 
-/**
- * <receivefax> command validation
- */
-ELEMENT(RAYO_RECEIVEFAX)
-       ATTRIB(xmlns,, any)
-ELEMENT_END
 
 /* For Emacs:
  * Local Variables:
index 4c061dc35edefaadfc1821a27c4d68582a6f1ac1..e76809cc9e10c0c8b7490505556d16f7505a4870 100644 (file)
 #include "iks_helpers.h"
 
 ELEMENT_DECL(RAYO_INPUT)
+ELEMENT_DECL(RAYO_JOIN)
 ELEMENT_DECL(RAYO_OUTPUT)
 ELEMENT_DECL(RAYO_OUTPUT_SEEK)
 ELEMENT_DECL(RAYO_PROMPT)
-ELEMENT_DECL(RAYO_RECORD)
-ELEMENT_DECL(RAYO_JOIN)
 ELEMENT_DECL(RAYO_RECEIVEFAX)
+ELEMENT_DECL(RAYO_RECORD)
+ELEMENT_DECL(RAYO_SENDFAX)
 
 #endif
 
similarity index 60%
rename from src/mod/event_handlers/mod_rayo/rayo_receivefax_component.c
rename to src/mod/event_handlers/mod_rayo/rayo_fax_components.c
index 75528f8b52bfb2653e7058cc570f49c651166d2d..9cd48e2c2c811aa1fc86232f605a9a1021e0faad 100644 (file)
@@ -23,7 +23,7 @@
  * Contributor(s):
  * Chris Rienzo <chris.rienzo@grasshopper.com>
  *
- * rayo_receivefax_component.c -- Rayo receivefax component implementation
+ * rayo_fax_components.c -- Rayo receivefax and sendfax components implementation
  *
  */
 #include "rayo_components.h"
@@ -36,23 +36,197 @@ static struct {
        const char *file_prefix;
 } globals;
 
-struct receivefax_component {
+struct fax_component {
        /** component base class */
        struct rayo_component base;
+       /** Flag to stop fax */
+       int stop;
+};
+
+#define FAX_COMPONENT(x) ((struct fax_component *)x)
+
+struct receivefax_component {
+       /** fax component base class */
+       struct fax_component base;
        /** true if HTTP PUT needs to be done after fax is received */
        int http_put_after_receive;
        /** fax stored on local filesystem */
        const char *local_filename;
        /** fax final target (may be same as local filename) */
        const char *filename;
-       /** Flag to stop fax */
-       int stop;
 };
 
-#define RECEIVEFAX_FINISH "finish", RAYO_FAX_COMPLETE_NS
-
 #define RECEIVEFAX_COMPONENT(x) ((struct receivefax_component *)x)
 
+#define FAX_FINISH "finish", RAYO_FAX_COMPLETE_NS
+
+/**
+ * Start execution of call sendfax component
+ * @param call the call to send fax to
+ * @param msg the original request
+ * @param session_data the call's session
+ */
+static iks *start_sendfax_component(struct rayo_actor *call, struct rayo_message *msg, void *session_data)
+{
+       iks *iq = msg->payload;
+       switch_core_session_t *session = (switch_core_session_t *)session_data;
+       struct fax_component *sendfax_component = NULL;
+       iks *sendfax = iks_find(iq, "sendfax");
+       iks *response = NULL;
+       switch_event_t *execute_event = NULL;
+       switch_channel_t *channel = switch_core_session_get_channel(session);
+       switch_memory_pool_t *pool;
+       iks *document;
+       const char *fax_document;
+       const char *fax_header;
+       const char *fax_identity;
+       const char *pages;
+
+       /* validate attributes */
+       if (!VALIDATE_RAYO_SENDFAX(sendfax)) {
+               return iks_new_error(iq, STANZA_ERROR_BAD_REQUEST);
+       }
+
+       /* fax is only allowed if the call is not currently joined */
+       if (rayo_call_is_joined(RAYO_CALL(call))) {
+               return iks_new_error_detailed(iq, STANZA_ERROR_UNEXPECTED_REQUEST, "can't send fax on a joined call");
+       }
+
+       if (!rayo_call_set_faxing(RAYO_CALL(call), 1)) {
+               return iks_new_error_detailed(iq, STANZA_ERROR_UNEXPECTED_REQUEST, "fax already in progress");
+       }
+
+       /* get fax document */
+       document = iks_find(sendfax, "document");
+       if (!document) {
+               return iks_new_error_detailed(iq, STANZA_ERROR_BAD_REQUEST, "missing document");
+       }
+       fax_document = iks_find_attrib_soft(document, "url");
+       if (zstr(fax_document)) {
+               return iks_new_error_detailed(iq, STANZA_ERROR_BAD_REQUEST, "missing document url");
+       }
+
+       /* is valid URL type? */
+       if (!strncasecmp(fax_document, "http://", 7) || strncasecmp(fax_document, "https://", 8)) {
+               switch_stream_handle_t stream = { 0 };
+               SWITCH_STANDARD_STREAM(stream);
+               /* need to fetch document from server... */
+               switch_api_execute("http_get", fax_document, session, &stream);
+               if (!zstr(stream.data) && !strncasecmp(fax_document, SWITCH_PATH_SEPARATOR, sizeof(SWITCH_PATH_SEPARATOR) - 1)) {
+                       fax_document = switch_core_session_strdup(session, stream.data);
+               } else {
+                       switch_safe_free(stream.data);
+                       return iks_new_error_detailed(iq, STANZA_ERROR_INTERNAL_SERVER_ERROR, "failed to fetch document");
+               }
+               switch_safe_free(stream.data);
+       } else if (!strncasecmp(fax_document, "file://", 7)) {
+               fax_document = fax_document + 7;
+               if (zstr(fax_document)) {
+                       return iks_new_error_detailed(iq, STANZA_ERROR_BAD_REQUEST, "invalid file:// url");
+               }
+       } else if (strncasecmp(fax_document, SWITCH_PATH_SEPARATOR, sizeof(SWITCH_PATH_SEPARATOR) - 1)) {
+               return iks_new_error_detailed(iq, STANZA_ERROR_BAD_REQUEST, "unsupported url type");
+       }
+
+       /* does document exist? */
+       if (!switch_file_exists(fax_document, pool)) {
+               return iks_new_error_detailed_printf(iq, STANZA_ERROR_BAD_REQUEST, "file not found: %s", fax_document);
+       }
+
+       /* get fax identity and header */
+       fax_identity = iks_find_attrib_soft(document, "identity");
+       if (!zstr(fax_identity)) {
+               switch_channel_set_variable(channel, "fax_ident", fax_identity);
+       } else {
+               switch_channel_set_variable(channel, "fax_ident", NULL);
+       }
+       fax_header = iks_find_attrib_soft(document, "header");
+       if (!zstr(fax_header)) {
+               switch_channel_set_variable(channel, "fax_header", fax_header);
+       } else {
+               switch_channel_set_variable(channel, "fax_header", NULL);
+       }
+
+       /* get pages to send */
+       pages = iks_find_attrib_soft(document, "pages");
+       if (!zstr(pages)) {
+               if (switch_regex_match(pages, "[1-9][0-9]*(-[1-9][0-9]*)?") == SWITCH_STATUS_FALSE) {
+                       return iks_new_error_detailed(iq, STANZA_ERROR_BAD_REQUEST, "invalid pages value");
+               } else {
+                       int start = 0;
+                       int end = 0;
+                       char *pages_dup = switch_core_session_strdup(session, pages);
+                       char *sep = strchr(pages_dup, '-');
+                       if (sep) {
+                               *sep = '\0';
+                               sep++;
+                               end = atoi(sep);
+                       }
+                       start = atoi(pages_dup);
+                       if (end && end < start) {
+                               return iks_new_error_detailed(iq, STANZA_ERROR_BAD_REQUEST, "invalid pages value");
+                       }
+                       switch_channel_set_variable(channel, "fax_start_page", pages_dup);
+                       switch_channel_set_variable(channel, "fax_end_page", sep);
+               }
+       } else {
+               switch_channel_set_variable(channel, "fax_start_page", NULL);
+               switch_channel_set_variable(channel, "fax_end_page", NULL);
+       }
+
+       /* create sendfax component */
+       switch_core_new_memory_pool(&pool);
+       sendfax_component = switch_core_alloc(pool, sizeof(*sendfax_component));
+       rayo_component_init((struct rayo_component *)sendfax_component, pool, RAT_CALL_COMPONENT, "sendfax", NULL, call, iks_find_attrib(iq, "from"));
+
+       /* add channel variable so that fax component can be located from fax events */
+       switch_channel_set_variable(channel, "rayo_fax_jid", RAYO_JID(sendfax_component));
+
+       /* clear fax result variables */
+       switch_channel_set_variable(channel, "fax_success", NULL);
+       switch_channel_set_variable(channel, "fax_result_code", NULL);
+       switch_channel_set_variable(channel, "fax_result_text", NULL);
+       switch_channel_set_variable(channel, "fax_document_transferred_pages", NULL);
+       switch_channel_set_variable(channel, "fax_document_total_pages", NULL);
+       switch_channel_set_variable(channel, "fax_image_resolution", NULL);
+       switch_channel_set_variable(channel, "fax_image_size", NULL);
+       switch_channel_set_variable(channel, "fax_bad_rows", NULL);
+       switch_channel_set_variable(channel, "fax_transfer_rate", NULL);
+       switch_channel_set_variable(channel, "fax_ecm_used", NULL);
+       switch_channel_set_variable(channel, "fax_local_station_id", NULL);
+       switch_channel_set_variable(channel, "fax_remote_station_id", NULL);
+
+       /* clear fax interrupt variable */
+       switch_channel_set_variable(switch_core_session_get_channel(session), "rayo_read_frame_interrupt", NULL);
+
+       /* execute txfax APP */
+       if (switch_event_create(&execute_event, SWITCH_EVENT_COMMAND) == SWITCH_STATUS_SUCCESS) {
+               switch_event_add_header_string(execute_event, SWITCH_STACK_BOTTOM, "call-command", "execute");
+               switch_event_add_header_string(execute_event, SWITCH_STACK_BOTTOM, "execute-app-name", "txfax");
+               switch_event_add_header_string(execute_event, SWITCH_STACK_BOTTOM, "execute-app-arg", fax_document);
+               switch_event_add_header_string(execute_event, SWITCH_STACK_BOTTOM, "event-lock", "true");
+               if (!switch_channel_test_flag(channel, CF_PROXY_MODE)) {
+                       switch_channel_set_flag(channel, CF_BLOCK_BROADCAST_UNTIL_MEDIA);
+               }
+
+               if (switch_core_session_queue_private_event(session, &execute_event, SWITCH_FALSE) != SWITCH_STATUS_SUCCESS) {
+                       response = iks_new_error_detailed(iq, STANZA_ERROR_INTERNAL_SERVER_ERROR, "failed to txfax (queue event failed)");
+                       if (execute_event) {
+                               switch_event_destroy(&execute_event);
+                       }
+                       RAYO_UNLOCK(sendfax_component);
+               } else {
+                       /* component starting... */
+                       rayo_component_send_start(RAYO_COMPONENT(sendfax_component), iq);
+               }
+       } else {
+               response = iks_new_error_detailed(iq, STANZA_ERROR_INTERNAL_SERVER_ERROR, "failed to create txfax event");
+               RAYO_UNLOCK(sendfax_component);
+       }
+
+       return response;
+}
+
 /**
  * Start execution of call receivefax component
  * @param call the call to receive fax from
@@ -153,9 +327,9 @@ static iks *start_receivefax_component(struct rayo_actor *call, struct rayo_mess
 }
 
 /**
- * Stop execution of receivefax component
+ * Stop execution of fax component
  */
-static iks *stop_receivefax_component(struct rayo_actor *component, struct rayo_message *msg, void *data)
+static iks *stop_fax_component(struct rayo_actor *component, struct rayo_message *msg, void *data)
 {
        iks *iq = msg->payload;
        switch_core_session_t *session = switch_core_session_locate(RAYO_COMPONENT(component)->parent->id);
@@ -164,7 +338,7 @@ static iks *stop_receivefax_component(struct rayo_actor *component, struct rayo_
                switch_channel_set_variable(switch_core_session_get_channel(session), "rayo_read_frame_interrupt", RAYO_JID(component));
                switch_core_session_rwunlock(session);
        }
-       RECEIVEFAX_COMPONENT(component)->stop = 1;
+       FAX_COMPONENT(component)->stop = 1;
        return iks_new_iq_result(iq);
 }
 
@@ -190,18 +364,19 @@ static void insert_fax_metadata(switch_event_t *event, const char *name, iks *re
 }
 
 /**
- * Handle rxfax completion event from FreeSWITCH core
+ * Handle fax completion event from FreeSWITCH core
  * @param event received from FreeSWITCH core.  It will be destroyed by the core after this function returns.
  */
 static void on_execute_complete_event(switch_event_t *event)
 {
        const char *application = switch_event_get_header(event, "Application");
-       if (!zstr(application) && !strcmp(application, "rxfax")) {
+       
+       if (!zstr(application) && (!strcmp(application, "rxfax") || !strcmp(application, "txfax"))) {
+               int is_rxfax = !strcmp(application, "rxfax");
                const char *uuid = switch_event_get_header(event, "Unique-ID");
                const char *fax_jid = switch_event_get_header(event, "variable_rayo_fax_jid");
                struct rayo_actor *component;
                if (!zstr(fax_jid) && (component = RAYO_LOCATE(fax_jid))) {
-                       const char *url = RECEIVEFAX_COMPONENT(component)->filename;
                        iks *result;
                        iks *complete;
                        iks *fax;
@@ -216,11 +391,8 @@ static void on_execute_complete_event(switch_event_t *event)
                                switch_core_session_rwunlock(session);
                        }
 
-                       /* flag faxing as done */
-                       rayo_call_set_faxing(RAYO_CALL(RAYO_COMPONENT(component)->parent), 0);
-
-                       /* transfer HTTP document and delete local copy */
-                       if (RECEIVEFAX_COMPONENT(component)->http_put_after_receive && switch_file_exists(RECEIVEFAX_COMPONENT(component)->local_filename, RAYO_POOL(component))) {
+                       /* RX only: transfer HTTP document and delete local copy */
+                       if (is_rxfax && RECEIVEFAX_COMPONENT(component)->http_put_after_receive && switch_file_exists(RECEIVEFAX_COMPONENT(component)->local_filename, RAYO_POOL(component))) {
                                switch_stream_handle_t stream = { 0 };
                                SWITCH_STANDARD_STREAM(stream);
                                switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "%s PUT fax to %s\n", RAYO_JID(component), RECEIVEFAX_COMPONENT(component)->filename);
@@ -237,16 +409,16 @@ static void on_execute_complete_event(switch_event_t *event)
 
                        /* successful fax? */
                        if (have_fax_document && switch_true(switch_event_get_header(event, "variable_fax_success"))) {
-                               result = rayo_component_create_complete_event(RAYO_COMPONENT(component), RECEIVEFAX_FINISH);
-                       } else if (have_fax_document && RECEIVEFAX_COMPONENT(component)->stop) {
+                               result = rayo_component_create_complete_event(RAYO_COMPONENT(component), FAX_FINISH);
+                       } else if (have_fax_document && FAX_COMPONENT(component)->stop)  {
                                result = rayo_component_create_complete_event(RAYO_COMPONENT(component), COMPONENT_COMPLETE_STOP);
                        } else {
                                result = rayo_component_create_complete_event(RAYO_COMPONENT(component), COMPONENT_COMPLETE_ERROR);
                        }
                        complete = iks_find(result, "complete");
 
-                       /* add fax document information */
-                       if (have_fax_document) {
+                       /* RX only: add fax document information */
+                       if (is_rxfax && have_fax_document) {
                                const char *pages = switch_event_get_header(event, "variable_fax_document_transferred_pages");
                                if (!zstr(pages) && switch_is_number(pages) && atoi(pages) > 0) {
                                        const char *resolution = switch_event_get_header(event, "variable_fax_file_image_resolution");
@@ -256,10 +428,10 @@ static void on_execute_complete_event(switch_event_t *event)
                                        iks_insert_attrib(fax, "xmlns", RAYO_FAX_COMPLETE_NS);
 
                                        if (RECEIVEFAX_COMPONENT(component)->http_put_after_receive) {
-                                               iks_insert_attrib(fax, "url", url);
+                                               iks_insert_attrib(fax, "url", RECEIVEFAX_COMPONENT(component)->filename);
                                        } else {
                                                /* convert absolute path to file:// URI */
-                                               iks_insert_attrib_printf(fax, "url", "file://%s", url);
+                                               iks_insert_attrib_printf(fax, "url", "file://%s", RECEIVEFAX_COMPONENT(component)->filename);
                                        }
 
                                        if (!zstr(resolution)) {
@@ -286,6 +458,9 @@ static void on_execute_complete_event(switch_event_t *event)
                        insert_fax_metadata(event, "fax_local_station_id", complete);
                        insert_fax_metadata(event, "fax_remote_station_id", complete);
 
+                       /* flag faxing as done */
+                       rayo_call_set_faxing(RAYO_CALL(RAYO_COMPONENT(component)->parent), 0);
+
                        rayo_component_send_complete_event(RAYO_COMPONENT(component), result);
 
                        RAYO_UNLOCK(component);
@@ -313,14 +488,14 @@ static switch_status_t do_config(switch_memory_pool_t *pool, const char *config_
 
        /* get params */
        {
-               switch_xml_t settings = switch_xml_child(cfg, "receivefax");
+               switch_xml_t settings = switch_xml_child(cfg, "fax");
                if (settings) {
                        switch_xml_t param;
                        for (param = switch_xml_child(settings, "param"); param; param = param->next) {
                                const char *var = switch_xml_attr_soft(param, "name");
                                const char *val = switch_xml_attr_soft(param, "value");
                                switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "param: %s = %s\n", var, val);
-                               if (!strcasecmp(var, "file-prefix")) {
+                               if (!strcasecmp(var, "receivefax-file-prefix")) {
                                        if (!zstr(val)) {
                                                globals.file_prefix = switch_core_strdup(pool, val);
                                        }
@@ -331,7 +506,7 @@ static switch_status_t do_config(switch_memory_pool_t *pool, const char *config_
                }
        }
 
-       switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "receivefax file-prefix = %s\n", globals.file_prefix);
+       switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "receivefax-file-prefix = %s\n", globals.file_prefix);
 
        switch_xml_free(xml);
 
@@ -339,31 +514,34 @@ static switch_status_t do_config(switch_memory_pool_t *pool, const char *config_
 }
 
 /**
- * Initialize receivefax component
+ * Initialize fax components
  * @param module_interface
  * @param pool memory pool to allocate from
  * @param config_file to use
  * @return SWITCH_STATUS_SUCCESS if successful
  */
-switch_status_t rayo_receivefax_component_load(switch_loadable_module_interface_t **module_interface, switch_memory_pool_t *pool, const char *config_file)
+switch_status_t rayo_fax_components_load(switch_loadable_module_interface_t **module_interface, switch_memory_pool_t *pool, const char *config_file)
 {
        if (do_config(pool, config_file) != SWITCH_STATUS_SUCCESS) {
                return SWITCH_STATUS_TERM;
        }
 
-       switch_event_bind("rayo_receivefax_component", SWITCH_EVENT_CHANNEL_EXECUTE_COMPLETE, NULL, on_execute_complete_event, NULL);
+       switch_event_bind("rayo_fax_components", SWITCH_EVENT_CHANNEL_EXECUTE_COMPLETE, NULL, on_execute_complete_event, NULL);
 
        rayo_actor_command_handler_add(RAT_CALL, "", "set:"RAYO_FAX_NS":receivefax", start_receivefax_component);
-       rayo_actor_command_handler_add(RAT_CALL_COMPONENT, "receivefax", "set:"RAYO_EXT_NS":stop", stop_receivefax_component);
+       rayo_actor_command_handler_add(RAT_CALL_COMPONENT, "receivefax", "set:"RAYO_EXT_NS":stop", stop_fax_component);
+
+       rayo_actor_command_handler_add(RAT_CALL, "", "set:"RAYO_FAX_NS":sendfax", start_sendfax_component);
+       rayo_actor_command_handler_add(RAT_CALL_COMPONENT, "sendfax", "set:"RAYO_EXT_NS":stop", stop_fax_component);
 
        return SWITCH_STATUS_SUCCESS;
 }
 
 /**
- * Shutdown receivefax component
+ * Shutdown fax components
  * @return SWITCH_STATUS_SUCCESS if successful
  */
-switch_status_t rayo_receivefax_component_shutdown(void)
+switch_status_t rayo_fax_components_shutdown(void)
 {
        switch_event_unbind_callback(on_execute_complete_event);
 
@@ -380,3 +558,4 @@ switch_status_t rayo_receivefax_component_shutdown(void)
  * For VIM:
  * vim:set softtabstop=4 shiftwidth=4 tabstop=4 noet
  */
+