Implemement support for handling multiple documents when sending.
authorMatthew Nicholson <mnicholson@digium.com>
Fri, 25 Jun 2010 19:42:54 +0000 (19:42 +0000)
committerMatthew Nicholson <mnicholson@digium.com>
Fri, 25 Jun 2010 19:42:54 +0000 (19:42 +0000)
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@272558 65c4cc65-6c06-0410-ace0-fbb531ad65f3

include/asterisk/res_fax.h
res/res_fax.c

index 8e8cb3b..a6b3a21 100644 (file)
 /*! \brief capabilities for res_fax to locate a fax technology module */
 enum ast_fax_capabilities {
        /*! SendFax is supported */
-       AST_FAX_TECH_SEND    = (1 << 0),
+       AST_FAX_TECH_SEND      = (1 << 0),
        /*! ReceiveFax is supported */
-       AST_FAX_TECH_RECEIVE = (1 << 1),
+       AST_FAX_TECH_RECEIVE   = (1 << 1),
        /*! Audio FAX session supported */
-       AST_FAX_TECH_AUDIO   = (1 << 2),
+       AST_FAX_TECH_AUDIO     = (1 << 2),
        /*! T.38 FAX session supported */
-       AST_FAX_TECH_T38     = (1 << 3),
+       AST_FAX_TECH_T38       = (1 << 3),
+       /*! sending mulitple documents supported */
+       AST_FAX_TECH_MULTI_DOC = (1 << 4),
 };
 
 /*! \brief fax modem capabilities */
index bcafa94..cae9660 100644 (file)
@@ -75,8 +75,8 @@ static const char descrip_receivefax[] = "ReceiveFAX(filename[,options]):\n"
 
 static const char app_sendfax[] = "SendFAX";
 static const char synopsis_sendfax[] = "Sends a specified TIFF/F file as a FAX.";
-static const char descrip_sendfax[] = "SendFAX(filename[,options]):\n"
- " The SendFAX() application sends the specified TIFF/F file as a FAX.\n"
+static const char descrip_sendfax[] = "SendFAX(filename[&filename[&filename]][,options]):\n"
+ " The SendFAX() application sends the specified TIFF/F file(s) as a FAX.\n"
  " The application arguments are:\n"
  "    'd' - enables FAX debugging\n"
  "    'f' - allow audio fallback FAX transfer on T.38 capable channels\n"
@@ -600,9 +600,62 @@ static void get_manager_event_info(struct ast_channel *chan, struct manager_even
        pbx_substitute_variables_helper(chan, "${CALLERID(num)}", info->cid, sizeof(info->cid));
 }
 
+
+/* \brief Generate a string of filenames using the given prefix and separator.
+ * \param details the fax session details
+ * \param prefix the prefix to each filename
+ * \param separator the separator between filenames
+ *
+ * This function generates a string of filenames from the given details
+ * structure and using the given prefix and separator.
+ *
+ * \retval NULL there was an error generating the string
+ * \return the string generated string
+ */
+static char *generate_filenames_string(struct ast_fax_session_details *details, char *prefix, char *separator)
+{
+       char *filenames, *c;
+       size_t size = 0;
+       int first = 1;
+       struct ast_fax_document *doc;
+
+       /* don't process empty lists */
+       if (AST_LIST_EMPTY(&details->documents)) {
+               return NULL;
+       }
+
+       /* Calculate the total length of all of the file names */
+       AST_LIST_TRAVERSE(&details->documents, doc, next) {
+               size += strlen(separator) + strlen(prefix) + strlen(doc->filename);
+       }
+       size += 1; /* add space for the terminating null */
+
+       if (!(filenames = ast_malloc(size))) {
+               return NULL;
+       }
+       c = filenames;
+
+       ast_build_string(&c, &size, "%s%s", prefix, AST_LIST_FIRST(&details->documents)->filename);
+       AST_LIST_TRAVERSE(&details->documents, doc, next) {
+               if (first) {
+                       first = 0;
+                       continue;
+               }
+
+               ast_build_string(&c, &size, "%s%s%s", separator, prefix, doc->filename);
+       }
+
+       return filenames;
+}
+
 /*! \brief send a FAX status manager event */
 static int report_fax_status(struct ast_channel *chan, struct ast_fax_session_details *details, const char *status)
 {
+       char *filenames = generate_filenames_string(details, "FileName: ", "\r\n");
+       if (!filenames) {
+               return 1;
+       }
+
        ast_channel_lock(chan);
        pbx_builtin_setvar_helper(chan, "FAXSTATUSSTRING", status);
        if (details->option.statusevents) {
@@ -617,16 +670,17 @@ static int report_fax_status(struct ast_channel *chan, struct ast_fax_session_de
                              "Exten: %s\r\n"
                              "CallerID: %s\r\n"
                              "LocalStationID: %s\r\n"
-                             "FileName: %s\r\n",
+                             "%s\r\n",
                              status,
                              chan->name,
                              info.context,
                              info.exten,
                              info.cid,
                              details->localstationid,
-                             AST_LIST_FIRST(&details->documents)->filename);
+                             filenames);
        }
        ast_channel_unlock(chan);
