Review Board 1.7.16


Add aliases for Directory

Review Request #2244 - Created Dec. 12, 2012 and submitted

Tilghman Lesher
Reviewers
asterisk-dev
Asterisk
This patch adds extra strings for comparison in the Directory application.  The purpose of this is to enable nicknames that otherwise would not be compared.  For example, Bob "Bongo Boy" Berman might have an alias of "Bongo", for which he would be searchable, even though his name would not normally include it.
Deployed and working on a production machine.

Changes between revision 1 and 2

1 2 3
1 2 3

  1. /trunk/apps/app_directory.c: Loading...
  2. /trunk/configs/voicemail.conf.sample: Loading...
  3. /trunk/contrib/realtime/mysql/voicemail.sql: Loading...
/trunk/apps/app_directory.c
Diff Revision 1 Diff Revision 2
1
/*
1
/*
2
 * Asterisk -- An open source telephony toolkit.
2
 * Asterisk -- An open source telephony toolkit.
3
 *
3
 *
4
 * Copyright (C) 1999 - 2005, Digium, Inc.
4
 * Copyright (C) 1999 - 2005, Digium, Inc.
5
 *
5
 *
6
 * Mark Spencer <markster@digium.com>
6
 * Mark Spencer <markster@digium.com>
7
 *
7
 *
8
 * See http://www.asterisk.org for more information about
8
 * See http://www.asterisk.org for more information about
9
 * the Asterisk project. Please do not directly contact
9
 * the Asterisk project. Please do not directly contact
10
 * any of the maintainers of this project for assistance;
10
 * any of the maintainers of this project for assistance;
11
 * the project provides a web site, mailing lists and IRC
11
 * the project provides a web site, mailing lists and IRC
12
 * channels for your use.
12
 * channels for your use.
13
 *
13
 *
14
 * This program is free software, distributed under the terms of
14
 * This program is free software, distributed under the terms of
15
 * the GNU General Public License Version 2. See the LICENSE file
15
 * the GNU General Public License Version 2. See the LICENSE file
16
 * at the top of the source tree.
16
 * at the top of the source tree.
17
 */
17
 */
18

    
   
18

   
19
/*! \file
19
/*! \file
20
 *
20
 *
21
 * \brief Provide a directory of extensions
21
 * \brief Provide a directory of extensions
22
 *
22
 *
23
 * \author Mark Spencer <markster@digium.com>
23
 * \author Mark Spencer <markster@digium.com>
24
 *
24
 *
25
 * \ingroup applications
25
 * \ingroup applications
26
 */
26
 */
27

    
   
27

   
28
/*** MODULEINFO
28
/*** MODULEINFO
29
	<depend>app_voicemail</depend>
29
	<depend>app_voicemail</depend>
30
	<support_level>core</support_level>
30
	<support_level>core</support_level>
31
 ***/
31
 ***/
32
#include "asterisk.h"
32
#include "asterisk.h"
33

    
   
33

   
34
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
34
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
35

    
   
35

   
36
#include <ctype.h>
36
#include <ctype.h>
37

    
   
37

   
38
#include "asterisk/paths.h" /* use ast_config_AST_SPOOL_DIR */
38
#include "asterisk/paths.h" /* use ast_config_AST_SPOOL_DIR */
39
#include "asterisk/file.h"
39
#include "asterisk/file.h"
40
#include "asterisk/pbx.h"
40
#include "asterisk/pbx.h"
41
#include "asterisk/module.h"
41
#include "asterisk/module.h"
42
#include "asterisk/say.h"
42
#include "asterisk/say.h"
43
#include "asterisk/app.h"
43
#include "asterisk/app.h"
44
#include "asterisk/utils.h"
44
#include "asterisk/utils.h"
45

    
   
45

   
46
/*** DOCUMENTATION
46
/*** DOCUMENTATION
47
	<application name="Directory" language="en_US">
47
	<application name="Directory" language="en_US">
48
		<synopsis>
48
		<synopsis>
49
			Provide directory of voicemail extensions.
49
			Provide directory of voicemail extensions.
50
		</synopsis>
50
		</synopsis>
51
		<syntax>
51
		<syntax>
52
			<parameter name="vm-context">
52
			<parameter name="vm-context">
53
				<para>This is the context within voicemail.conf to use for the Directory. If not 
53
				<para>This is the context within voicemail.conf to use for the Directory. If not 
54
				specified and <literal>searchcontexts=no</literal> in 
54
				specified and <literal>searchcontexts=no</literal> in 
55
				<filename>voicemail.conf</filename>, then <literal>default</literal> 
55
				<filename>voicemail.conf</filename>, then <literal>default</literal> 
56
				will be assumed.</para>
56
				will be assumed.</para>
57
			</parameter>
57
			</parameter>
58
			<parameter name="dial-context" required="false">
58
			<parameter name="dial-context" required="false">
59
				<para>This is the dialplan context to use when looking for an
59
				<para>This is the dialplan context to use when looking for an
60
				extension that the user has selected, or when jumping to the
60
				extension that the user has selected, or when jumping to the
61
				<literal>o</literal> or <literal>a</literal> extension. If not
61
				<literal>o</literal> or <literal>a</literal> extension. If not
62
				specified, the current context will be used.</para>
62
				specified, the current context will be used.</para>
63
			</parameter>
63
			</parameter>
64
			<parameter name="options" required="false">
64
			<parameter name="options" required="false">
65
				<optionlist>
65
				<optionlist>
66
					<option name="e">
66
					<option name="e">
67
						<para>In addition to the name, also read the extension number to the
67
						<para>In addition to the name, also read the extension number to the
68
						caller before presenting dialing options.</para>
68
						caller before presenting dialing options.</para>
69
					</option>
69
					</option>
70
					<option name="f">
70
					<option name="f">
71
						<para>Allow the caller to enter the first name of a user in the
71
						<para>Allow the caller to enter the first name of a user in the
72
						directory instead of using the last name.  If specified, the
72
						directory instead of using the last name.  If specified, the
73
						optional number argument will be used for the number of
73
						optional number argument will be used for the number of
74
						characters the user should enter.</para>
74
						characters the user should enter.</para>
75
						<argument name="n" required="true" />
75
						<argument name="n" required="true" />
76
					</option>
76
					</option>
77
					<option name="l">
77
					<option name="l">
78
						<para>Allow the caller to enter the last name of a user in the
78
						<para>Allow the caller to enter the last name of a user in the
79
						directory.  This is the default.  If specified, the
79
						directory.  This is the default.  If specified, the
80
						optional number argument will be used for the number of
80
						optional number argument will be used for the number of
81
						characters the user should enter.</para>
81
						characters the user should enter.</para>
82
						<argument name="n" required="true" />
82
						<argument name="n" required="true" />
83
					</option>
83
					</option>
84
					<option name="b">
84
					<option name="b">
85
						<para> Allow the caller to enter either the first or the last name
85
						<para> Allow the caller to enter either the first or the last name
86
						of a user in the directory.  If specified, the optional number
86
						of a user in the directory.  If specified, the optional number
87
						argument will be used for the number of characters the user should enter.</para>
87
						argument will be used for the number of characters the user should enter.</para>
88
						<argument name="n" required="true" />
88
						<argument name="n" required="true" />
89
					</option>
89
					</option>
90
					<option name="m">
90
					<option name="m">
91
						<para>Instead of reading each name sequentially and asking for
91
						<para>Instead of reading each name sequentially and asking for
92
						confirmation, create a menu of up to 8 names.</para>
92
						confirmation, create a menu of up to 8 names.</para>
93
					</option>
93
					</option>
94
					<option name="n">
94
					<option name="n">
95
						<para>Read digits even if the channel is not answered.</para>
95
						<para>Read digits even if the channel is not answered.</para>
96
					</option>
96
					</option>
97
					<option name="p">
97
					<option name="p">
98
						<para>Pause for n milliseconds after the digits are typed.  This is
98
						<para>Pause for n milliseconds after the digits are typed.  This is
99
						helpful for people with cellphones, who are not holding the
99
						helpful for people with cellphones, who are not holding the
100
						receiver to their ear while entering DTMF.</para>
100
						receiver to their ear while entering DTMF.</para>
101
						<argument name="n" required="true" />
101
						<argument name="n" required="true" />
102
					</option>
102
					</option>
103
				</optionlist>
103
				</optionlist>
104
				<note><para>Only one of the <replaceable>f</replaceable>, <replaceable>l</replaceable>, or <replaceable>b</replaceable>
104
				<note><para>Only one of the <replaceable>f</replaceable>, <replaceable>l</replaceable>, or <replaceable>b</replaceable>
105
				options may be specified. <emphasis>If more than one is specified</emphasis>, then Directory will act as 
105
				options may be specified. <emphasis>If more than one is specified</emphasis>, then Directory will act as 
106
				if <replaceable>b</replaceable> was specified.  The number
106
				if <replaceable>b</replaceable> was specified.  The number
107
				of characters for the user to type defaults to <literal>3</literal>.</para></note>
107
				of characters for the user to type defaults to <literal>3</literal>.</para></note>
108
			</parameter>
108
			</parameter>
109
		</syntax>
109
		</syntax>
110
		<description>
110
		<description>
111
			<para>This application will present the calling channel with a directory of extensions from which they can search
111
			<para>This application will present the calling channel with a directory of extensions from which they can search
112
			by name. The list of names and corresponding extensions is retrieved from the
112
			by name. The list of names and corresponding extensions is retrieved from the
113
			voicemail configuration file, <filename>voicemail.conf</filename>.</para>
113
			voicemail configuration file, <filename>voicemail.conf</filename>.</para>
114
			<para>This application will immediately exit if one of the following DTMF digits are
114
			<para>This application will immediately exit if one of the following DTMF digits are
115
			received and the extension to jump to exists:</para>
115
			received and the extension to jump to exists:</para>
116
			<para><literal>0</literal> - Jump to the 'o' extension, if it exists.</para>
116
			<para><literal>0</literal> - Jump to the 'o' extension, if it exists.</para>
117
			<para><literal>*</literal> - Jump to the 'a' extension, if it exists.</para>
117
			<para><literal>*</literal> - Jump to the 'a' extension, if it exists.</para>
118
		</description>
118
		</description>
119
	</application>
119
	</application>
120

    
   
120

   
121
 ***/
