]>
Commit | Line | Data |
---|---|---|
d7e66fb6 | 1 | /* |
2 | * Asterisk -- A telephony toolkit for Linux. | |
3 | * | |
4 | * Trivial application to send a TIFF file as a FAX | |
5 | * | |
6 | * Copyright (C) 2003, Steve Underwood | |
7 | * | |
8 | * Steve Underwood <steveu@coppice.org> | |
9 | * | |
10 | * This program is free software, distributed under the terms of | |
11 | * the GNU General Public License | |
12 | */ | |
13 | ||
14 | #include <string.h> | |
15 | #include <stdlib.h> | |
16 | #include <stdio.h> | |
17 | #include <inttypes.h> | |
18 | #include <pthread.h> | |
19 | #include <errno.h> | |
20 | #include <tiffio.h> | |
21 | ||
22 | #include <spandsp.h> | |
23 | ||
24 | #include "asterisk.h" | |
25 | ||
26 | ASTERISK_FILE_VERSION(__FILE__, "$Revision$") | |
27 | ||
28 | #include "asterisk/lock.h" | |
29 | #include "asterisk/file.h" | |
30 | #include "asterisk/logger.h" | |
31 | #include "asterisk/channel.h" | |
32 | #include "asterisk/pbx.h" | |
33 | #include "asterisk/module.h" | |
34 | #include "asterisk/translate.h" | |
35 | ||
36 | static char *tdesc = "Trivial FAX Transmit Application"; | |
37 | ||
38 | static char *app = "TxFAX"; | |
39 | ||
40 | static char *synopsis = "Send a FAX file"; | |
41 | ||
42 | static char *descrip = | |
43 | " TxFAX(filename[|caller][|debug]): Send a given TIFF file to the channel as a FAX.\n" | |
44 | "The \"caller\" option makes the application behave as a calling machine,\n" | |
45 | "rather than the answering machine. The default behaviour is to behave as\n" | |
46 | "an answering machine.\n" | |
47 | "Uses LOCALSTATIONID to identify itself to the remote end.\n" | |
48 | " LOCALHEADERINFO to generate a header line on each page.\n" | |
49 | "Sets REMOTESTATIONID to the receiver CSID.\n" | |
50 | "Returns -1 when the user hangs up, or if the file does not exist.\n" | |
51 | "Returns 0 otherwise.\n"; | |
52 | ||
53 | STANDARD_LOCAL_USER; | |
54 | ||
55 | LOCAL_USER_DECL; | |
56 | ||
57 | #define MAX_BLOCK_SIZE 240 | |
58 | ||
59 | static void span_message(int level, const char *msg) | |
60 | { | |
61 | int ast_level; | |
62 | ||
63 | if (level == SPAN_LOG_WARNING) | |
64 | ast_level = __LOG_WARNING; | |
65 | else if (level == SPAN_LOG_WARNING) | |
66 | ast_level = __LOG_WARNING; | |
67 | else | |
68 | ast_level = __LOG_DEBUG; | |
69 | ast_log(ast_level, __FILE__, __LINE__, __PRETTY_FUNCTION__, msg); | |
70 | } | |
71 | /*- End of function --------------------------------------------------------*/ | |
72 | ||
73 | static void t30_flush(t30_state_t *s, int which) | |
74 | { | |
75 | //TODO: | |
76 | } | |
77 | /*- End of function --------------------------------------------------------*/ | |
78 | ||
79 | static void phase_e_handler(t30_state_t *s, void *user_data, int result) | |
80 | { | |
81 | struct ast_channel *chan; | |
82 | char far_ident[21]; | |
83 | ||
84 | chan = (struct ast_channel *) user_data; | |
85 | if (result == T30_ERR_OK) | |
86 | { | |
87 | t30_get_far_ident(s, far_ident); | |
88 | pbx_builtin_setvar_helper(chan, "REMOTESTATIONID", far_ident); | |
89 | } | |
90 | else | |
91 | { | |
92 | ast_log(LOG_DEBUG, "==============================================================================\n"); | |
93 | ast_log(LOG_DEBUG, "Fax send not successful - result (%d) %s.\n", result, t30_completion_code_to_str(result)); | |
94 | ast_log(LOG_DEBUG, "==============================================================================\n"); | |
95 | } | |
96 | } | |
97 | /*- End of function --------------------------------------------------------*/ | |
98 | ||
99 | static int txfax_exec(struct ast_channel *chan, void *data) | |
100 | { | |
101 | int res = 0; | |
102 | char source_file[256]; | |
103 | char *x; | |
104 | char *s; | |
105 | char *t; | |
106 | char *v; | |
107 | int option; | |
108 | int len; | |
109 | t30_state_t fax; | |
110 | int calling_party; | |
111 | int verbose; | |
112 | int samples; | |
113 | ||
114 | struct localuser *u; | |
115 | struct ast_frame *inf = NULL; | |
116 | struct ast_frame outf; | |
117 | ||
118 | int original_read_fmt; | |
119 | int original_write_fmt; | |
120 | ||
121 | uint8_t __buf[sizeof(uint16_t)*MAX_BLOCK_SIZE + 2*AST_FRIENDLY_OFFSET]; | |
122 | uint8_t *buf = __buf + AST_FRIENDLY_OFFSET; | |
123 | ||
124 | if (chan == NULL) | |
125 | { | |
126 | ast_log(LOG_WARNING, "Fax transmit channel is NULL. Giving up.\n"); | |
127 | return -1; | |
128 | } | |
129 | ||
130 | span_set_message_handler(span_message); | |
131 | ||
132 | /* The next few lines of code parse out the filename and header from the input string */ | |
133 | if (data == NULL) | |
134 | { | |
135 | /* No data implies no filename or anything is present */ | |
136 | ast_log(LOG_WARNING, "Txfax requires an argument (filename)\n"); | |
137 | return -1; | |
138 | } | |
139 | ||
140 | calling_party = FALSE; | |
141 | verbose = FALSE; | |
142 | source_file[0] = '\0'; | |
143 | ||
144 | for (option = 0, v = s = data; v; option++, s++) | |
145 | { | |
146 | t = s; | |
147 | v = strchr(s, '|'); | |
148 | s = (v) ? v : s + strlen(s); | |
149 | strncpy((char *) buf, t, s - t); | |
150 | buf[s - t] = '\0'; | |
151 | if (option == 0) | |
152 | { | |
153 | /* The first option is always the file name */ | |
154 | len = s - t; | |
155 | if (len > 255) | |
156 | len = 255; | |
157 | strncpy(source_file, t, len); | |
158 | source_file[len] = '\0'; | |
159 | } | |
160 | else if (strncmp("caller", t, s - t) == 0) | |
161 | { | |
162 | calling_party = TRUE; | |
163 | } | |
164 | else if (strncmp("debug", t, s - t) == 0) | |
165 | { | |
166 | verbose = TRUE; | |
167 | } | |
168 | } | |
169 | ||
170 | /* Done parsing */ | |
171 | ||
172 | LOCAL_USER_ADD(u); | |
173 | ||
174 | if (chan->_state != AST_STATE_UP) | |
175 | { | |
176 | /* Shouldn't need this, but checking to see if channel is already answered | |
177 | * Theoretically asterisk should already have answered before running the app */ | |
178 | res = ast_answer(chan); | |
179 | } | |
180 | ||
181 | if (!res) | |
182 | { | |
183 | original_read_fmt = chan->readformat; | |
184 | if (original_read_fmt != AST_FORMAT_SLINEAR) | |
185 | { | |
186 | res = ast_set_read_format(chan, AST_FORMAT_SLINEAR); | |
187 | if (res < 0) | |
188 | { | |
189 | ast_log(LOG_WARNING, "Unable to set to linear read mode, giving up\n"); | |
190 | return -1; | |
191 | } | |
192 | } | |
193 | original_write_fmt = chan->writeformat; | |
194 | if (original_write_fmt != AST_FORMAT_SLINEAR) | |
195 | { | |
196 | res = ast_set_write_format(chan, AST_FORMAT_SLINEAR); | |
197 | if (res < 0) | |
198 | { | |
199 | ast_log(LOG_WARNING, "Unable to set to linear write mode, giving up\n"); | |
200 | res = ast_set_read_format(chan, original_read_fmt); | |
201 | if (res) | |
202 | ast_log(LOG_WARNING, "Unable to restore read format on '%s'\n", chan->name); | |
203 | return -1; | |
204 | } | |
205 | } | |
206 | fax_init(&fax, calling_party, NULL); | |
207 | if (verbose) | |
208 | fax.logging.level = SPAN_LOG_SHOW_SEVERITY | SPAN_LOG_SHOW_PROTOCOL | SPAN_LOG_FLOW; | |
209 | ||
210 | x = pbx_builtin_getvar_helper(chan, "LOCALSTATIONID"); | |
211 | if (x && x[0]) | |
212 | t30_set_local_ident(&fax, x); | |
213 | x = pbx_builtin_getvar_helper(chan, "LOCALHEADERINFO"); | |
214 | if (x && x[0]) | |
215 | t30_set_header_info(&fax, x); | |
216 | t30_set_tx_file(&fax, source_file, -1, -1); | |
217 | //t30_set_phase_b_handler(&fax, phase_b_handler, chan); | |
218 | //t30_set_phase_d_handler(&fax, phase_d_handler, chan); | |
219 | t30_set_phase_e_handler(&fax, phase_e_handler, chan); | |
220 | while (ast_waitfor(chan, -1) > -1) | |
221 | { | |
222 | inf = ast_read(chan); | |
223 | if (inf == NULL) | |
224 | { | |
225 | res = -1; | |
226 | break; | |
227 | } | |
228 | if (inf->frametype == AST_FRAME_VOICE) | |
229 | { | |
230 | if (fax_rx(&fax, inf->data, inf->samples)) | |
231 | break; | |
232 | samples = (inf->samples <= MAX_BLOCK_SIZE) ? inf->samples : MAX_BLOCK_SIZE; | |
233 | len = fax_tx(&fax, (int16_t *) &buf[AST_FRIENDLY_OFFSET], samples); | |
234 | if (len) | |
235 | { | |
236 | memset(&outf, 0, sizeof(outf)); | |
237 | outf.frametype = AST_FRAME_VOICE; | |
238 | outf.subclass = AST_FORMAT_SLINEAR; | |
239 | outf.datalen = len*sizeof(int16_t); | |
240 | outf.samples = len; | |
241 | outf.data = &buf[AST_FRIENDLY_OFFSET]; | |
242 | outf.offset = AST_FRIENDLY_OFFSET; | |
243 | if (ast_write(chan, &outf) < 0) | |
244 | { | |
245 | ast_log(LOG_WARNING, "Unable to write frame to channel; %s\n", strerror(errno)); | |
246 | break; | |
247 | } | |
248 | } | |
249 | } | |
250 | ast_frfree(inf); | |
251 | } | |
252 | if (inf == NULL) | |
253 | { | |
254 | ast_log(LOG_DEBUG, "Got hangup\n"); | |
255 | res = -1; | |
256 | } | |
257 | if (original_read_fmt != AST_FORMAT_SLINEAR) | |
258 | { | |
259 | res = ast_set_read_format(chan, original_read_fmt); | |
260 | if (res) | |
261 | ast_log(LOG_WARNING, "Unable to restore read format on '%s'\n", chan->name); | |
262 | } | |
263 | if (original_write_fmt != AST_FORMAT_SLINEAR) | |
264 | { | |
265 | res = ast_set_write_format(chan, original_write_fmt); | |
266 | if (res) | |
267 | ast_log(LOG_WARNING, "Unable to restore write format on '%s'\n", chan->name); | |
268 | } | |
269 | fax_release(&fax); | |
270 | } | |
271 | else | |
272 | { | |
273 | ast_log(LOG_WARNING, "Could not answer channel '%s'\n", chan->name); | |
274 | } | |
275 | LOCAL_USER_REMOVE(u); | |
276 | return res; | |
277 | } | |
278 | /*- End of function --------------------------------------------------------*/ | |
279 | ||
280 | int unload_module(void) | |
281 | { | |
282 | STANDARD_HANGUP_LOCALUSERS; | |
283 | return ast_unregister_application(app); | |
284 | } | |
285 | /*- End of function --------------------------------------------------------*/ | |
286 | ||
287 | int load_module(void) | |
288 | { | |
289 | return ast_register_application(app, txfax_exec, synopsis, descrip); | |
290 | } | |
291 | /*- End of function --------------------------------------------------------*/ | |
292 | ||
293 | char *description(void) | |
294 | { | |
295 | return tdesc; | |
296 | } | |
297 | /*- End of function --------------------------------------------------------*/ | |
298 | ||
299 | int usecount(void) | |
300 | { | |
301 | int res; | |
302 | ||
303 | STANDARD_USECOUNT(res); | |
304 | return res; | |
305 | } | |
306 | /*- End of function --------------------------------------------------------*/ | |
307 | ||
308 | char *key(void) | |
309 | { | |
310 | return ASTERISK_GPL_KEY; | |
311 | } | |
312 | /*- End of function --------------------------------------------------------*/ | |
313 | /*- End of file ------------------------------------------------------------*/ |