+       ast_free(filenames);
 
        return 0;
 }
@@ -1545,19 +1599,19 @@ static int sendfax_t38_init(struct ast_channel *chan, struct ast_fax_session_det
 /*! \brief initiate a send FAX session */
 static int sendfax_exec(struct ast_channel *chan, const char *data)
 {
-       char *parse;
-       int channel_alive;
+       char *parse, *filenames, *c;
+       int channel_alive, file_count;
        struct ast_fax_session_details *details;
        struct ast_fax_document *doc;
        AST_DECLARE_APP_ARGS(args,
-               AST_APP_ARG(filename);
+               AST_APP_ARG(filenames);
                AST_APP_ARG(options);
        );
        struct ast_flags opts = { 0, };
        struct manager_event_info info;
 
        if (ast_strlen_zero(data)) {
-               ast_log(LOG_WARNING, "%s requires an argument (filename[,options])\n", app_sendfax);
+               ast_log(LOG_WARNING, "%s requires an argument (filename[&filename[&filename]][,options])\n", app_sendfax);
                return -1;
        }
        parse = ast_strdupa(data);
@@ -1576,8 +1630,8 @@ static int sendfax_exec(struct ast_channel *chan, const char *data)
            ast_app_parse_options(fax_exec_options, &opts, NULL, args.options)) {
                return -1;
        }
-       if (ast_strlen_zero(args.filename)) {
-               ast_log(LOG_WARNING, "%s requires an argument (filename[,options])\n", app_sendfax);
+       if (ast_strlen_zero(args.filenames)) {
+               ast_log(LOG_WARNING, "%s requires an argument (filename[&filename[&filename]],options])\n", app_sendfax);
                return -1;
        }
        
@@ -1587,11 +1641,6 @@ static int sendfax_exec(struct ast_channel *chan, const char *data)
                return -1;
        }
 
-       if (access(args.filename, (F_OK | R_OK)) < 0) {
-               ast_log(LOG_ERROR, "access failure.  Verify '%s' exists and check permissions.\n", args.filename);
-               return -1;
-       }
-       
        /* make sure the channel is up */
        if (chan->_state != AST_STATE_UP) {
                if (ast_answer(chan)) {
@@ -1612,17 +1661,35 @@ static int sendfax_exec(struct ast_channel *chan, const char *data)
                return -1;
        }
 
-       if (!(doc = ast_calloc(1, sizeof(*doc) + strlen(args.filename) + 1))) {
-               ast_log(LOG_ERROR, "System cannot provide memory for session requirements.\n");
-               ao2_ref(details, -1);
-               return -1;
+       file_count = 0;
+       filenames = args.filenames;
+       while ((c = strsep(&filenames, "&"))) {
+               if (access(c, (F_OK | R_OK)) < 0) {
+                       ast_log(LOG_ERROR, "access failure.  Verify '%s' exists and check permissions.\n", args.filenames);
+                       ao2_ref(details, -1);
+                       return -1;
+               }
+
+               if (!(doc = ast_calloc(1, sizeof(*doc) + strlen(c) + 1))) {
+                       ast_log(LOG_ERROR, "System cannot provide memory for session requirements.\n");
+                       ao2_ref(details, -1);
+                       return -1;
+               }
+
+               strcpy(doc->filename, c);
+               AST_LIST_INSERT_TAIL(&details->documents, doc, next);
+               file_count++;
        }
 
-       strcpy(doc->filename, args.filename);
-       AST_LIST_INSERT_TAIL(&details->documents, doc, next);
+       if (file_count > 1) {
+               details->caps |= AST_FAX_TECH_MULTI_DOC;
+       }
+
+       ast_verb(3, "Channel '%s' sending FAX:\n", chan->name);
+       AST_LIST_TRAVERSE(&details->documents, doc, next) {
+               ast_verb(3, "   %s\n", doc->filename);
+       }
 
-       ast_verb(3, "Channel '%s' sending FAX '%s'\n", chan->name, args.filename);
-       
        details->caps = AST_FAX_TECH_SEND;
 
        /* check for debug */
@@ -1669,6 +1736,12 @@ static int sendfax_exec(struct ast_channel *chan, const char *data)
                }
        }
 
+       if (!(filenames = generate_filenames_string(details, "FileName: ", "\r\n"))) {
+               ast_log(LOG_ERROR, "Error generating SendFAX manager event\n");
+               ao2_ref(details, -1);
+               return (!channel_alive) ? -1 : 0;
+       }
+
        /* send out the AMI completion event */
        ast_channel_lock(chan);
        get_manager_event_info(chan, &info);
@@ -1683,7 +1756,7 @@ static int sendfax_exec(struct ast_channel *chan, const char *data)
                      "PagesTransferred: %s\r\n"
                      "Resolution: %s\r\n"
                      "TransferRate: %s\r\n"
-                     "FileName: %s\r\n",
+                     "%s\r\n",
                      chan->name,
                      info.context,
                      info.exten,
@@ -1693,9 +1766,11 @@ static int sendfax_exec(struct ast_channel *chan, const char *data)
                      pbx_builtin_getvar_helper(chan, "FAXPAGES"),
                      pbx_builtin_getvar_helper(chan, "FAXRESOLUTION"),
                      pbx_builtin_getvar_helper(chan, "FAXBITRATE"),
-                     args.filename);
+                     filenames);
        ast_channel_unlock(chan);
 