121
 ***/
122
static const char app[] = "Directory";
122
static const char app[] = "Directory";
123

    
   
123

   
124
/* For simplicity, I'm keeping the format compatible with the voicemail config,
124
/* For simplicity, I'm keeping the format compatible with the voicemail config,
125
   but i'm open to suggestions for isolating it */
125
   but i'm open to suggestions for isolating it */
126

    
   
126

   
127
#define VOICEMAIL_CONFIG "voicemail.conf"
127
#define VOICEMAIL_CONFIG "voicemail.conf"
128

    
   
128

   
129
enum {
129
enum {
130
	OPT_LISTBYFIRSTNAME = (1 << 0),
130
	OPT_LISTBYFIRSTNAME = (1 << 0),
131
	OPT_SAYEXTENSION =    (1 << 1),
131
	OPT_SAYEXTENSION =    (1 << 1),
132
	OPT_FROMVOICEMAIL =   (1 << 2),
132
	OPT_FROMVOICEMAIL =   (1 << 2),
133
	OPT_SELECTFROMMENU =  (1 << 3),
133
	OPT_SELECTFROMMENU =  (1 << 3),
134
	OPT_LISTBYLASTNAME =  (1 << 4),
134
	OPT_LISTBYLASTNAME =  (1 << 4),
135
	OPT_LISTBYEITHER =    OPT_LISTBYFIRSTNAME | OPT_LISTBYLASTNAME,
135
	OPT_LISTBYEITHER =    OPT_LISTBYFIRSTNAME | OPT_LISTBYLASTNAME,
136
	OPT_PAUSE =           (1 << 5),
136
	OPT_PAUSE =           (1 << 5),
137
	OPT_NOANSWER =        (1 << 6),
137
	OPT_NOANSWER =        (1 << 6),
138
};
138
};
139

    
   
139

   
140
enum {
140
enum {
141
	OPT_ARG_FIRSTNAME =   0,
141
	OPT_ARG_FIRSTNAME =   0,
142
	OPT_ARG_LASTNAME =    1,
142
	OPT_ARG_LASTNAME =    1,
143
	OPT_ARG_EITHER =      2,
143
	OPT_ARG_EITHER =      2,
144
	OPT_ARG_PAUSE =       3,
144
	OPT_ARG_PAUSE =       3,
145
	/* This *must* be the last value in this enum! */
145
	/* This *must* be the last value in this enum! */
146
	OPT_ARG_ARRAY_SIZE =  4,
146
	OPT_ARG_ARRAY_SIZE =  4,
147
};
147
};
148

    
   
148

   
149
struct directory_item {
149
struct directory_item {
150
	char exten[AST_MAX_EXTENSION + 1];
150
	char exten[AST_MAX_EXTENSION + 1];
151
	char name[AST_MAX_EXTENSION + 1];
151
	char name[AST_MAX_EXTENSION + 1];
152
	char context[AST_MAX_CONTEXT + 1];
152
	char context[AST_MAX_CONTEXT + 1];
153
	char key[50]; /* Text to order items. Either lastname+firstname or firstname+lastname */
153
	char key[50]; /* Text to order items. Either lastname+firstname or firstname+lastname */
154

    
   
154

   
155
	AST_LIST_ENTRY(directory_item) entry;
155
	AST_LIST_ENTRY(directory_item) entry;
156
};
156
};
157

    
   
157

   
158
AST_APP_OPTIONS(directory_app_options, {
158
AST_APP_OPTIONS(directory_app_options, {
159
	AST_APP_OPTION_ARG('f', OPT_LISTBYFIRSTNAME, OPT_ARG_FIRSTNAME),
159
	AST_APP_OPTION_ARG('f', OPT_LISTBYFIRSTNAME, OPT_ARG_FIRSTNAME),
160
	AST_APP_OPTION_ARG('l', OPT_LISTBYLASTNAME, OPT_ARG_LASTNAME),
160
	AST_APP_OPTION_ARG('l', OPT_LISTBYLASTNAME, OPT_ARG_LASTNAME),
161
	AST_APP_OPTION_ARG('b', OPT_LISTBYEITHER, OPT_ARG_EITHER),
161
	AST_APP_OPTION_ARG('b', OPT_LISTBYEITHER, OPT_ARG_EITHER),
162
	AST_APP_OPTION_ARG('p', OPT_PAUSE, OPT_ARG_PAUSE),
162
	AST_APP_OPTION_ARG('p', OPT_PAUSE, OPT_ARG_PAUSE),
163
	AST_APP_OPTION('e', OPT_SAYEXTENSION),
163
	AST_APP_OPTION('e', OPT_SAYEXTENSION),
164
	AST_APP_OPTION('v', OPT_FROMVOICEMAIL),
164
	AST_APP_OPTION('v', OPT_FROMVOICEMAIL),
165
	AST_APP_OPTION('m', OPT_SELECTFROMMENU),
165
	AST_APP_OPTION('m', OPT_SELECTFROMMENU),
166
	AST_APP_OPTION('n', OPT_NOANSWER),
166
	AST_APP_OPTION('n', OPT_NOANSWER),
167
});
167
});
168

    
   
168

   
169
static int compare(const char *text, const char *template)
169
static int compare(const char *text, const char *template)
170
{
170
{
171
	char digit;
171
	char digit;
172

    
   
172

   
173
	if (ast_strlen_zero(text)) {
173
	if (ast_strlen_zero(text)) {
174
		return -1;
174
		return -1;
175
	}
175
	}
176

    
   
176

   
177
	while (*template) {
177
	while (*template) {
178
		digit = toupper(*text++);
178
		digit = toupper(*text++);
179
		switch (digit) {
179
		switch (digit) {
180
		case 0:
180
		case 0:
181
			return -1;
181
			return -1;
182
		case '1':
182
		case '1':
183
			digit = '1';
183
			digit = '1';
184
			break;
184
			break;
185
		case '2':
185
		case '2':
186
		case 'A':
186
		case 'A':
187
		case 'B':
187
		case 'B':
188
		case 'C':
188
		case 'C':
189
			digit = '2';
189
			digit = '2';
190
			break;
190
			break;
191
		case '3':
191
		case '3':
192
		case 'D':
192
		case 'D':
193
		case 'E':
193
		case 'E':
194
		case 'F':
194
		case 'F':
195
			digit = '3';
195
			digit = '3';
196
			break;
196
			break;
197
		case '4':
197
		case '4':
198
		case 'G':
198
		case 'G':
199
		case 'H':
199
		case 'H':
200
		case 'I':
200
		case 'I':
201
			digit = '4';
201
			digit = '4';
202
			break;
202
			break;
203
		case '5':
203
		case '5':
204
		case 'J':
204
		case 'J':
205
		case 'K':
205
		case 'K':
206
		case 'L':
206
		case 'L':
207
			digit = '5';
207
			digit = '5';
208
			break;
208
			break;
209
		case '6':
209
		case '6':
210
		case 'M':
210
		case 'M':
211
		case 'N':
211
		case 'N':
212
		case 'O':
212
		case 'O':
213
			digit = '6';
213
			digit = '6';
214
			break;
214
			break;
215
		case '7':
215
		case '7':
216
		case 'P':
216
		case 'P':
217
		case 'Q':
217
		case 'Q':
218
		case 'R':
218
		case 'R':
219
		case 'S':
219
		case 'S':
220
			digit = '7';
220
			digit = '7';
221
			break;
221
			break;
222
		case '8':
222
		case '8':
223
		case 'T':
223
		case 'T':
224
		case 'U':
224
		case 'U':
225
		case 'V':
225
		case 'V':
226
			digit = '8';
226
			digit = '8';
227
			break;
227
			break;
228
		case '9':
228
		case '9':
229
		case 'W':
229
		case 'W':
230
		case 'X':
230
		case 'X':
231
		case 'Y':
231
		case 'Y':
232
		case 'Z':
232
		case 'Z':
233
			digit = '9';
233
			digit = '9';
234
			break;
234
			break;
235

    
   
235

   
236
		default:
236
		default:
237
			if (digit > ' ')
237
			if (digit > ' ')
238
				return -1;
238
				return -1;
239
			continue;
239
			continue;
240
		}
240
		}
241

    
   
241

   
242
		if (*template++ != digit)
242
		if (*template++ != digit)
243
			return -1;
243
			return -1;
244
	}
244
	}
245

    
   
245

   
246
	return 0;
246
	return 0;
247
}
247
}
248

    
   
