Line 0
Link Here
|
|
|
1 |
--- apps/app_rxfax.c.orig Tue Jul 10 15:28:04 2007 |
2 |
+++ apps/app_rxfax.c Tue Jul 10 15:27:28 2007 |
3 |
@@ -0,0 +1,380 @@ |
4 |
+/* |
5 |
+ * Asterisk -- A telephony toolkit for Linux. |
6 |
+ * |
7 |
+ * Trivial application to receive a TIFF FAX file |
8 |
+ * |
9 |
+ * Copyright (C) 2003, Steve Underwood |
10 |
+ * |
11 |
+ * Steve Underwood <steveu@coppice.org> |
12 |
+ * |
13 |
+ * This program is free software, distributed under the terms of |
14 |
+ * the GNU General Public License |
15 |
+ */ |
16 |
+ |
17 |
+/*** MODULEINFO |
18 |
+ <depend>spandsp</depend> |
19 |
+***/ |
20 |
+ |
21 |
+#include "asterisk.h" |
22 |
+ |
23 |
+ASTERISK_FILE_VERSION(__FILE__, "$Revision:$") |
24 |
+ |
25 |
+#include <string.h> |
26 |
+#include <stdlib.h> |
27 |
+#include <stdio.h> |
28 |
+#include <inttypes.h> |
29 |
+#include <pthread.h> |
30 |
+#include <errno.h> |
31 |
+#include <tiffio.h> |
32 |
+ |
33 |
+#include <spandsp.h> |
34 |
+ |
35 |
+#include "asterisk/lock.h" |
36 |
+#include "asterisk/file.h" |
37 |
+#include "asterisk/logger.h" |
38 |
+#include "asterisk/channel.h" |
39 |
+#include "asterisk/pbx.h" |
40 |
+#include "asterisk/module.h" |
41 |
+#include "asterisk/manager.h" |
42 |
+ |
43 |
+#ifndef AST_MODULE |
44 |
+#define AST_MODULE "app_rxfax" |
45 |
+#endif |
46 |
+ |
47 |
+static char *app = "RxFAX"; |
48 |
+ |
49 |
+static char *synopsis = "Receive a FAX to a file"; |
50 |
+ |
51 |
+static char *descrip = |
52 |
+" RxFAX(filename[|caller][|debug]): Receives a FAX from the channel into the\n" |
53 |
+"given filename. If the file exists it will be overwritten. The file\n" |
54 |
+"should be in TIFF/F format.\n" |
55 |
+"The \"caller\" option makes the application behave as a calling machine,\n" |
56 |
+"rather than the answering machine. The default behaviour is to behave as\n" |
57 |
+"an answering machine.\n" |
58 |
+"Uses LOCALSTATIONID to identify itself to the remote end.\n" |
59 |
+" LOCALHEADERINFO to generate a header line on each page.\n" |
60 |
+"Sets REMOTESTATIONID to the sender CSID.\n" |
61 |
+" FAXPAGES to the number of pages received.\n" |
62 |
+" FAXBITRATE to the transmition rate.\n" |
63 |
+" FAXRESOLUTION to the resolution.\n" |
64 |
+"Returns -1 when the user hangs up.\n" |
65 |
+"Returns 0 otherwise.\n"; |
66 |
+ |
67 |
+#define MAX_BLOCK_SIZE 240 |
68 |
+ |
69 |
+static void span_message(int level, const char *msg) |
70 |
+{ |
71 |
+ int ast_level; |
72 |
+ |
73 |
+ if (level == SPAN_LOG_WARNING) |
74 |
+ ast_level = __LOG_WARNING; |
75 |
+ else if (level == SPAN_LOG_WARNING) |
76 |
+ ast_level = __LOG_WARNING; |
77 |
+ else |
78 |
+ ast_level = __LOG_DEBUG; |
79 |
+ ast_log(ast_level, __FILE__, __LINE__, __PRETTY_FUNCTION__, msg); |
80 |
+} |
81 |
+/*- End of function --------------------------------------------------------*/ |
82 |
+ |
83 |
+#if 0 |
84 |
+static void t30_flush(t30_state_t *s, int which) |
85 |
+{ |
86 |
+ /* TODO: */ |
87 |
+} |
88 |
+/*- End of function --------------------------------------------------------*/ |
89 |
+#endif |
90 |
+ |
91 |
+static void phase_e_handler(t30_state_t *s, void *user_data, int result) |
92 |
+{ |
93 |
+ struct ast_channel *chan; |
94 |
+ t30_stats_t t; |
95 |
+ char local_ident[21]; |
96 |
+ char far_ident[21]; |
97 |
+ char buf[11]; |
98 |
+ |
99 |
+ chan = (struct ast_channel *) user_data; |
100 |
+ if (result == T30_ERR_OK) |
101 |
+ { |
102 |
+ t30_get_transfer_statistics(s, &t); |
103 |
+ t30_get_far_ident(s, far_ident); |
104 |
+ t30_get_local_ident(s, local_ident); |
105 |
+ ast_log(LOG_DEBUG, "==============================================================================\n"); |
106 |
+ ast_log(LOG_DEBUG, "Fax successfully received.\n"); |
107 |
+ ast_log(LOG_DEBUG, "Remote station id: %s\n", far_ident); |
108 |
+ ast_log(LOG_DEBUG, "Local station id: %s\n", local_ident); |
109 |
+ ast_log(LOG_DEBUG, "Pages transferred: %i\n", t.pages_transferred); |
110 |
+ ast_log(LOG_DEBUG, "Image resolution: %i x %i\n", t.x_resolution, t.y_resolution); |
111 |
+ ast_log(LOG_DEBUG, "Transfer Rate: %i\n", t.bit_rate); |
112 |
+ ast_log(LOG_DEBUG, "==============================================================================\n"); |
113 |
+ manager_event(EVENT_FLAG_CALL, |
114 |
+ "FaxReceived", "Channel: %s\nExten: %s\nCallerID: %s\nRemoteStationID: %s\nLocalStationID: %s\nPagesTransferred: %i\nResolution: %i\nTransferRate: %i\nFileName: %s\n", |
115 |
+ chan->name, |
116 |
+ chan->exten, |
117 |
+ (chan->cid.cid_num) ? chan->cid.cid_num : "", |
118 |
+ far_ident, |
119 |
+ local_ident, |
120 |
+ t.pages_transferred, |
121 |
+ t.y_resolution, |
122 |
+ t.bit_rate, |
123 |
+ s->rx_file); |
124 |
+ pbx_builtin_setvar_helper(chan, "REMOTESTATIONID", far_ident); |
125 |
+ snprintf(buf, sizeof(buf), "%i", t.pages_transferred); |
126 |
+ pbx_builtin_setvar_helper(chan, "FAXPAGES", buf); |
127 |
+ snprintf(buf, sizeof(buf), "%i", t.y_resolution); |
128 |
+ pbx_builtin_setvar_helper(chan, "FAXRESOLUTION", buf); |
129 |
+ snprintf(buf, sizeof(buf), "%i", t.bit_rate); |
130 |
+ pbx_builtin_setvar_helper(chan, "FAXBITRATE", buf); |
131 |
+ } |
132 |
+ else |
133 |
+ { |
134 |
+ ast_log(LOG_DEBUG, "==============================================================================\n"); |
135 |
+ ast_log(LOG_DEBUG, "Fax receive not successful - result (%d) %s.\n", result, t30_completion_code_to_str(result)); |
136 |
+ ast_log(LOG_DEBUG, "==============================================================================\n"); |
137 |
+ } |
138 |
+} |
139 |
+/*- End of function --------------------------------------------------------*/ |
140 |
+ |
141 |
+static void phase_d_handler(t30_state_t *s, void *user_data, int result) |
142 |
+{ |
143 |
+ struct ast_channel *chan; |
144 |
+ t30_stats_t t; |
145 |
+ |
146 |
+ chan = (struct ast_channel *) user_data; |
147 |
+ if (result) |
148 |
+ { |
149 |
+ t30_get_transfer_statistics(s, &t); |
150 |
+ ast_log(LOG_DEBUG, "==============================================================================\n"); |
151 |
+ ast_log(LOG_DEBUG, "Pages transferred: %i\n", t.pages_transferred); |
152 |
+ ast_log(LOG_DEBUG, "Image size: %i x %i\n", t.width, t.length); |
153 |
+ ast_log(LOG_DEBUG, "Image resolution %i x %i\n", t.x_resolution, t.y_resolution); |
154 |
+ ast_log(LOG_DEBUG, "Transfer Rate: %i\n", t.bit_rate); |
155 |
+ ast_log(LOG_DEBUG, "Bad rows %i\n", t.bad_rows); |
156 |
+ ast_log(LOG_DEBUG, "Longest bad row run %i\n", t.longest_bad_row_run); |
157 |
+ ast_log(LOG_DEBUG, "Compression type %i\n", t.encoding); |
158 |
+ ast_log(LOG_DEBUG, "Image size (bytes) %i\n", t.image_size); |
159 |
+ ast_log(LOG_DEBUG, "==============================================================================\n"); |
160 |
+ } |
161 |
+} |
162 |
+/*- End of function --------------------------------------------------------*/ |
163 |
+ |
164 |
+static int rxfax_exec(struct ast_channel *chan, void *data) |
165 |
+{ |
166 |
+ int res = 0; |
167 |
+ char template_file[256]; |
168 |
+ char target_file[256]; |
169 |
+ char *s; |
170 |
+ char *t; |
171 |
+ char *v; |
172 |
+ const char *x; |
173 |
+ int option; |
174 |
+ int len; |
175 |
+ int i; |
176 |
+ fax_state_t fax; |
177 |
+ int calling_party; |
178 |
+ int verbose; |
179 |
+ int samples; |
180 |
+ |
181 |
+ struct ast_module_user *u; |
182 |
+ struct ast_frame *inf = NULL; |
183 |
+ struct ast_frame outf; |
184 |
+ |
185 |
+ int original_read_fmt; |
186 |
+ int original_write_fmt; |
187 |
+ |
188 |
+ uint8_t __buf[sizeof(uint16_t)*MAX_BLOCK_SIZE + 2*AST_FRIENDLY_OFFSET]; |
189 |
+ uint8_t *buf = __buf + AST_FRIENDLY_OFFSET; |
190 |
+ |
191 |
+ if (chan == NULL) |
192 |
+ { |
193 |
+ ast_log(LOG_WARNING, "Fax receive channel is NULL. Giving up.\n"); |
194 |
+ return -1; |
195 |
+ } |
196 |
+ |
197 |
+ span_set_message_handler(span_message); |
198 |
+ |
199 |
+ /* The next few lines of code parse out the filename and header from the input string */ |
200 |
+ if (data == NULL) |
201 |
+ { |
202 |
+ /* No data implies no filename or anything is present */ |
203 |
+ ast_log(LOG_WARNING, "Rxfax requires an argument (filename)\n"); |
204 |
+ return -1; |
205 |
+ } |
206 |
+ |
207 |
+ calling_party = FALSE; |
208 |
+ verbose = FALSE; |
209 |
+ target_file[0] = '\0'; |
210 |
+ |
211 |
+ for (option = 0, v = s = data; v; option++, s++) |
212 |
+ { |
213 |
+ t = s; |
214 |
+ v = strchr(s, '|'); |
215 |
+ s = (v) ? v : s + strlen(s); |
216 |
+ strncpy((char *) buf, t, s - t); |
217 |
+ buf[s - t] = '\0'; |
218 |
+ if (option == 0) |
219 |
+ { |
220 |
+ /* The first option is always the file name */ |
221 |
+ len = s - t; |
222 |
+ if (len > 255) |
223 |
+ len = 255; |
224 |
+ strncpy(target_file, t, len); |
225 |
+ target_file[len] = '\0'; |
226 |
+ /* Allow the use of %d in the file name for a wild card of sorts, to |
227 |
+ create a new file with the specified name scheme */ |
228 |
+ if ((x = strchr(target_file, '%')) && x[1] == 'd') |
229 |
+ { |
230 |
+ strcpy(template_file, target_file); |
231 |
+ i = 0; |
232 |
+ do |
233 |
+ { |
234 |
+ snprintf(target_file, 256, template_file, 1); |
235 |
+ i++; |
236 |
+ } |
237 |
+ while (ast_fileexists(target_file, "", chan->language) != -1); |
238 |
+ } |
239 |
+ } |
240 |
+ else if (strncmp("caller", t, s - t) == 0) |
241 |
+ { |
242 |
+ calling_party = TRUE; |
243 |
+ } |
244 |
+ else if (strncmp("debug", t, s - t) == 0) |
245 |
+ { |
246 |
+ verbose = TRUE; |
247 |
+ } |
248 |
+ } |
249 |
+ |
250 |
+ /* Done parsing */ |
251 |
+ |
252 |
+ u = ast_module_user_add(chan); |
253 |
+ |
254 |
+ if (chan->_state != AST_STATE_UP) |
255 |
+ { |
256 |
+ /* Shouldn't need this, but checking to see if channel is already answered |
257 |
+ * Theoretically asterisk should already have answered before running the app */ |
258 |
+ res = ast_answer(chan); |
259 |
+ } |
260 |
+ |
261 |
+ if (!res) |
262 |
+ { |
263 |
+ original_read_fmt = chan->readformat; |
264 |
+ if (original_read_fmt != AST_FORMAT_SLINEAR) |
265 |
+ { |
266 |
+ res = ast_set_read_format(chan, AST_FORMAT_SLINEAR); |
267 |
+ if (res < 0) |
268 |
+ { |
269 |
+ ast_log(LOG_WARNING, "Unable to set to linear read mode, giving up\n"); |
270 |
+ return -1; |
271 |
+ } |
272 |
+ } |
273 |
+ original_write_fmt = chan->writeformat; |
274 |
+ if (original_write_fmt != AST_FORMAT_SLINEAR) |
275 |
+ { |
276 |
+ res = ast_set_write_format(chan, AST_FORMAT_SLINEAR); |
277 |
+ if (res < 0) |
278 |
+ { |
279 |
+ ast_log(LOG_WARNING, "Unable to set to linear write mode, giving up\n"); |
280 |
+ res = ast_set_read_format(chan, original_read_fmt); |
281 |
+ if (res) |
282 |
+ ast_log(LOG_WARNING, "Unable to restore read format on '%s'\n", chan->name); |
283 |
+ return -1; |
284 |
+ } |
285 |
+ } |
286 |
+ fax_init(&fax, calling_party); |
287 |
+ if (verbose) |
288 |
+ fax.logging.level = SPAN_LOG_SHOW_SEVERITY | SPAN_LOG_SHOW_PROTOCOL | SPAN_LOG_FLOW; |
289 |
+ x = pbx_builtin_getvar_helper(chan, "LOCALSTATIONID"); |
290 |
+ if (x && x[0]) |
291 |
+ t30_set_local_ident(&fax.t30_state, x); |
292 |
+ x = pbx_builtin_getvar_helper(chan, "LOCALHEADERINFO"); |
293 |
+ if (x && x[0]) |
294 |
+ t30_set_header_info(&fax.t30_state, x); |
295 |
+ t30_set_rx_file(&fax.t30_state, target_file, -1); |
296 |
+ //t30_set_phase_b_handler(&fax.t30_state, phase_b_handler, chan); |
297 |
+ t30_set_phase_d_handler(&fax.t30_state, phase_d_handler, chan); |
298 |
+ t30_set_phase_e_handler(&fax.t30_state, phase_e_handler, chan); |
299 |
+ t30_set_ecm_capability(&fax.t30_state, TRUE); |
300 |
+ t30_set_supported_compressions(&fax.t30_state, T30_SUPPORT_T4_1D_COMPRESSION | T30_SUPPORT_T4_2D_COMPRESSION | T30_SUPPORT_T6_COMPRESSION); |
301 |
+ while (ast_waitfor(chan, -1) > -1) |
302 |
+ { |
303 |
+ inf = ast_read(chan); |
304 |
+ if (inf == NULL) |
305 |
+ { |
306 |
+ res = -1; |
307 |
+ break; |
308 |
+ } |
309 |
+ if (inf->frametype == AST_FRAME_VOICE) |
310 |
+ { |
311 |
+ if (fax_rx(&fax, inf->data, inf->samples)) |
312 |
+ break; |
313 |
+ samples = (inf->samples <= MAX_BLOCK_SIZE) ? inf->samples : MAX_BLOCK_SIZE; |
314 |
+ len = fax_tx(&fax, (int16_t *) &buf[AST_FRIENDLY_OFFSET], samples); |
315 |
+ if (len) |
316 |
+ { |
317 |
+ memset(&outf, 0, sizeof(outf)); |
318 |
+ outf.frametype = AST_FRAME_VOICE; |
319 |
+ outf.subclass = AST_FORMAT_SLINEAR; |
320 |
+ outf.datalen = len*sizeof(int16_t); |
321 |
+ outf.samples = len; |
322 |
+ outf.data = &buf[AST_FRIENDLY_OFFSET]; |
323 |
+ outf.offset = AST_FRIENDLY_OFFSET; |
324 |
+ outf.src = "RxFAX"; |
325 |
+ if (ast_write(chan, &outf) < 0) |
326 |
+ { |
327 |
+ ast_log(LOG_WARNING, "Unable to write frame to channel; %s\n", strerror(errno)); |
328 |
+ break; |
329 |
+ } |
330 |
+ } |
331 |
+ } |
332 |
+ ast_frfree(inf); |
333 |
+ } |
334 |
+ if (inf == NULL) |
335 |
+ { |
336 |
+ ast_log(LOG_DEBUG, "Got hangup\n"); |
337 |
+ res = -1; |
338 |
+ } |
339 |
+ if (original_read_fmt != AST_FORMAT_SLINEAR) |
340 |
+ { |
341 |
+ res = ast_set_read_format(chan, original_read_fmt); |
342 |
+ if (res) |
343 |
+ ast_log(LOG_WARNING, "Unable to restore read format on '%s'\n", chan->name); |
344 |
+ } |
345 |
+ if (original_write_fmt != AST_FORMAT_SLINEAR) |
346 |
+ { |
347 |
+ res = ast_set_write_format(chan, original_write_fmt); |
348 |
+ if (res) |
349 |
+ ast_log(LOG_WARNING, "Unable to restore write format on '%s'\n", chan->name); |
350 |
+ } |
351 |
+ t30_terminate(&fax.t30_state); |
352 |
+ } |
353 |
+ else |
354 |
+ { |
355 |
+ ast_log(LOG_WARNING, "Could not answer channel '%s'\n", chan->name); |
356 |
+ } |
357 |
+ ast_module_user_remove(u); |
358 |
+ return res; |
359 |
+} |
360 |
+/*- End of function --------------------------------------------------------*/ |
361 |
+ |
362 |
+static int unload_module(void) |
363 |
+{ |
364 |
+ int res; |
365 |
+ |
366 |
+ ast_module_user_hangup_all(); |
367 |
+ |
368 |
+ res = ast_unregister_application(app); |
369 |
+ |
370 |
+ |
371 |
+ return res; |
372 |
+} |
373 |
+/*- End of function --------------------------------------------------------*/ |
374 |
+ |
375 |
+static int load_module(void) |
376 |
+{ |
377 |
+ return ast_register_application(app, rxfax_exec, synopsis, descrip); |
378 |
+} |
379 |
+/*- End of function --------------------------------------------------------*/ |
380 |
+ |
381 |
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Trivial FAX Receive Application"); |
382 |
+ |
383 |
+/*- End of file ------------------------------------------------------------*/ |