+       ast_free(filenames);
+
        ao2_ref(details, -1);
 
        /* If the channel hungup return -1; otherwise, return 0 to continue in the dialplan */
@@ -1954,6 +2029,7 @@ static char *cli_fax_show_sessions(struct ast_cli_entry *e, int cmd, struct ast_
        struct ast_fax_session *s;
        struct ao2_iterator i;
        int session_count;
+       char *filenames;
 
        switch (cmd) {
        case CLI_INIT:
@@ -1968,15 +2044,28 @@ static char *cli_fax_show_sessions(struct ast_cli_entry *e, int cmd, struct ast_
 
        ast_cli(a->fd, "\nCurrent FAX Sessions:\n\n");
        ast_cli(a->fd, "%-20.20s %-10.10s %-10.10s %-5.5s %-10.10s %-15.15s %-30.30s\n",
-               "Channel", "Tech", "FAXID", "Type", "Operation", "State", "File");
+               "Channel", "Tech", "FAXID", "Type", "Operation", "State", "File(s)");
        i = ao2_iterator_init(faxregistry.container, 0);
        while ((s = ao2_iterator_next(&i))) {
                ao2_lock(s);
-               ast_cli(a->fd, "%-20.20s %-10.10s %-10d %-5.5s %-10.10s %-15.15s %-30.30s\n",
+
+               if (!(filenames = generate_filenames_string(s->details, "", ", "))) {
+                       ast_log(LOG_ERROR, "error printing filenames for 'fax show sessions' command");
+                       ao2_unlock(s);
+                       ao2_ref(s, -1);
+                       if (ao2_iterator_destroy != NULL) {
+                               ao2_iterator_destroy(&i);
+                       }
+                       return CLI_FAILURE;
+               }
+
+               ast_cli(a->fd, "%-20.20s %-10.10s %-10d %-5.5s %-10.10s %-15.15s %-30s\n",
                        s->channame, s->tech->type, s->id,
                        (s->details->caps & AST_FAX_TECH_AUDIO) ? "G.711" : "T.38",
                        (s->details->caps & AST_FAX_TECH_SEND) ? "send" : "receive",
-                       ast_fax_state_to_str(s->state), AST_LIST_FIRST(&s->details->documents)->filename);
+                       ast_fax_state_to_str(s->state), filenames);
+
+               ast_free(filenames);
                ao2_unlock(s);
                ao2_ref(s, -1);
        }
@@ -2063,6 +2152,7 @@ static int acf_faxopt_read(struct ast_channel *chan, const char *cmd, char *data
 {
        struct ast_fax_session_details *details = find_details(chan);
        int res = 0;
+       char *filenames;
 
        if (!details) {
                ast_log(LOG_ERROR, "channel '%s' can't read FAXOPT(%s) because it has never been written.\n", chan->name, data);
@@ -2079,6 +2169,17 @@ static int acf_faxopt_read(struct ast_channel *chan, const char *cmd, char *data
                } else {
                        ast_copy_string(buf, AST_LIST_FIRST(&details->documents)->filename, len);
                }
+       } else if (!strcasecmp(data, "filenames")) {
+               if (AST_LIST_EMPTY(&details->documents)) {
+                       ast_log(LOG_ERROR, "channel '%s' can't read FAXOPT(%s) because it has never been written.\n", chan->name, data);
+                       res = -1;
+               } else if ((filenames = generate_filenames_string(details, "", ","))) {
+                       ast_copy_string(buf, filenames, len);
+                       ast_free(filenames);
+               } else {
+                       ast_log(LOG_ERROR, "channel '%s' can't read FAXOPT(%s), there was an error generating the filenames list.\n", chan->name, data);
+                       res = -1;
+               }
        } else if (!strcasecmp(data, "headerinfo")) {
                ast_copy_string(buf, details->headerinfo, len);
        } else if (!strcasecmp(data, "localstationid")) {
@@ -2169,7 +2270,8 @@ struct ast_custom_function acf_faxopt = {
 "  ------             ----     -----------\n"
 "  ecm                 RW      Specify Error Correction Mode (ECM) with 'yes', disable with 'no'.\n"
 "  error               RO      Read the FAX transmission error upon failure.\n"
-"  filename            RO      Read the filename of the FAX transmission.\n"
+"  filename            RO      Read the filename of the first file of the FAX transmission.\n"
+"  filenames           RO      Read the filenames of all of the files in the FAX transmission (comma separated).\n"
 "  headerinfo          RW      Specify or read the FAX header.\n"
 "  localstationid      RW      Specify or read the local station identification\n"
 "  maxrate             RW      Specify or read the maximum transfer rate before transmission\n"