248

   
249
static int goto_exten(struct ast_channel *chan, const char *dialcontext, char *ext)
249
static int goto_exten(struct ast_channel *chan, const char *dialcontext, char *ext)
250
{
250
{
251
	if (!ast_goto_if_exists(chan, S_OR(dialcontext, ast_channel_context(chan)), ext, 1) ||
251
	if (!ast_goto_if_exists(chan, S_OR(dialcontext, ast_channel_context(chan)), ext, 1) ||
252
		(!ast_strlen_zero(ast_channel_macrocontext(chan)) &&
252
		(!ast_strlen_zero(ast_channel_macrocontext(chan)) &&
253
		!ast_goto_if_exists(chan, ast_channel_macrocontext(chan), ext, 1))) {
253
		!ast_goto_if_exists(chan, ast_channel_macrocontext(chan), ext, 1))) {
254
		return 0;
254
		return 0;
255
	} else {
255
	} else {
256
		ast_log(LOG_WARNING, "Can't find extension '%s' in current context.  "
256
		ast_log(LOG_WARNING, "Can't find extension '%s' in current context.  "
257
			"Not Exiting the Directory!\n", ext);
257
			"Not Exiting the Directory!\n", ext);
258
		return -1;
258
		return -1;
259
	}
259
	}
260
}
260
}
261

    
   
261

   
262
/* play name of mailbox owner.
262
/* play name of mailbox owner.
263
 * returns:  -1 for bad or missing extension
263
 * returns:  -1 for bad or missing extension
264
 *           '1' for selected entry from directory
264
 *           '1' for selected entry from directory
265
 *           '*' for skipped entry from directory
265
 *           '*' for skipped entry from directory
266
 */
266
 */
267
static int play_mailbox_owner(struct ast_channel *chan, const char *context,
267
static int play_mailbox_owner(struct ast_channel *chan, const char *context,
268
	const char *ext, const char *name, struct ast_flags *flags)
268
	const char *ext, const char *name, struct ast_flags *flags)
269
{
269
{
270
	int res = 0;
270
	int res = 0;
271
	if ((res = ast_app_sayname(chan, ext, context)) >= 0) {
271
	if ((res = ast_app_sayname(chan, ext, context)) >= 0) {
272
		ast_stopstream(chan);
272
		ast_stopstream(chan);
273
		/* If Option 'e' was specified, also read the extension number with the name */
273
		/* If Option 'e' was specified, also read the extension number with the name */
274
		if (ast_test_flag(flags, OPT_SAYEXTENSION)) {
274
		if (ast_test_flag(flags, OPT_SAYEXTENSION)) {
275
			ast_stream_and_wait(chan, "vm-extension", AST_DIGIT_ANY);
275
			ast_stream_and_wait(chan, "vm-extension", AST_DIGIT_ANY);
276
			res = ast_say_character_str(chan, ext, AST_DIGIT_ANY, ast_channel_language(chan));
276
			res = ast_say_character_str(chan, ext, AST_DIGIT_ANY, ast_channel_language(chan));
277
		}
277
		}
278
	} else {
278
	} else {
279
		res = ast_say_character_str(chan, S_OR(name, ext), AST_DIGIT_ANY, ast_channel_language(chan));
279
		res = ast_say_character_str(chan, S_OR(name, ext), AST_DIGIT_ANY, ast_channel_language(chan));
280
		if (!ast_strlen_zero(name) && ast_test_flag(flags, OPT_SAYEXTENSION)) {
280
		if (!ast_strlen_zero(name) && ast_test_flag(flags, OPT_SAYEXTENSION)) {
281
			ast_stream_and_wait(chan, "vm-extension", AST_DIGIT_ANY);
281
			ast_stream_and_wait(chan, "vm-extension", AST_DIGIT_ANY);
282
			res = ast_say_character_str(chan, ext, AST_DIGIT_ANY, ast_channel_language(chan));
282
			res = ast_say_character_str(chan, ext, AST_DIGIT_ANY, ast_channel_language(chan));
283
		}
283
		}
284
	}
284
	}
285

    
   
285

   
286
	return res;
286
	return res;
287
}
287
}
288

    
   
288

   
289
static int select_entry(struct ast_channel *chan, const char *dialcontext, const struct directory_item *item, struct ast_flags *flags)
289
static int select_entry(struct ast_channel *chan, const char *dialcontext, const struct directory_item *item, struct ast_flags *flags)
290
{
290
{
291
	ast_debug(1, "Selecting '%s' - %s@%s\n", item->name, item->exten, S_OR(dialcontext, item->context));
291
	ast_debug(1, "Selecting '%s' - %s@%s\n", item->name, item->exten, S_OR(dialcontext, item->context));
292

    
   
292

   
293
	if (ast_test_flag(flags, OPT_FROMVOICEMAIL)) {
293
	if (ast_test_flag(flags, OPT_FROMVOICEMAIL)) {
294
		/* We still want to set the exten though */
294
		/* We still want to set the exten though */
295
		ast_channel_exten_set(chan, item->exten);
295
		ast_channel_exten_set(chan, item->exten);
296
	} else if (ast_goto_if_exists(chan, S_OR(dialcontext, item->context), item->exten, 1)) {
296
	} else if (ast_goto_if_exists(chan, S_OR(dialcontext, item->context), item->exten, 1)) {
297
		ast_log(LOG_WARNING,
297
		ast_log(LOG_WARNING,
298
			"Can't find extension '%s' in context '%s'.  "
298
			"Can't find extension '%s' in context '%s'.  "
299
			"Did you pass the wrong context to Directory?\n",
299
			"Did you pass the wrong context to Directory?\n",
300
			item->exten, S_OR(dialcontext, item->context));
300
			item->exten, S_OR(dialcontext, item->context));
301
		return -1;
301
		return -1;
302
	}
302
	}
303

    
   
303

   
304
	return 0;
304
	return 0;
305
}
305
}
306

    
   
306

   
307
static int select_item_pause(struct ast_channel *chan, struct ast_flags *flags, char *opts[])
307
static int select_item_pause(struct ast_channel *chan, struct ast_flags *flags, char *opts[])
308
{
308
{
309
	int res = 0, opt_pause = 0;
309
	int res = 0, opt_pause = 0;
310

    
   
310

   
311
	if (ast_test_flag(flags, OPT_PAUSE) && !ast_strlen_zero(opts[OPT_ARG_PAUSE])) {
311
	if (ast_test_flag(flags, OPT_PAUSE) && !ast_strlen_zero(opts[OPT_ARG_PAUSE])) {
312
		opt_pause = atoi(opts[OPT_ARG_PAUSE]);
312
		opt_pause = atoi(opts[OPT_ARG_PAUSE]);
313
		if (opt_pause > 3000) {
313
		if (opt_pause > 3000) {
314
			opt_pause = 3000;
314
			opt_pause = 3000;
315
		}
315
		}
316
		res = ast_waitfordigit(chan, opt_pause);
316
		res = ast_waitfordigit(chan, opt_pause);
317
	}
317
	}
318
	return res;
318
	return res;
319
}
319
}
320

    
   
320

   
321
static int select_item_seq(struct ast_channel *chan, struct directory_item **items, int count, const char *dialcontext, struct ast_flags *flags, char *opts[])
321
static int select_item_seq(struct ast_channel *chan, struct directory_item **items, int count, const char *dialcontext, struct ast_flags *flags, char *opts[])
322
{
322
{
323
	struct directory_item *item, **ptr;
323
	struct directory_item *item, **ptr;
324
	int i, res, loop;
324
	int i, res, loop;
325

    
   
325

   
326
	/* option p(n): cellphone pause option */
326
	/* option p(n): cellphone pause option */
327
	/* allow early press of selection key */
327
	/* allow early press of selection key */
328
	res = select_item_pause(chan, flags, opts);
328
	res = select_item_pause(chan, flags, opts);
329

    
   
329

   
330
	for (ptr = items, i = 0; i < count; i++, ptr++) {
330
	for (ptr = items, i = 0; i < count; i++, ptr++) {
331
		item = *ptr;
331
		item = *ptr;
332

    
   
332

   
333
		for (loop = 3 ; loop > 0; loop--) {
333
		for (loop = 3 ; loop > 0; loop--) {
334
			if (!res)
334
			if (!res)
335
				res = play_mailbox_owner(chan, item->context, item->exten, item->name, flags);
335
				res = play_mailbox_owner(chan, item->context, item->exten, item->name, flags);
336
			if (!res)
336
			if (!res)
337
				res = ast_stream_and_wait(chan, "dir-instr", AST_DIGIT_ANY);
337
				res = ast_stream_and_wait(chan, "dir-instr", AST_DIGIT_ANY);
338
			if (!res)
338
			if (!res)
339
				res = ast_waitfordigit(chan, 3000);
339
				res = ast_waitfordigit(chan, 3000);
340
			ast_stopstream(chan);
340
			ast_stopstream(chan);
341
	
341
	
342
			if (res == '0') { /* operator selected */
342
			if (res == '0') { /* operator selected */
343
				goto_exten(chan, dialcontext, "o");
343
				goto_exten(chan, dialcontext, "o");
344
				return '0';
344
				return '0';
345
			} else if (res == '1') { /* Name selected */
345
			} else if (res == '1') { /* Name selected */
346
				return select_entry(chan, dialcontext, item, flags) ? -1 : 1;
346
				return select_entry(chan, dialcontext, item, flags) ? -1 : 1;
347
			} else if (res == '*') {
347
			} else if (res == '*') {
348
				/* Skip to next match in list */
348
				/* Skip to next match in list */
349
				break;
349
				break;
350
			} else if (res == '#') {
350
			} else if (res == '#') {
351
				/* Exit reading, continue in dialplan */
351
				/* Exit reading, continue in dialplan */
352
				return res;
352
				return res;
353
			}
353
			}
354

    
   
354

   
355
			if (res < 0)
355
			if (res < 0)
356
				return -1;
356
				return -1;
357

    
   
357

   
358
			res = 0;
358
			res = 0;
359
		}
359
		}
360
		res = 0;
360
		res = 0;
361
	}
361
	}
362

    
   
362

   
363
	/* Nothing was selected */
363
	/* Nothing was selected */
364
	return 0;
364
	return 0;
365
}
365
}
366

    
   
