Fri Aug 29 06:37:58 2008

Asterisk developer's documentation


app_voicemail.c

Go to the documentation of this file.
00001 /*
00002  * Asterisk -- An open source telephony toolkit.
00003  *
00004  * Copyright (C) 1999 - 2005, Digium, Inc.
00005  *
00006  * Mark Spencer <markster@digium.com>
00007  *
00008  * See http://www.asterisk.org for more information about
00009  * the Asterisk project. Please do not directly contact
00010  * any of the maintainers of this project for assistance;
00011  * the project provides a web site, mailing lists and IRC
00012  * channels for your use.
00013  *
00014  * This program is free software, distributed under the terms of
00015  * the GNU General Public License Version 2. See the LICENSE file
00016  * at the top of the source tree.
00017  */
00018 
00019 /*! \file
00020  *
00021  * \brief Comedian Mail - Voicemail System
00022  * 
00023  * \par See also
00024  * \arg \ref Config_vm
00025  * \ingroup applications
00026  */
00027 
00028 /*
00029  * 12-16-2004 : Support for Greek added by InAccess Networks (work funded by HOL, www.hol.gr)
00030  *           George Konstantoulakis <gkon@inaccessnetworks.com>
00031  *
00032  * 05-10-2005 : Support for Swedish and Norwegian added by Daniel Nylander, http://www.danielnylander.se/
00033  *
00034  * 05-11-2005 : An option for maximum number of messsages per mailbox added by GDS Partners (www.gdspartners.com)
00035  * 07-11-2005 : An issue with voicemail synchronization has been fixed by GDS Partners (www.gdspartners.com)
00036  *           Stojan Sljivic <stojan.sljivic@gdspartners.com>
00037  *
00038  */
00039 
00040 #include <stdlib.h>
00041 #include <errno.h>
00042 #include <unistd.h>
00043 #include <string.h>
00044 #include <stdlib.h>
00045 #include <stdio.h>
00046 #include <sys/time.h>
00047 #include <sys/stat.h>
00048 #include <sys/types.h>
00049 #include <sys/mman.h>
00050 #include <time.h>
00051 #include <dirent.h>
00052 
00053 #include "asterisk.h"
00054 
00055 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 75748 $")
00056 
00057 #include "asterisk/lock.h"
00058 #include "asterisk/file.h"
00059 #include "asterisk/logger.h"
00060 #include "asterisk/channel.h"
00061 #include "asterisk/pbx.h"
00062 #include "asterisk/options.h"
00063 #include "asterisk/config.h"
00064 #include "asterisk/say.h"
00065 #include "asterisk/module.h"
00066 #include "asterisk/adsi.h"
00067 #include "asterisk/app.h"
00068 #include "asterisk/manager.h"
00069 #include "asterisk/dsp.h"
00070 #include "asterisk/localtime.h"
00071 #include "asterisk/cli.h"
00072 #include "asterisk/utils.h"
00073 #ifdef USE_ODBC_STORAGE
00074 #include "asterisk/res_odbc.h"
00075 #endif
00076 
00077 #define COMMAND_TIMEOUT 5000
00078 #define  VOICEMAIL_DIR_MODE   0700
00079 #define  VOICEMAIL_FILE_MODE  0600
00080 #define  CHUNKSIZE   65536
00081 
00082 #define VOICEMAIL_CONFIG "voicemail.conf"
00083 #define ASTERISK_USERNAME "asterisk"
00084 
00085 /* Default mail command to mail voicemail. Change it with the
00086     mailcmd= command in voicemail.conf */
00087 #define SENDMAIL "/usr/sbin/sendmail -t"
00088 
00089 #define INTRO "vm-intro"
00090 
00091 #define MAXMSG 100
00092 #define MAXMSGLIMIT 9999
00093 
00094 #define BASEMAXINLINE 256
00095 #define BASELINELEN 72
00096 #define BASEMAXINLINE 256
00097 #define eol "\r\n"
00098 
00099 #define MAX_DATETIME_FORMAT   512
00100 #define MAX_NUM_CID_CONTEXTS 10
00101 
00102 #define VM_REVIEW    (1 << 0)
00103 #define VM_OPERATOR     (1 << 1)
00104 #define VM_SAYCID    (1 << 2)
00105 #define VM_SVMAIL    (1 << 3)
00106 #define VM_ENVELOPE     (1 << 4)
00107 #define VM_SAYDURATION     (1 << 5)
00108 #define VM_SKIPAFTERCMD    (1 << 6)
00109 #define VM_FORCENAME    (1 << 7) /*!< Have new users record their name */
00110 #define VM_FORCEGREET      (1 << 8) /*!< Have new users record their greetings */
00111 #define VM_PBXSKIP      (1 << 9)
00112 #define VM_DIRECFORWARD    (1 << 10)   /*!< directory_forward */
00113 #define VM_ATTACH    (1 << 11)
00114 #define VM_DELETE    (1 << 12)
00115 #define VM_ALLOCED      (1 << 13)
00116 #define VM_SEARCH    (1 << 14)
00117 
00118 #define ERROR_LOCK_PATH    -100
00119 
00120 enum {
00121    OPT_SILENT =           (1 << 0),
00122    OPT_BUSY_GREETING =    (1 << 1),
00123    OPT_UNAVAIL_GREETING = (1 << 2),
00124    OPT_RECORDGAIN =       (1 << 3),
00125    OPT_PREPEND_MAILBOX =  (1 << 4),
00126    OPT_PRIORITY_JUMP =    (1 << 5),
00127 } vm_option_flags;
00128 
00129 enum {
00130    OPT_ARG_RECORDGAIN = 0,
00131    OPT_ARG_ARRAY_SIZE = 1,
00132 } vm_option_args;
00133 
00134 AST_APP_OPTIONS(vm_app_options, {
00135    AST_APP_OPTION('s', OPT_SILENT),
00136    AST_APP_OPTION('b', OPT_BUSY_GREETING),
00137    AST_APP_OPTION('u', OPT_UNAVAIL_GREETING),
00138    AST_APP_OPTION_ARG('g', OPT_RECORDGAIN, OPT_ARG_RECORDGAIN),
00139    AST_APP_OPTION('p', OPT_PREPEND_MAILBOX),
00140    AST_APP_OPTION('j', OPT_PRIORITY_JUMP),
00141 });
00142 
00143 static int load_config(void);
00144 
00145 /*! \page vmlang Voicemail Language Syntaxes Supported
00146 
00147    \par Syntaxes supported, not really language codes.
00148    \arg \b en - English
00149    \arg \b de - German
00150    \arg \b es - Spanish
00151    \arg \b fr - French
00152    \arg \b it = Italian
00153    \arg \b nl - Dutch
00154    \arg \b pt - Portuguese
00155    \arg \b gr - Greek
00156    \arg \b no - Norwegian
00157    \arg \b se - Swedish
00158 
00159 German requires the following additional soundfile:
00160 \arg \b 1F  einE (feminine)
00161 
00162 Spanish requires the following additional soundfile:
00163 \arg \b 1M      un (masculine)
00164 
00165 Dutch, Portuguese & Spanish require the following additional soundfiles:
00166 \arg \b vm-INBOXs singular of 'new'
00167 \arg \b vm-Olds      singular of 'old/heard/read'
00168 
00169 NB these are plural:
00170 \arg \b vm-INBOX  nieuwe (nl)
00171 \arg \b vm-Old    oude (nl)
00172 
00173 Swedish uses:
00174 \arg \b vm-nytt      singular of 'new'
00175 \arg \b vm-nya    plural of 'new'
00176 \arg \b vm-gammalt   singular of 'old'
00177 \arg \b vm-gamla  plural of 'old'
00178 \arg \b digits/ett   'one', not always same as 'digits/1'
00179 
00180 Norwegian uses:
00181 \arg \b vm-ny     singular of 'new'
00182 \arg \b vm-nye    plural of 'new'
00183 \arg \b vm-gammel singular of 'old'
00184 \arg \b vm-gamle  plural of 'old'
00185 
00186 Dutch also uses:
00187 \arg \b nl-om     'at'?
00188 
00189 Spanish also uses:
00190 \arg \b vm-youhaveno
00191 
00192 Italian requires the following additional soundfile:
00193 
00194 For vm_intro_it:
00195 \arg \b vm-nuovo  new
00196 \arg \b vm-nuovi  new plural
00197 \arg \b vm-vecchio   old
00198 \arg \b vm-vecchi old plural
00199 
00200 \note Don't use vm-INBOX or vm-Old, because they are the name of the INBOX and Old folders,
00201 spelled among others when you have to change folder. For the above reasons, vm-INBOX
00202 and vm-Old are spelled plural, to make them sound more as folder name than an adjective.
00203 
00204 */
00205 
00206 struct baseio {
00207    int iocp;
00208    int iolen;
00209    int linelength;
00210    int ateof;
00211    unsigned char iobuf[BASEMAXINLINE];
00212 };
00213 
00214 /*! Structure for linked list of users */
00215 struct ast_vm_user {
00216    char context[AST_MAX_CONTEXT];   /*!< Voicemail context */
00217    char mailbox[AST_MAX_EXTENSION];/*!< Mailbox id, unique within vm context */
00218    char password[80];      /*!< Secret pin code, numbers only */
00219    char fullname[80];      /*!< Full name, for directory app */
00220    char email[80];         /*!< E-mail address */
00221    char pager[80];         /*!< E-mail address to pager (no attachment) */
00222    char serveremail[80];      /*!< From: Mail address */
00223    char mailcmd[160];      /*!< Configurable mail command */
00224    char language[MAX_LANGUAGE];    /*!< Config: Language setting */
00225    char zonetag[80];    /*!< Time zone */
00226    char callback[80];
00227    char dialout[80];
00228    char uniqueid[20];      /*!< Unique integer identifier */
00229    char exit[80];
00230    unsigned int flags;     /*!< VM_ flags */ 
00231    int saydurationm;
00232    int maxmsg;       /*!< Maximum number of msgs per folder for this mailbox */
00233    struct ast_vm_user *next;
00234 };
00235 
00236 struct vm_zone {
00237    char name[80];
00238    char timezone[80];
00239    char msg_format[512];
00240    struct vm_zone *next;
00241 };
00242 
00243 struct vm_state {
00244    char curbox[80];
00245    char username[80];
00246    char curdir[PATH_MAX];
00247    char vmbox[PATH_MAX];
00248    char fn[PATH_MAX];
00249    char fn2[PATH_MAX];
00250    int *deleted;
00251    int *heard;
00252    int curmsg;
00253    int lastmsg;
00254    int newmessages;
00255    int oldmessages;
00256    int starting;
00257    int repeats;
00258 };
00259 static int advanced_options(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int msg,
00260              int option, signed char record_gain);
00261 static int dialout(struct ast_channel *chan, struct ast_vm_user *vmu, char *num, char *outgoing_context);
00262 static int play_record_review(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime,
00263                char *fmt, int outsidecaller, struct ast_vm_user *vmu, int *duration, const char *unlockdir,
00264                signed char record_gain);
00265 static int vm_tempgreeting(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain);
00266 static int vm_play_folder_name(struct ast_channel *chan, char *mbox);
00267 
00268 static void apply_options(struct ast_vm_user *vmu, const char *options);
00269 
00270 #ifdef USE_ODBC_STORAGE
00271 static char odbc_database[80];
00272 static char odbc_table[80];
00273 #define RETRIEVE(a,b) retrieve_file(a,b)
00274 #define DISPOSE(a,b) remove_file(a,b)
00275 #define STORE(a,b,c,d) store_file(a,b,c,d)
00276 #define EXISTS(a,b,c,d) (message_exists(a,b))
00277 #define RENAME(a,b,c,d,e,f,g,h) (rename_file(a,b,c,d,e,f))
00278 #define COPY(a,b,c,d,e,f,g,h) (copy_file(a,b,c,d,e,f))
00279 #define DELETE(a,b,c) (delete_file(a,b))
00280 #else
00281 #define RETRIEVE(a,b)
00282 #define DISPOSE(a,b)
00283 #define STORE(a,b,c,d)
00284 #define EXISTS(a,b,c,d) (ast_fileexists(c,NULL,d) > 0)
00285 #define RENAME(a,b,c,d,e,f,g,h) (rename_file(g,h));
00286 #define COPY(a,b,c,d,e,f,g,h) (copy_file(g,h));
00287 #define DELETE(a,b,c) (vm_delete(c))
00288 #endif
00289 
00290 static char VM_SPOOL_DIR[AST_CONFIG_MAX_PATH];
00291 
00292 static char ext_pass_cmd[128];
00293 
00294 static char *tdesc = "Comedian Mail (Voicemail System)";
00295 
00296 static char *addesc = "Comedian Mail";
00297 
00298 static char *synopsis_vm =
00299 "Leave a Voicemail message";
00300 
00301 static char *descrip_vm =
00302 "  VoiceMail(mailbox[@context][&mailbox[@context]][...][|options]): This\n"
00303 "application allows the calling party to leave a message for the specified\n"
00304 "list of mailboxes. When multiple mailboxes are specified, the greeting will\n"
00305 "be taken from the first mailbox specified. Dialplan execution will stop if the\n"
00306 "specified mailbox does not exist.\n"
00307 "  The Voicemail application will exit if any of the following DTMF digits are\n"
00308 "received:\n"
00309 "    0 - Jump to the 'o' extension in the current dialplan context.\n"
00310 "    * - Jump to the 'a' extension in the current dialplan context.\n"
00311 "  This application will set the following channel variable upon completion:\n"
00312 "    VMSTATUS - This indicates the status of the execution of the VoiceMail\n"
00313 "               application. The possible values are:\n"
00314 "               SUCCESS | USEREXIT | FAILED\n\n"
00315 "  Options:\n"
00316 "    b    - Play the 'busy' greeting to the calling party.\n"
00317 "    g(#) - Use the specified amount of gain when recording the voicemail\n"
00318 "           message. The units are whole-number decibels (dB).\n"
00319 "    s    - Skip the playback of instructions for leaving a message to the\n"
00320 "           calling party.\n"
00321 "    u    - Play the 'unavailable' greeting.\n"
00322 "    j    - Jump to priority n+101 if the mailbox is not found or some other\n"
00323 "           error occurs.\n";
00324 
00325 static char *synopsis_vmain =
00326 "Check Voicemail messages";
00327 
00328 static char *descrip_vmain =
00329 "  VoiceMailMain([mailbox][@context][|options]): This application allows the\n"
00330 "calling party to check voicemail messages. A specific mailbox, and optional\n"
00331 "corresponding context, may be specified. If a mailbox is not provided, the\n"
00332 "calling party will be prompted to enter one. If a context is not specified,\n"
00333 "the 'default' context will be used.\n\n"
00334 "  Options:\n"
00335 "    p    - Consider the mailbox parameter as a prefix to the mailbox that\n"
00336 "           is entered by the caller.\n"
00337 "    g(#) - Use the specified amount of gain when recording a voicemail\n"
00338 "           message. The units are whole-number decibels (dB).\n"
00339 "    s    - Skip checking the passcode for the mailbox.\n";
00340 
00341 static char *synopsis_vm_box_exists =
00342 "Check to see if Voicemail mailbox exists";
00343 
00344 static char *descrip_vm_box_exists =
00345 "  MailboxExists(mailbox[@context][|options]): Check to see if the specified\n"
00346 "mailbox exists. If no voicemail context is specified, the 'default' context\n"
00347 "will be used.\n"
00348 "  This application will set the following channel variable upon completion:\n"
00349 "    VMBOXEXISTSSTATUS - This will contain the status of the execution of the\n"
00350 "                        MailboxExists application. Possible values include:\n"
00351 "                        SUCCESS | FAILED\n\n"
00352 "  Options:\n"
00353 "    j - Jump to priority n+101 if the mailbox is found.\n";
00354 
00355 static char *synopsis_vmauthenticate =
00356 "Authenticate with Voicemail passwords";
00357 
00358 static char *descrip_vmauthenticate =
00359 "  VMAuthenticate([mailbox][@context][|options]): This application behaves the\n"
00360 "same way as the Authenticate application, but the passwords are taken from\n"
00361 "voicemail.conf.\n"
00362 "  If the mailbox is specified, only that mailbox's password will be considered\n"
00363 "valid. If the mailbox is not specified, the channel variable AUTH_MAILBOX will\n"
00364 "be set with the authenticated mailbox.\n\n"
00365 "  Options:\n"
00366 "    s - Skip playing the initial prompts.\n";
00367 
00368 /* Leave a message */
00369 static char *app = "VoiceMail";
00370 
00371 /* Check mail, control, etc */
00372 static char *app2 = "VoiceMailMain";
00373 
00374 static char *app3 = "MailboxExists";
00375 static char *app4 = "VMAuthenticate";
00376 
00377 AST_MUTEX_DEFINE_STATIC(vmlock);
00378 struct ast_vm_user *users;
00379 struct ast_vm_user *usersl;
00380 struct vm_zone *zones = NULL;
00381 struct vm_zone *zonesl = NULL;
00382 static int maxsilence;
00383 static int maxmsg;
00384 static int silencethreshold = 128;
00385 static char serveremail[80];
00386 static char mailcmd[160];  /* Configurable mail cmd */
00387 static char externnotify[160]; 
00388 
00389 static char vmfmts[80];
00390 static int vmminmessage;
00391 static int vmmaxmessage;
00392 static int maxgreet;
00393 static int skipms;
00394 static int maxlogins;
00395 
00396 static struct ast_flags globalflags = {0};
00397 
00398 static int saydurationminfo;
00399 
00400 static char dialcontext[AST_MAX_CONTEXT];
00401 static char callcontext[AST_MAX_CONTEXT];
00402 static char exitcontext[AST_MAX_CONTEXT];
00403 
00404 static char cidinternalcontexts[MAX_NUM_CID_CONTEXTS][64];
00405 
00406 
00407 static char *emailbody = NULL;
00408 static char *emailsubject = NULL;
00409 static char *pagerbody = NULL;
00410 static char *pagersubject = NULL;
00411 static char fromstring[100];
00412 static char pagerfromstring[100];
00413 static char emailtitle[100];
00414 static char charset[32] = "ISO-8859-1";
00415 
00416 static unsigned char adsifdn[4] = "\x00\x00\x00\x0F";
00417 static unsigned char adsisec[4] = "\x9B\xDB\xF7\xAC";
00418 static int adsiver = 1;
00419 static char emaildateformat[32] = "%A, %B %d, %Y at %r";
00420 
00421 STANDARD_LOCAL_USER;
00422 
00423 LOCAL_USER_DECL;
00424 
00425 static void populate_defaults(struct ast_vm_user *vmu)
00426 {
00427    ast_copy_flags(vmu, (&globalflags), AST_FLAGS_ALL);   
00428    if (saydurationminfo)
00429       vmu->saydurationm = saydurationminfo;
00430    if (callcontext)
00431       ast_copy_string(vmu->callback, callcontext, sizeof(vmu->callback));
00432    if (dialcontext)
00433       ast_copy_string(vmu->dialout, dialcontext, sizeof(vmu->dialout));
00434    if (exitcontext)
00435       ast_copy_string(vmu->exit, exitcontext, sizeof(vmu->exit));
00436    if (maxmsg)
00437       vmu->maxmsg = maxmsg;
00438 }
00439 
00440 static void apply_option(struct ast_vm_user *vmu, const char *var, const char *value)
00441 {
00442    int x;
00443    if (!strcasecmp(var, "attach")) {
00444       ast_set2_flag(vmu, ast_true(value), VM_ATTACH); 
00445    } else if (!strcasecmp(var, "serveremail")) {
00446       ast_copy_string(vmu->serveremail, value, sizeof(vmu->serveremail));
00447    } else if (!strcasecmp(var, "language")) {
00448       ast_copy_string(vmu->language, value, sizeof(vmu->language));
00449    } else if (!strcasecmp(var, "tz")) {
00450       ast_copy_string(vmu->zonetag, value, sizeof(vmu->zonetag));
00451    } else if (!strcasecmp(var, "delete") || !strcasecmp(var, "deletevoicemail")) {
00452       ast_set2_flag(vmu, ast_true(value), VM_DELETE); 
00453    } else if (!strcasecmp(var, "saycid")){
00454       ast_set2_flag(vmu, ast_true(value), VM_SAYCID); 
00455    } else if (!strcasecmp(var,"sendvoicemail")){
00456       ast_set2_flag(vmu, ast_true(value), VM_SVMAIL); 
00457    } else if (!strcasecmp(var, "review")){
00458       ast_set2_flag(vmu, ast_true(value), VM_REVIEW); 
00459    } else if (!strcasecmp(var, "operator")){
00460       ast_set2_flag(vmu, ast_true(value), VM_OPERATOR);  
00461    } else if (!strcasecmp(var, "envelope")){
00462       ast_set2_flag(vmu, ast_true(value), VM_ENVELOPE);  
00463    } else if (!strcasecmp(var, "sayduration")){
00464       ast_set2_flag(vmu, ast_true(value), VM_SAYDURATION);  
00465    } else if (!strcasecmp(var, "saydurationm")){
00466       if (sscanf(value, "%d", &x) == 1) {
00467          vmu->saydurationm = x;
00468       } else {
00469          ast_log(LOG_WARNING, "Invalid min duration for say duration\n");
00470       }
00471    } else if (!strcasecmp(var, "forcename")){
00472       ast_set2_flag(vmu, ast_true(value), VM_FORCENAME); 
00473    } else if (!strcasecmp(var, "forcegreetings")){
00474       ast_set2_flag(vmu, ast_true(value), VM_FORCEGREET);   
00475    } else if (!strcasecmp(var, "callback")) {
00476       ast_copy_string(vmu->callback, value, sizeof(vmu->callback));
00477    } else if (!strcasecmp(var, "dialout")) {
00478       ast_copy_string(vmu->dialout, value, sizeof(vmu->dialout));
00479    } else if (!strcasecmp(var, "exitcontext")) {
00480       ast_copy_string(vmu->exit, value, sizeof(vmu->exit));
00481    } else if (!strcasecmp(var, "maxmsg")) {
00482       vmu->maxmsg = atoi(value);
00483       if (vmu->maxmsg <= 0) {
00484          ast_log(LOG_WARNING, "Invalid number of messages per folder maxmsg=%s. Using default value %i\n", value, MAXMSG);
00485          vmu->maxmsg = MAXMSG;
00486       } else if (vmu->maxmsg > MAXMSGLIMIT) {
00487          ast_log(LOG_WARNING, "Maximum number of messages per folder is %i. Cannot accept value maxmsg=%s\n", MAXMSGLIMIT, value);
00488          vmu->maxmsg = MAXMSGLIMIT;
00489       }
00490    } else if (!strcasecmp(var, "options")) {
00491       apply_options(vmu, value);
00492    }
00493 }
00494 
00495 static int change_password_realtime(struct ast_vm_user *vmu, const char *password)
00496 {
00497    int res;
00498    if (!ast_strlen_zero(vmu->uniqueid)) {
00499       res = ast_update_realtime("voicemail", "uniqueid", vmu->uniqueid, "password", password, NULL);
00500       if (res > 0) {
00501          ast_copy_string(vmu->password, password, sizeof(vmu->password));
00502          res = 0;
00503       } else if (!res) {
00504          res = -1;
00505       }
00506       return res;
00507    }
00508    return -1;
00509 }
00510 
00511 static void apply_options(struct ast_vm_user *vmu, const char *options)
00512 {  /* Destructively Parse options and apply */
00513    char *stringp;
00514    char *s;
00515    char *var, *value;
00516    stringp = ast_strdupa(options);
00517    while ((s = strsep(&stringp, "|"))) {
00518       value = s;
00519       if ((var = strsep(&value, "=")) && value) {
00520          apply_option(vmu, var, value);
00521       }
00522    }  
00523 }
00524 
00525 static struct ast_vm_user *find_user_realtime(struct ast_vm_user *ivm, const char *context, const char *mailbox)
00526 {
00527    struct ast_variable *var, *tmp;
00528    struct ast_vm_user *retval;
00529 
00530    if (ivm)
00531       retval=ivm;
00532    else
00533       retval=malloc(sizeof(struct ast_vm_user));
00534 
00535    if (retval) {
00536       memset(retval, 0, sizeof(struct ast_vm_user));
00537       if (!ivm)
00538          ast_set_flag(retval, VM_ALLOCED);   
00539       if (mailbox) 
00540          ast_copy_string(retval->mailbox, mailbox, sizeof(retval->mailbox));
00541       populate_defaults(retval);
00542       if (!context && ast_test_flag((&globalflags), VM_SEARCH))
00543          var = ast_load_realtime("voicemail", "mailbox", mailbox, NULL);
00544       else
00545          var = ast_load_realtime("voicemail", "mailbox", mailbox, "context", context, NULL);
00546       if (var) {
00547          tmp = var;
00548          while(tmp) {
00549             printf("%s => %s\n", tmp->name, tmp->value);
00550             if (!strcasecmp(tmp->name, "password")) {
00551                ast_copy_string(retval->password, tmp->value, sizeof(retval->password));
00552             } else if (!strcasecmp(tmp->name, "uniqueid")) {
00553                ast_copy_string(retval->uniqueid, tmp->value, sizeof(retval->uniqueid));
00554             } else if (!strcasecmp(tmp->name, "pager")) {
00555                ast_copy_string(retval->pager, tmp->value, sizeof(retval->pager));
00556             } else if (!strcasecmp(tmp->name, "email")) {
00557                ast_copy_string(retval->email, tmp->value, sizeof(retval->email));
00558             } else if (!strcasecmp(tmp->name, "fullname")) {
00559                ast_copy_string(retval->fullname, tmp->value, sizeof(retval->fullname));
00560             } else if (!strcasecmp(tmp->name, "context")) {
00561                ast_copy_string(retval->context, tmp->value, sizeof(retval->context));
00562             } else
00563                apply_option(retval, tmp->name, tmp->value);
00564             tmp = tmp->next;
00565          } 
00566          ast_variables_destroy(var);
00567       } else { 
00568          if (!ivm) 
00569             free(retval);
00570          retval = NULL;
00571       }  
00572    } 
00573    return retval;
00574 }
00575 
00576 static struct ast_vm_user *find_user(struct ast_vm_user *ivm, const char *context, const char *mailbox)
00577 {
00578    /* This function could be made to generate one from a database, too */
00579    struct ast_vm_user *vmu=NULL, *cur;
00580    ast_mutex_lock(&vmlock);
00581    cur = users;
00582 
00583    if (!context && !ast_test_flag((&globalflags), VM_SEARCH))
00584       context = "default";
00585 
00586    while (cur) {
00587       if (ast_test_flag((&globalflags), VM_SEARCH) && !strcasecmp(mailbox, cur->mailbox))
00588          break;
00589       if (context && (!strcasecmp(context, cur->context)) && (!strcasecmp(mailbox, cur->mailbox)))
00590          break;
00591       cur=cur->next;
00592    }
00593    if (cur) {
00594       if (ivm)
00595          vmu = ivm;
00596       else
00597          /* Make a copy, so that on a reload, we have no race */
00598          vmu = malloc(sizeof(struct ast_vm_user));
00599       if (vmu) {
00600          memcpy(vmu, cur, sizeof(struct ast_vm_user));
00601          ast_set2_flag(vmu, !ivm, VM_ALLOCED);  
00602          vmu->next = NULL;
00603       }
00604    } else
00605       vmu = find_user_realtime(ivm, context, mailbox);
00606    ast_mutex_unlock(&vmlock);
00607    return vmu;
00608 }
00609 
00610 static int reset_user_pw(const char *context, const char *mailbox, const char *newpass)
00611 {
00612    /* This function could be made to generate one from a database, too */
00613    struct ast_vm_user *cur;
00614    int res = -1;
00615    ast_mutex_lock(&vmlock);
00616    cur = users;
00617    while (cur) {
00618       if ((!context || !strcasecmp(context, cur->context)) &&
00619          (!strcasecmp(mailbox, cur->mailbox)))
00620             break;
00621       cur=cur->next;
00622    }
00623    if (cur) {
00624       ast_copy_string(cur->password, newpass, sizeof(cur->password));
00625       res = 0;
00626    }
00627    ast_mutex_unlock(&vmlock);
00628    return res;
00629 }
00630 
00631 static void vm_change_password(struct ast_vm_user *vmu, const char *newpassword)
00632 {
00633    /*  There's probably a better way of doing this. */
00634    /*  That's why I've put the password change in a separate function. */
00635    /*  This could also be done with a database function */
00636    
00637    FILE *configin;
00638    FILE *configout;
00639    int linenum=0;
00640    char inbuf[256];
00641    char orig[256];
00642    char currcontext[256] = "";
00643    char tmpin[AST_CONFIG_MAX_PATH];
00644    char tmpout[AST_CONFIG_MAX_PATH];
00645    struct stat statbuf;
00646 
00647    if (!change_password_realtime(vmu, newpassword))
00648       return;
00649 
00650    snprintf(tmpin, sizeof(tmpin), "%s/voicemail.conf", ast_config_AST_CONFIG_DIR);
00651    snprintf(tmpout, sizeof(tmpout), "%s/voicemail.conf.new", ast_config_AST_CONFIG_DIR);
00652    configin = fopen(tmpin,"r");
00653    if (configin)
00654       configout = fopen(tmpout,"w+");
00655    else
00656       configout = NULL;
00657    if (!configin || !configout) {
00658       if (configin)
00659          fclose(configin);
00660       else
00661          ast_log(LOG_WARNING, "Warning: Unable to open '%s' for reading: %s\n", tmpin, strerror(errno));
00662       if (configout)
00663          fclose(configout);
00664       else
00665          ast_log(LOG_WARNING, "Warning: Unable to open '%s' for writing: %s\n", tmpout, strerror(errno));
00666          return;
00667    }
00668 
00669    while (!feof(configin)) {
00670       char *user = NULL, *pass = NULL, *rest = NULL, *comment = NULL, *tmpctx = NULL, *tmpctxend = NULL;
00671 
00672       /* Read in the line */
00673       if (fgets(inbuf, sizeof(inbuf), configin) == NULL)
00674          continue;
00675       linenum++;
00676 
00677       /* Make a backup of it */
00678       ast_copy_string(orig, inbuf, sizeof(orig));
00679 
00680       /*
00681         Read the file line by line, split each line into a comment and command section
00682         only parse the command portion of the line
00683       */
00684       if (inbuf[strlen(inbuf) - 1] == '\n')
00685          inbuf[strlen(inbuf) - 1] = '\0';
00686 
00687       if ((comment = strchr(inbuf, ';')))
00688          *comment++ = '\0'; /* Now inbuf is terminated just before the comment */
00689 
00690       if (ast_strlen_zero(inbuf)) {
00691          fprintf(configout, "%s", orig);
00692          continue;
00693       }
00694 
00695       /* Check for a context, first '[' to first ']' */
00696       if ((tmpctx = strchr(inbuf, '['))) {
00697          tmpctxend = strchr(tmpctx, ']');
00698          if (tmpctxend) {
00699             /* Valid context */
00700             ast_copy_string(currcontext, tmpctx + 1, tmpctxend - tmpctx);
00701             fprintf(configout, "%s", orig);
00702             continue;
00703          }
00704       }
00705 
00706       /* This isn't a context line, check for MBX => PSWD... */
00707       user = inbuf;
00708       if ((pass = strchr(user, '='))) {
00709          /* We have a line in the form of aaaaa=aaaaaa */
00710          *pass++ = '\0';
00711 
00712          user = ast_strip(user);
00713 
00714          if (*pass == '>')
00715             *pass++ = '\0';
00716 
00717          pass = ast_skip_blanks(pass);
00718 
00719          /* 
00720             Since no whitespace allowed in fields, or more correctly white space
00721             inside the fields is there for a purpose, we can just terminate pass
00722             at the comma or EOL whichever comes first.
00723          */
00724          if ((rest = strchr(pass, ',')))
00725             *rest++ = '\0';
00726       } else {
00727          user = NULL;
00728       }        
00729 
00730       /* Compare user, pass AND context */
00731       if (!ast_strlen_zero(user) && !strcmp(user, vmu->mailbox) &&
00732           !ast_strlen_zero(pass) && !strcmp(pass, vmu->password) &&
00733           !strcasecmp(currcontext, vmu->context)) {
00734          /* This is the line */
00735          if (rest) {
00736             fprintf(configout, "%s => %s,%s", user, newpassword, rest);
00737          } else {
00738             fprintf(configout, "%s => %s", user, newpassword);
00739          }
00740          /* If there was a comment on the line print it out */
00741          if (comment) {
00742             fprintf(configout, ";%s\n", comment);
00743          } else {
00744             fprintf(configout, "\n");
00745          }
00746       } else {
00747          /* Put it back like it was */
00748          fprintf(configout, "%s", orig);
00749       }
00750    }
00751    fclose(configin);
00752    fclose(configout);
00753 
00754    stat(tmpin, &statbuf);
00755    chmod(tmpout, statbuf.st_mode);
00756    chown(tmpout, statbuf.st_uid, statbuf.st_gid);
00757    unlink(tmpin);
00758    rename(tmpout, tmpin);
00759    reset_user_pw(vmu->context, vmu->mailbox, newpassword);
00760    ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
00761 }
00762 
00763 static void vm_change_password_shell(struct ast_vm_user *vmu, char *newpassword)
00764 {
00765    char buf[255];
00766    snprintf(buf,255,"%s %s %s %s",ext_pass_cmd,vmu->context,vmu->mailbox,newpassword);
00767    if (!ast_safe_system(buf)) {
00768       reset_user_pw(vmu->context, vmu->mailbox, newpassword);
00769       ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
00770    }
00771 }
00772 
00773 static int make_dir(char *dest, int len, char *context, char *ext, char *mailbox)
00774 {
00775    return snprintf(dest, len, "%s%s/%s/%s", VM_SPOOL_DIR, context, ext, mailbox);
00776 }
00777 
00778 static int make_file(char *dest, int len, char *dir, int num)
00779 {
00780    return snprintf(dest, len, "%s/msg%04d", dir, num);
00781 }
00782 
00783 /** basically mkdir -p $dest/$context/$ext/$mailbox
00784  * @dest    String. base directory.
00785  * @context String. Ignored if is null or empty string.
00786  * @ext     String. Ignored if is null or empty string.
00787  * @mailbox String. Ignored if is null or empty string. 
00788  * @returns -1 on failure, 0 on success.
00789  * */
00790 static int create_dirpath(char *dest, int len, char *context, char *ext, char *mailbox)
00791 {
00792    mode_t   mode = VOICEMAIL_DIR_MODE;
00793 
00794    if(context && context[0] != '\0') {
00795       make_dir(dest, len, context, "", "");
00796       if(mkdir(dest, mode) && errno != EEXIST) {
00797          ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dest, strerror(errno));
00798          return -1;
00799       }
00800    }
00801    if(ext && ext[0] != '\0') {
00802       make_dir(dest, len, context, ext, "");
00803       if(mkdir(dest, mode) && errno != EEXIST) {
00804          ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dest, strerror(errno));
00805          return -1;
00806       }
00807    }
00808    if(mailbox && mailbox[0] != '\0') {
00809       make_dir(dest, len, context, ext, mailbox);
00810       if(mkdir(dest, mode) && errno != EEXIST) {
00811          ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dest, strerror(errno));
00812          return -1;
00813       }
00814    }
00815    return 0;
00816 }
00817 
00818 /* only return failure if ast_lock_path returns 'timeout',
00819    not if the path does not exist or any other reason
00820 */
00821 static int vm_lock_path(const char *path)
00822 {
00823    switch (ast_lock_path(path)) {
00824    case AST_LOCK_TIMEOUT:
00825       return -1;
00826    default:
00827       return 0;
00828    }
00829 }
00830 
00831 
00832 #ifdef USE_ODBC_STORAGE
00833 static int retrieve_file(char *dir, int msgnum)
00834 {
00835    int x = 0;
00836    int res;
00837    int fd=-1;
00838    size_t fdlen = 0;
00839    void *fdm = MAP_FAILED;
00840    SQLSMALLINT colcount=0;
00841    SQLHSTMT stmt;
00842    char sql[PATH_MAX];
00843    char fmt[80]="";
00844    char *c;
00845    char coltitle[256];
00846    SQLSMALLINT collen;
00847    SQLSMALLINT datatype;
00848    SQLSMALLINT decimaldigits;
00849    SQLSMALLINT nullable;
00850    SQLULEN colsize;
00851    FILE *f=NULL;
00852    char rowdata[80];
00853    char fn[PATH_MAX];
00854    char full_fn[PATH_MAX];
00855    char msgnums[80];
00856    
00857    odbc_obj *obj;
00858    obj = fetch_odbc_obj(odbc_database, 0);
00859    if (obj) {
00860       ast_copy_string(fmt, vmfmts, sizeof(fmt));
00861       c = strchr(fmt, '|');
00862       if (c)
00863          *c = '\0';
00864       if (!strcasecmp(fmt, "wav49"))
00865          strcpy(fmt, "WAV");
00866       snprintf(msgnums, sizeof(msgnums),"%d", msgnum);
00867       if (msgnum > -1)
00868          make_file(fn, sizeof(fn), dir, msgnum);
00869       else
00870          ast_copy_string(fn, dir, sizeof(fn));
00871       snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
00872       
00873       if (!(f = fopen(full_fn, "w+"))) {
00874               ast_log(LOG_WARNING, "Failed to open/create '%s'\n", full_fn);
00875               goto yuck;
00876       }
00877       
00878       snprintf(full_fn, sizeof(full_fn), "%s.%s", fn, fmt);
00879       res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
00880       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00881          ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
00882          goto yuck;
00883       }
00884       snprintf(sql, sizeof(sql), "SELECT * FROM %s WHERE dir=? AND msgnum=?",odbc_table);
00885       res = SQLPrepare(stmt, sql, SQL_NTS);
00886       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00887          ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
00888          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
00889          goto yuck;
00890       }
00891       SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(dir), 0, (void *)dir, 0, NULL);
00892       SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
00893       res = odbc_smart_execute(obj, stmt);
00894