366

   
367
static int select_item_menu(struct ast_channel *chan, struct directory_item **items, int count, const char *dialcontext, struct ast_flags *flags, char *opts[])
367
static int select_item_menu(struct ast_channel *chan, struct directory_item **items, int count, const char *dialcontext, struct ast_flags *flags, char *opts[])
368
{
368
{
369
	struct directory_item **block, *item;
369
	struct directory_item **block, *item;
370
	int i, limit, res = 0;
370
	int i, limit, res = 0;
371
	char buf[9];
371
	char buf[9];
372

    
   
372

   
373
	/* option p(n): cellphone pause option */
373
	/* option p(n): cellphone pause option */
374
	select_item_pause(chan, flags, opts);
374
	select_item_pause(chan, flags, opts);
375

    
   
375

   
376
	for (block = items; count; block += limit, count -= limit) {
376
	for (block = items; count; block += limit, count -= limit) {
377
		limit = count;
377
		limit = count;
378
		if (limit > 8)
378
		if (limit > 8)
379
			limit = 8;
379
			limit = 8;
380

    
   
380

   
381
		for (i = 0; i < limit && !res; i++) {
381
		for (i = 0; i < limit && !res; i++) {
382
			item = block[i];
382
			item = block[i];
383

    
   
383

   
384
			snprintf(buf, sizeof(buf), "digits/%d", i + 1);
384
			snprintf(buf, sizeof(buf), "digits/%d", i + 1);
385
			/* Press <num> for <name>, [ extension <ext> ] */
385
			/* Press <num> for <name>, [ extension <ext> ] */
386
			res = ast_streamfile(chan, "dir-multi1", ast_channel_language(chan));
386
			res = ast_streamfile(chan, "dir-multi1", ast_channel_language(chan));
387
			if (!res)
387
			if (!res)
388
				res = ast_waitstream(chan, AST_DIGIT_ANY);
388
				res = ast_waitstream(chan, AST_DIGIT_ANY);
389
			if (!res)
389
			if (!res)
390
				res = ast_streamfile(chan, buf, ast_channel_language(chan));
390
				res = ast_streamfile(chan, buf, ast_channel_language(chan));
391
			if (!res)
391
			if (!res)
392
				res = ast_waitstream(chan, AST_DIGIT_ANY);
392
				res = ast_waitstream(chan, AST_DIGIT_ANY);
393
			if (!res)
393
			if (!res)
394
				res = ast_streamfile(chan, "dir-multi2", ast_channel_language(chan));
394
				res = ast_streamfile(chan, "dir-multi2", ast_channel_language(chan));
395
			if (!res)
395
			if (!res)
396
				res = ast_waitstream(chan, AST_DIGIT_ANY);
396
				res = ast_waitstream(chan, AST_DIGIT_ANY);
397
			if (!res)
397
			if (!res)
398
				res = play_mailbox_owner(chan, item->context, item->exten, item->name, flags);
398
				res = play_mailbox_owner(chan, item->context, item->exten, item->name, flags);
399
			if (!res)
399
			if (!res)
400
				res = ast_waitstream(chan, AST_DIGIT_ANY);
400
				res = ast_waitstream(chan, AST_DIGIT_ANY);
401
			if (!res)
401
			if (!res)
402
				res = ast_waitfordigit(chan, 800);
402
				res = ast_waitfordigit(chan, 800);
403
		}
403
		}
404

    
   
404

   
405
		/* Press "9" for more names. */
405
		/* Press "9" for more names. */
406
		if (!res && count > limit) {
406
		if (!res && count > limit) {
407
			res = ast_streamfile(chan, "dir-multi9", ast_channel_language(chan));
407
			res = ast_streamfile(chan, "dir-multi9", ast_channel_language(chan));
408
			if (!res)
408
			if (!res)
409
				res = ast_waitstream(chan, AST_DIGIT_ANY);
409
				res = ast_waitstream(chan, AST_DIGIT_ANY);
410
		}
410
		}
411

    
   
411

   
412
		if (!res) {
412
		if (!res) {
413
			res = ast_waitfordigit(chan, 3000);
413
			res = ast_waitfordigit(chan, 3000);
414
		}
414
		}
415

    
   
415

   
416
		if (res && res > '0' && res < '1' + limit) {
416
		if (res && res > '0' && res < '1' + limit) {
417
			return select_entry(chan, dialcontext, block[res - '1'], flags) ? -1 : 1;
417
			return select_entry(chan, dialcontext, block[res - '1'], flags) ? -1 : 1;
418
		}
418
		}
419

    
   
419

   
420
		if (res < 0)
420
		if (res < 0)
421
			return -1;
421
			return -1;
422

    
   
422

   
423
		res = 0;
423
		res = 0;
424
	}
424
	}
425

    
   
425

   
426
	/* Nothing was selected */
426
	/* Nothing was selected */
427
	return 0;
427
	return 0;
428
}
428
}
429

    
   
429

   
430
AST_THREADSTORAGE(commonbuf);
430
AST_THREADSTORAGE(commonbuf);
431

    
   
431

   
432
static struct ast_config *realtime_directory(char *context)
432
static struct ast_config *realtime_directory(char *context)
433
{
433
{
434
	struct ast_config *cfg;
434
	struct ast_config *cfg;
435
	struct ast_config *rtdata;
435
	struct ast_config *rtdata;
436
	struct ast_category *cat;
436
	struct ast_category *cat;
437
	struct ast_variable *var;
437
	struct ast_variable *var;
438
	char *mailbox;
438
	char *mailbox;
439
	const char *fullname;
439
	const char *fullname;
440
	const char *hidefromdir, *searchcontexts = NULL;
440
	const char *hidefromdir, *searchcontexts = NULL;
441
	struct ast_flags config_flags = { 0 };
441
	struct ast_flags config_flags = { 0 };
442
	struct ast_str *tmp = ast_str_thread_get(&commonbuf, 100);
442
	struct ast_str *tmp = ast_str_thread_get(&commonbuf, 100);
443

    
   
443

   
444
	if (!tmp) {
444
	if (!tmp) {
445
		return NULL;
445
		return NULL;
446
	}
446
	}
447

    
   
447

   
448
	/* Load flat file config. */
448
	/* Load flat file config. */
449
	cfg = ast_config_load(VOICEMAIL_CONFIG, config_flags);
449
	cfg = ast_config_load(VOICEMAIL_CONFIG, config_flags);
450

    
   
450

   
451
	if (!cfg) {
451
	if (!cfg) {
452
		/* Loading config failed. */
452
		/* Loading config failed. */
453
		ast_log(LOG_WARNING, "Loading config failed.\n");
453
		ast_log(LOG_WARNING, "Loading config failed.\n");
454
		return NULL;
454
		return NULL;
455
	} else if (cfg == CONFIG_STATUS_FILEINVALID) {
455
	} else if (cfg == CONFIG_STATUS_FILEINVALID) {
456
		ast_log(LOG_ERROR, "Config file %s is in an invalid format.  Aborting.\n", VOICEMAIL_CONFIG);
456
		ast_log(LOG_ERROR, "Config file %s is in an invalid format.  Aborting.\n", VOICEMAIL_CONFIG);
457
		return NULL;
457
		return NULL;
458
	}
458
	}
459

    
   
459

   
460
	/* Get realtime entries, categorized by their mailbox number
460
	/* Get realtime entries, categorized by their mailbox number
461
	   and present in the requested context */
461
	   and present in the requested context */
462
	if (ast_strlen_zero(context) && (searchcontexts = ast_variable_retrieve(cfg, "general", "searchcontexts"))) {
462
	if (ast_strlen_zero(context) && (searchcontexts = ast_variable_retrieve(cfg, "general", "searchcontexts"))) {
463
		if (ast_true(searchcontexts)) {
463
		if (ast_true(searchcontexts)) {
464
			rtdata = ast_load_realtime_multientry("voicemail", "mailbox LIKE", "%", SENTINEL);
464
			rtdata = ast_load_realtime_multientry("voicemail", "mailbox LIKE", "%", SENTINEL);
465
			context = NULL;
465
			context = NULL;
466
		} else {
466
		} else {
467
			rtdata = ast_load_realtime_multientry("voicemail", "mailbox LIKE", "%", "context", "default", SENTINEL);
467
			rtdata = ast_load_realtime_multientry("voicemail", "mailbox LIKE", "%", "context", "default", SENTINEL);
468
			context = "default";
468
			context = "default";
469
		}
469
		}
470
	} else {
470
	} else {
471
		rtdata = ast_load_realtime_multientry("voicemail", "mailbox LIKE", "%", "context", context, SENTINEL);
471
		rtdata = ast_load_realtime_multientry("voicemail", "mailbox LIKE", "%", "context", context, SENTINEL);
472
	}
472
	}
473

    
   
473

   
474
	/* if there are no results, just return the entries from the config file */
474
	/* if there are no results, just return the entries from the config file */
475
	if (!rtdata) {
475
	if (!rtdata) {
476
		return cfg;
476
		return cfg;
477
	}
477
	}
478

    
   
478

   
479
	mailbox = NULL;
479
	mailbox = NULL;
480
	while ( (mailbox = ast_category_browse(rtdata, mailbox)) ) {
480
	while ( (mailbox = ast_category_browse(rtdata, mailbox)) ) {
481
		struct ast_variable *alias;
481
		struct ast_variable *alias;
482
		const char *ctx = ast_variable_retrieve(rtdata, mailbox, "context");
482
		const char *ctx = ast_variable_retrieve(rtdata, mailbox, "context");
483

    
   
483

   
484
		fullname = ast_variable_retrieve(rtdata, mailbox, "fullname");
484
		fullname = ast_variable_retrieve(rtdata, mailbox, "fullname");
485
		hidefromdir = ast_variable_retrieve(rtdata, mailbox, "hidefromdir");
485
		hidefromdir = ast_variable_retrieve(rtdata, mailbox, "hidefromdir");
486
		if (ast_true(hidefromdir)) {
486
		if (ast_true(hidefromdir)) {
487
			/* Skip hidden */
487
			/* Skip hidden */
488
			continue;
488
			continue;
489
		}
489
		}
490
		ast_str_set(&tmp, 0, "no-password,%s", S_OR(fullname, ""));
490
		ast_str_set(&tmp, 0, "no-password,%s", S_OR(fullname, ""));
491
		if (ast_variable_retrieve(rtdata, mailbox, "alias")) {
491
		if (ast_variable_retrieve(rtdata, mailbox, "alias")) {
492
			for (alias = ast_variable_browse(rtdata, mailbox); alias; alias = alias->next) {
492
			for (alias = ast_variable_browse(rtdata, mailbox); alias; alias = alias->next) {
493
				if (!strcmp(alias->name, "alias")) {
493
				if (!strcasecmp(alias->name, "alias")) {
494
					ast_str_append(&tmp, 0, "|alias=%s", alias->value);
494
					ast_str_append(&tmp, 0, "|alias=%s", alias->value);
495
				}
495
				}
496
			}
496
			}
497
		}
497
		}
498

    
   
498

   
499
		/* Does the context exist within the config file? If not, make one */
499
		/* Does the context exist within the config file? If not, make one */
500
		if (!(cat = ast_category_get(cfg, ctx))) {
500
		if (!(cat = ast_category_get(cfg, ctx))) {
501
			if (!(cat = ast_category_new(ctx, "", 99999))) {
501
			if (!(cat = ast_category_new(ctx, "", 99999))) {
502
				ast_log(LOG_WARNING, "Out of memory\n");
502
				ast_log(LOG_WARNING, "Out of memory\n");
503
				ast_config_destroy(cfg);
503
				ast_config_destroy(cfg);
504
				if (rtdata) {
504
				if (rtdata) {
505
					ast_config_destroy(rtdata);
505
					ast_config_destroy(rtdata);
506
				}
506
				}
507
				return NULL;
507
				return NULL;
508
			}
508
			}
509
			ast_category_append(cfg, cat);
509
			ast_category_append(cfg, cat);
510
		}
510
		}
511

    
   
511

   
512
		if ((var = ast_variable_new(mailbox, ast_str_buffer(tmp), ""))) {
512
		if ((var = ast_variable_new(mailbox, ast_str_buffer(tmp), ""))) {
513
			ast_variable_append(cat, var);
513
			ast_variable_append(cat, var);
514
		} else {
514
		} else {
515
			ast_log(LOG_WARNING, "Out of memory adding mailbox '%s'\n", mailbox);
515
			ast_log(LOG_WARNING, "Out of memory adding mailbox '%s'\n", mailbox);
516
		}
516
		}
517
	}
517
	}
518
	ast_config_destroy(rtdata);
518
	ast_config_destroy(rtdata);
519

    
   
519

   
520
	return cfg;
520
	return cfg;
521
}
521
}
522

    
   
522

   
523
static int check_match(struct directory_item **result, const char *item_context, const char *item_fullname, const char *item_ext, const char *pattern_ext, int use_first_name)
523
static int check_match(struct directory_item **result, const char *item_context, const char *item_fullname, const char *item_ext, const char *pattern_ext, int use_first_name)
524
{
524
{
525
	struct directory_item *item;
525
	struct directory_item *item;
526
	const char *key = NULL;
526
	const char *key = NULL;
527
	int namelen;
527
	int namelen;
528

    
   
528

   
529
	if (ast_strlen_zero(item_fullname)) {
529
	if (ast_strlen_zero(item_fullname)) {
530
		return 0;
530
		return 0;
531
	}
531
	}
532

    
   
532

   
533
	/* Set key to last name or first name depending on search mode */
533
	/* Set key to last name or first name depending on search mode */
534
	if (!use_first_name)
534
	if (!use_first_name)
535
		key = strchr(item_fullname, ' ');
535
		key = strchr(item_fullname, ' ');
536

    
   
536

   
537
	if (key)
537
	if (key)
538
		key++;
538
		key++;
539
	else
539
	else
540
		key = item_fullname;
540
		key = item_fullname;
541

    
   
541

   
542
	if (compare(key, pattern_ext))
542
	if (compare(key, pattern_ext))
543
		return 0;
543
		return 0;
544

    
   
544

   
545
	ast_debug(1, "Found match %s@%s\n", item_ext, item_context);
545
	ast_debug(1, "Found match %s@%s\n", item_ext, item_context);
546

    
   
546

   
547
	/* Match */
547
	/* Match */
548
	item = ast_calloc(1, sizeof(*item));
548
	item = ast_calloc(1, sizeof(*item));
549
	if (!item)
549
	if (!item)
550
		return -1;
550
		return -1;
551
	ast_copy_string(item->context, item_context, sizeof(item->context));
551
	ast_copy_string(item->context, item_context, sizeof(item->context));
552
	ast_copy_string(item->name, item_fullname, sizeof(item->name));
552
	ast_copy_string(item->name, item_fullname, sizeof(item->name));
553
	ast_copy_string(item->exten, item_ext, sizeof(item->exten));
553
	ast_copy_string(item->exten, item_ext, sizeof(item->exten));
554

    
   
554

   
555
	ast_copy_string(item->key, key, sizeof(item->key));
555
	ast_copy_string(item->key, key, sizeof(item->key));
556
	if (key != item_fullname) {
556
	if (key != item_fullname) {
557
		/* Key is the last name. Append first name to key in order to sort Last,First */
557
		/* Key is the last name. Append first name to key in order to sort Last,First */
558
		namelen = key - item_fullname - 1;
558
		namelen = key - item_fullname - 1;
559
		if (namelen > sizeof(item->key) - strlen(item->key) - 1)
559
		if (namelen > sizeof(item->key) - strlen(item->key) - 1)
560
			namelen = sizeof(item->key) - strlen(item->key) - 1;
560
			namelen = sizeof(item->key) - strlen(item->key) - 1;
561
		strncat(item->key, item_fullname, namelen);
561
		strncat(item->key, item_fullname, namelen);
562
	}
562
	}
563

    
   
563

   
564
	*result = item;
564
	*result = item;
565
	return 1;
565
	return 1;
566
}
566
}
567

    
   
567

   
568
typedef AST_LIST_HEAD_NOLOCK(, directory_item) itemlist;
568
typedef AST_LIST_HEAD_NOLOCK(, directory_item) itemlist;
569

    
   
569

   
570
static int search_directory_sub(const char *context, struct ast_config *vmcfg, struct ast_config *ucfg, const char *ext, struct ast_flags flags, itemlist *alist)
570
static int search_directory_sub(const char *context, struct ast_config *vmcfg, struct ast_config *ucfg, const char *ext, struct ast_flags flags, itemlist *alist)
571
{
571
{
572
	struct ast_variable *v;
572
	struct ast_variable *v;
573
	struct ast_str *buf = ast_str_thread_get(&commonbuf, 100);
573
	struct ast_str *buf = ast_str_thread_get(&commonbuf, 100);
574
	char *pos, *bufptr, *cat, *alias;
574
	char *pos, *bufptr, *cat, *alias;
575
	struct directory_item *item;
575
	struct directory_item *item;
576
	int res;
576
	int res;
577

    
   
577

   
578
	if (!buf) {
578
	if (!buf) {
579
		return -1;
579
		return -1;
580
	}
580
	}
581

    
   
581

   
582
	ast_debug(2, "Pattern: %s\n", ext);
582
	ast_debug(2, "Pattern: %s\n", ext);
583

    
   
583

   
584
	for (v = ast_variable_browse(vmcfg, context); v; v = v->next) {
584
	for (v = ast_variable_browse(vmcfg, context); v; v = v->next) {
585

    
   
585

   
586
		/* Ignore hidden */
586
		/* Ignore hidden */
587
		if (strcasestr(v->value, "hidefromdir=yes")) {
587
		if (strcasestr(v->value, "hidefromdir=yes")) {
588
			continue;
588
			continue;
589
		}
589
		}
590

    
   
590

   
591
		ast_str_set(&buf, 0, "%s", v->value);
591
		ast_str_set(&buf, 0, "%s", v->value);
592
		bufptr = ast_str_buffer(buf);
592
		bufptr = ast_str_buffer(buf);
593

    
   
593

   
594
		/* password,Full Name,email,pager,options */
594
		/* password,Full Name,email,pager,options */
595
		strsep(&bufptr, ",");
595
		strsep(&bufptr, ",");
596
		pos = strsep(&bufptr, ",");
596
		pos = strsep(&bufptr, ",");
597

    
   
597

   
598
		/* No name to compare against */
598
		/* No name to compare against */
599
		if (ast_strlen_zero(pos)) {
599
		if (ast_strlen_zero(pos)) {
600
			continue;
600
			continue;
601
		}
601
		}
602

    
   
602

   
603
		res = 0;
603
		res = 0;
604
		if (ast_test_flag(&flags, OPT_LISTBYLASTNAME)) {
604
		if (ast_test_flag(&flags, OPT_LISTBYLASTNAME)) {
605
			res = check_match(&item, context, pos, v->name, ext, 0 /* use_first_name */);
605
			res = check_match(&item, context, pos, v->name, ext, 0 /* use_first_name */);
606
		}
606
		}
607
		if (!res && ast_test_flag(&flags, OPT_LISTBYFIRSTNAME)) {
607
		if (!res && ast_test_flag(&flags, OPT_LISTBYFIRSTNAME)) {
608
			res = check_match(&item, context, pos, v->name, ext, 1 /* use_first_name */);
608
			res = check_match(&item, context, pos, v->name, ext, 1 /* use_first_name */);
609
		}
609
		}
610
		if (!res && (alias = strcasestr(bufptr, "alias="))) {
610
		if (!res && (alias = strcasestr(bufptr, "alias="))) {
611
			char *a;
611
			char *a;
612
			ast_debug(1, "Found alias: %s\n", alias);
612
			ast_debug(1, "Found alias: %s\n", alias);
613
			while ((a = strsep(&alias, "|"))) {
613
			while ((a = strsep(&alias, "|"))) {
614
				if (!strncmp(a, "alias=", 6)) {
614
				if (!strncasecmp(a, "alias=", 6)) {
615
					if ((res = check_match(&item, context, a + 6, v->name, ext, 1))) {
615
					if ((res = check_match(&item, context, a + 6, v->name, ext, 1))) {
616
						break;
616
						break;
617
					}
617
					}
618
				}
618
				}
619
			}
619
			}
620
		}
620
		}
621

    
   
621

   
622
		if (!res) {
622
		if (!res) {
623
			continue;
623
			continue;
624
		} else if (res < 0) {
624
		} else if (res < 0) {
625
			return -1;
625
			return -1;
626
		}
626
		}
627

    
   
627

   
628
		AST_LIST_INSERT_TAIL(alist, item, entry);
628
		AST_LIST_INSERT_TAIL(alist, item, entry);
629
	}
629
	}
630

    
   
630

   
631
	if (ucfg) {
631
	if (ucfg) {
632
		for (cat = ast_category_browse(ucfg, NULL); cat ; cat = ast_category_browse(ucfg, cat)) {
632
		for (cat = ast_category_browse(ucfg, NULL); cat ; cat = ast_category_browse(ucfg, cat)) {
633
			const char *position;
633
			const char *position;
634

    
   
634

   
635
			if (!strcasecmp(cat, "general")) {
635
			if (!strcasecmp(cat, "general")) {
636
				continue;
636
				continue;
637
			}
637
			}
638
			if (!ast_true(ast_config_option(ucfg, cat, "hasdirectory"))) {
638
			if (!ast_true(ast_config_option(ucfg, cat, "hasdirectory"))) {
639
				continue;
639
				continue;
640
			}
640
			}
641

    
   
641

   
642
			/* Find all candidate extensions */
642
			/* Find all candidate extensions */
643
			if (!(position = ast_variable_retrieve(ucfg, cat, "fullname"))) {
643
			if (!(position = ast_variable_retrieve(ucfg, cat, "fullname"))) {
644
				continue;
644
				continue;
645
			}
645
			}
646

    
   
646

   
647
			res = 0;
647
			res = 0;
648
			if (ast_test_flag(&flags, OPT_LISTBYLASTNAME)) {
648
			if (ast_test_flag(&flags, OPT_LISTBYLASTNAME)) {
649
				res = check_match(&item, context, position, cat, ext, 0 /* use_first_name */);
649
				res = check_match(&item, context, position, cat, ext, 0 /* use_first_name */);
650
			}
650
			}
651
			if (!res && ast_test_flag(&flags, OPT_LISTBYFIRSTNAME)) {
651
			if (!res && ast_test_flag(&flags, OPT_LISTBYFIRSTNAME)) {
652
				res = check_match(&item, context, position, cat, ext, 1 /* use_first_name */);
652
				res = check_match(&item, context, position, cat, ext, 1 /* use_first_name */);
653
			}
653
			}
654
			if (!res) {
654
			if (!res) {
655
				struct ast_variable *alias;
655
				struct ast_variable *alias;
656
				for (alias = ast_variable_browse(ucfg, cat); alias; alias = alias->next) {
656
				for (alias = ast_variable_browse(ucfg, cat); alias; alias = alias->next) {
657
					if (!strcasecmp(v->name, "alias") && (res = check_match(&item, context, v->value, cat, ext, 1))) {
657
					if (!strcasecmp(v->name, "alias") && (res = check_match(&item, context, v->value, cat, ext, 1))) {
658
						break;
658
						break;
659
					}
659
					}
660
				}
660
				}
661
			}
661
			}
662

    
   
662

   
663
			if (!res) {
663
			if (!res) {
664
				continue;
664
				continue;
665
			} else if (res < 0) {
665
			} else if (res < 0) {
666
				return -1;
666
				return -1;
667
			}
667
			}
668

    
   
668

   
669
			AST_LIST_INSERT_TAIL(alist, item, entry);
669
			AST_LIST_INSERT_TAIL(alist, item, entry);
670
		}
670
		}
671
	}
671
	}
672
	return 0;
672
	return 0;
673
}
673
}
674

    
   
674

   
675
static int search_directory(const char *context, struct ast_config *vmcfg, struct ast_config *ucfg, const char *ext, struct ast_flags flags, itemlist *alist)
675
static int search_directory(const char *context, struct ast_config *vmcfg, struct ast_config *ucfg, const char *ext, struct ast_flags flags, itemlist *alist)
676
{
676
{
677
	const char *searchcontexts = ast_variable_retrieve(vmcfg, "general", "searchcontexts");
677
	const char *searchcontexts = ast_variable_retrieve(vmcfg, "general", "searchcontexts");
678
	if (ast_strlen_zero(context)) {
678
	if (ast_strlen_zero(context)) {
679
		if (!ast_strlen_zero(searchcontexts) && ast_true(searchcontexts)) {
679
		if (!ast_strlen_zero(searchcontexts) && ast_true(searchcontexts)) {
680
			/* Browse each context for a match */
680
			/* Browse each context for a match */
681
			int res;
681
			int res;
682
			const char *catg;
682
			const char *catg;
683
			for (catg = ast_category_browse(vmcfg, NULL); catg; catg = ast_category_browse(vmcfg, catg)) {
683
			for (catg = ast_category_browse(vmcfg, NULL); catg; catg = ast_category_browse(vmcfg, catg)) {
684
				if (!strcmp(catg, "general") || !strcmp(catg, "zonemessages")) {
684
				if (!strcmp(catg, "general") || !strcmp(catg, "zonemessages")) {
685
					continue;
685
					continue;
686
				}
686
				}
687

    
   
687

   
688
				if ((res = search_directory_sub(catg, vmcfg, ucfg, ext, flags, alist))) {
688
				if ((res = search_directory_sub(catg, vmcfg, ucfg, ext, flags, alist))) {
689
					return res;
689
					return res;
690
				}
690
				}
691
			}
691
			}
692
			return 0;
692
			return 0;
693
		} else {
693
		} else {
694
			ast_debug(1, "Searching by category default\n");
694
			ast_debug(1, "Searching by category default\n");
695
			return search_directory_sub("default", vmcfg, ucfg, ext, flags, alist);
695
			return search_directory_sub("default", vmcfg, ucfg, ext, flags, alist);
696
		}
696
		}
697
	} else {
697
	} else {
698
		/* Browse only the listed context for a match */
698
		/* Browse only the listed context for a match */
699
		ast_debug(1, "Searching by category %s\n", context);
699
		ast_debug(1, "Searching by category %s\n", context);
700
		return search_directory_sub(context, vmcfg, ucfg, ext, flags, alist);
700
		return search_directory_sub(context, vmcfg, ucfg, ext, flags, alist);
701
	}
701
	}
702
}
702
}
703

    
   
703

   
704
static void sort_items(struct directory_item **sorted, int count)
704
static void sort_items(struct directory_item **sorted, int count)
705
{
705
{
706
	int reordered, i;
706
	int reordered, i;
707
	struct directory_item **ptr, *tmp;
707
	struct directory_item **ptr, *tmp;
708

    
   
708

   
709
	if (count < 2)
709
	if (count < 2)
710
		return;
710
		return;
711

    
   
711

   
712
	/* Bubble-sort items by the key */
712
	/* Bubble-sort items by the key */
713
	do {
713
	do {
714
		reordered = 0;
714
		reordered = 0;
715
		for (ptr = sorted, i = 0; i < count - 1; i++, ptr++) {
715
		for (ptr = sorted, i = 0; i < count - 1; i++, ptr++) {
716
			if (strcasecmp(ptr[0]->key, ptr[1]->key) > 0) {
716
			if (strcasecmp(ptr[0]->key, ptr[1]->key) > 0) {
717
				tmp = ptr[0];
717
				tmp = ptr[0];
718
				ptr[0] = ptr[1];
718
				ptr[0] = ptr[1];
719
				ptr[1] = tmp;
719
				ptr[1] = tmp;
720
				reordered++;
720
				reordered++;
721
			}
721
			}
722
		}
722
		}
723
	} while (reordered);
723
	} while (reordered);
724
}
724
}
725

    
   
725

   
726
static int do_directory(struct ast_channel *chan, struct ast_config *vmcfg, struct ast_config *ucfg, char *context, char *dialcontext, char digit, int digits, struct ast_flags *flags, char *opts[])
726
static int do_directory(struct ast_channel *chan, struct ast_config *vmcfg, struct ast_config *ucfg, char *context, char *dialcontext, char digit, int digits, struct ast_flags *flags, char *opts[])
727
{
727
{
728
	/* Read in the first three digits..  "digit" is the first digit, already read */
728
	/* Read in the first three digits..  "digit" is the first digit, already read */
729
	int res = 0;
729
	int res = 0;
730
	itemlist alist = AST_LIST_HEAD_NOLOCK_INIT_VALUE;
730
	itemlist alist = AST_LIST_HEAD_NOLOCK_INIT_VALUE;
731
	struct directory_item *item, **ptr, **sorted = NULL;
731
	struct directory_item *item, **ptr, **sorted = NULL;
732
	int count, i;
732
	int count, i;
733
	char ext[10] = "";
733
	char ext[10] = "";
734

    
   
734

   
735
	if (digit == '0' && !goto_exten(chan, dialcontext, "o")) {
735
	if (digit == '0' && !goto_exten(chan, dialcontext, "o")) {
736
		return digit;
736
		return digit;
737
	}
737
	}
738

    
   
738

   
739
	if (digit == '*' && !goto_exten(chan, dialcontext, "a")) {
739
	if (digit == '*' && !goto_exten(chan, dialcontext, "a")) {
740
		return digit;
740
		return digit;
741
	}
741
	}
742

    
   
742

   
743
	ext[0] = digit;
743
	ext[0] = digit;
744
	if (ast_readstring(chan, ext + 1, digits - 1, 3000, 3000, "#") < 0)
744
	if (ast_readstring(chan, ext + 1, digits - 1, 3000, 3000, "#") < 0)
745
		return -1;
745
		return -1;
746

    
   
746

   
747
	res = search_directory(context, vmcfg, ucfg, ext, *flags, &alist);
747
	res = search_directory(context, vmcfg, ucfg, ext, *flags, &alist);
748
	if (res)
748
	if (res)
749
		goto exit;
749
		goto exit;
750

    
   
750

   
751
	/* Count items in the list */
751
	/* Count items in the list */
752
	count = 0;
752
	count = 0;
753
	AST_LIST_TRAVERSE(&alist, item, entry) {
753
	AST_LIST_TRAVERSE(&alist, item, entry) {
754
		count++;
754
		count++;
755
	}
755
	}
756

    
   
756

   
757
	if (count < 1) {
757
	if (count < 1) {
758
		res = ast_streamfile(chan, "dir-nomatch", ast_channel_language(chan));
758
		res = ast_streamfile(chan, "dir-nomatch", ast_channel_language(chan));
759
		goto exit;
759
		goto exit;
760
	}
760
	}
761

    
   
761

   
762

    
   
762

   
763
	/* Create plain array of pointers to items (for sorting) */
763
	/* Create plain array of pointers to items (for sorting) */
764
	sorted = ast_calloc(count, sizeof(*sorted));
764
	sorted = ast_calloc(count, sizeof(*sorted));
765

    
   
765

   
766
	ptr = sorted;
766
	ptr = sorted;
767
	AST_LIST_TRAVERSE(&alist, item, entry) {
767
	AST_LIST_TRAVERSE(&alist, item, entry) {
768
		*ptr++ = item;
768
		*ptr++ = item;
769
	}
769
	}
770

    
   
770

   
771
	/* Sort items */
771
	/* Sort items */
772
	sort_items(sorted, count);
772
	sort_items(sorted, count);
773

    
   
773

   
774
	if (option_debug) {
774
	if (option_debug) {
775
		ast_debug(2, "Listing matching entries:\n");
775
		ast_debug(2, "Listing matching entries:\n");
776
		for (ptr = sorted, i = 0; i < count; i++, ptr++) {
776
		for (ptr = sorted, i = 0; i < count; i++, ptr++) {
777
			ast_debug(2, "%s: %s\n", ptr[0]->exten, ptr[0]->name);
777
			ast_debug(2, "%s: %s\n", ptr[0]->exten, ptr[0]->name);
778
		}
778
		}
779
	}
779
	}
780

    
   
780

   
781
	if (ast_test_flag(flags, OPT_SELECTFROMMENU)) {
781
	if (ast_test_flag(flags, OPT_SELECTFROMMENU)) {
782
		/* Offer multiple entries at the same time */
782
		/* Offer multiple entries at the same time */
783
		res = select_item_menu(chan, sorted, count, dialcontext, flags, opts);
783
		res = select_item_menu(chan, sorted, count, dialcontext, flags, opts);
784
	} else {
784
	} else {
785
		/* Offer entries one by one */
785
		/* Offer entries one by one */
786
		res = select_item_seq(chan, sorted, count, dialcontext, flags, opts);
786
		res = select_item_seq(chan, sorted, count, dialcontext, flags, opts);
787
	}
787
	}
788

    
   
788

   
789
	if (!res) {
789
	if (!res) {
790
		res = ast_streamfile(chan, "dir-nomore", ast_channel_language(chan));
790
		res = ast_streamfile(chan, "dir-nomore", ast_channel_language(chan));
791
	}
791
	}
792

    
   
792

   
793
exit:
793
exit:
794
	if (sorted)
794
	if (sorted)
795
		ast_free(sorted);
795
		ast_free(sorted);
796

    
   
796

   
797
	while ((item = AST_LIST_REMOVE_HEAD(&alist, entry)))
797
	while ((item = AST_LIST_REMOVE_HEAD(&alist, entry)))
798
		ast_free(item);
798
		ast_free(item);
799

    
   
799

   
800
	return res;
800
	return res;
801
}
801
}
802

    
   
802

   
803
static int directory_exec(struct ast_channel *chan, const char *data)
803
static int directory_exec(struct ast_channel *chan, const char *data)
804
{
804
{
805
	int res = 0, digit = 3;
805
	int res = 0, digit = 3;
806
	struct ast_config *cfg, *ucfg;
806
	struct ast_config *cfg, *ucfg;
807
	const char *dirintro;
807
	const char *dirintro;
808
	char *parse, *opts[OPT_ARG_ARRAY_SIZE] = { 0, };
808
	char *parse, *opts[OPT_ARG_ARRAY_SIZE] = { 0, };
809
	struct ast_flags flags = { 0 };
809
	struct ast_flags flags = { 0 };
810
	struct ast_flags config_flags = { 0 };
810
	struct ast_flags config_flags = { 0 };
811
	enum { FIRST, LAST, BOTH } which = LAST;
811
	enum { FIRST, LAST, BOTH } which = LAST;
812
	char digits[9] = "digits/3";
812
	char digits[9] = "digits/3";
813
	AST_DECLARE_APP_ARGS(args,
813
	AST_DECLARE_APP_ARGS(args,
814
		AST_APP_ARG(vmcontext);
814
		AST_APP_ARG(vmcontext);
815
		AST_APP_ARG(dialcontext);
815
		AST_APP_ARG(dialcontext);
816
		AST_APP_ARG(options);
816
		AST_APP_ARG(options);
817
	);
817
	);
818

    
   
818

   
819
	parse = ast_strdupa(data);
819
	parse = ast_strdupa(data);
820

    
   
820

   
821
	AST_STANDARD_APP_ARGS(args, parse);
821
	AST_STANDARD_APP_ARGS(args, parse);
822

    
   
822

   
823
	if (args.options && ast_app_parse_options(directory_app_options, &flags, opts, args.options))
823
	if (args.options && ast_app_parse_options(directory_app_options, &flags, opts, args.options))
824
		return -1;
824
		return -1;
825

    
   
825

   
826
	if (!(cfg = realtime_directory(args.vmcontext))) {
826
	if (!(cfg = realtime_directory(args.vmcontext))) {
827
		ast_log(LOG_ERROR, "Unable to read the configuration data!\n");
827
		ast_log(LOG_ERROR, "Unable to read the configuration data!\n");
828
		return -1;
828
		return -1;
829
	}
829
	}
830

    
   
830

   
831
	if ((ucfg = ast_config_load("users.conf", config_flags)) == CONFIG_STATUS_FILEINVALID) {
831
	if ((ucfg = ast_config_load("users.conf", config_flags)) == CONFIG_STATUS_FILEINVALID) {
832
		ast_log(LOG_ERROR, "Config file users.conf is in an invalid format.  Aborting.\n");
832
		ast_log(LOG_ERROR, "Config file users.conf is in an invalid format.  Aborting.\n");
833
		ucfg = NULL;
833
		ucfg = NULL;
834
	}
834
	}
835

    
   
835

   
836
	dirintro = ast_variable_retrieve(cfg, args.vmcontext, "directoryintro");
836
	dirintro = ast_variable_retrieve(cfg, args.vmcontext, "directoryintro");
837
	if (ast_strlen_zero(dirintro))
837
	if (ast_strlen_zero(dirintro))
838
		dirintro = ast_variable_retrieve(cfg, "general", "directoryintro");
838
		dirintro = ast_variable_retrieve(cfg, "general", "directoryintro");
839
	/* the above prompts probably should be modified to include 0 for dialing operator
839
	/* the above prompts probably should be modified to include 0 for dialing operator
840
	   and # for exiting (continues in dialplan) */
840
	   and # for exiting (continues in dialplan) */
841

    
   
841

   
842
	if (ast_test_flag(&flags, OPT_LISTBYFIRSTNAME) && ast_test_flag(&flags, OPT_LISTBYLASTNAME)) {
842
	if (ast_test_flag(&flags, OPT_LISTBYFIRSTNAME) && ast_test_flag(&flags, OPT_LISTBYLASTNAME)) {
843
		if (!ast_strlen_zero(opts[OPT_ARG_EITHER])) {
843
		if (!ast_strlen_zero(opts[OPT_ARG_EITHER])) {
844
			digit = atoi(opts[OPT_ARG_EITHER]);
844
			digit = atoi(opts[OPT_ARG_EITHER]);
845
		}
845
		}
846
		which = BOTH;
846
		which = BOTH;
847
	} else if (ast_test_flag(&flags, OPT_LISTBYFIRSTNAME)) {
847
	} else if (ast_test_flag(&flags, OPT_LISTBYFIRSTNAME)) {
848
		if (!ast_strlen_zero(opts[OPT_ARG_FIRSTNAME])) {
848
		if (!ast_strlen_zero(opts[OPT_ARG_FIRSTNAME])) {
849
			digit = atoi(opts[OPT_ARG_FIRSTNAME]);
849
			digit = atoi(opts[OPT_ARG_FIRSTNAME]);
850
		}
850
		}
851
		which = FIRST;
851
		which = FIRST;
852
	} else {
852
	} else {
853
		if (!ast_strlen_zero(opts[OPT_ARG_LASTNAME])) {
853
		if (!ast_strlen_zero(opts[OPT_ARG_LASTNAME])) {
854
			digit = atoi(opts[OPT_ARG_LASTNAME]);
854
			digit = atoi(opts[OPT_ARG_LASTNAME]);
855
		}
855
		}
856
		which = LAST;
856
		which = LAST;
857
	}
857
	}
858

    
   
858

   
859
	/* If no options specified, search by last name */
859
	/* If no options specified, search by last name */
860
	if (!ast_test_flag(&flags, OPT_LISTBYFIRSTNAME) && !ast_test_flag(&flags, OPT_LISTBYLASTNAME)) {
860
	if (!ast_test_flag(&flags, OPT_LISTBYFIRSTNAME) && !ast_test_flag(&flags, OPT_LISTBYLASTNAME)) {
861
		ast_set_flag(&flags, OPT_LISTBYLASTNAME);
861
		ast_set_flag(&flags, OPT_LISTBYLASTNAME);
862
		which = LAST;
862
		which = LAST;
863
	}
863
	}
864

    
   
864

   
865
	if (digit > 9) {
865
	if (digit > 9) {
866
		digit = 9;
866
		digit = 9;
867
	} else if (digit < 1) {
867
	} else if (digit < 1) {
868
		digit = 3;
868
		digit = 3;
869
	}
869
	}
870
	digits[7] = digit + '0';
870
	digits[7] = digit + '0';
871

    
   
871

   
872
	if (ast_channel_state(chan) != AST_STATE_UP) {
872
	if (ast_channel_state(chan) != AST_STATE_UP) {
873
		if (!ast_test_flag(&flags, OPT_NOANSWER)) {
873
		if (!ast_test_flag(&flags, OPT_NOANSWER)) {
874
			/* Otherwise answer unless we're supposed to read while on-hook */
874
			/* Otherwise answer unless we're supposed to read while on-hook */
875
			res = ast_answer(chan);
875
			res = ast_answer(chan);
876
		}
876
		}
877
	}
877
	}
878
	for (;;) {
878
	for (;;) {
879
		if (!ast_strlen_zero(dirintro) && !res) {
879
		if (!ast_strlen_zero(dirintro) && !res) {
880
			res = ast_stream_and_wait(chan, dirintro, AST_DIGIT_ANY);
880
			res = ast_stream_and_wait(chan, dirintro, AST_DIGIT_ANY);
881
		} else if (!res) {
881
		} else if (!res) {
882
			/* Stop playing sounds as soon as we have a digit. */
882
			/* Stop playing sounds as soon as we have a digit. */
883
			res = ast_stream_and_wait(chan, "dir-welcome", AST_DIGIT_ANY);
883
			res = ast_stream_and_wait(chan, "dir-welcome", AST_DIGIT_ANY);
884
			if (!res) {
884
			if (!res) {
885
				res = ast_stream_and_wait(chan, "dir-pls-enter", AST_DIGIT_ANY);
885
				res = ast_stream_and_wait(chan, "dir-pls-enter", AST_DIGIT_ANY);
886
			}
886
			}
887
			if (!res) {
887
			if (!res) {
888
				res = ast_stream_and_wait(chan, digits, AST_DIGIT_ANY);
888
				res = ast_stream_and_wait(chan, digits, AST_DIGIT_ANY);
889
			}
889
			}
890
			if (!res) {
890
			if (!res) {
891
				res = ast_stream_and_wait(chan, 
891
				res = ast_stream_and_wait(chan, 
892
					which == FIRST ? "dir-first" :
892
					which == FIRST ? "dir-first" :
893
					which == LAST ? "dir-last" :
893
					which == LAST ? "dir-last" :
894
					"dir-firstlast", AST_DIGIT_ANY);
894
					"dir-firstlast", AST_DIGIT_ANY);
895
			}
895
			}
896
			if (!res) {
896
			if (!res) {
897
				res = ast_stream_and_wait(chan, "dir-usingkeypad", AST_DIGIT_ANY);
897
				res = ast_stream_and_wait(chan, "dir-usingkeypad", AST_DIGIT_ANY);
898
			}
898
			}
899
		}
899
		}
900
		ast_stopstream(chan);
900
		ast_stopstream(chan);
901
		if (!res)
901
		if (!res)
902
			res = ast_waitfordigit(chan, 5000);
902
			res = ast_waitfordigit(chan, 5000);
903

    
   
903

   
904
		if (res <= 0)
904
		if (res <= 0)
905
			break;
905
			break;
906

    
   
906

   
907
		res = do_directory(chan, cfg, ucfg, args.vmcontext, args.dialcontext, res, digit, &flags, opts);
907
		res = do_directory(chan, cfg, ucfg, args.vmcontext, args.dialcontext, res, digit, &flags, opts);
908
		if (res)
908
		if (res)
909
			break;
909
			break;
910

    
   
910

   
911
		res = ast_waitstream(chan, AST_DIGIT_ANY);
911
		res = ast_waitstream(chan, AST_DIGIT_ANY);
912
		ast_stopstream(chan);
912
		ast_stopstream(chan);
913

    
   
913

   
914
		if (res)
914
		if (res)
915
			break;
915
			break;
916
	}
916
	}
917

    
   
917

   
918
	if (ucfg)
918
	if (ucfg)
919
		ast_config_destroy(ucfg);
919
		ast_config_destroy(ucfg);
920
	ast_config_destroy(cfg);
920
	ast_config_destroy(cfg);
921

    
   
921

   
922
	return res < 0 ? -1 : 0;
922
	return res < 0 ? -1 : 0;
923
}
923
}
924

    
   
924

   
925
static int unload_module(void)
925
static int unload_module(void)
926
{
926
{
927
	int res;
927
	int res;
928
	res = ast_unregister_application(app);
928
	res = ast_unregister_application(app);
929
	return res;
929
	return res;
930
}
930
}
931

    
   
931

   
932
static int load_module(void)
932
static int load_module(void)
933
{
933
{
934
	return ast_register_application_xml(app, directory_exec);
934
	return ast_register_application_xml(app, directory_exec);
935
}
935
}
936

    
   
936

   
937
AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Extension Directory");
937
AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Extension Directory");
/trunk/configs/voicemail.conf.sample
Diff Revision 1 Diff Revision 2
 
/trunk/contrib/realtime/mysql/voicemail.sql
Diff Revision 1 Diff Revision 2
 
  1. /trunk/apps/app_directory.c: Loading...
  2. /trunk/configs/voicemail.conf.sample: Loading...
  3. /trunk/contrib/realtime/mysql/voicemail.sql: Loading...

https://reviewboard.asterisk.org/ runs on a server provided by Digium, Inc. and uses bandwidth donated to the open source Asterisk community by API Digital Communications in Huntsville, AL USA.
Please report problems with this site to asteriskteam@digium.com.