app_voicemail.c 515 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984398539863987398839893990399139923993399439953996399739983999400040014002400340044005400640074008400940104011401240134014401540164017401840194020402140224023402440254026402740284029403040314032403340344035403640374038403940404041404240434044404540464047404840494050405140524053405440554056405740584059406040614062406340644065406640674068406940704071407240734074407540764077407840794080408140824083408440854086408740884089409040914092409340944095409640974098409941004101410241034104410541064107410841094110411141124113411441154116411741184119412041214122412341244125412641274128412941304131413241334134413541364137413841394140414141424143414441454146414741484149415041514152415341544155415641574158415941604161416241634164416541664167416841694170417141724173417441754176417741784179418041814182418341844185418641874188418941904191419241934194419541964197419841994200420142024203420442054206420742084209421042114212421342144215421642174218421942204221422242234224422542264227422842294230423142324233423442354236423742384239424042414242424342444245424642474248424942504251425242534254425542564257425842594260426142624263426442654266426742684269427042714272427342744275427642774278427942804281428242834284428542864287428842894290429142924293429442954296429742984299430043014302430343044305430643074308430943104311431243134314431543164317431843194320432143224323432443254326432743284329433043314332433343344335433643374338433943404341434243434344434543464347434843494350435143524353435443554356435743584359436043614362436343644365436643674368436943704371437243734374437543764377437843794380438143824383438443854386438743884389439043914392439343944395439643974398439944004401440244034404440544064407440844094410441144124413441444154416441744184419442044214422442344244425442644274428442944304431443244334434443544364437443844394440444144424443444444454446444744484449445044514452445344544455445644574458445944604461446244634464446544664467446844694470447144724473447444754476447744784479448044814482448344844485448644874488448944904491449244934494449544964497449844994500450145024503450445054506450745084509451045114512451345144515451645174518451945204521452245234524452545264527452845294530453145324533453445354536453745384539454045414542454345444545454645474548454945504551455245534554455545564557455845594560456145624563456445654566456745684569457045714572457345744575457645774578457945804581458245834584458545864587458845894590459145924593459445954596459745984599460046014602460346044605460646074608460946104611461246134614461546164617461846194620462146224623462446254626462746284629463046314632463346344635463646374638463946404641464246434644464546464647464846494650465146524653465446554656465746584659466046614662466346644665466646674668466946704671467246734674467546764677467846794680468146824683468446854686468746884689469046914692469346944695469646974698469947004701470247034704470547064707470847094710471147124713471447154716471747184719472047214722472347244725472647274728472947304731473247334734473547364737473847394740474147424743474447454746474747484749475047514752475347544755475647574758475947604761476247634764476547664767476847694770477147724773477447754776477747784779478047814782478347844785478647874788478947904791479247934794479547964797479847994800480148024803480448054806480748084809481048114812481348144815481648174818481948204821482248234824482548264827482848294830483148324833483448354836483748384839484048414842484348444845484648474848484948504851485248534854485548564857485848594860486148624863486448654866486748684869487048714872487348744875487648774878487948804881488248834884488548864887488848894890489148924893489448954896489748984899490049014902490349044905490649074908490949104911491249134914491549164917491849194920492149224923492449254926492749284929493049314932493349344935493649374938493949404941494249434944494549464947494849494950495149524953495449554956495749584959496049614962496349644965496649674968496949704971497249734974497549764977497849794980498149824983498449854986498749884989499049914992499349944995499649974998499950005001500250035004500550065007500850095010501150125013501450155016501750185019502050215022502350245025502650275028502950305031503250335034503550365037503850395040504150425043504450455046504750485049505050515052505350545055505650575058505950605061506250635064506550665067506850695070507150725073507450755076507750785079508050815082508350845085508650875088508950905091509250935094509550965097509850995100510151025103510451055106510751085109511051115112511351145115511651175118511951205121512251235124512551265127512851295130513151325133513451355136513751385139514051415142514351445145514651475148514951505151515251535154515551565157515851595160516151625163516451655166516751685169517051715172517351745175517651775178517951805181518251835184518551865187518851895190519151925193519451955196519751985199520052015202520352045205520652075208520952105211521252135214521552165217521852195220522152225223522452255226522752285229523052315232523352345235523652375238523952405241524252435244524552465247524852495250525152525253525452555256525752585259526052615262526352645265526652675268526952705271527252735274527552765277527852795280528152825283528452855286528752885289529052915292529352945295529652975298529953005301530253035304530553065307530853095310531153125313531453155316531753185319532053215322532353245325532653275328532953305331533253335334533553365337533853395340534153425343534453455346534753485349535053515352535353545355535653575358535953605361536253635364536553665367536853695370537153725373537453755376537753785379538053815382538353845385538653875388538953905391539253935394539553965397539853995400540154025403540454055406540754085409541054115412541354145415541654175418541954205421542254235424542554265427542854295430543154325433543454355436543754385439544054415442544354445445544654475448544954505451545254535454545554565457545854595460546154625463546454655466546754685469547054715472547354745475547654775478547954805481548254835484548554865487548854895490549154925493549454955496549754985499550055015502550355045505550655075508550955105511551255135514551555165517551855195520552155225523552455255526552755285529553055315532553355345535553655375538553955405541554255435544554555465547554855495550555155525553555455555556555755585559556055615562556355645565556655675568556955705571557255735574557555765577557855795580558155825583558455855586558755885589559055915592559355945595559655975598559956005601560256035604560556065607560856095610561156125613561456155616561756185619562056215622562356245625562656275628562956305631563256335634563556365637563856395640564156425643564456455646564756485649565056515652565356545655565656575658565956605661566256635664566556665667566856695670567156725673567456755676567756785679568056815682568356845685568656875688568956905691569256935694569556965697569856995700570157025703570457055706570757085709571057115712571357145715571657175718571957205721572257235724572557265727572857295730573157325733573457355736573757385739574057415742574357445745574657475748574957505751575257535754575557565757575857595760576157625763576457655766576757685769577057715772577357745775577657775778577957805781578257835784578557865787578857895790579157925793579457955796579757985799580058015802580358045805580658075808580958105811581258135814581558165817581858195820582158225823582458255826582758285829583058315832583358345835583658375838583958405841584258435844584558465847584858495850585158525853585458555856585758585859586058615862586358645865586658675868586958705871587258735874587558765877587858795880588158825883588458855886588758885889589058915892589358945895589658975898589959005901590259035904590559065907590859095910591159125913591459155916591759185919592059215922592359245925592659275928592959305931593259335934593559365937593859395940594159425943594459455946594759485949595059515952595359545955595659575958595959605961596259635964596559665967596859695970597159725973597459755976597759785979598059815982598359845985598659875988598959905991599259935994599559965997599859996000600160026003600460056006600760086009601060116012601360146015601660176018601960206021602260236024602560266027602860296030603160326033603460356036603760386039604060416042604360446045604660476048604960506051605260536054605560566057605860596060606160626063606460656066606760686069607060716072607360746075607660776078607960806081608260836084608560866087608860896090609160926093609460956096609760986099610061016102610361046105610661076108610961106111611261136114611561166117611861196120612161226123612461256126612761286129613061316132613361346135613661376138613961406141614261436144614561466147614861496150615161526153615461556156615761586159616061616162616361646165616661676168616961706171617261736174617561766177617861796180618161826183618461856186618761886189619061916192619361946195619661976198619962006201620262036204620562066207620862096210621162126213621462156216621762186219622062216222622362246225622662276228622962306231623262336234623562366237623862396240624162426243624462456246624762486249625062516252625362546255625662576258625962606261626262636264626562666267626862696270627162726273627462756276627762786279628062816282628362846285628662876288628962906291629262936294629562966297629862996300630163026303630463056306630763086309631063116312631363146315631663176318631963206321632263236324632563266327632863296330633163326333633463356336633763386339634063416342634363446345634663476348634963506351635263536354635563566357635863596360636163626363636463656366636763686369637063716372637363746375637663776378637963806381638263836384638563866387638863896390639163926393639463956396639763986399640064016402640364046405640664076408640964106411641264136414641564166417641864196420642164226423642464256426642764286429643064316432643364346435643664376438643964406441644264436444644564466447644864496450645164526453645464556456645764586459646064616462646364646465646664676468646964706471647264736474647564766477647864796480648164826483648464856486648764886489649064916492649364946495649664976498649965006501650265036504650565066507650865096510651165126513651465156516651765186519652065216522652365246525652665276528652965306531653265336534653565366537653865396540654165426543654465456546654765486549655065516552655365546555655665576558655965606561656265636564656565666567656865696570657165726573657465756576657765786579658065816582658365846585658665876588658965906591659265936594659565966597659865996600660166026603660466056606660766086609661066116612661366146615661666176618661966206621662266236624662566266627662866296630663166326633663466356636663766386639664066416642664366446645664666476648664966506651665266536654665566566657665866596660666166626663666466656666666766686669667066716672667366746675667666776678667966806681668266836684668566866687668866896690669166926693669466956696669766986699670067016702670367046705670667076708670967106711671267136714671567166717671867196720672167226723672467256726672767286729673067316732673367346735673667376738673967406741674267436744674567466747674867496750675167526753675467556756675767586759676067616762676367646765676667676768676967706771677267736774677567766777677867796780678167826783678467856786678767886789679067916792679367946795679667976798679968006801680268036804680568066807680868096810681168126813681468156816681768186819682068216822682368246825682668276828682968306831683268336834683568366837683868396840684168426843684468456846684768486849685068516852685368546855685668576858685968606861686268636864686568666867686868696870687168726873687468756876687768786879688068816882688368846885688668876888688968906891689268936894689568966897689868996900690169026903690469056906690769086909691069116912691369146915691669176918691969206921692269236924692569266927692869296930693169326933693469356936693769386939694069416942694369446945694669476948694969506951695269536954695569566957695869596960696169626963696469656966696769686969697069716972697369746975697669776978697969806981698269836984698569866987698869896990699169926993699469956996699769986999700070017002700370047005700670077008700970107011701270137014701570167017701870197020702170227023702470257026702770287029703070317032703370347035703670377038703970407041704270437044704570467047704870497050705170527053705470557056705770587059706070617062706370647065706670677068706970707071707270737074707570767077707870797080708170827083708470857086708770887089709070917092709370947095709670977098709971007101710271037104710571067107710871097110711171127113711471157116711771187119712071217122712371247125712671277128712971307131713271337134713571367137713871397140714171427143714471457146714771487149715071517152715371547155715671577158715971607161716271637164716571667167716871697170717171727173717471757176717771787179718071817182718371847185718671877188718971907191719271937194719571967197719871997200720172027203720472057206720772087209721072117212721372147215721672177218721972207221722272237224722572267227722872297230723172327233723472357236723772387239724072417242724372447245724672477248724972507251725272537254725572567257725872597260726172627263726472657266726772687269727072717272727372747275727672777278727972807281728272837284728572867287728872897290729172927293729472957296729772987299730073017302730373047305730673077308730973107311731273137314731573167317731873197320732173227323732473257326732773287329733073317332733373347335733673377338733973407341734273437344734573467347734873497350735173527353735473557356735773587359736073617362736373647365736673677368736973707371737273737374737573767377737873797380738173827383738473857386738773887389739073917392739373947395739673977398739974007401740274037404740574067407740874097410741174127413741474157416741774187419742074217422742374247425742674277428742974307431743274337434743574367437743874397440744174427443744474457446744774487449745074517452745374547455745674577458745974607461746274637464746574667467746874697470747174727473747474757476747774787479748074817482748374847485748674877488748974907491749274937494749574967497749874997500750175027503750475057506750775087509751075117512751375147515751675177518751975207521752275237524752575267527752875297530753175327533753475357536753775387539754075417542754375447545754675477548754975507551755275537554755575567557755875597560756175627563756475657566756775687569757075717572757375747575757675777578757975807581758275837584758575867587758875897590759175927593759475957596759775987599760076017602760376047605760676077608760976107611761276137614761576167617761876197620762176227623762476257626762776287629763076317632763376347635763676377638763976407641764276437644764576467647764876497650765176527653765476557656765776587659766076617662766376647665766676677668766976707671767276737674767576767677767876797680768176827683768476857686768776887689769076917692769376947695769676977698769977007701770277037704770577067707770877097710771177127713771477157716771777187719772077217722772377247725772677277728772977307731773277337734773577367737773877397740774177427743774477457746774777487749775077517752775377547755775677577758775977607761776277637764776577667767776877697770777177727773777477757776777777787779778077817782778377847785778677877788778977907791779277937794779577967797779877997800780178027803780478057806780778087809781078117812781378147815781678177818781978207821782278237824782578267827782878297830783178327833783478357836783778387839784078417842784378447845784678477848784978507851785278537854785578567857785878597860786178627863786478657866786778687869787078717872787378747875787678777878787978807881788278837884788578867887788878897890789178927893789478957896789778987899790079017902790379047905790679077908790979107911791279137914791579167917791879197920792179227923792479257926792779287929793079317932793379347935793679377938793979407941794279437944794579467947794879497950795179527953795479557956795779587959796079617962796379647965796679677968796979707971797279737974797579767977797879797980798179827983798479857986798779887989799079917992799379947995799679977998799980008001800280038004800580068007800880098010801180128013801480158016801780188019802080218022802380248025802680278028802980308031803280338034803580368037803880398040804180428043804480458046804780488049805080518052805380548055805680578058805980608061806280638064806580668067806880698070807180728073807480758076807780788079808080818082808380848085808680878088808980908091809280938094809580968097809880998100810181028103810481058106810781088109811081118112811381148115811681178118811981208121812281238124812581268127812881298130813181328133813481358136813781388139814081418142814381448145814681478148814981508151815281538154815581568157815881598160816181628163816481658166816781688169817081718172817381748175817681778178817981808181818281838184818581868187818881898190819181928193819481958196819781988199820082018202820382048205820682078208820982108211821282138214821582168217821882198220822182228223822482258226822782288229823082318232823382348235823682378238823982408241824282438244824582468247824882498250825182528253825482558256825782588259826082618262826382648265826682678268826982708271827282738274827582768277827882798280828182828283828482858286828782888289829082918292829382948295829682978298829983008301830283038304830583068307830883098310831183128313831483158316831783188319832083218322832383248325832683278328832983308331833283338334833583368337833883398340834183428343834483458346834783488349835083518352835383548355835683578358835983608361836283638364836583668367836883698370837183728373837483758376837783788379838083818382838383848385838683878388838983908391839283938394839583968397839883998400840184028403840484058406840784088409841084118412841384148415841684178418841984208421842284238424842584268427842884298430843184328433843484358436843784388439844084418442844384448445844684478448844984508451845284538454845584568457845884598460846184628463846484658466846784688469847084718472847384748475847684778478847984808481848284838484848584868487848884898490849184928493849484958496849784988499850085018502850385048505850685078508850985108511851285138514851585168517851885198520852185228523852485258526852785288529853085318532853385348535853685378538853985408541854285438544854585468547854885498550855185528553855485558556855785588559856085618562856385648565856685678568856985708571857285738574857585768577857885798580858185828583858485858586858785888589859085918592859385948595859685978598859986008601860286038604860586068607860886098610861186128613861486158616861786188619862086218622862386248625862686278628862986308631863286338634863586368637863886398640864186428643864486458646864786488649865086518652865386548655865686578658865986608661866286638664866586668667866886698670867186728673867486758676867786788679868086818682868386848685868686878688868986908691869286938694869586968697869886998700870187028703870487058706870787088709871087118712871387148715871687178718871987208721872287238724872587268727872887298730873187328733873487358736873787388739874087418742874387448745874687478748874987508751875287538754875587568757875887598760876187628763876487658766876787688769877087718772877387748775877687778778877987808781878287838784878587868787878887898790879187928793879487958796879787988799880088018802880388048805880688078808880988108811881288138814881588168817881888198820882188228823882488258826882788288829883088318832883388348835883688378838883988408841884288438844884588468847884888498850885188528853885488558856885788588859886088618862886388648865886688678868886988708871887288738874887588768877887888798880888188828883888488858886888788888889889088918892889388948895889688978898889989008901890289038904890589068907890889098910891189128913891489158916891789188919892089218922892389248925892689278928892989308931893289338934893589368937893889398940894189428943894489458946894789488949895089518952895389548955895689578958895989608961896289638964896589668967896889698970897189728973897489758976897789788979898089818982898389848985898689878988898989908991899289938994899589968997899889999000900190029003900490059006900790089009901090119012901390149015901690179018901990209021902290239024902590269027902890299030903190329033903490359036903790389039904090419042904390449045904690479048904990509051905290539054905590569057905890599060906190629063906490659066906790689069907090719072907390749075907690779078907990809081908290839084908590869087908890899090909190929093909490959096909790989099910091019102910391049105910691079108910991109111911291139114911591169117911891199120912191229123912491259126912791289129913091319132913391349135913691379138913991409141914291439144914591469147914891499150915191529153915491559156915791589159916091619162916391649165916691679168916991709171917291739174917591769177917891799180918191829183918491859186918791889189919091919192919391949195919691979198919992009201920292039204920592069207920892099210921192129213921492159216921792189219922092219222922392249225922692279228922992309231923292339234923592369237923892399240924192429243924492459246924792489249925092519252925392549255925692579258925992609261926292639264926592669267926892699270927192729273927492759276927792789279928092819282928392849285928692879288928992909291929292939294929592969297929892999300930193029303930493059306930793089309931093119312931393149315931693179318931993209321932293239324932593269327932893299330933193329333933493359336933793389339934093419342934393449345934693479348934993509351935293539354935593569357935893599360936193629363936493659366936793689369937093719372937393749375937693779378937993809381938293839384938593869387938893899390939193929393939493959396939793989399940094019402940394049405940694079408940994109411941294139414941594169417941894199420942194229423942494259426942794289429943094319432943394349435943694379438943994409441944294439444944594469447944894499450945194529453945494559456945794589459946094619462946394649465946694679468946994709471947294739474947594769477947894799480948194829483948494859486948794889489949094919492949394949495949694979498949995009501950295039504950595069507950895099510951195129513951495159516951795189519952095219522952395249525952695279528952995309531953295339534953595369537953895399540954195429543954495459546954795489549955095519552955395549555955695579558955995609561956295639564956595669567956895699570957195729573957495759576957795789579958095819582958395849585958695879588958995909591959295939594959595969597959895999600960196029603960496059606960796089609961096119612961396149615961696179618961996209621962296239624962596269627962896299630963196329633963496359636963796389639964096419642964396449645964696479648964996509651965296539654965596569657965896599660966196629663966496659666966796689669967096719672967396749675967696779678967996809681968296839684968596869687968896899690969196929693969496959696969796989699970097019702970397049705970697079708970997109711971297139714971597169717971897199720972197229723972497259726972797289729973097319732973397349735973697379738973997409741974297439744974597469747974897499750975197529753975497559756975797589759976097619762976397649765976697679768976997709771977297739774977597769777977897799780978197829783978497859786978797889789979097919792979397949795979697979798979998009801980298039804980598069807980898099810981198129813981498159816981798189819982098219822982398249825982698279828982998309831983298339834983598369837983898399840984198429843984498459846984798489849985098519852985398549855985698579858985998609861986298639864986598669867986898699870987198729873987498759876987798789879988098819882988398849885988698879888988998909891989298939894989598969897989898999900990199029903990499059906990799089909991099119912991399149915991699179918991999209921992299239924992599269927992899299930993199329933993499359936993799389939994099419942994399449945994699479948994999509951995299539954995599569957995899599960996199629963996499659966996799689969997099719972997399749975997699779978997999809981998299839984998599869987998899899990999199929993999499959996999799989999100001000110002100031000410005100061000710008100091001010011100121001310014100151001610017100181001910020100211002210023100241002510026100271002810029100301003110032100331003410035100361003710038100391004010041100421004310044100451004610047100481004910050100511005210053100541005510056100571005810059100601006110062100631006410065100661006710068100691007010071100721007310074100751007610077100781007910080100811008210083100841008510086100871008810089100901009110092100931009410095100961009710098100991010010101101021010310104101051010610107101081010910110101111011210113101141011510116101171011810119101201012110122101231012410125101261012710128101291013010131101321013310134101351013610137101381013910140101411014210143101441014510146101471014810149101501015110152101531015410155101561015710158101591016010161101621016310164101651016610167101681016910170101711017210173101741017510176101771017810179101801018110182101831018410185101861018710188101891019010191101921019310194101951019610197101981019910200102011020210203102041020510206102071020810209102101021110212102131021410215102161021710218102191022010221102221022310224102251022610227102281022910230102311023210233102341023510236102371023810239102401024110242102431024410245102461024710248102491025010251102521025310254102551025610257102581025910260102611026210263102641026510266102671026810269102701027110272102731027410275102761027710278102791028010281102821028310284102851028610287102881028910290102911029210293102941029510296102971029810299103001030110302103031030410305103061030710308103091031010311103121031310314103151031610317103181031910320103211032210323103241032510326103271032810329103301033110332103331033410335103361033710338103391034010341103421034310344103451034610347103481034910350103511035210353103541035510356103571035810359103601036110362103631036410365103661036710368103691037010371103721037310374103751037610377103781037910380103811038210383103841038510386103871038810389103901039110392103931039410395103961039710398103991040010401104021040310404104051040610407104081040910410104111041210413104141041510416104171041810419104201042110422104231042410425104261042710428104291043010431104321043310434104351043610437104381043910440104411044210443104441044510446104471044810449104501045110452104531045410455104561045710458104591046010461104621046310464104651046610467104681046910470104711047210473104741047510476104771047810479104801048110482104831048410485104861048710488104891049010491104921049310494104951049610497104981049910500105011050210503105041050510506105071050810509105101051110512105131051410515105161051710518105191052010521105221052310524105251052610527105281052910530105311053210533105341053510536105371053810539105401054110542105431054410545105461054710548105491055010551105521055310554105551055610557105581055910560105611056210563105641056510566105671056810569105701057110572105731057410575105761057710578105791058010581105821058310584105851058610587105881058910590105911059210593105941059510596105971059810599106001060110602106031060410605106061060710608106091061010611106121061310614106151061610617106181061910620106211062210623106241062510626106271062810629106301063110632106331063410635106361063710638106391064010641106421064310644106451064610647106481064910650106511065210653106541065510656106571065810659106601066110662106631066410665106661066710668106691067010671106721067310674106751067610677106781067910680106811068210683106841068510686106871068810689106901069110692106931069410695106961069710698106991070010701107021070310704107051070610707107081070910710107111071210713107141071510716107171071810719107201072110722107231072410725107261072710728107291073010731107321073310734107351073610737107381073910740107411074210743107441074510746107471074810749107501075110752107531075410755107561075710758107591076010761107621076310764107651076610767107681076910770107711077210773107741077510776107771077810779107801078110782107831078410785107861078710788107891079010791107921079310794107951079610797107981079910800108011080210803108041080510806108071080810809108101081110812108131081410815108161081710818108191082010821108221082310824108251082610827108281082910830108311083210833108341083510836108371083810839108401084110842108431084410845108461084710848108491085010851108521085310854108551085610857108581085910860108611086210863108641086510866108671086810869108701087110872108731087410875108761087710878108791088010881108821088310884108851088610887108881088910890108911089210893108941089510896108971089810899109001090110902109031090410905109061090710908109091091010911109121091310914109151091610917109181091910920109211092210923109241092510926109271092810929109301093110932109331093410935109361093710938109391094010941109421094310944109451094610947109481094910950109511095210953109541095510956109571095810959109601096110962109631096410965109661096710968109691097010971109721097310974109751097610977109781097910980109811098210983109841098510986109871098810989109901099110992109931099410995109961099710998109991100011001110021100311004110051100611007110081100911010110111101211013110141101511016110171101811019110201102111022110231102411025110261102711028110291103011031110321103311034110351103611037110381103911040110411104211043110441104511046110471104811049110501105111052110531105411055110561105711058110591106011061110621106311064110651106611067110681106911070110711107211073110741107511076110771107811079110801108111082110831108411085110861108711088110891109011091110921109311094110951109611097110981109911100111011110211103111041110511106111071110811109111101111111112111131111411115111161111711118111191112011121111221112311124111251112611127111281112911130111311113211133111341113511136111371113811139111401114111142111431114411145111461114711148111491115011151111521115311154111551115611157111581115911160111611116211163111641116511166111671116811169111701117111172111731117411175111761117711178111791118011181111821118311184111851118611187111881118911190111911119211193111941119511196111971119811199112001120111202112031120411205112061120711208112091121011211112121121311214112151121611217112181121911220112211122211223112241122511226112271122811229112301123111232112331123411235112361123711238112391124011241112421124311244112451124611247112481124911250112511125211253112541125511256112571125811259112601126111262112631126411265112661126711268112691127011271112721127311274112751127611277112781127911280112811128211283112841128511286112871128811289112901129111292112931129411295112961129711298112991130011301113021130311304113051130611307113081130911310113111131211313113141131511316113171131811319113201132111322113231132411325113261132711328113291133011331113321133311334113351133611337113381133911340113411134211343113441134511346113471134811349113501135111352113531135411355113561135711358113591136011361113621136311364113651136611367113681136911370113711137211373113741137511376113771137811379113801138111382113831138411385113861138711388113891139011391113921139311394113951139611397113981139911400114011140211403114041140511406114071140811409114101141111412114131141411415114161141711418114191142011421114221142311424114251142611427114281142911430114311143211433114341143511436114371143811439114401144111442114431144411445114461144711448114491145011451114521145311454114551145611457114581145911460114611146211463114641146511466114671146811469114701147111472114731147411475114761147711478114791148011481114821148311484114851148611487114881148911490114911149211493114941149511496114971149811499115001150111502115031150411505115061150711508115091151011511115121151311514115151151611517115181151911520115211152211523115241152511526115271152811529115301153111532115331153411535115361153711538115391154011541115421154311544115451154611547115481154911550115511155211553115541155511556115571155811559115601156111562115631156411565115661156711568115691157011571115721157311574115751157611577115781157911580115811158211583115841158511586115871158811589115901159111592115931159411595115961159711598115991160011601116021160311604116051160611607116081160911610116111161211613116141161511616116171161811619116201162111622116231162411625116261162711628116291163011631116321163311634116351163611637116381163911640116411164211643116441164511646116471164811649116501165111652116531165411655116561165711658116591166011661116621166311664116651166611667116681166911670116711167211673116741167511676116771167811679116801168111682116831168411685116861168711688116891169011691116921169311694116951169611697116981169911700117011170211703117041170511706117071170811709117101171111712117131171411715117161171711718117191172011721117221172311724117251172611727117281172911730117311173211733117341173511736117371173811739117401174111742117431174411745117461174711748117491175011751117521175311754117551175611757117581175911760117611176211763117641176511766117671176811769117701177111772117731177411775117761177711778117791178011781117821178311784117851178611787117881178911790117911179211793117941179511796117971179811799118001180111802118031180411805118061180711808118091181011811118121181311814118151181611817118181181911820118211182211823118241182511826118271182811829118301183111832118331183411835118361183711838118391184011841118421184311844118451184611847118481184911850118511185211853118541185511856118571185811859118601186111862118631186411865118661186711868118691187011871118721187311874118751187611877118781187911880118811188211883118841188511886118871188811889118901189111892118931189411895118961189711898118991190011901119021190311904119051190611907119081190911910119111191211913119141191511916119171191811919119201192111922119231192411925119261192711928119291193011931119321193311934119351193611937119381193911940119411194211943119441194511946119471194811949119501195111952119531195411955119561195711958119591196011961119621196311964119651196611967119681196911970119711197211973119741197511976119771197811979119801198111982119831198411985119861198711988119891199011991119921199311994119951199611997119981199912000120011200212003120041200512006120071200812009120101201112012120131201412015120161201712018120191202012021120221202312024120251202612027120281202912030120311203212033120341203512036120371203812039120401204112042120431204412045120461204712048120491205012051120521205312054120551205612057120581205912060120611206212063120641206512066120671206812069120701207112072120731207412075120761207712078120791208012081120821208312084120851208612087120881208912090120911209212093120941209512096120971209812099121001210112102121031210412105121061210712108121091211012111121121211312114121151211612117121181211912120121211212212123121241212512126121271212812129121301213112132121331213412135121361213712138121391214012141121421214312144121451214612147121481214912150121511215212153121541215512156121571215812159121601216112162121631216412165121661216712168121691217012171121721217312174121751217612177121781217912180121811218212183121841218512186121871218812189121901219112192121931219412195121961219712198121991220012201122021220312204122051220612207122081220912210122111221212213122141221512216122171221812219122201222112222122231222412225122261222712228122291223012231122321223312234122351223612237122381223912240122411224212243122441224512246122471224812249122501225112252122531225412255122561225712258122591226012261122621226312264122651226612267122681226912270122711227212273122741227512276122771227812279122801228112282122831228412285122861228712288122891229012291122921229312294122951229612297122981229912300123011230212303123041230512306123071230812309123101231112312123131231412315123161231712318123191232012321123221232312324123251232612327123281232912330123311233212333123341233512336123371233812339123401234112342123431234412345123461234712348123491235012351123521235312354123551235612357123581235912360123611236212363123641236512366123671236812369123701237112372123731237412375123761237712378123791238012381123821238312384123851238612387123881238912390123911239212393123941239512396123971239812399124001240112402124031240412405124061240712408124091241012411124121241312414124151241612417124181241912420124211242212423124241242512426124271242812429124301243112432124331243412435124361243712438124391244012441124421244312444124451244612447124481244912450124511245212453124541245512456124571245812459124601246112462124631246412465124661246712468124691247012471124721247312474124751247612477124781247912480124811248212483124841248512486124871248812489124901249112492124931249412495124961249712498124991250012501125021250312504125051250612507125081250912510125111251212513125141251512516125171251812519125201252112522125231252412525125261252712528125291253012531125321253312534125351253612537125381253912540125411254212543125441254512546125471254812549125501255112552125531255412555125561255712558125591256012561125621256312564125651256612567125681256912570125711257212573125741257512576125771257812579125801258112582125831258412585125861258712588125891259012591125921259312594125951259612597125981259912600126011260212603126041260512606126071260812609126101261112612126131261412615126161261712618126191262012621126221262312624126251262612627126281262912630126311263212633126341263512636126371263812639126401264112642126431264412645126461264712648126491265012651126521265312654126551265612657126581265912660126611266212663126641266512666126671266812669126701267112672126731267412675126761267712678126791268012681126821268312684126851268612687126881268912690126911269212693126941269512696126971269812699127001270112702127031270412705127061270712708127091271012711127121271312714127151271612717127181271912720127211272212723127241272512726127271272812729127301273112732127331273412735127361273712738127391274012741127421274312744127451274612747127481274912750127511275212753127541275512756127571275812759127601276112762127631276412765127661276712768127691277012771127721277312774127751277612777127781277912780127811278212783127841278512786127871278812789127901279112792127931279412795127961279712798127991280012801128021280312804128051280612807128081280912810128111281212813128141281512816128171281812819128201282112822128231282412825128261282712828128291283012831128321283312834128351283612837128381283912840128411284212843128441284512846128471284812849128501285112852128531285412855128561285712858128591286012861128621286312864128651286612867128681286912870128711287212873128741287512876128771287812879128801288112882128831288412885128861288712888128891289012891128921289312894128951289612897128981289912900129011290212903129041290512906129071290812909129101291112912129131291412915129161291712918129191292012921129221292312924129251292612927129281292912930129311293212933129341293512936129371293812939129401294112942129431294412945129461294712948129491295012951129521295312954129551295612957129581295912960129611296212963129641296512966129671296812969129701297112972129731297412975129761297712978129791298012981129821298312984129851298612987129881298912990129911299212993129941299512996129971299812999130001300113002130031300413005130061300713008130091301013011130121301313014130151301613017130181301913020130211302213023130241302513026130271302813029130301303113032130331303413035130361303713038130391304013041130421304313044130451304613047130481304913050130511305213053130541305513056130571305813059130601306113062130631306413065130661306713068130691307013071130721307313074130751307613077130781307913080130811308213083130841308513086130871308813089130901309113092130931309413095130961309713098130991310013101131021310313104131051310613107131081310913110131111311213113131141311513116131171311813119131201312113122131231312413125131261312713128131291313013131131321313313134131351313613137131381313913140131411314213143131441314513146131471314813149131501315113152131531315413155131561315713158131591316013161131621316313164131651316613167131681316913170131711317213173131741317513176131771317813179131801318113182131831318413185131861318713188131891319013191131921319313194131951319613197131981319913200132011320213203132041320513206132071320813209132101321113212132131321413215132161321713218132191322013221132221322313224132251322613227132281322913230132311323213233132341323513236132371323813239132401324113242132431324413245132461324713248132491325013251132521325313254132551325613257132581325913260132611326213263132641326513266132671326813269132701327113272132731327413275132761327713278132791328013281132821328313284132851328613287132881328913290132911329213293132941329513296132971329813299133001330113302133031330413305133061330713308133091331013311133121331313314133151331613317133181331913320133211332213323133241332513326133271332813329133301333113332133331333413335133361333713338133391334013341133421334313344133451334613347133481334913350133511335213353133541335513356133571335813359133601336113362133631336413365133661336713368133691337013371133721337313374133751337613377133781337913380133811338213383133841338513386133871338813389133901339113392133931339413395133961339713398133991340013401134021340313404134051340613407134081340913410134111341213413134141341513416134171341813419134201342113422134231342413425134261342713428134291343013431134321343313434134351343613437134381343913440134411344213443134441344513446134471344813449134501345113452134531345413455134561345713458134591346013461134621346313464134651346613467134681346913470134711347213473134741347513476134771347813479134801348113482134831348413485134861348713488134891349013491134921349313494134951349613497134981349913500135011350213503135041350513506135071350813509135101351113512135131351413515135161351713518135191352013521135221352313524135251352613527135281352913530135311353213533135341353513536135371353813539135401354113542135431354413545135461354713548135491355013551135521355313554135551355613557135581355913560135611356213563135641356513566135671356813569135701357113572135731357413575135761357713578135791358013581135821358313584135851358613587135881358913590135911359213593135941359513596135971359813599136001360113602136031360413605136061360713608136091361013611136121361313614136151361613617136181361913620136211362213623136241362513626136271362813629136301363113632136331363413635136361363713638136391364013641136421364313644136451364613647136481364913650136511365213653136541365513656136571365813659136601366113662136631366413665136661366713668136691367013671136721367313674136751367613677136781367913680136811368213683136841368513686136871368813689136901369113692136931369413695136961369713698136991370013701137021370313704137051370613707137081370913710137111371213713137141371513716137171371813719137201372113722137231372413725137261372713728137291373013731137321373313734137351373613737137381373913740137411374213743137441374513746137471374813749137501375113752137531375413755137561375713758137591376013761137621376313764137651376613767137681376913770137711377213773137741377513776137771377813779137801378113782137831378413785137861378713788137891379013791137921379313794137951379613797137981379913800138011380213803138041380513806138071380813809138101381113812138131381413815138161381713818138191382013821138221382313824138251382613827138281382913830138311383213833138341383513836138371383813839138401384113842138431384413845138461384713848138491385013851138521385313854138551385613857138581385913860138611386213863138641386513866138671386813869138701387113872138731387413875138761387713878138791388013881138821388313884138851388613887138881388913890138911389213893138941389513896138971389813899139001390113902139031390413905139061390713908139091391013911139121391313914139151391613917139181391913920139211392213923139241392513926139271392813929139301393113932139331393413935139361393713938139391394013941139421394313944139451394613947139481394913950139511395213953139541395513956139571395813959139601396113962139631396413965139661396713968139691397013971139721397313974139751397613977139781397913980139811398213983139841398513986139871398813989139901399113992139931399413995139961399713998139991400014001140021400314004140051400614007140081400914010140111401214013140141401514016140171401814019140201402114022140231402414025140261402714028140291403014031140321403314034140351403614037140381403914040140411404214043140441404514046140471404814049140501405114052140531405414055140561405714058140591406014061140621406314064140651406614067140681406914070140711407214073140741407514076140771407814079140801408114082140831408414085140861408714088140891409014091140921409314094140951409614097140981409914100141011410214103141041410514106141071410814109141101411114112141131411414115141161411714118141191412014121141221412314124141251412614127141281412914130141311413214133141341413514136141371413814139141401414114142141431414414145141461414714148141491415014151141521415314154141551415614157141581415914160141611416214163141641416514166141671416814169141701417114172141731417414175141761417714178141791418014181141821418314184141851418614187141881418914190141911419214193141941419514196141971419814199142001420114202142031420414205142061420714208142091421014211142121421314214142151421614217142181421914220142211422214223142241422514226142271422814229142301423114232142331423414235142361423714238142391424014241142421424314244142451424614247142481424914250142511425214253142541425514256142571425814259142601426114262142631426414265142661426714268142691427014271142721427314274142751427614277142781427914280142811428214283142841428514286142871428814289142901429114292142931429414295142961429714298142991430014301143021430314304143051430614307143081430914310143111431214313143141431514316143171431814319143201432114322143231432414325143261432714328143291433014331143321433314334143351433614337143381433914340143411434214343143441434514346143471434814349143501435114352143531435414355143561435714358143591436014361143621436314364143651436614367143681436914370143711437214373143741437514376143771437814379143801438114382143831438414385143861438714388143891439014391143921439314394143951439614397143981439914400144011440214403144041440514406144071440814409144101441114412144131441414415144161441714418144191442014421144221442314424144251442614427144281442914430144311443214433144341443514436144371443814439144401444114442144431444414445144461444714448144491445014451144521445314454144551445614457144581445914460144611446214463144641446514466144671446814469144701447114472144731447414475144761447714478144791448014481144821448314484144851448614487144881448914490144911449214493144941449514496144971449814499145001450114502145031450414505145061450714508145091451014511145121451314514145151451614517145181451914520145211452214523145241452514526145271452814529145301453114532145331453414535145361453714538145391454014541145421454314544145451454614547145481454914550145511455214553145541455514556145571455814559145601456114562145631456414565145661456714568145691457014571145721457314574145751457614577145781457914580145811458214583145841458514586145871458814589145901459114592145931459414595145961459714598145991460014601146021460314604146051460614607146081460914610146111461214613146141461514616146171461814619146201462114622146231462414625146261462714628146291463014631146321463314634146351463614637146381463914640146411464214643146441464514646146471464814649146501465114652146531465414655146561465714658146591466014661146621466314664146651466614667146681466914670146711467214673146741467514676146771467814679146801468114682146831468414685146861468714688146891469014691146921469314694146951469614697146981469914700147011470214703147041470514706147071470814709147101471114712147131471414715147161471714718147191472014721147221472314724147251472614727147281472914730147311473214733147341473514736147371473814739147401474114742147431474414745147461474714748147491475014751147521475314754147551475614757147581475914760147611476214763147641476514766147671476814769147701477114772147731477414775147761477714778147791478014781147821478314784147851478614787147881478914790147911479214793147941479514796147971479814799148001480114802148031480414805148061480714808148091481014811148121481314814148151481614817148181481914820148211482214823148241482514826148271482814829148301483114832148331483414835148361483714838148391484014841148421484314844148451484614847148481484914850148511485214853148541485514856148571485814859148601486114862148631486414865148661486714868148691487014871148721487314874148751487614877148781487914880148811488214883148841488514886148871488814889148901489114892148931489414895148961489714898148991490014901149021490314904149051490614907149081490914910149111491214913149141491514916149171491814919149201492114922149231492414925149261492714928149291493014931149321493314934149351493614937149381493914940149411494214943149441494514946149471494814949149501495114952149531495414955149561495714958149591496014961149621496314964149651496614967149681496914970149711497214973149741497514976149771497814979149801498114982149831498414985149861498714988149891499014991149921499314994149951499614997149981499915000150011500215003150041500515006150071500815009150101501115012150131501415015150161501715018150191502015021150221502315024150251502615027150281502915030150311503215033150341503515036150371503815039150401504115042150431504415045150461504715048150491505015051150521505315054150551505615057150581505915060150611506215063150641506515066150671506815069150701507115072150731507415075150761507715078150791508015081150821508315084150851508615087150881508915090150911509215093150941509515096150971509815099151001510115102151031510415105151061510715108151091511015111151121511315114151151511615117151181511915120151211512215123151241512515126151271512815129151301513115132151331513415135151361513715138151391514015141151421514315144151451514615147151481514915150151511515215153151541515515156151571515815159151601516115162151631516415165151661516715168151691517015171151721517315174151751517615177151781517915180151811518215183151841518515186151871518815189151901519115192151931519415195151961519715198151991520015201152021520315204152051520615207152081520915210152111521215213152141521515216152171521815219152201522115222152231522415225152261522715228152291523015231152321523315234152351523615237152381523915240152411524215243152441524515246152471524815249152501525115252152531525415255152561525715258152591526015261152621526315264152651526615267152681526915270152711527215273152741527515276152771527815279152801528115282152831528415285152861528715288152891529015291152921529315294152951529615297152981529915300153011530215303153041530515306153071530815309153101531115312153131531415315153161531715318153191532015321153221532315324153251532615327153281532915330153311533215333153341533515336153371533815339153401534115342153431534415345153461534715348153491535015351153521535315354153551535615357153581535915360153611536215363153641536515366153671536815369153701537115372153731537415375153761537715378153791538015381153821538315384153851538615387153881538915390153911539215393153941539515396153971539815399154001540115402154031540415405154061540715408154091541015411154121541315414154151541615417154181541915420154211542215423154241542515426154271542815429154301543115432154331543415435154361543715438154391544015441154421544315444154451544615447154481544915450154511545215453154541545515456154571545815459154601546115462154631546415465154661546715468154691547015471154721547315474154751547615477154781547915480154811548215483154841548515486154871548815489154901549115492154931549415495154961549715498154991550015501155021550315504155051550615507155081550915510155111551215513155141551515516155171551815519155201552115522155231552415525155261552715528155291553015531155321553315534155351553615537155381553915540155411554215543155441554515546155471554815549155501555115552155531555415555155561555715558155591556015561155621556315564155651556615567155681556915570155711557215573155741557515576155771557815579155801558115582155831558415585155861558715588155891559015591155921559315594155951559615597155981559915600156011560215603156041560515606156071560815609156101561115612156131561415615156161561715618156191562015621156221562315624156251562615627156281562915630156311563215633156341563515636156371563815639156401564115642156431564415645156461564715648156491565015651156521565315654156551565615657156581565915660156611566215663156641566515666156671566815669156701567115672156731567415675156761567715678156791568015681156821568315684156851568615687156881568915690156911569215693156941569515696156971569815699157001570115702157031570415705157061570715708157091571015711157121571315714157151571615717157181571915720157211572215723157241572515726157271572815729157301573115732157331573415735157361573715738157391574015741157421574315744157451574615747157481574915750157511575215753157541575515756157571575815759157601576115762157631576415765157661576715768157691577015771157721577315774157751577615777157781577915780157811578215783157841578515786157871578815789157901579115792157931579415795157961579715798157991580015801158021580315804158051580615807158081580915810158111581215813158141581515816158171581815819158201582115822158231582415825158261582715828158291583015831158321583315834158351583615837158381583915840158411584215843158441584515846158471584815849158501585115852158531585415855158561585715858158591586015861158621586315864158651586615867158681586915870158711587215873158741587515876158771587815879158801588115882158831588415885158861588715888158891589015891158921589315894158951589615897158981589915900159011590215903159041590515906159071590815909159101591115912159131591415915159161591715918159191592015921159221592315924159251592615927159281592915930159311593215933159341593515936159371593815939159401594115942159431594415945159461594715948159491595015951159521595315954159551595615957159581595915960159611596215963159641596515966159671596815969159701597115972159731597415975159761597715978159791598015981159821598315984159851598615987159881598915990159911599215993159941599515996159971599815999160001600116002160031600416005160061600716008160091601016011160121601316014160151601616017160181601916020160211602216023160241602516026160271602816029160301603116032160331603416035160361603716038160391604016041160421604316044160451604616047160481604916050160511605216053160541605516056160571605816059160601606116062160631606416065160661606716068160691607016071160721607316074160751607616077160781607916080160811608216083160841608516086160871608816089160901609116092160931609416095160961609716098160991610016101161021610316104161051610616107161081610916110161111611216113161141611516116161171611816119161201612116122161231612416125
  1. /*
  2. * Asterisk -- An open source telephony toolkit.
  3. *
  4. * Copyright (C) 1999 - 2006, Digium, Inc.
  5. *
  6. * Mark Spencer <markster@digium.com>
  7. *
  8. * See http://www.asterisk.org for more information about
  9. * the Asterisk project. Please do not directly contact
  10. * any of the maintainers of this project for assistance;
  11. * the project provides a web site, mailing lists and IRC
  12. * channels for your use.
  13. *
  14. * This program is free software, distributed under the terms of
  15. * the GNU General Public License Version 2. See the LICENSE file
  16. * at the top of the source tree.
  17. */
  18. /*!
  19. * \file
  20. * \author Mark Spencer <markster@digium.com>
  21. * \brief Comedian Mail - Voicemail System
  22. *
  23. * unixODBC (http://www.unixodbc.org/)
  24. * A source distribution of University of Washington's IMAP c-client
  25. * (http://www.washington.edu/imap/)
  26. *
  27. * \par See also
  28. * \arg \ref Config_vm
  29. * \note For information about voicemail IMAP storage, https://wiki.asterisk.org/wiki/display/AST/IMAP+Voicemail+Storage
  30. * \ingroup applications
  31. * \todo This module requires res_adsi to load. This needs to be optional
  32. * during compilation.
  33. *
  34. * \todo This file is now almost impossible to work with, due to all \#ifdefs.
  35. * Feels like the database code before realtime. Someone - please come up
  36. * with a plan to clean this up.
  37. */
  38. /*! \li \ref app_voicemail.c uses configuration file \ref voicemail.conf
  39. * \addtogroup configuration_file Configuration Files
  40. */
  41. /*!
  42. * \page voicemail.conf voicemail.conf
  43. * \verbinclude voicemail.conf.sample
  44. */
  45. /*** MODULEINFO
  46. <defaultenabled>yes</defaultenabled>
  47. <conflict>res_mwi_external</conflict>
  48. <use type="module">res_adsi</use>
  49. <use type="module">res_smdi</use>
  50. <support_level>core</support_level>
  51. ***/
  52. /*** MAKEOPTS
  53. <category name="MENUSELECT_OPTS_app_voicemail" displayname="Voicemail Build Options" positive_output="yes" touch_on_change="apps/app_voicemail.c apps/app_directory.c">
  54. <member name="FILE_STORAGE" displayname="Storage of Voicemail using filesystem">
  55. <conflict>ODBC_STORAGE</conflict>
  56. <conflict>IMAP_STORAGE</conflict>
  57. <defaultenabled>yes</defaultenabled>
  58. <support_level>core</support_level>
  59. </member>
  60. <member name="ODBC_STORAGE" displayname="Storage of Voicemail using ODBC">
  61. <depend>generic_odbc</depend>
  62. <depend>ltdl</depend>
  63. <conflict>IMAP_STORAGE</conflict>
  64. <conflict>FILE_STORAGE</conflict>
  65. <defaultenabled>no</defaultenabled>
  66. <support_level>core</support_level>
  67. </member>
  68. <member name="IMAP_STORAGE" displayname="Storage of Voicemail using IMAP4">
  69. <depend>imap_tk</depend>
  70. <conflict>ODBC_STORAGE</conflict>
  71. <conflict>FILE_STORAGE</conflict>
  72. <use type="external">openssl</use>
  73. <defaultenabled>no</defaultenabled>
  74. <support_level>core</support_level>
  75. </member>
  76. </category>
  77. ***/
  78. #include "asterisk.h"
  79. #ifdef IMAP_STORAGE
  80. #include <ctype.h>
  81. #include <signal.h>
  82. #include <pwd.h>
  83. #ifdef USE_SYSTEM_IMAP
  84. #include <imap/c-client.h>
  85. #include <imap/imap4r1.h>
  86. #include <imap/linkage.h>
  87. #elif defined (USE_SYSTEM_CCLIENT)
  88. #include <c-client/c-client.h>
  89. #include <c-client/imap4r1.h>
  90. #include <c-client/linkage.h>
  91. #else
  92. #include "c-client.h"
  93. #include "imap4r1.h"
  94. #include "linkage.h"
  95. #endif
  96. #endif
  97. ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
  98. #include "asterisk/paths.h" /* use ast_config_AST_SPOOL_DIR */
  99. #include <sys/time.h>
  100. #include <sys/stat.h>
  101. #include <sys/mman.h>
  102. #include <time.h>
  103. #include <dirent.h>
  104. #if defined(__FreeBSD__) || defined(__OpenBSD__)
  105. #include <sys/wait.h>
  106. #endif
  107. #include "asterisk/logger.h"
  108. #include "asterisk/lock.h"
  109. #include "asterisk/file.h"
  110. #include "asterisk/channel.h"
  111. #include "asterisk/pbx.h"
  112. #include "asterisk/config.h"
  113. #include "asterisk/say.h"
  114. #include "asterisk/module.h"
  115. #include "asterisk/adsi.h"
  116. #include "asterisk/app.h"
  117. #include "asterisk/manager.h"
  118. #include "asterisk/dsp.h"
  119. #include "asterisk/localtime.h"
  120. #include "asterisk/cli.h"
  121. #include "asterisk/utils.h"
  122. #include "asterisk/stringfields.h"
  123. #include "asterisk/strings.h"
  124. #include "asterisk/smdi.h"
  125. #include "asterisk/astobj2.h"
  126. #include "asterisk/taskprocessor.h"
  127. #include "asterisk/test.h"
  128. #include "asterisk/format_cache.h"
  129. #ifdef ODBC_STORAGE
  130. #include "asterisk/res_odbc.h"
  131. #endif
  132. #ifdef IMAP_STORAGE
  133. #include "asterisk/threadstorage.h"
  134. #endif
  135. /*** DOCUMENTATION
  136. <application name="VoiceMail" language="en_US">
  137. <synopsis>
  138. Leave a Voicemail message.
  139. </synopsis>
  140. <syntax>
  141. <parameter name="mailboxs" argsep="&amp;" required="true">
  142. <argument name="mailbox1" argsep="@" required="true">
  143. <argument name="mailbox" required="true" />
  144. <argument name="context" />
  145. </argument>
  146. <argument name="mailbox2" argsep="@" multiple="true">
  147. <argument name="mailbox" required="true" />
  148. <argument name="context" />
  149. </argument>
  150. </parameter>
  151. <parameter name="options">
  152. <optionlist>
  153. <option name="b">
  154. <para>Play the <literal>busy</literal> greeting to the calling party.</para>
  155. </option>
  156. <option name="d">
  157. <argument name="c" />
  158. <para>Accept digits for a new extension in context <replaceable>c</replaceable>,
  159. if played during the greeting. Context defaults to the current context.</para>
  160. </option>
  161. <option name="g">
  162. <argument name="#" required="true" />
  163. <para>Use the specified amount of gain when recording the voicemail
  164. message. The units are whole-number decibels (dB). Only works on supported
  165. technologies, which is DAHDI only.</para>
  166. </option>
  167. <option name="s">
  168. <para>Skip the playback of instructions for leaving a message to the
  169. calling party.</para>
  170. </option>
  171. <option name="u">
  172. <para>Play the <literal>unavailable</literal> greeting.</para>
  173. </option>
  174. <option name="U">
  175. <para>Mark message as <literal>URGENT</literal>.</para>
  176. </option>
  177. <option name="P">
  178. <para>Mark message as <literal>PRIORITY</literal>.</para>
  179. </option>
  180. </optionlist>
  181. </parameter>
  182. </syntax>
  183. <description>
  184. <para>This application allows the calling party to leave a message for the specified
  185. list of mailboxes. When multiple mailboxes are specified, the greeting will be taken from
  186. the first mailbox specified. Dialplan execution will stop if the specified mailbox does not
  187. exist.</para>
  188. <para>The Voicemail application will exit if any of the following DTMF digits are received:</para>
  189. <enumlist>
  190. <enum name="0">
  191. <para>Jump to the <literal>o</literal> extension in the current dialplan context.</para>
  192. </enum>
  193. <enum name="*">
  194. <para>Jump to the <literal>a</literal> extension in the current dialplan context.</para>
  195. </enum>
  196. </enumlist>
  197. <para>This application will set the following channel variable upon completion:</para>
  198. <variablelist>
  199. <variable name="VMSTATUS">
  200. <para>This indicates the status of the execution of the VoiceMail application.</para>
  201. <value name="SUCCESS" />
  202. <value name="USEREXIT" />
  203. <value name="FAILED" />
  204. </variable>
  205. </variablelist>
  206. </description>
  207. <see-also>
  208. <ref type="application">VoiceMailMain</ref>
  209. </see-also>
  210. </application>
  211. <application name="VoiceMailMain" language="en_US">
  212. <synopsis>
  213. Check Voicemail messages.
  214. </synopsis>
  215. <syntax>
  216. <parameter name="mailbox" required="true" argsep="@">
  217. <argument name="mailbox" />
  218. <argument name="context" />
  219. </parameter>
  220. <parameter name="options">
  221. <optionlist>
  222. <option name="p">
  223. <para>Consider the <replaceable>mailbox</replaceable> parameter as a prefix to
  224. the mailbox that is entered by the caller.</para>
  225. </option>
  226. <option name="g">
  227. <argument name="#" required="true" />
  228. <para>Use the specified amount of gain when recording a voicemail message.
  229. The units are whole-number decibels (dB).</para>
  230. </option>
  231. <option name="s">
  232. <para>Skip checking the passcode for the mailbox.</para>
  233. </option>
  234. <option name="a">
  235. <argument name="folder" required="true" />
  236. <para>Skip folder prompt and go directly to <replaceable>folder</replaceable> specified.
  237. Defaults to <literal>INBOX</literal> (or <literal>0</literal>).</para>
  238. <enumlist>
  239. <enum name="0"><para>INBOX</para></enum>
  240. <enum name="1"><para>Old</para></enum>
  241. <enum name="2"><para>Work</para></enum>
  242. <enum name="3"><para>Family</para></enum>
  243. <enum name="4"><para>Friends</para></enum>
  244. <enum name="5"><para>Cust1</para></enum>
  245. <enum name="6"><para>Cust2</para></enum>
  246. <enum name="7"><para>Cust3</para></enum>
  247. <enum name="8"><para>Cust4</para></enum>
  248. <enum name="9"><para>Cust5</para></enum>
  249. </enumlist>
  250. </option>
  251. </optionlist>
  252. </parameter>
  253. </syntax>
  254. <description>
  255. <para>This application allows the calling party to check voicemail messages. A specific
  256. <replaceable>mailbox</replaceable>, and optional corresponding <replaceable>context</replaceable>,
  257. may be specified. If a <replaceable>mailbox</replaceable> is not provided, the calling party will
  258. be prompted to enter one. If a <replaceable>context</replaceable> is not specified, the
  259. <literal>default</literal> context will be used.</para>
  260. <para>The VoiceMailMain application will exit if the following DTMF digit is entered as Mailbox
  261. or Password, and the extension exists:</para>
  262. <enumlist>
  263. <enum name="*">
  264. <para>Jump to the <literal>a</literal> extension in the current dialplan context.</para>
  265. </enum>
  266. </enumlist>
  267. </description>
  268. <see-also>
  269. <ref type="application">VoiceMail</ref>
  270. </see-also>
  271. </application>
  272. <application name="MailboxExists" language="en_US">
  273. <synopsis>
  274. Check to see if Voicemail mailbox exists.
  275. </synopsis>
  276. <syntax>
  277. <parameter name="mailbox" required="true" argsep="@">
  278. <argument name="mailbox" required="true" />
  279. <argument name="context" />
  280. </parameter>
  281. <parameter name="options">
  282. <para>None options.</para>
  283. </parameter>
  284. </syntax>
  285. <description>
  286. <note><para>DEPRECATED. Use VM_INFO(mailbox[@context],exists) instead.</para></note>
  287. <para>Check to see if the specified <replaceable>mailbox</replaceable> exists. If no voicemail
  288. <replaceable>context</replaceable> is specified, the <literal>default</literal> context
  289. will be used.</para>
  290. <para>This application will set the following channel variable upon completion:</para>
  291. <variablelist>
  292. <variable name="VMBOXEXISTSSTATUS">
  293. <para>This will contain the status of the execution of the MailboxExists application.
  294. Possible values include:</para>
  295. <value name="SUCCESS" />
  296. <value name="FAILED" />
  297. </variable>
  298. </variablelist>
  299. </description>
  300. <see-also>
  301. <ref type="function">VM_INFO</ref>
  302. </see-also>
  303. </application>
  304. <application name="VMAuthenticate" language="en_US">
  305. <synopsis>
  306. Authenticate with Voicemail passwords.
  307. </synopsis>
  308. <syntax>
  309. <parameter name="mailbox" required="true" argsep="@">
  310. <argument name="mailbox" />
  311. <argument name="context" />
  312. </parameter>
  313. <parameter name="options">
  314. <optionlist>
  315. <option name="s">
  316. <para>Skip playing the initial prompts.</para>
  317. </option>
  318. </optionlist>
  319. </parameter>
  320. </syntax>
  321. <description>
  322. <para>This application behaves the same way as the Authenticate application, but the passwords
  323. are taken from <filename>voicemail.conf</filename>. If the <replaceable>mailbox</replaceable> is
  324. specified, only that mailbox's password will be considered valid. If the <replaceable>mailbox</replaceable>
  325. is not specified, the channel variable <variable>AUTH_MAILBOX</variable> will be set with the authenticated
  326. mailbox.</para>
  327. <para>The VMAuthenticate application will exit if the following DTMF digit is entered as Mailbox
  328. or Password, and the extension exists:</para>
  329. <enumlist>
  330. <enum name="*">
  331. <para>Jump to the <literal>a</literal> extension in the current dialplan context.</para>
  332. </enum>
  333. </enumlist>
  334. </description>
  335. </application>
  336. <application name="VoiceMailPlayMsg" language="en_US">
  337. <synopsis>
  338. Play a single voice mail msg from a mailbox by msg id.
  339. </synopsis>
  340. <syntax>
  341. <parameter name="mailbox" required="true" argsep="@">
  342. <argument name="mailbox" />
  343. <argument name="context" />
  344. </parameter>
  345. <parameter name="msg_id" required="true">
  346. <para>The msg id of the msg to play back. </para>
  347. </parameter>
  348. </syntax>
  349. <description>
  350. <para>This application sets the following channel variable upon completion:</para>
  351. <variablelist>
  352. <variable name="VOICEMAIL_PLAYBACKSTATUS">
  353. <para>The status of the playback attempt as a text string.</para>
  354. <value name="SUCCESS"/>
  355. <value name="FAILED"/>
  356. </variable>
  357. </variablelist>
  358. </description>
  359. </application>
  360. <application name="VMSayName" language="en_US">
  361. <synopsis>
  362. Play the name of a voicemail user
  363. </synopsis>
  364. <syntax>
  365. <parameter name="mailbox" required="true" argsep="@">
  366. <argument name="mailbox" />
  367. <argument name="context" />
  368. </parameter>
  369. </syntax>
  370. <description>
  371. <para>This application will say the recorded name of the voicemail user specified as the
  372. argument to this application. If no context is provided, <literal>default</literal> is assumed.</para>
  373. </description>
  374. </application>
  375. <function name="MAILBOX_EXISTS" language="en_US">
  376. <synopsis>
  377. Tell if a mailbox is configured.
  378. </synopsis>
  379. <syntax argsep="@">
  380. <parameter name="mailbox" required="true" />
  381. <parameter name="context" />
  382. </syntax>
  383. <description>
  384. <note><para>DEPRECATED. Use VM_INFO(mailbox[@context],exists) instead.</para></note>
  385. <para>Returns a boolean of whether the corresponding <replaceable>mailbox</replaceable> exists.
  386. If <replaceable>context</replaceable> is not specified, defaults to the <literal>default</literal>
  387. context.</para>
  388. </description>
  389. <see-also>
  390. <ref type="function">VM_INFO</ref>
  391. </see-also>
  392. </function>
  393. <function name="VM_INFO" language="en_US">
  394. <synopsis>
  395. Returns the selected attribute from a mailbox.
  396. </synopsis>
  397. <syntax argsep=",">
  398. <parameter name="mailbox" argsep="@" required="true">
  399. <argument name="mailbox" required="true" />
  400. <argument name="context" />
  401. </parameter>
  402. <parameter name="attribute" required="true">
  403. <optionlist>
  404. <option name="count">
  405. <para>Count of messages in specified <replaceable>folder</replaceable>.
  406. If <replaceable>folder</replaceable> is not specified, defaults to <literal>INBOX</literal>.</para>
  407. </option>
  408. <option name="email">
  409. <para>E-mail address associated with the mailbox.</para>
  410. </option>
  411. <option name="exists">
  412. <para>Returns a boolean of whether the corresponding <replaceable>mailbox</replaceable> exists.</para>
  413. </option>
  414. <option name="fullname">
  415. <para>Full name associated with the mailbox.</para>
  416. </option>
  417. <option name="language">
  418. <para>Mailbox language if overridden, otherwise the language of the channel.</para>
  419. </option>
  420. <option name="locale">
  421. <para>Mailbox locale if overridden, otherwise global locale.</para>
  422. </option>
  423. <option name="pager">
  424. <para>Pager e-mail address associated with the mailbox.</para>
  425. </option>
  426. <option name="password">
  427. <para>Mailbox access password.</para>
  428. </option>
  429. <option name="tz">
  430. <para>Mailbox timezone if overridden, otherwise global timezone</para>
  431. </option>
  432. </optionlist>
  433. </parameter>
  434. <parameter name="folder" required="false">
  435. <para>If not specified, <literal>INBOX</literal> is assumed.</para>
  436. </parameter>
  437. </syntax>
  438. <description>
  439. <para>Returns the selected attribute from the specified <replaceable>mailbox</replaceable>.
  440. If <replaceable>context</replaceable> is not specified, defaults to the <literal>default</literal>
  441. context. Where the <replaceable>folder</replaceable> can be specified, common folders
  442. include <literal>INBOX</literal>, <literal>Old</literal>, <literal>Work</literal>,
  443. <literal>Family</literal> and <literal>Friends</literal>.</para>
  444. </description>
  445. </function>
  446. <manager name="VoicemailUsersList" language="en_US">
  447. <synopsis>
  448. List All Voicemail User Information.
  449. </synopsis>
  450. <syntax>
  451. <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
  452. </syntax>
  453. <description>
  454. </description>
  455. </manager>
  456. <manager name="VoicemailRefresh" language="en_US">
  457. <synopsis>
  458. Tell Asterisk to poll mailboxes for a change
  459. </synopsis>
  460. <syntax>
  461. <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
  462. <parameter name="Context" />
  463. <parameter name="Mailbox" />
  464. </syntax>
  465. <description>
  466. <para>Normally, MWI indicators are only sent when Asterisk itself
  467. changes a mailbox. With external programs that modify the content
  468. of a mailbox from outside the application, an option exists called
  469. <literal>pollmailboxes</literal> that will cause voicemail to
  470. continually scan all mailboxes on a system for changes. This can
  471. cause a large amount of load on a system. This command allows
  472. external applications to signal when a particular mailbox has
  473. changed, thus permitting external applications to modify mailboxes
  474. and MWI to work without introducing considerable CPU load.</para>
  475. <para>If <replaceable>Context</replaceable> is not specified, all
  476. mailboxes on the system will be polled for changes. If
  477. <replaceable>Context</replaceable> is specified, but
  478. <replaceable>Mailbox</replaceable> is omitted, then all mailboxes
  479. within <replaceable>Context</replaceable> will be polled.
  480. Otherwise, only a single mailbox will be polled for changes.</para>
  481. </description>
  482. </manager>
  483. ***/
  484. #ifdef IMAP_STORAGE
  485. static char imapserver[48];
  486. static char imapport[8];
  487. static char imapflags[128];
  488. static char imapfolder[64];
  489. static char imapparentfolder[64] = "\0";
  490. static char greetingfolder[64];
  491. static char authuser[32];
  492. static char authpassword[42];
  493. static int imapversion = 1;
  494. static int expungeonhangup = 1;
  495. static int imapgreetings = 0;
  496. static char delimiter = '\0';
  497. /* mail_open cannot be protected on a stream basis */
  498. ast_mutex_t mail_open_lock;
  499. struct vm_state;
  500. struct ast_vm_user;
  501. AST_THREADSTORAGE(ts_vmstate);
  502. /* Forward declarations for IMAP */
  503. static int init_mailstream(struct vm_state *vms, int box);
  504. static void write_file(char *filename, char *buffer, unsigned long len);
  505. static char *get_header_by_tag(char *header, char *tag, char *buf, size_t len);
  506. static void vm_imap_delete(char *file, int msgnum, struct ast_vm_user *vmu);
  507. static char *get_user_by_mailbox(char *mailbox, char *buf, size_t len);
  508. static struct vm_state *get_vm_state_by_imapuser(const char *user, int interactive);
  509. static struct vm_state *get_vm_state_by_mailbox(const char *mailbox, const char *context, int interactive);
  510. static struct vm_state *create_vm_state_from_user(struct ast_vm_user *vmu);
  511. static void vmstate_insert(struct vm_state *vms);
  512. static void vmstate_delete(struct vm_state *vms);
  513. static void set_update(MAILSTREAM * stream);
  514. static void init_vm_state(struct vm_state *vms);
  515. static int save_body(BODY *body, struct vm_state *vms, char *section, char *format, int is_intro);
  516. static void get_mailbox_delimiter(struct vm_state *vms, MAILSTREAM *stream);
  517. static void mm_parsequota (MAILSTREAM *stream, unsigned char *msg, QUOTALIST *pquota);
  518. static void imap_mailbox_name(char *spec, size_t len, struct vm_state *vms, int box, int target);
  519. static int imap_store_file(const char *dir, const char *mailboxuser, const char *mailboxcontext, int msgnum, struct ast_channel *chan, struct ast_vm_user *vmu, char *fmt, int duration, struct vm_state *vms, const char *flag, const char *msg_id);
  520. static void vm_imap_update_msg_id(char *dir, int msgnum, const char *msg_id, struct ast_vm_user *vmu, struct ast_config *msg_cfg, int folder);
  521. static void update_messages_by_imapuser(const char *user, unsigned long number);
  522. static int vm_delete(char *file);
  523. static int imap_remove_file (char *dir, int msgnum);
  524. static int imap_retrieve_file (const char *dir, const int msgnum, const char *mailbox, const char *context);
  525. static int imap_delete_old_greeting (char *dir, struct vm_state *vms);
  526. static void check_quota(struct vm_state *vms, char *mailbox);
  527. static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu, int box);
  528. struct vmstate {
  529. struct vm_state *vms;
  530. AST_LIST_ENTRY(vmstate) list;
  531. };
  532. static AST_LIST_HEAD_STATIC(vmstates, vmstate);
  533. #endif
  534. #define SMDI_MWI_WAIT_TIMEOUT 1000 /* 1 second */
  535. #define COMMAND_TIMEOUT 5000
  536. /* Don't modify these here; set your umask at runtime instead */
  537. #define VOICEMAIL_DIR_MODE 0777
  538. #define VOICEMAIL_FILE_MODE 0666
  539. #define CHUNKSIZE 65536
  540. #define VOICEMAIL_CONFIG "voicemail.conf"
  541. #define ASTERISK_USERNAME "asterisk"
  542. /* Define fast-forward, pause, restart, and reverse keys
  543. * while listening to a voicemail message - these are
  544. * strings, not characters */
  545. #define DEFAULT_LISTEN_CONTROL_FORWARD_KEY "#"
  546. #define DEFAULT_LISTEN_CONTROL_REVERSE_KEY "*"
  547. #define DEFAULT_LISTEN_CONTROL_PAUSE_KEY "0"
  548. #define DEFAULT_LISTEN_CONTROL_RESTART_KEY "2"
  549. #define DEFAULT_LISTEN_CONTROL_STOP_KEY "13456789"
  550. #define VALID_DTMF "1234567890*#" /* Yes ABCD are valid dtmf but what phones have those? */
  551. /* Default mail command to mail voicemail. Change it with the
  552. * mailcmd= command in voicemail.conf */
  553. #define SENDMAIL "/usr/sbin/sendmail -t"
  554. #define INTRO "vm-intro"
  555. #define MAXMSG 100
  556. #define MAXMSGLIMIT 9999
  557. #define MINPASSWORD 0 /*!< Default minimum mailbox password length */
  558. #define BASELINELEN 72
  559. #define BASEMAXINLINE 256
  560. #ifdef IMAP_STORAGE
  561. #define ENDL "\r\n"
  562. #else
  563. #define ENDL "\n"
  564. #endif
  565. #define MAX_DATETIME_FORMAT 512
  566. #define MAX_NUM_CID_CONTEXTS 10
  567. #define VM_REVIEW (1 << 0) /*!< After recording, permit the caller to review the recording before saving */
  568. #define VM_OPERATOR (1 << 1) /*!< Allow 0 to be pressed to go to 'o' extension */
  569. #define VM_SAYCID (1 << 2) /*!< Repeat the CallerID info during envelope playback */
  570. #define VM_SVMAIL (1 << 3) /*!< Allow the user to compose a new VM from within VoicemailMain */
  571. #define VM_ENVELOPE (1 << 4) /*!< Play the envelope information (who-from, time received, etc.) */
  572. #define VM_SAYDURATION (1 << 5) /*!< Play the length of the message during envelope playback */
  573. #define VM_SKIPAFTERCMD (1 << 6) /*!< After deletion, assume caller wants to go to the next message */
  574. #define VM_FORCENAME (1 << 7) /*!< Have new users record their name */
  575. #define VM_FORCEGREET (1 << 8) /*!< Have new users record their greetings */
  576. #define VM_PBXSKIP (1 << 9) /*!< Skip the [PBX] preamble in the Subject line of emails */
  577. #define VM_DIRECFORWARD (1 << 10) /*!< Permit caller to use the Directory app for selecting to which mailbox to forward a VM */
  578. #define VM_ATTACH (1 << 11) /*!< Attach message to voicemail notifications? */
  579. #define VM_DELETE (1 << 12) /*!< Delete message after sending notification */
  580. #define VM_ALLOCED (1 << 13) /*!< Structure was malloc'ed, instead of placed in a return (usually static) buffer */
  581. #define VM_SEARCH (1 << 14) /*!< Search all contexts for a matching mailbox */
  582. #define VM_TEMPGREETWARN (1 << 15) /*!< Remind user tempgreeting is set */
  583. #define VM_MOVEHEARD (1 << 16) /*!< Move a "heard" message to Old after listening to it */
  584. #define VM_MESSAGEWRAP (1 << 17) /*!< Wrap around from the last message to the first, and vice-versa */
  585. #define VM_FWDURGAUTO (1 << 18) /*!< Autoset of Urgent flag on forwarded Urgent messages set globally */
  586. #define ERROR_LOCK_PATH -100
  587. #define OPERATOR_EXIT 300
  588. enum vm_box {
  589. NEW_FOLDER,
  590. OLD_FOLDER,
  591. WORK_FOLDER,
  592. FAMILY_FOLDER,
  593. FRIENDS_FOLDER,
  594. GREETINGS_FOLDER
  595. };
  596. enum vm_option_flags {
  597. OPT_SILENT = (1 << 0),
  598. OPT_BUSY_GREETING = (1 << 1),
  599. OPT_UNAVAIL_GREETING = (1 << 2),
  600. OPT_RECORDGAIN = (1 << 3),
  601. OPT_PREPEND_MAILBOX = (1 << 4),
  602. OPT_AUTOPLAY = (1 << 6),
  603. OPT_DTMFEXIT = (1 << 7),
  604. OPT_MESSAGE_Urgent = (1 << 8),
  605. OPT_MESSAGE_PRIORITY = (1 << 9)
  606. };
  607. enum vm_option_args {
  608. OPT_ARG_RECORDGAIN = 0,
  609. OPT_ARG_PLAYFOLDER = 1,
  610. OPT_ARG_DTMFEXIT = 2,
  611. /* This *must* be the last value in this enum! */
  612. OPT_ARG_ARRAY_SIZE = 3,
  613. };
  614. enum vm_passwordlocation {
  615. OPT_PWLOC_VOICEMAILCONF = 0,
  616. OPT_PWLOC_SPOOLDIR = 1,
  617. OPT_PWLOC_USERSCONF = 2,
  618. };
  619. AST_APP_OPTIONS(vm_app_options, {
  620. AST_APP_OPTION('s', OPT_SILENT),
  621. AST_APP_OPTION('b', OPT_BUSY_GREETING),
  622. AST_APP_OPTION('u', OPT_UNAVAIL_GREETING),
  623. AST_APP_OPTION_ARG('g', OPT_RECORDGAIN, OPT_ARG_RECORDGAIN),
  624. AST_APP_OPTION_ARG('d', OPT_DTMFEXIT, OPT_ARG_DTMFEXIT),
  625. AST_APP_OPTION('p', OPT_PREPEND_MAILBOX),
  626. AST_APP_OPTION_ARG('a', OPT_AUTOPLAY, OPT_ARG_PLAYFOLDER),
  627. AST_APP_OPTION('U', OPT_MESSAGE_Urgent),
  628. AST_APP_OPTION('P', OPT_MESSAGE_PRIORITY)
  629. });
  630. static const char * const mailbox_folders[] = {
  631. #ifdef IMAP_STORAGE
  632. imapfolder,
  633. #else
  634. "INBOX",
  635. #endif
  636. "Old",
  637. "Work",
  638. "Family",
  639. "Friends",
  640. "Cust1",
  641. "Cust2",
  642. "Cust3",
  643. "Cust4",
  644. "Cust5",
  645. "Deleted",
  646. "Urgent",
  647. };
  648. static int load_config(int reload);
  649. #ifdef TEST_FRAMEWORK
  650. static int load_config_from_memory(int reload, struct ast_config *cfg, struct ast_config *ucfg);
  651. #endif
  652. static int actual_load_config(int reload, struct ast_config *cfg, struct ast_config *ucfg);
  653. /*! \page vmlang Voicemail Language Syntaxes Supported
  654. \par Syntaxes supported, not really language codes.
  655. \arg \b en - English
  656. \arg \b de - German
  657. \arg \b es - Spanish
  658. \arg \b fr - French
  659. \arg \b it - Italian
  660. \arg \b nl - Dutch
  661. \arg \b pt - Portuguese
  662. \arg \b pt_BR - Portuguese (Brazil)
  663. \arg \b gr - Greek
  664. \arg \b no - Norwegian
  665. \arg \b se - Swedish
  666. \arg \b tw - Chinese (Taiwan)
  667. \arg \b ua - Ukrainian
  668. German requires the following additional soundfile:
  669. \arg \b 1F einE (feminine)
  670. Spanish requires the following additional soundfile:
  671. \arg \b 1M un (masculine)
  672. Dutch, Portuguese & Spanish require the following additional soundfiles:
  673. \arg \b vm-INBOXs singular of 'new'
  674. \arg \b vm-Olds singular of 'old/heard/read'
  675. NB these are plural:
  676. \arg \b vm-INBOX nieuwe (nl)
  677. \arg \b vm-Old oude (nl)
  678. Polish uses:
  679. \arg \b vm-new-a 'new', feminine singular accusative
  680. \arg \b vm-new-e 'new', feminine plural accusative
  681. \arg \b vm-new-ych 'new', feminine plural genitive
  682. \arg \b vm-old-a 'old', feminine singular accusative
  683. \arg \b vm-old-e 'old', feminine plural accusative
  684. \arg \b vm-old-ych 'old', feminine plural genitive
  685. \arg \b digits/1-a 'one', not always same as 'digits/1'
  686. \arg \b digits/2-ie 'two', not always same as 'digits/2'
  687. Swedish uses:
  688. \arg \b vm-nytt singular of 'new'
  689. \arg \b vm-nya plural of 'new'
  690. \arg \b vm-gammalt singular of 'old'
  691. \arg \b vm-gamla plural of 'old'
  692. \arg \b digits/ett 'one', not always same as 'digits/1'
  693. Norwegian uses:
  694. \arg \b vm-ny singular of 'new'
  695. \arg \b vm-nye plural of 'new'
  696. \arg \b vm-gammel singular of 'old'
  697. \arg \b vm-gamle plural of 'old'
  698. Dutch also uses:
  699. \arg \b nl-om 'at'?
  700. Spanish also uses:
  701. \arg \b vm-youhaveno
  702. Italian requires the following additional soundfile:
  703. For vm_intro_it:
  704. \arg \b vm-nuovo new
  705. \arg \b vm-nuovi new plural
  706. \arg \b vm-vecchio old
  707. \arg \b vm-vecchi old plural
  708. Japanese requires the following additional soundfile:
  709. \arg \b jp-arimasu there is
  710. \arg \b jp-arimasen there is not
  711. \arg \b jp-oshitekudasai please press
  712. \arg \b jp-ni article ni
  713. \arg \b jp-ga article ga
  714. \arg \b jp-wa article wa
  715. \arg \b jp-wo article wo
  716. Chinese (Taiwan) requires the following additional soundfile:
  717. \arg \b vm-tong A class-word for call (tong1)
  718. \arg \b vm-ri A class-word for day (ri4)
  719. \arg \b vm-you You (ni3)
  720. \arg \b vm-haveno Have no (mei2 you3)
  721. \arg \b vm-have Have (you3)
  722. \arg \b vm-listen To listen (yao4 ting1)
  723. \note Don't use vm-INBOX or vm-Old, because they are the name of the INBOX and Old folders,
  724. spelled among others when you have to change folder. For the above reasons, vm-INBOX
  725. and vm-Old are spelled plural, to make them sound more as folder name than an adjective.
  726. */
  727. struct baseio {
  728. int iocp;
  729. int iolen;
  730. int linelength;
  731. int ateof;
  732. unsigned char iobuf[BASEMAXINLINE];
  733. };
  734. /*! Structure for linked list of users
  735. * Use ast_vm_user_destroy() to free one of these structures. */
  736. struct ast_vm_user {
  737. char context[AST_MAX_CONTEXT]; /*!< Voicemail context */
  738. char mailbox[AST_MAX_EXTENSION]; /*!< Mailbox id, unique within vm context */
  739. char password[80]; /*!< Secret pin code, numbers only */
  740. char fullname[80]; /*!< Full name, for directory app */
  741. char *email; /*!< E-mail address */
  742. char *emailsubject; /*!< E-mail subject */
  743. char *emailbody; /*!< E-mail body */
  744. char pager[80]; /*!< E-mail address to pager (no attachment) */
  745. char serveremail[80]; /*!< From: Mail address */
  746. char language[MAX_LANGUAGE]; /*!< Config: Language setting */
  747. char zonetag[80]; /*!< Time zone */
  748. char locale[20]; /*!< The locale (for presentation of date/time) */
  749. char callback[80];
  750. char dialout[80];
  751. char uniqueid[80]; /*!< Unique integer identifier */
  752. char exit[80];
  753. char attachfmt[20]; /*!< Attachment format */
  754. unsigned int flags; /*!< VM_ flags */
  755. int saydurationm;
  756. int minsecs; /*!< Minimum number of seconds per message for this mailbox */
  757. int maxmsg; /*!< Maximum number of msgs per folder for this mailbox */
  758. int maxdeletedmsg; /*!< Maximum number of deleted msgs saved for this mailbox */
  759. int maxsecs; /*!< Maximum number of seconds per message for this mailbox */
  760. int passwordlocation; /*!< Storage location of the password */
  761. #ifdef IMAP_STORAGE
  762. char imapserver[48]; /*!< IMAP server address */
  763. char imapport[8]; /*!< IMAP server port */
  764. char imapflags[128]; /*!< IMAP optional flags */
  765. char imapuser[80]; /*!< IMAP server login */
  766. char imappassword[80]; /*!< IMAP server password if authpassword not defined */
  767. char imapfolder[64]; /*!< IMAP voicemail folder */
  768. char imapvmshareid[80]; /*!< Shared mailbox ID to use rather than the dialed one */
  769. int imapversion; /*!< If configuration changes, use the new values */
  770. #endif
  771. double volgain; /*!< Volume gain for voicemails sent via email */
  772. AST_LIST_ENTRY(ast_vm_user) list;
  773. };
  774. /*! Voicemail time zones */
  775. struct vm_zone {
  776. AST_LIST_ENTRY(vm_zone) list;
  777. char name[80];
  778. char timezone[80];
  779. char msg_format[512];
  780. };
  781. #define VMSTATE_MAX_MSG_ARRAY 256
  782. /*! Voicemail mailbox state */
  783. struct vm_state {
  784. char curbox[80];
  785. char username[80];
  786. char context[80];
  787. char curdir[PATH_MAX];
  788. char vmbox[PATH_MAX];
  789. char fn[PATH_MAX];
  790. char intro[PATH_MAX];
  791. int *deleted;
  792. int *heard;
  793. int dh_arraysize; /* used for deleted / heard allocation */
  794. int curmsg;
  795. int lastmsg;
  796. int newmessages;
  797. int oldmessages;
  798. int urgentmessages;
  799. int starting;
  800. int repeats;
  801. #ifdef IMAP_STORAGE
  802. ast_mutex_t lock;
  803. int updated; /*!< decremented on each mail check until 1 -allows delay */
  804. long *msgArray;
  805. unsigned msg_array_max;
  806. MAILSTREAM *mailstream;
  807. int vmArrayIndex;
  808. char imapuser[80]; /*!< IMAP server login */
  809. char imapfolder[64]; /*!< IMAP voicemail folder */
  810. char imapserver[48]; /*!< IMAP server address */
  811. char imapport[8]; /*!< IMAP server port */
  812. char imapflags[128]; /*!< IMAP optional flags */
  813. int imapversion;
  814. int interactive;
  815. char introfn[PATH_MAX]; /*!< Name of prepended file */
  816. unsigned int quota_limit;
  817. unsigned int quota_usage;
  818. struct vm_state *persist_vms;
  819. #endif
  820. };
  821. #ifdef ODBC_STORAGE
  822. static char odbc_database[80];
  823. static char odbc_table[80];
  824. #define RETRIEVE(a,b,c,d) retrieve_file(a,b)
  825. #define DISPOSE(a,b) remove_file(a,b)
  826. #define STORE(a,b,c,d,e,f,g,h,i,j,k) store_file(a,b,c,d)
  827. #define EXISTS(a,b,c,d) (message_exists(a,b))
  828. #define RENAME(a,b,c,d,e,f,g,h) (rename_file(a,b,c,d,e,f))
  829. #define COPY(a,b,c,d,e,f,g,h) (copy_file(a,b,c,d,e,f))
  830. #define DELETE(a,b,c,d) (delete_file(a,b))
  831. #define UPDATE_MSG_ID(a, b, c, d, e, f) (odbc_update_msg_id((a), (b), (c)))
  832. #else
  833. #ifdef IMAP_STORAGE
  834. #define DISPOSE(a,b) (imap_remove_file(a,b))
  835. #define STORE(a,b,c,d,e,f,g,h,i,j,k) (imap_store_file(a,b,c,d,e,f,g,h,i,j,k))
  836. #define RETRIEVE(a,b,c,d) imap_retrieve_file(a,b,c,d)
  837. #define EXISTS(a,b,c,d) (ast_fileexists(c,NULL,d) > 0)
  838. #define RENAME(a,b,c,d,e,f,g,h) (rename_file(g,h));
  839. #define COPY(a,b,c,d,e,f,g,h) (copy_file(g,h));
  840. #define DELETE(a,b,c,d) (vm_imap_delete(a,b,d))
  841. #define UPDATE_MSG_ID(a, b, c, d, e, f) (vm_imap_update_msg_id((a), (b), (c), (d), (e), (f)))
  842. #else
  843. #define RETRIEVE(a,b,c,d)
  844. #define DISPOSE(a,b)
  845. #define STORE(a,b,c,d,e,f,g,h,i,j,k)
  846. #define EXISTS(a,b,c,d) (ast_fileexists(c,NULL,d) > 0)
  847. #define RENAME(a,b,c,d,e,f,g,h) (rename_file(g,h));
  848. #define COPY(a,b,c,d,e,f,g,h) (copy_plain_file(g,h));
  849. #define DELETE(a,b,c,d) (vm_delete(c))
  850. #define UPDATE_MSG_ID(a, b, c, d, e, f)
  851. #endif
  852. #endif
  853. static char VM_SPOOL_DIR[PATH_MAX];
  854. static char ext_pass_cmd[128];
  855. static char ext_pass_check_cmd[128];
  856. static int my_umask;
  857. #define PWDCHANGE_INTERNAL (1 << 1)
  858. #define PWDCHANGE_EXTERNAL (1 << 2)
  859. static int pwdchange = PWDCHANGE_INTERNAL;
  860. #ifdef ODBC_STORAGE
  861. #define tdesc "Comedian Mail (Voicemail System) with ODBC Storage"
  862. #else
  863. # ifdef IMAP_STORAGE
  864. # define tdesc "Comedian Mail (Voicemail System) with IMAP Storage"
  865. # else
  866. # define tdesc "Comedian Mail (Voicemail System)"
  867. # endif
  868. #endif
  869. static char userscontext[AST_MAX_EXTENSION] = "default";
  870. static char *addesc = "Comedian Mail";
  871. /* Leave a message */
  872. static char *app = "VoiceMail";
  873. /* Check mail, control, etc */
  874. static char *app2 = "VoiceMailMain";
  875. static char *app3 = "MailboxExists";
  876. static char *app4 = "VMAuthenticate";
  877. static char *playmsg_app = "VoiceMailPlayMsg";
  878. static char *sayname_app = "VMSayName";
  879. static AST_LIST_HEAD_STATIC(users, ast_vm_user);
  880. static AST_LIST_HEAD_STATIC(zones, vm_zone);
  881. static char zonetag[80];
  882. static char locale[20];
  883. static int maxsilence;
  884. static int maxmsg;
  885. static int maxdeletedmsg;
  886. static int silencethreshold = 128;
  887. static char serveremail[80];
  888. static char mailcmd[160]; /* Configurable mail cmd */
  889. static char externnotify[160];
  890. static struct ast_smdi_interface *smdi_iface = NULL;
  891. static char vmfmts[80];
  892. static double volgain;
  893. static int vmminsecs;
  894. static int vmmaxsecs;
  895. static int maxgreet;
  896. static int skipms;
  897. static int maxlogins;
  898. static int minpassword;
  899. static int passwordlocation;
  900. /*! Poll mailboxes for changes since there is something external to
  901. * app_voicemail that may change them. */
  902. static unsigned int poll_mailboxes;
  903. /*! Polling frequency */
  904. static unsigned int poll_freq;
  905. /*! By default, poll every 30 seconds */
  906. #define DEFAULT_POLL_FREQ 30
  907. AST_MUTEX_DEFINE_STATIC(poll_lock);
  908. static ast_cond_t poll_cond = PTHREAD_COND_INITIALIZER;
  909. static pthread_t poll_thread = AST_PTHREADT_NULL;
  910. static unsigned char poll_thread_run;
  911. /*! Subscription to MWI event subscription changes */
  912. static struct stasis_subscription *mwi_sub_sub;
  913. /*!
  914. * \brief An MWI subscription
  915. *
  916. * This is so we can keep track of which mailboxes are subscribed to.
  917. * This way, we know which mailboxes to poll when the pollmailboxes
  918. * option is being used.
  919. */
  920. struct mwi_sub {
  921. AST_RWLIST_ENTRY(mwi_sub) entry;
  922. int old_urgent;
  923. int old_new;
  924. int old_old;
  925. char *uniqueid;
  926. char mailbox[1];
  927. };
  928. struct mwi_sub_task {
  929. const char *mailbox;
  930. const char *context;
  931. const char *uniqueid;
  932. };
  933. static void mwi_sub_task_dtor(struct mwi_sub_task *mwist)
  934. {
  935. ast_free((void *) mwist->mailbox);
  936. ast_free((void *) mwist->context);
  937. ast_free((void *) mwist->uniqueid);
  938. ast_free(mwist);
  939. }
  940. static struct ast_taskprocessor *mwi_subscription_tps;
  941. static AST_RWLIST_HEAD_STATIC(mwi_subs, mwi_sub);
  942. /* custom audio control prompts for voicemail playback */
  943. static char listen_control_forward_key[12];
  944. static char listen_control_reverse_key[12];
  945. static char listen_control_pause_key[12];
  946. static char listen_control_restart_key[12];
  947. static char listen_control_stop_key[12];
  948. /* custom password sounds */
  949. static char vm_password[80] = "vm-password";
  950. static char vm_newpassword[80] = "vm-newpassword";
  951. static char vm_passchanged[80] = "vm-passchanged";
  952. static char vm_reenterpassword[80] = "vm-reenterpassword";
  953. static char vm_mismatch[80] = "vm-mismatch";
  954. static char vm_invalid_password[80] = "vm-invalid-password";
  955. static char vm_pls_try_again[80] = "vm-pls-try-again";
  956. /*
  957. * XXX If we have the time, motivation, etc. to fix up this prompt, one of the following would be appropriate:
  958. * 1. create a sound along the lines of "Please try again. When done, press the pound key" which could be spliced
  959. * from existing sound clips. This would require some programming changes in the area of vm_forward options and also
  960. * app.c's __ast_play_and_record function
  961. * 2. create a sound prompt saying "Please try again. When done recording, press any key to stop and send the prepended
  962. * message." At the time of this comment, I think this would require new voice work to be commissioned.
  963. * 3. Something way different like providing instructions before a time out or a post-recording menu. This would require
  964. * more effort than either of the other two.
  965. */
  966. static char vm_prepend_timeout[80] = "vm-then-pound";
  967. static struct ast_flags globalflags = {0};
  968. static int saydurationminfo;
  969. static char dialcontext[AST_MAX_CONTEXT] = "";
  970. static char callcontext[AST_MAX_CONTEXT] = "";
  971. static char exitcontext[AST_MAX_CONTEXT] = "";
  972. static char cidinternalcontexts[MAX_NUM_CID_CONTEXTS][64];
  973. static char *emailbody = NULL;
  974. static char *emailsubject = NULL;
  975. static char *pagerbody = NULL;
  976. static char *pagersubject = NULL;
  977. static char fromstring[100];
  978. static char pagerfromstring[100];
  979. static char charset[32] = "ISO-8859-1";
  980. static unsigned char adsifdn[4] = "\x00\x00\x00\x0F";
  981. static unsigned char adsisec[4] = "\x9B\xDB\xF7\xAC";
  982. static int adsiver = 1;
  983. static char emaildateformat[32] = "%A, %B %d, %Y at %r";
  984. static char pagerdateformat[32] = "%A, %B %d, %Y at %r";
  985. /* Forward declarations - generic */
  986. static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu, int box);
  987. static int close_mailbox(struct vm_state *vms, struct ast_vm_user *vmu);
  988. static int advanced_options(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int msg, int option, signed char record_gain);
  989. static int dialout(struct ast_channel *chan, struct ast_vm_user *vmu, char *num, char *outgoing_context);
  990. static int play_record_review(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime,
  991. char *fmt, int outsidecaller, struct ast_vm_user *vmu, int *duration, int *sound_duration, const char *unlockdir,
  992. signed char record_gain, struct vm_state *vms, char *flag, const char *msg_id);
  993. static int vm_tempgreeting(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain);
  994. static int vm_play_folder_name(struct ast_channel *chan, char *mbox);
  995. static int notify_new_message(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int msgnum, long duration, char *fmt, char *cidnum, char *cidname, const char *flag);
  996. static void make_email_file(FILE *p, char *srcemail, struct ast_vm_user *vmu, int msgnum, char *context, char *mailbox, const char *fromfolder, char *cidnum, char *cidname, char *attach, char *attach2, char *format, int duration, int attach_user_voicemail, struct ast_channel *chan, const char *category, int imap, const char *flag, const char *msg_id);
  997. static void apply_options(struct ast_vm_user *vmu, const char *options);
  998. static int add_email_attachment(FILE *p, struct ast_vm_user *vmu, char *format, char *attach, char *greeting_attachment, char *mailbox, char *bound, char *filename, int last, int msgnum);
  999. static int is_valid_dtmf(const char *key);
  1000. static void read_password_from_file(const char *secretfn, char *password, int passwordlen);
  1001. static int write_password_to_file(const char *secretfn, const char *password);
  1002. static const char *substitute_escapes(const char *value);
  1003. static int message_range_and_existence_check(struct vm_state *vms, const char *msg_ids [], size_t num_msgs, int *msg_nums, struct ast_vm_user *vmu);
  1004. static void notify_new_state(struct ast_vm_user *vmu);
  1005. /*!
  1006. * Place a message in the indicated folder
  1007. *
  1008. * \param vmu Voicemail user
  1009. * \param vms Current voicemail state for the user
  1010. * \param msg The message number to save
  1011. * \param box The folder into which the message should be saved
  1012. * \param[out] newmsg The new message number of the saved message
  1013. * \param move Tells whether to copy or to move the message
  1014. *
  1015. * \note the "move" parameter is only honored for IMAP voicemail presently
  1016. * \retval 0 Success
  1017. * \retval other Failure
  1018. */
  1019. static int save_to_folder(struct ast_vm_user *vmu, struct vm_state *vms, int msg, int box, int *newmsg, int move);
  1020. static struct ast_vm_mailbox_snapshot *vm_mailbox_snapshot_create(const char *mailbox, const char *context, const char *folder, int descending, enum ast_vm_snapshot_sort_val sort_val, int combine_INBOX_and_OLD);
  1021. static struct ast_vm_mailbox_snapshot *vm_mailbox_snapshot_destroy(struct ast_vm_mailbox_snapshot *mailbox_snapshot);
  1022. static int vm_msg_forward(const char *from_mailbox, const char *from_context, const char *from_folder, const char *to_mailbox, const char *to_context, const char *to_folder, size_t num_msgs, const char *msg_ids[], int delete_old);
  1023. static int vm_msg_move(const char *mailbox, const char *context, size_t num_msgs, const char *oldfolder, const char *old_msg_ids[], const char *newfolder);
  1024. static int vm_msg_remove(const char *mailbox, const char *context, size_t num_msgs, const char *folder, const char *msgs[]);
  1025. static int vm_msg_play(struct ast_channel *chan, const char *mailbox, const char *context, const char *folder, const char *msg_num, ast_vm_msg_play_cb cb);
  1026. #ifdef TEST_FRAMEWORK
  1027. static int vm_test_destroy_user(const char *context, const char *mailbox);
  1028. static int vm_test_create_user(const char *context, const char *mailbox);
  1029. #endif
  1030. /*!
  1031. * \internal
  1032. * \brief Parse the given mailbox_id into mailbox and context.
  1033. * \since 12.0.0
  1034. *
  1035. * \param mailbox_id The mailbox@context string to separate.
  1036. * \param mailbox Where the mailbox part will start.
  1037. * \param context Where the context part will start. ("default" if not present)
  1038. *
  1039. * \retval 0 on success.
  1040. * \retval -1 on error.
  1041. */
  1042. static int separate_mailbox(char *mailbox_id, char **mailbox, char **context)
  1043. {
  1044. if (ast_strlen_zero(mailbox_id) || !mailbox || !context) {
  1045. return -1;
  1046. }
  1047. *context = mailbox_id;
  1048. *mailbox = strsep(context, "@");
  1049. if (ast_strlen_zero(*mailbox)) {
  1050. return -1;
  1051. }
  1052. if (ast_strlen_zero(*context)) {
  1053. *context = "default";
  1054. }
  1055. return 0;
  1056. }
  1057. struct ao2_container *inprocess_container;
  1058. struct inprocess {
  1059. int count;
  1060. char *context;
  1061. char mailbox[0];
  1062. };
  1063. static int inprocess_hash_fn(const void *obj, const int flags)
  1064. {
  1065. const struct inprocess *i = obj;
  1066. return atoi(i->mailbox);
  1067. }
  1068. static int inprocess_cmp_fn(void *obj, void *arg, int flags)
  1069. {
  1070. struct inprocess *i = obj, *j = arg;
  1071. if (strcmp(i->mailbox, j->mailbox)) {
  1072. return 0;
  1073. }
  1074. return !strcmp(i->context, j->context) ? CMP_MATCH : 0;
  1075. }
  1076. static int inprocess_count(const char *context, const char *mailbox, int delta)
  1077. {
  1078. struct inprocess *i, *arg = ast_alloca(sizeof(*arg) + strlen(context) + strlen(mailbox) + 2);
  1079. arg->context = arg->mailbox + strlen(mailbox) + 1;
  1080. strcpy(arg->mailbox, mailbox); /* SAFE */
  1081. strcpy(arg->context, context); /* SAFE */
  1082. ao2_lock(inprocess_container);
  1083. if ((i = ao2_find(inprocess_container, arg, 0))) {
  1084. int ret = ast_atomic_fetchadd_int(&i->count, delta);
  1085. ao2_unlock(inprocess_container);
  1086. ao2_ref(i, -1);
  1087. return ret;
  1088. }
  1089. if (delta < 0) {
  1090. ast_log(LOG_WARNING, "BUG: ref count decrement on non-existing object???\n");
  1091. }
  1092. if (!(i = ao2_alloc(sizeof(*i) + strlen(context) + strlen(mailbox) + 2, NULL))) {
  1093. ao2_unlock(inprocess_container);
  1094. return 0;
  1095. }
  1096. i->context = i->mailbox + strlen(mailbox) + 1;
  1097. strcpy(i->mailbox, mailbox); /* SAFE */
  1098. strcpy(i->context, context); /* SAFE */
  1099. i->count = delta;
  1100. ao2_link(inprocess_container, i);
  1101. ao2_unlock(inprocess_container);
  1102. ao2_ref(i, -1);
  1103. return 0;
  1104. }
  1105. #if !(defined(ODBC_STORAGE) || defined(IMAP_STORAGE))
  1106. static int __has_voicemail(const char *context, const char *mailbox, const char *folder, int shortcircuit);
  1107. #endif
  1108. /*!
  1109. * \brief Strips control and non 7-bit clean characters from input string.
  1110. *
  1111. * \note To map control and none 7-bit characters to a 7-bit clean characters
  1112. * please use ast_str_encode_mine().
  1113. */
  1114. static char *strip_control_and_high(const char *input, char *buf, size_t buflen)
  1115. {
  1116. char *bufptr = buf;
  1117. for (; *input; input++) {
  1118. if (*input < 32) {
  1119. continue;
  1120. }
  1121. *bufptr++ = *input;
  1122. if (bufptr == buf + buflen - 1) {
  1123. break;
  1124. }
  1125. }
  1126. *bufptr = '\0';
  1127. return buf;
  1128. }
  1129. /*!
  1130. * \brief Sets default voicemail system options to a voicemail user.
  1131. *
  1132. * This applies select global settings to a newly created (dynamic) instance of a voicemail user.
  1133. * - all the globalflags
  1134. * - the saydurationminfo
  1135. * - the callcontext
  1136. * - the dialcontext
  1137. * - the exitcontext
  1138. * - vmmaxsecs, vmmaxmsg, maxdeletedmsg
  1139. * - volume gain.
  1140. * - emailsubject, emailbody set to NULL
  1141. */
  1142. static void populate_defaults(struct ast_vm_user *vmu)
  1143. {
  1144. ast_copy_flags(vmu, (&globalflags), AST_FLAGS_ALL);
  1145. vmu->passwordlocation = passwordlocation;
  1146. if (saydurationminfo) {
  1147. vmu->saydurationm = saydurationminfo;
  1148. }
  1149. ast_copy_string(vmu->callback, callcontext, sizeof(vmu->callback));
  1150. ast_copy_string(vmu->dialout, dialcontext, sizeof(vmu->dialout));
  1151. ast_copy_string(vmu->exit, exitcontext, sizeof(vmu->exit));
  1152. ast_copy_string(vmu->zonetag, zonetag, sizeof(vmu->zonetag));
  1153. ast_copy_string(vmu->locale, locale, sizeof(vmu->locale));
  1154. if (vmminsecs) {
  1155. vmu->minsecs = vmminsecs;
  1156. }
  1157. if (vmmaxsecs) {
  1158. vmu->maxsecs = vmmaxsecs;
  1159. }
  1160. if (maxmsg) {
  1161. vmu->maxmsg = maxmsg;
  1162. }
  1163. if (maxdeletedmsg) {
  1164. vmu->maxdeletedmsg = maxdeletedmsg;
  1165. }
  1166. vmu->volgain = volgain;
  1167. ast_free(vmu->email);
  1168. vmu->email = NULL;
  1169. ast_free(vmu->emailsubject);
  1170. vmu->emailsubject = NULL;
  1171. ast_free(vmu->emailbody);
  1172. vmu->emailbody = NULL;
  1173. #ifdef IMAP_STORAGE
  1174. ast_copy_string(vmu->imapfolder, imapfolder, sizeof(vmu->imapfolder));
  1175. ast_copy_string(vmu->imapserver, imapserver, sizeof(vmu->imapserver));
  1176. ast_copy_string(vmu->imapport, imapport, sizeof(vmu->imapport));
  1177. ast_copy_string(vmu->imapflags, imapflags, sizeof(vmu->imapflags));
  1178. #endif
  1179. }
  1180. /*!
  1181. * \brief Sets a a specific property value.
  1182. * \param vmu The voicemail user object to work with.
  1183. * \param var The name of the property to be set.
  1184. * \param value The value to be set to the property.
  1185. *
  1186. * The property name must be one of the understood properties. See the source for details.
  1187. */
  1188. static void apply_option(struct ast_vm_user *vmu, const char *var, const char *value)
  1189. {
  1190. int x;
  1191. if (!strcasecmp(var, "attach")) {
  1192. ast_set2_flag(vmu, ast_true(value), VM_ATTACH);
  1193. } else if (!strcasecmp(var, "attachfmt")) {
  1194. ast_copy_string(vmu->attachfmt, value, sizeof(vmu->attachfmt));
  1195. } else if (!strcasecmp(var, "serveremail")) {
  1196. ast_copy_string(vmu->serveremail, value, sizeof(vmu->serveremail));
  1197. } else if (!strcasecmp(var, "emailbody")) {
  1198. ast_free(vmu->emailbody);
  1199. vmu->emailbody = ast_strdup(substitute_escapes(value));
  1200. } else if (!strcasecmp(var, "emailsubject")) {
  1201. ast_free(vmu->emailsubject);
  1202. vmu->emailsubject = ast_strdup(substitute_escapes(value));
  1203. } else if (!strcasecmp(var, "language")) {
  1204. ast_copy_string(vmu->language, value, sizeof(vmu->language));
  1205. } else if (!strcasecmp(var, "tz")) {
  1206. ast_copy_string(vmu->zonetag, value, sizeof(vmu->zonetag));
  1207. } else if (!strcasecmp(var, "locale")) {
  1208. ast_copy_string(vmu->locale, value, sizeof(vmu->locale));
  1209. #ifdef IMAP_STORAGE
  1210. } else if (!strcasecmp(var, "imapuser")) {
  1211. ast_copy_string(vmu->imapuser, value, sizeof(vmu->imapuser));
  1212. vmu->imapversion = imapversion;
  1213. } else if (!strcasecmp(var, "imapserver")) {
  1214. ast_copy_string(vmu->imapserver, value, sizeof(vmu->imapserver));
  1215. vmu->imapversion = imapversion;
  1216. } else if (!strcasecmp(var, "imapport")) {
  1217. ast_copy_string(vmu->imapport, value, sizeof(vmu->imapport));
  1218. vmu->imapversion = imapversion;
  1219. } else if (!strcasecmp(var, "imapflags")) {
  1220. ast_copy_string(vmu->imapflags, value, sizeof(vmu->imapflags));
  1221. vmu->imapversion = imapversion;
  1222. } else if (!strcasecmp(var, "imappassword") || !strcasecmp(var, "imapsecret")) {
  1223. ast_copy_string(vmu->imappassword, value, sizeof(vmu->imappassword));
  1224. vmu->imapversion = imapversion;
  1225. } else if (!strcasecmp(var, "imapfolder")) {
  1226. ast_copy_string(vmu->imapfolder, value, sizeof(vmu->imapfolder));
  1227. vmu->imapversion = imapversion;
  1228. } else if (!strcasecmp(var, "imapvmshareid")) {
  1229. ast_copy_string(vmu->imapvmshareid, value, sizeof(vmu->imapvmshareid));
  1230. vmu->imapversion = imapversion;
  1231. #endif
  1232. } else if (!strcasecmp(var, "delete") || !strcasecmp(var, "deletevoicemail")) {
  1233. ast_set2_flag(vmu, ast_true(value), VM_DELETE);
  1234. } else if (!strcasecmp(var, "saycid")){
  1235. ast_set2_flag(vmu, ast_true(value), VM_SAYCID);
  1236. } else if (!strcasecmp(var, "sendvoicemail")){
  1237. ast_set2_flag(vmu, ast_true(value), VM_SVMAIL);
  1238. } else if (!strcasecmp(var, "review")){
  1239. ast_set2_flag(vmu, ast_true(value), VM_REVIEW);
  1240. } else if (!strcasecmp(var, "tempgreetwarn")){
  1241. ast_set2_flag(vmu, ast_true(value), VM_TEMPGREETWARN);
  1242. } else if (!strcasecmp(var, "messagewrap")){
  1243. ast_set2_flag(vmu, ast_true(value), VM_MESSAGEWRAP);
  1244. } else if (!strcasecmp(var, "operator")) {
  1245. ast_set2_flag(vmu, ast_true(value), VM_OPERATOR);
  1246. } else if (!strcasecmp(var, "envelope")){
  1247. ast_set2_flag(vmu, ast_true(value), VM_ENVELOPE);
  1248. } else if (!strcasecmp(var, "moveheard")){
  1249. ast_set2_flag(vmu, ast_true(value), VM_MOVEHEARD);
  1250. } else if (!strcasecmp(var, "sayduration")){
  1251. ast_set2_flag(vmu, ast_true(value), VM_SAYDURATION);
  1252. } else if (!strcasecmp(var, "saydurationm")){
  1253. if (sscanf(value, "%30d", &x) == 1) {
  1254. vmu->saydurationm = x;
  1255. } else {
  1256. ast_log(AST_LOG_WARNING, "Invalid min duration for say duration\n");
  1257. }
  1258. } else if (!strcasecmp(var, "forcename")){
  1259. ast_set2_flag(vmu, ast_true(value), VM_FORCENAME);
  1260. } else if (!strcasecmp(var, "forcegreetings")){
  1261. ast_set2_flag(vmu, ast_true(value), VM_FORCEGREET);
  1262. } else if (!strcasecmp(var, "callback")) {
  1263. ast_copy_string(vmu->callback, value, sizeof(vmu->callback));
  1264. } else if (!strcasecmp(var, "dialout")) {
  1265. ast_copy_string(vmu->dialout, value, sizeof(vmu->dialout));
  1266. } else if (!strcasecmp(var, "exitcontext")) {
  1267. ast_copy_string(vmu->exit, value, sizeof(vmu->exit));
  1268. } else if (!strcasecmp(var, "minsecs")) {
  1269. if (sscanf(value, "%30d", &x) == 1 && x >= 0) {
  1270. vmu->minsecs = x;
  1271. } else {
  1272. ast_log(LOG_WARNING, "Invalid min message length of %s. Using global value %d\n", value, vmminsecs);
  1273. vmu->minsecs = vmminsecs;
  1274. }
  1275. } else if (!strcasecmp(var, "maxmessage") || !strcasecmp(var, "maxsecs")) {
  1276. vmu->maxsecs = atoi(value);
  1277. if (vmu->maxsecs <= 0) {
  1278. ast_log(AST_LOG_WARNING, "Invalid max message length of %s. Using global value %d\n", value, vmmaxsecs);
  1279. vmu->maxsecs = vmmaxsecs;
  1280. } else {
  1281. vmu->maxsecs = atoi(value);
  1282. }
  1283. if (!strcasecmp(var, "maxmessage"))
  1284. ast_log(AST_LOG_WARNING, "Option 'maxmessage' has been deprecated in favor of 'maxsecs'. Please make that change in your voicemail config.\n");
  1285. } else if (!strcasecmp(var, "maxmsg")) {
  1286. vmu->maxmsg = atoi(value);
  1287. /* Accept maxmsg=0 (Greetings only voicemail) */
  1288. if (vmu->maxmsg < 0) {
  1289. ast_log(AST_LOG_WARNING, "Invalid number of messages per folder maxmsg=%s. Using default value %d\n", value, MAXMSG);
  1290. vmu->maxmsg = MAXMSG;
  1291. } else if (vmu->maxmsg > MAXMSGLIMIT) {
  1292. ast_log(AST_LOG_WARNING, "Maximum number of messages per folder is %d. Cannot accept value maxmsg=%s\n", MAXMSGLIMIT, value);
  1293. vmu->maxmsg = MAXMSGLIMIT;
  1294. }
  1295. } else if (!strcasecmp(var, "nextaftercmd")) {
  1296. ast_set2_flag(vmu, ast_true(value), VM_SKIPAFTERCMD);
  1297. } else if (!strcasecmp(var, "backupdeleted")) {
  1298. if (sscanf(value, "%30d", &x) == 1)
  1299. vmu->maxdeletedmsg = x;
  1300. else if (ast_true(value))
  1301. vmu->maxdeletedmsg = MAXMSG;
  1302. else
  1303. vmu->maxdeletedmsg = 0;
  1304. if (vmu->maxdeletedmsg < 0) {
  1305. ast_log(AST_LOG_WARNING, "Invalid number of deleted messages saved per mailbox backupdeleted=%s. Using default value %d\n", value, MAXMSG);
  1306. vmu->maxdeletedmsg = MAXMSG;
  1307. } else if (vmu->maxdeletedmsg > MAXMSGLIMIT) {
  1308. ast_log(AST_LOG_WARNING, "Maximum number of deleted messages saved per mailbox is %d. Cannot accept value backupdeleted=%s\n", MAXMSGLIMIT, value);
  1309. vmu->maxdeletedmsg = MAXMSGLIMIT;
  1310. }
  1311. } else if (!strcasecmp(var, "volgain")) {
  1312. sscanf(value, "%30lf", &vmu->volgain);
  1313. } else if (!strcasecmp(var, "passwordlocation")) {
  1314. if (!strcasecmp(value, "spooldir")) {
  1315. vmu->passwordlocation = OPT_PWLOC_SPOOLDIR;
  1316. } else {
  1317. vmu->passwordlocation = OPT_PWLOC_VOICEMAILCONF;
  1318. }
  1319. } else if (!strcasecmp(var, "options")) {
  1320. apply_options(vmu, value);
  1321. }
  1322. }
  1323. static char *vm_check_password_shell(char *command, char *buf, size_t len)
  1324. {
  1325. int fds[2], pid = 0;
  1326. memset(buf, 0, len);
  1327. if (pipe(fds)) {
  1328. snprintf(buf, len, "FAILURE: Pipe failed: %s", strerror(errno));
  1329. } else {
  1330. /* good to go*/
  1331. pid = ast_safe_fork(0);
  1332. if (pid < 0) {
  1333. /* ok maybe not */
  1334. close(fds[0]);
  1335. close(fds[1]);
  1336. snprintf(buf, len, "FAILURE: Fork failed");
  1337. } else if (pid) {
  1338. /* parent */
  1339. close(fds[1]);
  1340. if (read(fds[0], buf, len) < 0) {
  1341. ast_log(LOG_WARNING, "read() failed: %s\n", strerror(errno));
  1342. }
  1343. close(fds[0]);
  1344. } else {
  1345. /* child */
  1346. AST_DECLARE_APP_ARGS(arg,
  1347. AST_APP_ARG(v)[20];
  1348. );
  1349. char *mycmd = ast_strdupa(command);
  1350. close(fds[0]);
  1351. dup2(fds[1], STDOUT_FILENO);
  1352. close(fds[1]);
  1353. ast_close_fds_above_n(STDOUT_FILENO);
  1354. AST_NONSTANDARD_APP_ARGS(arg, mycmd, ' ');
  1355. execv(arg.v[0], arg.v);
  1356. printf("FAILURE: %s", strerror(errno));
  1357. _exit(0);
  1358. }
  1359. }
  1360. return buf;
  1361. }
  1362. /*!
  1363. * \brief Check that password meets minimum required length
  1364. * \param vmu The voicemail user to change the password for.
  1365. * \param password The password string to check
  1366. *
  1367. * \return zero on ok, 1 on not ok.
  1368. */
  1369. static int check_password(struct ast_vm_user *vmu, char *password)
  1370. {
  1371. /* check minimum length */
  1372. if (strlen(password) < minpassword)
  1373. return 1;
  1374. /* check that password does not contain '*' character */
  1375. if (!ast_strlen_zero(password) && password[0] == '*')
  1376. return 1;
  1377. if (!ast_strlen_zero(ext_pass_check_cmd)) {
  1378. char cmd[255], buf[255];
  1379. ast_debug(1, "Verify password policies for %s\n", password);
  1380. snprintf(cmd, sizeof(cmd), "%s %s %s %s %s", ext_pass_check_cmd, vmu->mailbox, vmu->context, vmu->password, password);
  1381. if (vm_check_password_shell(cmd, buf, sizeof(buf))) {
  1382. ast_debug(5, "Result: %s\n", buf);
  1383. if (!strncasecmp(buf, "VALID", 5)) {
  1384. ast_debug(3, "Passed password check: '%s'\n", buf);
  1385. return 0;
  1386. } else if (!strncasecmp(buf, "FAILURE", 7)) {
  1387. ast_log(AST_LOG_WARNING, "Unable to execute password validation script: '%s'.\n", buf);
  1388. return 0;
  1389. } else {
  1390. ast_log(AST_LOG_NOTICE, "Password doesn't match policies for user %s %s\n", vmu->mailbox, password);
  1391. return 1;
  1392. }
  1393. }
  1394. }
  1395. return 0;
  1396. }
  1397. /*!
  1398. * \brief Performs a change of the voicemail passowrd in the realtime engine.
  1399. * \param vmu The voicemail user to change the password for.
  1400. * \param password The new value to be set to the password for this user.
  1401. *
  1402. * This only works if there is a realtime engine configured.
  1403. * This is called from the (top level) vm_change_password.
  1404. *
  1405. * \return zero on success, -1 on error.
  1406. */
  1407. static int change_password_realtime(struct ast_vm_user *vmu, const char *password)
  1408. {
  1409. int res = -1;
  1410. if (!strcmp(vmu->password, password)) {
  1411. /* No change (but an update would return 0 rows updated, so we opt out here) */
  1412. return 0;
  1413. }
  1414. if (strlen(password) > 10) {
  1415. ast_realtime_require_field("voicemail", "password", RQ_CHAR, strlen(password), SENTINEL);
  1416. }
  1417. if (ast_update2_realtime("voicemail", "context", vmu->context, "mailbox", vmu->mailbox, SENTINEL, "password", password, SENTINEL) > 0) {
  1418. ast_test_suite_event_notify("PASSWORDCHANGED", "Message: realtime engine updated with new password\r\nPasswordSource: realtime");
  1419. ast_copy_string(vmu->password, password, sizeof(vmu->password));
  1420. res = 0;
  1421. }
  1422. return res;
  1423. }
  1424. /*!
  1425. * \brief Destructively Parse options and apply.
  1426. */
  1427. static void apply_options(struct ast_vm_user *vmu, const char *options)
  1428. {
  1429. char *stringp;
  1430. char *s;
  1431. char *var, *value;
  1432. stringp = ast_strdupa(options);
  1433. while ((s = strsep(&stringp, "|"))) {
  1434. value = s;
  1435. if ((var = strsep(&value, "=")) && value) {
  1436. apply_option(vmu, var, value);
  1437. }
  1438. }
  1439. }
  1440. /*!
  1441. * \brief Loads the options specific to a voicemail user.
  1442. *
  1443. * This is called when a vm_user structure is being set up, such as from load_options.
  1444. */
  1445. static void apply_options_full(struct ast_vm_user *retval, struct ast_variable *var)
  1446. {
  1447. for (; var; var = var->next) {
  1448. if (!strcasecmp(var->name, "vmsecret")) {
  1449. ast_copy_string(retval->password, var->value, sizeof(retval->password));
  1450. } else if (!strcasecmp(var->name, "secret") || !strcasecmp(var->name, "password")) { /* don't overwrite vmsecret if it exists */
  1451. if (ast_strlen_zero(retval->password)) {
  1452. if (!ast_strlen_zero(var->value) && var->value[0] == '*') {
  1453. ast_log(LOG_WARNING, "Invalid password detected for mailbox %s. The password"
  1454. "\n\tmust be reset in voicemail.conf.\n", retval->mailbox);
  1455. } else {
  1456. ast_copy_string(retval->password, var->value, sizeof(retval->password));
  1457. }
  1458. }
  1459. } else if (!strcasecmp(var->name, "uniqueid")) {
  1460. ast_copy_string(retval->uniqueid, var->value, sizeof(retval->uniqueid));
  1461. } else if (!strcasecmp(var->name, "pager")) {
  1462. ast_copy_string(retval->pager, var->value, sizeof(retval->pager));
  1463. } else if (!strcasecmp(var->name, "email")) {
  1464. ast_free(retval->email);
  1465. retval->email = ast_strdup(var->value);
  1466. } else if (!strcasecmp(var->name, "fullname")) {
  1467. ast_copy_string(retval->fullname, var->value, sizeof(retval->fullname));
  1468. } else if (!strcasecmp(var->name, "context")) {
  1469. ast_copy_string(retval->context, var->value, sizeof(retval->context));
  1470. } else if (!strcasecmp(var->name, "emailsubject")) {
  1471. ast_free(retval->emailsubject);
  1472. retval->emailsubject = ast_strdup(substitute_escapes(var->value));
  1473. } else if (!strcasecmp(var->name, "emailbody")) {
  1474. ast_free(retval->emailbody);
  1475. retval->emailbody = ast_strdup(substitute_escapes(var->value));
  1476. #ifdef IMAP_STORAGE
  1477. } else if (!strcasecmp(var->name, "imapuser")) {
  1478. ast_copy_string(retval->imapuser, var->value, sizeof(retval->imapuser));
  1479. retval->imapversion = imapversion;
  1480. } else if (!strcasecmp(var->name, "imapserver")) {
  1481. ast_copy_string(retval->imapserver, var->value, sizeof(retval->imapserver));
  1482. retval->imapversion = imapversion;
  1483. } else if (!strcasecmp(var->name, "imapport")) {
  1484. ast_copy_string(retval->imapport, var->value, sizeof(retval->imapport));
  1485. retval->imapversion = imapversion;
  1486. } else if (!strcasecmp(var->name, "imapflags")) {
  1487. ast_copy_string(retval->imapflags, var->value, sizeof(retval->imapflags));
  1488. retval->imapversion = imapversion;
  1489. } else if (!strcasecmp(var->name, "imappassword") || !strcasecmp(var->name, "imapsecret")) {
  1490. ast_copy_string(retval->imappassword, var->value, sizeof(retval->imappassword));
  1491. retval->imapversion = imapversion;
  1492. } else if (!strcasecmp(var->name, "imapfolder")) {
  1493. ast_copy_string(retval->imapfolder, var->value, sizeof(retval->imapfolder));
  1494. retval->imapversion = imapversion;
  1495. } else if (!strcasecmp(var->name, "imapvmshareid")) {
  1496. ast_copy_string(retval->imapvmshareid, var->value, sizeof(retval->imapvmshareid));
  1497. retval->imapversion = imapversion;
  1498. #endif
  1499. } else
  1500. apply_option(retval, var->name, var->value);
  1501. }
  1502. }
  1503. /*!
  1504. * \brief Determines if a DTMF key entered is valid.
  1505. * \param key The character to be compared. expects a single character. Though is capable of handling a string, this is internally copies using ast_strdupa.
  1506. *
  1507. * Tests the character entered against the set of valid DTMF characters.
  1508. * \return 1 if the character entered is a valid DTMF digit, 0 if the character is invalid.
  1509. */
  1510. static int is_valid_dtmf(const char *key)
  1511. {
  1512. int i;
  1513. char *local_key = ast_strdupa(key);
  1514. for (i = 0; i < strlen(key); ++i) {
  1515. if (!strchr(VALID_DTMF, *local_key)) {
  1516. ast_log(AST_LOG_WARNING, "Invalid DTMF key \"%c\" used in voicemail configuration file\n", *local_key);
  1517. return 0;
  1518. }
  1519. local_key++;
  1520. }
  1521. return 1;
  1522. }
  1523. /*!
  1524. * \brief Finds a voicemail user from the realtime engine.
  1525. * \param ivm
  1526. * \param context
  1527. * \param mailbox
  1528. *
  1529. * This is called as a fall through case when the normal find_user() was not able to find a user. That is, the default it so look in the usual voicemail users file first.
  1530. *
  1531. * \return The ast_vm_user structure for the user that was found.
  1532. */
  1533. static struct ast_vm_user *find_user_realtime(struct ast_vm_user *ivm, const char *context, const char *mailbox)
  1534. {
  1535. struct ast_variable *var;
  1536. struct ast_vm_user *retval;
  1537. if ((retval = (ivm ? ivm : ast_calloc(1, sizeof(*retval))))) {
  1538. if (ivm) {
  1539. memset(retval, 0, sizeof(*retval));
  1540. }
  1541. populate_defaults(retval);
  1542. if (!ivm) {
  1543. ast_set_flag(retval, VM_ALLOCED);
  1544. }
  1545. if (mailbox) {
  1546. ast_copy_string(retval->mailbox, mailbox, sizeof(retval->mailbox));
  1547. }
  1548. if (!context && ast_test_flag((&globalflags), VM_SEARCH)) {
  1549. var = ast_load_realtime("voicemail", "mailbox", mailbox, SENTINEL);
  1550. } else {
  1551. var = ast_load_realtime("voicemail", "mailbox", mailbox, "context", context, SENTINEL);
  1552. }
  1553. if (var) {
  1554. apply_options_full(retval, var);
  1555. ast_variables_destroy(var);
  1556. } else {
  1557. if (!ivm)
  1558. ast_free(retval);
  1559. retval = NULL;
  1560. }
  1561. }
  1562. return retval;
  1563. }
  1564. /*!
  1565. * \brief Finds a voicemail user from the users file or the realtime engine.
  1566. * \param ivm
  1567. * \param context
  1568. * \param mailbox
  1569. *
  1570. * \return The ast_vm_user structure for the user that was found.
  1571. */
  1572. static struct ast_vm_user *find_user(struct ast_vm_user *ivm, const char *context, const char *mailbox)
  1573. {
  1574. /* This function could be made to generate one from a database, too */
  1575. struct ast_vm_user *vmu = NULL, *cur;
  1576. AST_LIST_LOCK(&users);
  1577. if (!context && !ast_test_flag((&globalflags), VM_SEARCH))
  1578. context = "default";
  1579. AST_LIST_TRAVERSE(&users, cur, list) {
  1580. #ifdef IMAP_STORAGE
  1581. if (cur->imapversion != imapversion) {
  1582. continue;
  1583. }
  1584. #endif
  1585. if (ast_test_flag((&globalflags), VM_SEARCH) && !strcasecmp(mailbox, cur->mailbox))
  1586. break;
  1587. if (context && (!strcasecmp(context, cur->context)) && (!strcasecmp(mailbox, cur->mailbox)))
  1588. break;
  1589. }
  1590. if (cur) {
  1591. /* Make a copy, so that on a reload, we have no race */
  1592. if ((vmu = (ivm ? ivm : ast_malloc(sizeof(*vmu))))) {
  1593. *vmu = *cur;
  1594. if (!ivm) {
  1595. vmu->email = ast_strdup(cur->email);
  1596. vmu->emailbody = ast_strdup(cur->emailbody);
  1597. vmu->emailsubject = ast_strdup(cur->emailsubject);
  1598. }
  1599. ast_set2_flag(vmu, !ivm, VM_ALLOCED);
  1600. AST_LIST_NEXT(vmu, list) = NULL;
  1601. }
  1602. } else
  1603. vmu = find_user_realtime(ivm, context, mailbox);
  1604. AST_LIST_UNLOCK(&users);
  1605. return vmu;
  1606. }
  1607. /*!
  1608. * \brief Resets a user password to a specified password.
  1609. * \param context
  1610. * \param mailbox
  1611. * \param newpass
  1612. *
  1613. * This does the actual change password work, called by the vm_change_password() function.
  1614. *
  1615. * \return zero on success, -1 on error.
  1616. */
  1617. static int reset_user_pw(const char *context, const char *mailbox, const char *newpass)
  1618. {
  1619. /* This function could be made to generate one from a database, too */
  1620. struct ast_vm_user *cur;
  1621. int res = -1;
  1622. AST_LIST_LOCK(&users);
  1623. AST_LIST_TRAVERSE(&users, cur, list) {
  1624. if ((!context || !strcasecmp(context, cur->context)) &&
  1625. (!strcasecmp(mailbox, cur->mailbox)))
  1626. break;
  1627. }
  1628. if (cur) {
  1629. ast_copy_string(cur->password, newpass, sizeof(cur->password));
  1630. res = 0;
  1631. }
  1632. AST_LIST_UNLOCK(&users);
  1633. return res;
  1634. }
  1635. /*!
  1636. * \brief Check if configuration file is valid
  1637. */
  1638. static inline int valid_config(const struct ast_config *cfg)
  1639. {
  1640. return cfg && cfg != CONFIG_STATUS_FILEINVALID;
  1641. }
  1642. /*!
  1643. * \brief The handler for the change password option.
  1644. * \param vmu The voicemail user to work with.
  1645. * \param newpassword The new password (that has been gathered from the appropriate prompting).
  1646. * This is called when a new user logs in for the first time and the option to force them to change their password is set.
  1647. * It is also called when the user wants to change their password from menu option '5' on the mailbox options menu.
  1648. */
  1649. static void vm_change_password(struct ast_vm_user *vmu, const char *newpassword)
  1650. {
  1651. struct ast_config *cfg = NULL;
  1652. struct ast_variable *var = NULL;
  1653. struct ast_category *cat = NULL;
  1654. char *category = NULL, *value = NULL, *new = NULL;
  1655. const char *tmp = NULL;
  1656. struct ast_flags config_flags = { CONFIG_FLAG_WITHCOMMENTS };
  1657. char secretfn[PATH_MAX] = "";
  1658. int found = 0;
  1659. if (!change_password_realtime(vmu, newpassword))
  1660. return;
  1661. /* check if we should store the secret in the spool directory next to the messages */
  1662. switch (vmu->passwordlocation) {
  1663. case OPT_PWLOC_SPOOLDIR:
  1664. snprintf(secretfn, sizeof(secretfn), "%s%s/%s/secret.conf", VM_SPOOL_DIR, vmu->context, vmu->mailbox);
  1665. if (write_password_to_file(secretfn, newpassword) == 0) {
  1666. ast_test_suite_event_notify("PASSWORDCHANGED", "Message: secret.conf updated with new password\r\nPasswordSource: secret.conf");
  1667. ast_verb(4, "Writing voicemail password to file %s succeeded\n", secretfn);
  1668. reset_user_pw(vmu->context, vmu->mailbox, newpassword);
  1669. ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
  1670. break;
  1671. } else {
  1672. ast_verb(4, "Writing voicemail password to file %s failed, falling back to config file\n", secretfn);
  1673. }
  1674. /* Fall-through */
  1675. case OPT_PWLOC_VOICEMAILCONF:
  1676. if ((cfg = ast_config_load(VOICEMAIL_CONFIG, config_flags)) && valid_config(cfg)) {
  1677. while ((category = ast_category_browse(cfg, category))) {
  1678. if (!strcasecmp(category, vmu->context)) {
  1679. if (!(tmp = ast_variable_retrieve(cfg, category, vmu->mailbox))) {
  1680. ast_log(AST_LOG_WARNING, "We could not find the mailbox.\n");
  1681. break;
  1682. }
  1683. value = strstr(tmp, ",");
  1684. if (!value) {
  1685. new = ast_alloca(strlen(newpassword)+1);
  1686. sprintf(new, "%s", newpassword);
  1687. } else {
  1688. new = ast_alloca((strlen(value) + strlen(newpassword) + 1));
  1689. sprintf(new, "%s%s", newpassword, value);
  1690. }
  1691. if (!(cat = ast_category_get(cfg, category, NULL))) {
  1692. ast_log(AST_LOG_WARNING, "Failed to get category structure.\n");
  1693. break;
  1694. }
  1695. ast_variable_update(cat, vmu->mailbox, new, NULL, 0);
  1696. found = 1;
  1697. }
  1698. }
  1699. /* save the results */
  1700. if (found) {
  1701. ast_test_suite_event_notify("PASSWORDCHANGED", "Message: voicemail.conf updated with new password\r\nPasswordSource: voicemail.conf");
  1702. reset_user_pw(vmu->context, vmu->mailbox, newpassword);
  1703. ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
  1704. ast_config_text_file_save(VOICEMAIL_CONFIG, cfg, "app_voicemail");
  1705. ast_config_destroy(cfg);
  1706. break;
  1707. }
  1708. ast_config_destroy(cfg);
  1709. }
  1710. /* Fall-through */
  1711. case OPT_PWLOC_USERSCONF:
  1712. /* check users.conf and update the password stored for the mailbox */
  1713. /* if no vmsecret entry exists create one. */
  1714. if ((cfg = ast_config_load("users.conf", config_flags)) && valid_config(cfg)) {
  1715. ast_debug(4, "we are looking for %s\n", vmu->mailbox);
  1716. for (category = ast_category_browse(cfg, NULL); category; category = ast_category_browse(cfg, category)) {
  1717. ast_debug(4, "users.conf: %s\n", category);
  1718. if (!strcasecmp(category, vmu->mailbox)) {
  1719. if (!ast_variable_retrieve(cfg, category, "vmsecret")) {
  1720. ast_debug(3, "looks like we need to make vmsecret!\n");
  1721. var = ast_variable_new("vmsecret", newpassword, "");
  1722. } else {
  1723. var = NULL;
  1724. }
  1725. new = ast_alloca(strlen(newpassword) + 1);
  1726. sprintf(new, "%s", newpassword);
  1727. if (!(cat = ast_category_get(cfg, category, NULL))) {
  1728. ast_debug(4, "failed to get category!\n");
  1729. ast_free(var);
  1730. break;
  1731. }
  1732. if (!var) {
  1733. ast_variable_update(cat, "vmsecret", new, NULL, 0);
  1734. } else {
  1735. ast_variable_append(cat, var);
  1736. }
  1737. found = 1;
  1738. break;
  1739. }
  1740. }
  1741. /* save the results and clean things up */
  1742. if (found) {
  1743. ast_test_suite_event_notify("PASSWORDCHANGED", "Message: users.conf updated with new password\r\nPasswordSource: users.conf");
  1744. reset_user_pw(vmu->context, vmu->mailbox, newpassword);
  1745. ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
  1746. ast_config_text_file_save("users.conf", cfg, "app_voicemail");
  1747. }
  1748. ast_config_destroy(cfg);
  1749. }
  1750. }
  1751. }
  1752. static void vm_change_password_shell(struct ast_vm_user *vmu, char *newpassword)
  1753. {
  1754. char buf[255];
  1755. snprintf(buf, sizeof(buf), "%s %s %s %s", ext_pass_cmd, vmu->context, vmu->mailbox, newpassword);
  1756. ast_debug(1, "External password: %s\n",buf);
  1757. if (!ast_safe_system(buf)) {
  1758. ast_test_suite_event_notify("PASSWORDCHANGED", "Message: external script updated with new password\r\nPasswordSource: external");
  1759. ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
  1760. /* Reset the password in memory, too */
  1761. reset_user_pw(vmu->context, vmu->mailbox, newpassword);
  1762. }
  1763. }
  1764. /*!
  1765. * \brief Creates a file system path expression for a folder within the voicemail data folder and the appropriate context.
  1766. * \param dest The variable to hold the output generated path expression. This buffer should be of size PATH_MAX.
  1767. * \param len The length of the path string that was written out.
  1768. * \param context
  1769. * \param ext
  1770. * \param folder
  1771. *
  1772. * The path is constructed as
  1773. * VM_SPOOL_DIRcontext/ext/folder
  1774. *
  1775. * \return zero on success, -1 on error.
  1776. */
  1777. static int make_dir(char *dest, int len, const char *context, const char *ext, const char *folder)
  1778. {
  1779. return snprintf(dest, len, "%s%s/%s/%s", VM_SPOOL_DIR, context, ext, folder);
  1780. }
  1781. /*!
  1782. * \brief Creates a file system path expression for a folder within the voicemail data folder and the appropriate context.
  1783. * \param dest The variable to hold the output generated path expression. This buffer should be of size PATH_MAX.
  1784. * \param len The length of the path string that was written out.
  1785. * \param dir
  1786. * \param num
  1787. *
  1788. * The path is constructed as
  1789. * VM_SPOOL_DIRcontext/ext/folder
  1790. *
  1791. * \return zero on success, -1 on error.
  1792. */
  1793. static int make_file(char *dest, const int len, const char *dir, const int num)
  1794. {
  1795. return snprintf(dest, len, "%s/msg%04d", dir, num);
  1796. }
  1797. /* same as mkstemp, but return a FILE * */
  1798. static FILE *vm_mkftemp(char *template)
  1799. {
  1800. FILE *p = NULL;
  1801. int pfd = mkstemp(template);
  1802. chmod(template, VOICEMAIL_FILE_MODE & ~my_umask);
  1803. if (pfd > -1) {
  1804. p = fdopen(pfd, "w+");
  1805. if (!p) {
  1806. close(pfd);
  1807. pfd = -1;
  1808. }
  1809. }
  1810. return p;
  1811. }
  1812. /*! \brief basically mkdir -p $dest/$context/$ext/$folder
  1813. * \param dest String. base directory.
  1814. * \param len Length of dest.
  1815. * \param context String. Ignored if is null or empty string.
  1816. * \param ext String. Ignored if is null or empty string.
  1817. * \param folder String. Ignored if is null or empty string.
  1818. * \return -1 on failure, 0 on success.
  1819. */
  1820. static int create_dirpath(char *dest, int len, const char *context, const char *ext, const char *folder)
  1821. {
  1822. mode_t mode = VOICEMAIL_DIR_MODE;
  1823. int res;
  1824. make_dir(dest, len, context, ext, folder);
  1825. if ((res = ast_mkdir(dest, mode))) {
  1826. ast_log(AST_LOG_WARNING, "ast_mkdir '%s' failed: %s\n", dest, strerror(res));
  1827. return -1;
  1828. }
  1829. return 0;
  1830. }
  1831. static const char *mbox(struct ast_vm_user *vmu, int id)
  1832. {
  1833. #ifdef IMAP_STORAGE
  1834. if (vmu && id == 0) {
  1835. return vmu->imapfolder;
  1836. }
  1837. #endif
  1838. return (id >= 0 && id < ARRAY_LEN(mailbox_folders)) ? mailbox_folders[id] : "Unknown";
  1839. }
  1840. static const char *vm_index_to_foldername(int id)
  1841. {
  1842. return mbox(NULL, id);
  1843. }
  1844. static int get_folder_by_name(const char *name)
  1845. {
  1846. size_t i;
  1847. for (i = 0; i < ARRAY_LEN(mailbox_folders); i++) {
  1848. if (strcasecmp(name, mailbox_folders[i]) == 0) {
  1849. return i;
  1850. }
  1851. }
  1852. return -1;
  1853. }
  1854. static void free_user(struct ast_vm_user *vmu)
  1855. {
  1856. if (ast_test_flag(vmu, VM_ALLOCED)) {
  1857. ast_free(vmu->email);
  1858. vmu->email = NULL;
  1859. ast_free(vmu->emailbody);
  1860. vmu->emailbody = NULL;
  1861. ast_free(vmu->emailsubject);
  1862. vmu->emailsubject = NULL;
  1863. ast_free(vmu);
  1864. }
  1865. }
  1866. static int vm_allocate_dh(struct vm_state *vms, struct ast_vm_user *vmu, int count_msg) {
  1867. int arraysize = (vmu->maxmsg > count_msg ? vmu->maxmsg : count_msg);
  1868. /* remove old allocation */
  1869. if (vms->deleted) {
  1870. ast_free(vms->deleted);
  1871. vms->deleted = NULL;
  1872. }
  1873. if (vms->heard) {
  1874. ast_free(vms->heard);
  1875. vms->heard = NULL;
  1876. }
  1877. vms->dh_arraysize = 0;
  1878. if (arraysize > 0) {
  1879. if (!(vms->deleted = ast_calloc(arraysize, sizeof(int)))) {
  1880. return -1;
  1881. }
  1882. if (!(vms->heard = ast_calloc(arraysize, sizeof(int)))) {
  1883. ast_free(vms->deleted);
  1884. vms->deleted = NULL;
  1885. return -1;
  1886. }
  1887. vms->dh_arraysize = arraysize;
  1888. }
  1889. return 0;
  1890. }
  1891. /* All IMAP-specific functions should go in this block. This
  1892. * keeps them from being spread out all over the code */
  1893. #ifdef IMAP_STORAGE
  1894. static void vm_imap_delete(char *file, int msgnum, struct ast_vm_user *vmu)
  1895. {
  1896. char arg[10];
  1897. struct vm_state *vms;
  1898. unsigned long messageNum;
  1899. /* If greetings aren't stored in IMAP, just delete the file */
  1900. if (msgnum < 0 && !imapgreetings) {
  1901. ast_filedelete(file, NULL);
  1902. return;
  1903. }
  1904. if (!(vms = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 1)) && !(vms = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 0))) {
  1905. ast_log(LOG_WARNING, "Couldn't find a vm_state for mailbox %s. Unable to set \\DELETED flag for message %d\n", vmu->mailbox, msgnum);
  1906. return;
  1907. }
  1908. if (msgnum < 0) {
  1909. imap_delete_old_greeting(file, vms);
  1910. return;
  1911. }
  1912. /* find real message number based on msgnum */
  1913. /* this may be an index into vms->msgArray based on the msgnum. */
  1914. messageNum = vms->msgArray[msgnum];
  1915. if (messageNum == 0) {
  1916. ast_log(LOG_WARNING, "msgnum %d, mailbox message %lu is zero.\n", msgnum, messageNum);
  1917. return;
  1918. }
  1919. ast_debug(3, "deleting msgnum %d, which is mailbox message %lu\n", msgnum, messageNum);
  1920. /* delete message */
  1921. snprintf (arg, sizeof(arg), "%lu", messageNum);
  1922. ast_mutex_lock(&vms->lock);
  1923. mail_setflag (vms->mailstream, arg, "\\DELETED");
  1924. mail_expunge(vms->mailstream);
  1925. ast_mutex_unlock(&vms->lock);
  1926. }
  1927. static void vm_imap_update_msg_id(char *dir, int msgnum, const char *msg_id, struct ast_vm_user *vmu, struct ast_config *msg_cfg, int folder)
  1928. {
  1929. struct ast_channel *chan;
  1930. char *cid;
  1931. char *cid_name;
  1932. char *cid_num;
  1933. struct vm_state *vms;
  1934. const char *duration_str;
  1935. int duration = 0;
  1936. /*
  1937. * First, get things initially set up. If any of this fails, then
  1938. * back out before doing anything substantial
  1939. */
  1940. vms = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 0);
  1941. if (!vms) {
  1942. return;
  1943. }
  1944. if (open_mailbox(vms, vmu, folder)) {
  1945. return;
  1946. }
  1947. chan = ast_dummy_channel_alloc();
  1948. if (!chan) {
  1949. close_mailbox(vms, vmu);
  1950. return;
  1951. }
  1952. /*
  1953. * We need to make sure the new message we save has the same
  1954. * callerid, flag, and duration as the original message
  1955. */
  1956. cid = ast_strdupa(ast_variable_retrieve(msg_cfg, "message", "callerid"));
  1957. if (!ast_strlen_zero(cid)) {
  1958. ast_callerid_parse(cid, &cid_name, &cid_num);
  1959. ast_party_caller_init(ast_channel_caller(chan));
  1960. if (!ast_strlen_zero(cid_name)) {
  1961. ast_channel_caller(chan)->id.name.valid = 1;
  1962. ast_channel_caller(chan)->id.name.str = ast_strdup(cid_name);
  1963. }
  1964. if (!ast_strlen_zero(cid_num)) {
  1965. ast_channel_caller(chan)->id.number.valid = 1;
  1966. ast_channel_caller(chan)->id.number.str = ast_strdup(cid_num);
  1967. }
  1968. }
  1969. duration_str = ast_variable_retrieve(msg_cfg, "message", "duration");
  1970. if (!ast_strlen_zero(duration_str)) {
  1971. sscanf(duration_str, "%30d", &duration);
  1972. }
  1973. /*
  1974. * IMAP messages cannot be altered once delivered. So we have to delete the
  1975. * current message and then re-add it with the updated message ID.
  1976. *
  1977. * Furthermore, there currently is no atomic way to create a new message and to
  1978. * store it in an arbitrary folder. So we have to save it to the INBOX and then
  1979. * move to the appropriate folder.
  1980. */
  1981. if (!imap_store_file(dir, vmu->mailbox, vmu->context, msgnum, chan, vmu, vmfmts,
  1982. duration, vms, ast_variable_retrieve(msg_cfg, "message", "flag"), msg_id)) {
  1983. if (folder != NEW_FOLDER) {
  1984. save_to_folder(vmu, vms, msgnum, folder, NULL, 1);
  1985. }
  1986. vm_imap_delete(dir, msgnum, vmu);
  1987. }
  1988. close_mailbox(vms, vmu);
  1989. ast_channel_unref(chan);
  1990. }
  1991. static int imap_retrieve_greeting(const char *dir, const int msgnum, struct ast_vm_user *vmu)
  1992. {
  1993. struct vm_state *vms_p;
  1994. char *file, *filename;
  1995. char *attachment;
  1996. int i;
  1997. BODY *body;
  1998. int ret = 0;
  1999. int curr_mbox;
  2000. /* This function is only used for retrieval of IMAP greetings
  2001. * regular messages are not retrieved this way, nor are greetings
  2002. * if they are stored locally*/
  2003. if (msgnum > -1 || !imapgreetings) {
  2004. return 0;
  2005. } else {
  2006. file = strrchr(ast_strdupa(dir), '/');
  2007. if (file)
  2008. *file++ = '\0';
  2009. else {
  2010. ast_debug(1, "Failed to procure file name from directory passed.\n");
  2011. return -1;
  2012. }
  2013. }
  2014. /* check if someone is accessing this box right now... */
  2015. if (!(vms_p = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 1)) &&
  2016. !(vms_p = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 0))) {
  2017. /* Unlike when retrieving a message, it is reasonable not to be able to find a
  2018. * vm_state for a mailbox when trying to retrieve a greeting. Just create one,
  2019. * that's all we need to do.
  2020. */
  2021. if (!(vms_p = create_vm_state_from_user(vmu))) {
  2022. ast_log(LOG_NOTICE, "Unable to create vm_state object!\n");
  2023. return -1;
  2024. }
  2025. }
  2026. /* Greetings will never have a prepended message */
  2027. *vms_p->introfn = '\0';
  2028. ast_mutex_lock(&vms_p->lock);
  2029. /* get the current mailbox so that we can point the mailstream back to it later */
  2030. curr_mbox = get_folder_by_name(vms_p->curbox);
  2031. if (init_mailstream(vms_p, GREETINGS_FOLDER) || !vms_p->mailstream) {
  2032. ast_log(AST_LOG_ERROR, "IMAP mailstream is NULL or can't init_mailstream\n");
  2033. ast_mutex_unlock(&vms_p->lock);
  2034. return -1;
  2035. }
  2036. /*XXX Yuck, this could probably be done a lot better */
  2037. for (i = 0; i < vms_p->mailstream->nmsgs; i++) {
  2038. mail_fetchstructure(vms_p->mailstream, i + 1, &body);
  2039. /* We have the body, now we extract the file name of the first attachment. */
  2040. if (body->nested.part && body->nested.part->next && body->nested.part->next->body.parameter->value) {
  2041. attachment = ast_strdupa(body->nested.part->next->body.parameter->value);
  2042. } else {
  2043. ast_log(AST_LOG_ERROR, "There is no file attached to this IMAP message.\n");
  2044. ret = -1;
  2045. break;
  2046. }
  2047. filename = strsep(&attachment, ".");
  2048. if (!strcmp(filename, file)) {
  2049. ast_copy_string(vms_p->fn, dir, sizeof(vms_p->fn));
  2050. vms_p->msgArray[vms_p->curmsg] = i + 1;
  2051. save_body(body, vms_p, "2", attachment, 0);
  2052. ret = 0;
  2053. break;
  2054. }
  2055. }
  2056. if (curr_mbox != -1) {
  2057. /* restore previous mbox stream */
  2058. if (init_mailstream(vms_p, curr_mbox) || !vms_p->mailstream) {
  2059. ast_log(AST_LOG_ERROR, "IMAP mailstream is NULL or can't init_mailstream\n");
  2060. ret = -1;
  2061. }
  2062. }
  2063. ast_mutex_unlock(&vms_p->lock);
  2064. return ret;
  2065. }
  2066. static int imap_retrieve_file(const char *dir, const int msgnum, const char *mailbox, const char *context)
  2067. {
  2068. BODY *body;
  2069. char *header_content;
  2070. char *attachedfilefmt;
  2071. char buf[80];
  2072. struct vm_state *vms;
  2073. char text_file[PATH_MAX];
  2074. FILE *text_file_ptr;
  2075. int res = 0;
  2076. struct ast_vm_user *vmu;
  2077. int curr_mbox;
  2078. if (!(vmu = find_user(NULL, context, mailbox))) {
  2079. ast_log(LOG_WARNING, "Couldn't find user with mailbox %s@%s\n", mailbox, context);
  2080. return -1;
  2081. }
  2082. if (msgnum < 0) {
  2083. if (imapgreetings) {
  2084. res = imap_retrieve_greeting(dir, msgnum, vmu);
  2085. goto exit;
  2086. } else {
  2087. res = 0;
  2088. goto exit;
  2089. }
  2090. }
  2091. /* Before anything can happen, we need a vm_state so that we can
  2092. * actually access the imap server through the vms->mailstream
  2093. */
  2094. if (!(vms = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 1)) && !(vms = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 0))) {
  2095. /* This should not happen. If it does, then I guess we'd
  2096. * need to create the vm_state, extract which mailbox to
  2097. * open, and then set up the msgArray so that the correct
  2098. * IMAP message could be accessed. If I have seen correctly
  2099. * though, the vms should be obtainable from the vmstates list
  2100. * and should have its msgArray properly set up.
  2101. */
  2102. ast_log(LOG_ERROR, "Couldn't find a vm_state for mailbox %s!!! Oh no!\n", vmu->mailbox);
  2103. res = -1;
  2104. goto exit;
  2105. }
  2106. /* Ensure we have the correct mailbox open and have a valid mailstream for it */
  2107. curr_mbox = get_folder_by_name(vms->curbox);
  2108. if (curr_mbox < 0) {
  2109. ast_debug(3, "Mailbox folder curbox not set, defaulting to Inbox\n");
  2110. curr_mbox = 0;
  2111. }
  2112. init_mailstream(vms, curr_mbox);
  2113. if (!vms->mailstream) {
  2114. ast_log(AST_LOG_ERROR, "IMAP mailstream for %s is NULL\n", vmu->mailbox);
  2115. res = -1;
  2116. goto exit;
  2117. }
  2118. make_file(vms->fn, sizeof(vms->fn), dir, msgnum);
  2119. snprintf(vms->introfn, sizeof(vms->introfn), "%sintro", vms->fn);
  2120. /* Don't try to retrieve a message from IMAP if it already is on the file system */
  2121. if (ast_fileexists(vms->fn, NULL, NULL) > 0) {
  2122. res = 0;
  2123. goto exit;
  2124. }
  2125. ast_debug(3, "Before mail_fetchheaders, curmsg is: %d, imap messages is %lu\n", msgnum, vms->msgArray[msgnum]);
  2126. if (vms->msgArray[msgnum] == 0) {
  2127. ast_log(LOG_WARNING, "Trying to access unknown message\n");
  2128. res = -1;
  2129. goto exit;
  2130. }
  2131. /* This will only work for new messages... */
  2132. ast_mutex_lock(&vms->lock);
  2133. header_content = mail_fetchheader (vms->mailstream, vms->msgArray[msgnum]);
  2134. ast_mutex_unlock(&vms->lock);
  2135. /* empty string means no valid header */
  2136. if (ast_strlen_zero(header_content)) {
  2137. ast_log(LOG_ERROR, "Could not fetch header for message number %ld\n", vms->msgArray[msgnum]);
  2138. res = -1;
  2139. goto exit;
  2140. }
  2141. ast_mutex_lock(&vms->lock);
  2142. mail_fetchstructure(vms->mailstream, vms->msgArray[msgnum], &body);
  2143. ast_mutex_unlock(&vms->lock);
  2144. /* We have the body, now we extract the file name of the first attachment. */
  2145. if (body->nested.part && body->nested.part->next && body->nested.part->next->body.parameter->value) {
  2146. attachedfilefmt = ast_strdupa(body->nested.part->next->body.parameter->value);
  2147. } else {
  2148. ast_log(LOG_ERROR, "There is no file attached to this IMAP message.\n");
  2149. res = -1;
  2150. goto exit;
  2151. }
  2152. /* Find the format of the attached file */
  2153. strsep(&attachedfilefmt, ".");
  2154. if (!attachedfilefmt) {
  2155. ast_log(LOG_ERROR, "File format could not be obtained from IMAP message attachment\n");
  2156. res = -1;
  2157. goto exit;
  2158. }
  2159. save_body(body, vms, "2", attachedfilefmt, 0);
  2160. if (save_body(body, vms, "3", attachedfilefmt, 1)) {
  2161. *vms->introfn = '\0';
  2162. }
  2163. /* Get info from headers!! */
  2164. snprintf(text_file, sizeof(text_file), "%s.%s", vms->fn, "txt");
  2165. if (!(text_file_ptr = fopen(text_file, "w"))) {
  2166. ast_log(LOG_WARNING, "Unable to open/create file %s: %s\n", text_file, strerror(errno));
  2167. }
  2168. fprintf(text_file_ptr, "%s\n", "[message]");
  2169. if (get_header_by_tag(header_content, "X-Asterisk-VM-Caller-ID-Name:", buf, sizeof(buf))) {
  2170. fprintf(text_file_ptr, "callerid=\"%s\" ", S_OR(buf, ""));
  2171. }
  2172. if (get_header_by_tag(header_content, "X-Asterisk-VM-Caller-ID-Num:", buf, sizeof(buf))) {
  2173. fprintf(text_file_ptr, "<%s>\n", S_OR(buf, ""));
  2174. }
  2175. if (get_header_by_tag(header_content, "X-Asterisk-VM-Context:", buf, sizeof(buf))) {
  2176. fprintf(text_file_ptr, "context=%s\n", S_OR(buf, ""));
  2177. }
  2178. if (get_header_by_tag(header_content, "X-Asterisk-VM-Orig-time:", buf, sizeof(buf))) {
  2179. fprintf(text_file_ptr, "origtime=%s\n", S_OR(buf, ""));
  2180. }
  2181. if (get_header_by_tag(header_content, "X-Asterisk-VM-Duration:", buf, sizeof(buf))) {
  2182. fprintf(text_file_ptr, "duration=%s\n", S_OR(buf, ""));
  2183. }
  2184. if (get_header_by_tag(header_content, "X-Asterisk-VM-Category:", buf, sizeof(buf))) {
  2185. fprintf(text_file_ptr, "category=%s\n", S_OR(buf, ""));
  2186. }
  2187. if (get_header_by_tag(header_content, "X-Asterisk-VM-Flag:", buf, sizeof(buf))) {
  2188. fprintf(text_file_ptr, "flag=%s\n", S_OR(buf, ""));
  2189. }
  2190. if (get_header_by_tag(header_content, "X-Asterisk-VM-Message-ID:", buf, sizeof(buf))) {
  2191. fprintf(text_file_ptr, "msg_id=%s\n", S_OR(buf, ""));
  2192. }
  2193. fclose(text_file_ptr);
  2194. exit:
  2195. free_user(vmu);
  2196. return res;
  2197. }
  2198. static int folder_int(const char *folder)
  2199. {
  2200. /*assume a NULL folder means INBOX*/
  2201. if (!folder) {
  2202. return 0;
  2203. }
  2204. if (!strcasecmp(folder, imapfolder)) {
  2205. return 0;
  2206. } else if (!strcasecmp(folder, "Old")) {
  2207. return 1;
  2208. } else if (!strcasecmp(folder, "Work")) {
  2209. return 2;
  2210. } else if (!strcasecmp(folder, "Family")) {
  2211. return 3;
  2212. } else if (!strcasecmp(folder, "Friends")) {
  2213. return 4;
  2214. } else if (!strcasecmp(folder, "Cust1")) {
  2215. return 5;
  2216. } else if (!strcasecmp(folder, "Cust2")) {
  2217. return 6;
  2218. } else if (!strcasecmp(folder, "Cust3")) {
  2219. return 7;
  2220. } else if (!strcasecmp(folder, "Cust4")) {
  2221. return 8;
  2222. } else if (!strcasecmp(folder, "Cust5")) {
  2223. return 9;
  2224. } else if (!strcasecmp(folder, "Urgent")) {
  2225. return 11;
  2226. } else { /*assume they meant INBOX if folder is not found otherwise*/
  2227. return 0;
  2228. }
  2229. }
  2230. static int __messagecount(const char *context, const char *mailbox, const char *folder)
  2231. {
  2232. SEARCHPGM *pgm;
  2233. SEARCHHEADER *hdr;
  2234. struct ast_vm_user *vmu, vmus;
  2235. struct vm_state *vms_p;
  2236. int ret = 0;
  2237. int fold = folder_int(folder);
  2238. int urgent = 0;
  2239. /* If URGENT, then look at INBOX */
  2240. if (fold == 11) {
  2241. fold = NEW_FOLDER;
  2242. urgent = 1;
  2243. }
  2244. if (ast_strlen_zero(mailbox))
  2245. return 0;
  2246. /* We have to get the user before we can open the stream! */
  2247. vmu = find_user(&vmus, context, mailbox);
  2248. if (!vmu) {
  2249. ast_log(AST_LOG_WARNING, "Couldn't find mailbox %s in context %s\n", mailbox, context);
  2250. return -1;
  2251. } else {
  2252. /* No IMAP account available */
  2253. if (vmu->imapuser[0] == '\0') {
  2254. ast_log(AST_LOG_WARNING, "IMAP user not set for mailbox %s\n", vmu->mailbox);
  2255. return -1;
  2256. }
  2257. }
  2258. /* No IMAP account available */
  2259. if (vmu->imapuser[0] == '\0') {
  2260. ast_log(AST_LOG_WARNING, "IMAP user not set for mailbox %s\n", vmu->mailbox);
  2261. free_user(vmu);
  2262. return -1;
  2263. }
  2264. /* check if someone is accessing this box right now... */
  2265. vms_p = get_vm_state_by_imapuser(vmu->imapuser, 1);
  2266. if (!vms_p) {
  2267. vms_p = get_vm_state_by_mailbox(mailbox, context, 1);
  2268. }
  2269. if (vms_p) {
  2270. ast_debug(3, "Returning before search - user is logged in\n");
  2271. if (fold == 0) { /* INBOX */
  2272. return urgent ? vms_p->urgentmessages : vms_p->newmessages;
  2273. }
  2274. if (fold == 1) { /* Old messages */
  2275. return vms_p->oldmessages;
  2276. }
  2277. }
  2278. /* add one if not there... */
  2279. vms_p = get_vm_state_by_imapuser(vmu->imapuser, 0);
  2280. if (!vms_p) {
  2281. vms_p = get_vm_state_by_mailbox(mailbox, context, 0);
  2282. }
  2283. if (!vms_p) {
  2284. vms_p = create_vm_state_from_user(vmu);
  2285. }
  2286. ret = init_mailstream(vms_p, fold);
  2287. if (!vms_p->mailstream) {
  2288. ast_log(AST_LOG_ERROR, "Houston we have a problem - IMAP mailstream is NULL\n");
  2289. return -1;
  2290. }
  2291. if (ret == 0) {
  2292. ast_mutex_lock(&vms_p->lock);
  2293. pgm = mail_newsearchpgm ();
  2294. hdr = mail_newsearchheader ("X-Asterisk-VM-Extension", (char *)(!ast_strlen_zero(vmu->imapvmshareid) ? vmu->imapvmshareid : mailbox));
  2295. hdr->next = mail_newsearchheader("X-Asterisk-VM-Context", (char *) S_OR(context, "default"));
  2296. pgm->header = hdr;
  2297. if (fold != OLD_FOLDER) {
  2298. pgm->unseen = 1;
  2299. pgm->seen = 0;
  2300. }
  2301. /* In the special case where fold is 1 (old messages) we have to do things a bit
  2302. * differently. Old messages are stored in the INBOX but are marked as "seen"
  2303. */
  2304. else {
  2305. pgm->unseen = 0;
  2306. pgm->seen = 1;
  2307. }
  2308. /* look for urgent messages */
  2309. if (fold == NEW_FOLDER) {
  2310. if (urgent) {
  2311. pgm->flagged = 1;
  2312. pgm->unflagged = 0;
  2313. } else {
  2314. pgm->flagged = 0;
  2315. pgm->unflagged = 1;
  2316. }
  2317. }
  2318. pgm->undeleted = 1;
  2319. pgm->deleted = 0;
  2320. vms_p->vmArrayIndex = 0;
  2321. mail_search_full (vms_p->mailstream, NULL, pgm, NIL);
  2322. if (fold == 0 && urgent == 0)
  2323. vms_p->newmessages = vms_p->vmArrayIndex;
  2324. if (fold == 1)
  2325. vms_p->oldmessages = vms_p->vmArrayIndex;
  2326. if (fold == 0 && urgent == 1)
  2327. vms_p->urgentmessages = vms_p->vmArrayIndex;
  2328. /*Freeing the searchpgm also frees the searchhdr*/
  2329. mail_free_searchpgm(&pgm);
  2330. ast_mutex_unlock(&vms_p->lock);
  2331. vms_p->updated = 0;
  2332. return vms_p->vmArrayIndex;
  2333. } else {
  2334. ast_mutex_lock(&vms_p->lock);
  2335. mail_ping(vms_p->mailstream);
  2336. ast_mutex_unlock(&vms_p->lock);
  2337. }
  2338. return 0;
  2339. }
  2340. static int imap_check_limits(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu, int msgnum)
  2341. {
  2342. /* Check if mailbox is full */
  2343. check_quota(vms, vmu->imapfolder);
  2344. if (vms->quota_limit && vms->quota_usage >= vms->quota_limit) {
  2345. ast_debug(1, "*** QUOTA EXCEEDED!! %u >= %u\n", vms->quota_usage, vms->quota_limit);
  2346. if (chan) {
  2347. ast_play_and_wait(chan, "vm-mailboxfull");
  2348. }
  2349. return -1;
  2350. }
  2351. /* Check if we have exceeded maxmsg */
  2352. ast_debug(3, "Checking message number quota: mailbox has %d messages, maximum is set to %d, current messages %d\n", msgnum, vmu->maxmsg, inprocess_count(vmu->mailbox, vmu->context, 0));
  2353. if (msgnum >= vmu->maxmsg - inprocess_count(vmu->mailbox, vmu->context, +1)) {
  2354. ast_log(LOG_WARNING, "Unable to leave message since we will exceed the maximum number of messages allowed (%u >= %u)\n", msgnum, vmu->maxmsg);
  2355. if (chan) {
  2356. ast_play_and_wait(chan, "vm-mailboxfull");
  2357. pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
  2358. }
  2359. return -1;
  2360. }
  2361. return 0;
  2362. }
  2363. /*!
  2364. * \brief Gets the number of messages that exist in a mailbox folder.
  2365. * \param mailbox_id
  2366. * \param folder
  2367. *
  2368. * This method is used when IMAP backend is used.
  2369. * \return The number of messages in this mailbox folder (zero or more).
  2370. */
  2371. static int messagecount(const char *mailbox_id, const char *folder)
  2372. {
  2373. char *context;
  2374. char *mailbox;
  2375. if (ast_strlen_zero(mailbox_id)
  2376. || separate_mailbox(ast_strdupa(mailbox_id), &mailbox, &context)) {
  2377. return 0;
  2378. }
  2379. if (ast_strlen_zero(folder) || !strcmp(folder, "INBOX")) {
  2380. return __messagecount(context, mailbox, "INBOX") + __messagecount(context, mailbox, "Urgent");
  2381. } else {
  2382. return __messagecount(context, mailbox, folder);
  2383. }
  2384. }
  2385. static int imap_store_file(const char *dir, const char *mailboxuser, const char *mailboxcontext, int msgnum, struct ast_channel *chan, struct ast_vm_user *vmu, char *fmt, int duration, struct vm_state *vms, const char *flag, const char *msg_id)
  2386. {
  2387. char *myserveremail = serveremail;
  2388. char fn[PATH_MAX];
  2389. char introfn[PATH_MAX];
  2390. char mailbox[256];
  2391. char *stringp;
  2392. FILE *p = NULL;
  2393. char tmp[80] = "/tmp/astmail-XXXXXX";
  2394. long len;
  2395. void *buf;
  2396. int tempcopy = 0;
  2397. STRING str;
  2398. int ret; /* for better error checking */
  2399. char *imap_flags = NIL;
  2400. int msgcount;
  2401. int box = NEW_FOLDER;
  2402. snprintf(mailbox, sizeof(mailbox), "%s@%s", vmu->mailbox, vmu->context);
  2403. msgcount = messagecount(mailbox, "INBOX") + messagecount(mailbox, "Old");
  2404. /* Back out early if this is a greeting and we don't want to store greetings in IMAP */
  2405. if (msgnum < 0) {
  2406. if(!imapgreetings) {
  2407. return 0;
  2408. } else {
  2409. box = GREETINGS_FOLDER;
  2410. }
  2411. }
  2412. if (imap_check_limits(chan, vms, vmu, msgcount)) {
  2413. return -1;
  2414. }
  2415. /* Set urgent flag for IMAP message */
  2416. if (!ast_strlen_zero(flag) && !strcmp(flag, "Urgent")) {
  2417. ast_debug(3, "Setting message flag \\\\FLAGGED.\n");
  2418. imap_flags = "\\FLAGGED";
  2419. }
  2420. /* Attach only the first format */
  2421. fmt = ast_strdupa(fmt);
  2422. stringp = fmt;
  2423. strsep(&stringp, "|");
  2424. if (!ast_strlen_zero(vmu->serveremail))
  2425. myserveremail = vmu->serveremail;
  2426. if (msgnum > -1)
  2427. make_file(fn, sizeof(fn), dir, msgnum);
  2428. else
  2429. ast_copy_string (fn, dir, sizeof(fn));
  2430. snprintf(introfn, sizeof(introfn), "%sintro", fn);
  2431. if (ast_fileexists(introfn, NULL, NULL) <= 0) {
  2432. *introfn = '\0';
  2433. }
  2434. if (ast_strlen_zero(vmu->email)) {
  2435. /* We need the vmu->email to be set when we call make_email_file, but
  2436. * if we keep it set, a duplicate e-mail will be created. So at the end
  2437. * of this function, we will revert back to an empty string if tempcopy
  2438. * is 1.
  2439. */
  2440. vmu->email = ast_strdup(vmu->imapuser);
  2441. tempcopy = 1;
  2442. }
  2443. if (!strcmp(fmt, "wav49"))
  2444. fmt = "WAV";
  2445. ast_debug(3, "Storing file '%s', format '%s'\n", fn, fmt);
  2446. /* Make a temporary file instead of piping directly to sendmail, in case the mail
  2447. command hangs. */
  2448. if (!(p = vm_mkftemp(tmp))) {
  2449. ast_log(AST_LOG_WARNING, "Unable to store '%s' (can't create temporary file)\n", fn);
  2450. if (tempcopy) {
  2451. ast_free(vmu->email);
  2452. vmu->email = NULL;
  2453. }
  2454. return -1;
  2455. }
  2456. if (msgnum < 0 && imapgreetings) {
  2457. if ((ret = init_mailstream(vms, GREETINGS_FOLDER))) {
  2458. ast_log(AST_LOG_WARNING, "Unable to open mailstream.\n");
  2459. return -1;
  2460. }
  2461. imap_delete_old_greeting(fn, vms);
  2462. }
  2463. make_email_file(p, myserveremail, vmu, msgnum, vmu->context, vmu->mailbox, "INBOX",
  2464. S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL),
  2465. S_COR(ast_channel_caller(chan)->id.name.valid, ast_channel_caller(chan)->id.name.str, NULL),
  2466. fn, introfn, fmt, duration, 1, chan, NULL, 1, flag, msg_id);
  2467. /* read mail file to memory */
  2468. len = ftell(p);
  2469. rewind(p);
  2470. if (!(buf = ast_malloc(len + 1))) {
  2471. ast_log(AST_LOG_ERROR, "Can't allocate %ld bytes to read message\n", len + 1);
  2472. fclose(p);
  2473. if (tempcopy)
  2474. *(vmu->email) = '\0';
  2475. return -1;
  2476. }
  2477. if (fread(buf, len, 1, p) < len) {
  2478. if (ferror(p)) {
  2479. ast_log(LOG_ERROR, "Short read while reading in mail file.\n");
  2480. return -1;
  2481. }
  2482. }
  2483. ((char *) buf)[len] = '\0';
  2484. INIT(&str, mail_string, buf, len);
  2485. ret = init_mailstream(vms, box);
  2486. if (ret == 0) {
  2487. imap_mailbox_name(mailbox, sizeof(mailbox), vms, box, 1);
  2488. ast_mutex_lock(&vms->lock);
  2489. if(!mail_append_full(vms->mailstream, mailbox, imap_flags, NIL, &str))
  2490. ast_log(LOG_ERROR, "Error while sending the message to %s\n", mailbox);
  2491. ast_mutex_unlock(&vms->lock);
  2492. fclose(p);
  2493. unlink(tmp);
  2494. ast_free(buf);
  2495. } else {
  2496. ast_log(LOG_ERROR, "Could not initialize mailstream for %s\n", mailbox);
  2497. fclose(p);
  2498. unlink(tmp);
  2499. ast_free(buf);
  2500. return -1;
  2501. }
  2502. ast_debug(3, "%s stored\n", fn);
  2503. if (tempcopy)
  2504. *(vmu->email) = '\0';
  2505. inprocess_count(vmu->mailbox, vmu->context, -1);
  2506. return 0;
  2507. }
  2508. /*!
  2509. * \brief Gets the number of messages that exist in the inbox folder.
  2510. * \param mailbox_context
  2511. * \param newmsgs The variable that is updated with the count of new messages within this inbox.
  2512. * \param oldmsgs The variable that is updated with the count of old messages within this inbox.
  2513. * \param urgentmsgs The variable that is updated with the count of urgent messages within this inbox.
  2514. *
  2515. * This method is used when IMAP backend is used.
  2516. * Simultaneously determines the count of new,old, and urgent messages. The total messages would then be the sum of these three.
  2517. *
  2518. * \return zero on success, -1 on error.
  2519. */
  2520. static int inboxcount2(const char *mailbox_context, int *urgentmsgs, int *newmsgs, int *oldmsgs)
  2521. {
  2522. char tmp[PATH_MAX] = "";
  2523. char *mailboxnc;
  2524. char *context;
  2525. char *mb;
  2526. char *cur;
  2527. if (newmsgs)
  2528. *newmsgs = 0;
  2529. if (oldmsgs)
  2530. *oldmsgs = 0;
  2531. if (urgentmsgs)
  2532. *urgentmsgs = 0;
  2533. ast_debug(3, "Mailbox is set to %s\n", mailbox_context);
  2534. /* If no mailbox, return immediately */
  2535. if (ast_strlen_zero(mailbox_context))
  2536. return 0;
  2537. ast_copy_string(tmp, mailbox_context, sizeof(tmp));
  2538. context = strchr(tmp, '@');
  2539. if (strchr(mailbox_context, ',')) {
  2540. int tmpnew, tmpold, tmpurgent;
  2541. ast_copy_string(tmp, mailbox_context, sizeof(tmp));
  2542. mb = tmp;
  2543. while ((cur = strsep(&mb, ", "))) {
  2544. if (!ast_strlen_zero(cur)) {
  2545. if (inboxcount2(cur, urgentmsgs ? &tmpurgent : NULL, newmsgs ? &tmpnew : NULL, oldmsgs ? &tmpold : NULL))
  2546. return -1;
  2547. else {
  2548. if (newmsgs)
  2549. *newmsgs += tmpnew;
  2550. if (oldmsgs)
  2551. *oldmsgs += tmpold;
  2552. if (urgentmsgs)
  2553. *urgentmsgs += tmpurgent;
  2554. }
  2555. }
  2556. }
  2557. return 0;
  2558. }
  2559. if (context) {
  2560. *context = '\0';
  2561. mailboxnc = tmp;
  2562. context++;
  2563. } else {
  2564. context = "default";
  2565. mailboxnc = (char *) mailbox_context;
  2566. }
  2567. if (newmsgs) {
  2568. struct ast_vm_user *vmu = find_user(NULL, context, mailboxnc);
  2569. if (!vmu) {
  2570. ast_log(AST_LOG_ERROR, "Couldn't find mailbox %s in context %s\n", mailboxnc, context);
  2571. return -1;
  2572. }
  2573. if ((*newmsgs = __messagecount(context, mailboxnc, vmu->imapfolder)) < 0) {
  2574. free_user(vmu);
  2575. return -1;
  2576. }
  2577. free_user(vmu);
  2578. }
  2579. if (oldmsgs) {
  2580. if ((*oldmsgs = __messagecount(context, mailboxnc, "Old")) < 0) {
  2581. return -1;
  2582. }
  2583. }
  2584. if (urgentmsgs) {
  2585. if ((*urgentmsgs = __messagecount(context, mailboxnc, "Urgent")) < 0) {
  2586. return -1;
  2587. }
  2588. }
  2589. return 0;
  2590. }
  2591. /**
  2592. * \brief Determines if the given folder has messages.
  2593. * \param mailbox The @ delimited string for user@context. If no context is found, uses 'default' for the context.
  2594. * \param folder the folder to look in
  2595. *
  2596. * This function is used when the mailbox is stored in an IMAP back end.
  2597. * This invokes the messagecount(). Here we are interested in the presence of messages (> 0) only, not the actual count.
  2598. * \return 1 if the folder has one or more messages. zero otherwise.
  2599. */
  2600. static int has_voicemail(const char *mailbox, const char *folder)
  2601. {
  2602. char tmp[256], *tmp2, *box, *context;
  2603. ast_copy_string(tmp, mailbox, sizeof(tmp));
  2604. tmp2 = tmp;
  2605. if (strchr(tmp2, ',') || strchr(tmp2, '&')) {
  2606. while ((box = strsep(&tmp2, ",&"))) {
  2607. if (!ast_strlen_zero(box)) {
  2608. if (has_voicemail(box, folder)) {
  2609. return 1;
  2610. }
  2611. }
  2612. }
  2613. }
  2614. if ((context = strchr(tmp, '@'))) {
  2615. *context++ = '\0';
  2616. } else {
  2617. context = "default";
  2618. }
  2619. return __messagecount(context, tmp, folder) ? 1 : 0;
  2620. }
  2621. /*!
  2622. * \brief Copies a message from one mailbox to another.
  2623. * \param chan
  2624. * \param vmu
  2625. * \param imbox
  2626. * \param msgnum
  2627. * \param duration
  2628. * \param recip
  2629. * \param fmt
  2630. * \param dir
  2631. *
  2632. * This works with IMAP storage based mailboxes.
  2633. *
  2634. * \return zero on success, -1 on error.
  2635. */
  2636. static int copy_message(struct ast_channel *chan, struct ast_vm_user *vmu, int imbox, int msgnum, long duration, struct ast_vm_user *recip, char *fmt, char *dir, char *flag, const char *dest_folder)
  2637. {
  2638. struct vm_state *sendvms = NULL;
  2639. char messagestring[10]; /*I guess this could be a problem if someone has more than 999999999 messages...*/
  2640. if (msgnum >= recip->maxmsg) {
  2641. ast_log(LOG_WARNING, "Unable to copy mail, mailbox %s is full\n", recip->mailbox);
  2642. return -1;
  2643. }
  2644. if (!(sendvms = get_vm_state_by_imapuser(vmu->imapuser, 0))) {
  2645. ast_log(LOG_ERROR, "Couldn't get vm_state for originator's mailbox!!\n");
  2646. return -1;
  2647. }
  2648. if (!get_vm_state_by_imapuser(recip->imapuser, 0)) {
  2649. ast_log(LOG_ERROR, "Couldn't get vm_state for destination mailbox!\n");
  2650. return -1;
  2651. }
  2652. snprintf(messagestring, sizeof(messagestring), "%ld", sendvms->msgArray[msgnum]);
  2653. ast_mutex_lock(&sendvms->lock);
  2654. if ((mail_copy(sendvms->mailstream, messagestring, (char *) mbox(vmu, imbox)) == T)) {
  2655. ast_mutex_unlock(&sendvms->lock);
  2656. return 0;
  2657. }
  2658. ast_mutex_unlock(&sendvms->lock);
  2659. ast_log(LOG_WARNING, "Unable to copy message from mailbox %s to mailbox %s\n", vmu->mailbox, recip->mailbox);
  2660. return -1;
  2661. }
  2662. static void imap_mailbox_name(char *spec, size_t len, struct vm_state *vms, int box, int use_folder)
  2663. {
  2664. char tmp[256], *t = tmp;
  2665. size_t left = sizeof(tmp);
  2666. if (box == OLD_FOLDER) {
  2667. ast_copy_string(vms->curbox, mbox(NULL, NEW_FOLDER), sizeof(vms->curbox));
  2668. } else {
  2669. ast_copy_string(vms->curbox, mbox(NULL, box), sizeof(vms->curbox));
  2670. }
  2671. if (box == NEW_FOLDER) {
  2672. ast_copy_string(vms->vmbox, "vm-INBOX", sizeof(vms->vmbox));
  2673. } else {
  2674. snprintf(vms->vmbox, sizeof(vms->vmbox), "vm-%s", mbox(NULL, box));
  2675. }
  2676. /* Build up server information */
  2677. ast_build_string(&t, &left, "{%s:%s/imap", S_OR(vms->imapserver, imapserver), S_OR(vms->imapport, imapport));
  2678. /* Add authentication user if present */
  2679. if (!ast_strlen_zero(authuser))
  2680. ast_build_string(&t, &left, "/authuser=%s", authuser);
  2681. /* Add flags if present */
  2682. if (!ast_strlen_zero(imapflags) || !(ast_strlen_zero(vms->imapflags))) {
  2683. ast_build_string(&t, &left, "/%s", S_OR(vms->imapflags, imapflags));
  2684. }
  2685. /* End with username */
  2686. #if 1
  2687. ast_build_string(&t, &left, "/user=%s}", vms->imapuser);
  2688. #else
  2689. ast_build_string(&t, &left, "/user=%s/novalidate-cert}", vms->imapuser);
  2690. #endif
  2691. if (box == NEW_FOLDER || box == OLD_FOLDER)
  2692. snprintf(spec, len, "%s%s", tmp, use_folder? vms->imapfolder: "INBOX");
  2693. else if (box == GREETINGS_FOLDER)
  2694. snprintf(spec, len, "%s%s", tmp, greetingfolder);
  2695. else { /* Other folders such as Friends, Family, etc... */
  2696. if (!ast_strlen_zero(imapparentfolder)) {
  2697. /* imapparentfolder would typically be set to INBOX */
  2698. snprintf(spec, len, "%s%s%c%s", tmp, imapparentfolder, delimiter, mbox(NULL, box));
  2699. } else {
  2700. snprintf(spec, len, "%s%s", tmp, mbox(NULL, box));
  2701. }
  2702. }
  2703. }
  2704. static int init_mailstream(struct vm_state *vms, int box)
  2705. {
  2706. MAILSTREAM *stream = NIL;
  2707. long debug;
  2708. char tmp[256];
  2709. if (!vms) {
  2710. ast_log(LOG_ERROR, "vm_state is NULL!\n");
  2711. return -1;
  2712. }
  2713. ast_debug(3, "vm_state user is:%s\n", vms->imapuser);
  2714. if (vms->mailstream == NIL || !vms->mailstream) {
  2715. ast_debug(1, "mailstream not set.\n");
  2716. } else {
  2717. stream = vms->mailstream;
  2718. }
  2719. /* debug = T; user wants protocol telemetry? */
  2720. debug = NIL; /* NO protocol telemetry? */
  2721. if (delimiter == '\0') { /* did not probe the server yet */
  2722. char *cp;
  2723. #ifdef USE_SYSTEM_IMAP
  2724. #include <imap/linkage.c>
  2725. #elif defined(USE_SYSTEM_CCLIENT)
  2726. #include <c-client/linkage.c>
  2727. #else
  2728. #include "linkage.c"
  2729. #endif
  2730. /* Connect to INBOX first to get folders delimiter */
  2731. imap_mailbox_name(tmp, sizeof(tmp), vms, 0, 1);
  2732. ast_mutex_lock(&vms->lock);
  2733. ast_mutex_lock(&mail_open_lock);
  2734. stream = mail_open (stream, tmp, debug ? OP_DEBUG : NIL);
  2735. ast_mutex_unlock(&mail_open_lock);
  2736. ast_mutex_unlock(&vms->lock);
  2737. if (stream == NIL) {
  2738. ast_log(LOG_ERROR, "Can't connect to imap server %s\n", tmp);
  2739. return -1;
  2740. }
  2741. get_mailbox_delimiter(vms, stream);
  2742. /* update delimiter in imapfolder */
  2743. for (cp = vms->imapfolder; *cp; cp++)
  2744. if (*cp == '/')
  2745. *cp = delimiter;
  2746. }
  2747. /* Now connect to the target folder */
  2748. imap_mailbox_name(tmp, sizeof(tmp), vms, box, 1);
  2749. ast_debug(3, "Before mail_open, server: %s, box:%d\n", tmp, box);
  2750. ast_mutex_lock(&vms->lock);
  2751. ast_mutex_lock(&mail_open_lock);
  2752. vms->mailstream = mail_open (stream, tmp, debug ? OP_DEBUG : NIL);
  2753. ast_mutex_unlock(&mail_open_lock);
  2754. ast_mutex_unlock(&vms->lock);
  2755. if (vms->mailstream == NIL) {
  2756. return -1;
  2757. } else {
  2758. return 0;
  2759. }
  2760. }
  2761. static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu, int box)
  2762. {
  2763. SEARCHPGM *pgm;
  2764. SEARCHHEADER *hdr;
  2765. int urgent = 0;
  2766. /* If Urgent, then look at INBOX */
  2767. if (box == 11) {
  2768. box = NEW_FOLDER;
  2769. urgent = 1;
  2770. }
  2771. ast_copy_string(vms->imapuser, vmu->imapuser, sizeof(vms->imapuser));
  2772. ast_copy_string(vms->imapfolder, vmu->imapfolder, sizeof(vms->imapfolder));
  2773. ast_copy_string(vms->imapserver, vmu->imapserver, sizeof(vms->imapserver));
  2774. ast_copy_string(vms->imapport, vmu->imapport, sizeof(vms->imapport));
  2775. ast_copy_string(vms->imapflags, vmu->imapflags, sizeof(vms->imapflags));
  2776. vms->imapversion = vmu->imapversion;
  2777. ast_debug(3, "Before init_mailstream, user is %s\n", vmu->imapuser);
  2778. if (init_mailstream(vms, box) || !vms->mailstream) {
  2779. ast_log(AST_LOG_ERROR, "Could not initialize mailstream\n");
  2780. return -1;
  2781. }
  2782. create_dirpath(vms->curdir, sizeof(vms->curdir), vmu->context, vms->username, vms->curbox);
  2783. /* Check Quota */
  2784. if (box == 0) {
  2785. ast_debug(3, "Mailbox name set to: %s, about to check quotas\n", mbox(vmu, box));
  2786. check_quota(vms, (char *) mbox(vmu, box));
  2787. }
  2788. ast_mutex_lock(&vms->lock);
  2789. pgm = mail_newsearchpgm();
  2790. /* Check IMAP folder for Asterisk messages only... */
  2791. hdr = mail_newsearchheader("X-Asterisk-VM-Extension", (!ast_strlen_zero(vmu->imapvmshareid) ? vmu->imapvmshareid : vmu->mailbox));
  2792. hdr->next = mail_newsearchheader("X-Asterisk-VM-Context", vmu->context);
  2793. pgm->header = hdr;
  2794. pgm->deleted = 0;
  2795. pgm->undeleted = 1;
  2796. /* if box = NEW_FOLDER, check for new, if box = OLD_FOLDER, check for read */
  2797. if (box == NEW_FOLDER && urgent == 1) {
  2798. pgm->unseen = 1;
  2799. pgm->seen = 0;
  2800. pgm->flagged = 1;
  2801. pgm->unflagged = 0;
  2802. } else if (box == NEW_FOLDER && urgent == 0) {
  2803. pgm->unseen = 1;
  2804. pgm->seen = 0;
  2805. pgm->flagged = 0;
  2806. pgm->unflagged = 1;
  2807. } else if (box == OLD_FOLDER) {
  2808. pgm->seen = 1;
  2809. pgm->unseen = 0;
  2810. }
  2811. ast_debug(3, "Before mail_search_full, user is %s\n", vmu->imapuser);
  2812. vms->vmArrayIndex = 0;
  2813. mail_search_full (vms->mailstream, NULL, pgm, NIL);
  2814. vms->lastmsg = vms->vmArrayIndex - 1;
  2815. mail_free_searchpgm(&pgm);
  2816. /* Since IMAP storage actually stores both old and new messages in the same IMAP folder,
  2817. * ensure to allocate enough space to account for all of them. Warn if old messages
  2818. * have not been checked first as that is required.
  2819. */
  2820. if (box == 0 && !vms->dh_arraysize) {
  2821. ast_log(LOG_WARNING, "The code expects the old messages to be checked first, fix the code.\n");
  2822. }
  2823. if (vm_allocate_dh(vms, vmu, box == 0 ? vms->vmArrayIndex + vms->oldmessages : vms->lastmsg)) {
  2824. ast_mutex_unlock(&vms->lock);
  2825. return -1;
  2826. }
  2827. ast_mutex_unlock(&vms->lock);
  2828. return 0;
  2829. }
  2830. static void write_file(char *filename, char *buffer, unsigned long len)
  2831. {
  2832. FILE *output;
  2833. output = fopen (filename, "w");
  2834. if (fwrite(buffer, len, 1, output) != 1) {
  2835. if (ferror(output)) {
  2836. ast_log(LOG_ERROR, "Short write while writing e-mail body: %s.\n", strerror(errno));
  2837. }
  2838. }
  2839. fclose (output);
  2840. }
  2841. static void update_messages_by_imapuser(const char *user, unsigned long number)
  2842. {
  2843. struct vm_state *vms = get_vm_state_by_imapuser(user, 1);
  2844. if (!vms && !(vms = get_vm_state_by_imapuser(user, 0))) {
  2845. return;
  2846. }
  2847. ast_debug(3, "saving mailbox message number %lu as message %d. Interactive set to %d\n", number, vms->vmArrayIndex, vms->interactive);
  2848. /* Ensure we have room for the next message. */
  2849. if (vms->vmArrayIndex >= vms->msg_array_max) {
  2850. long *new_mem = ast_realloc(vms->msgArray, 2 * vms->msg_array_max * sizeof(long));
  2851. if (!new_mem) {
  2852. return;
  2853. }
  2854. vms->msgArray = new_mem;
  2855. vms->msg_array_max *= 2;
  2856. }
  2857. vms->msgArray[vms->vmArrayIndex++] = number;
  2858. }
  2859. void mm_searched(MAILSTREAM *stream, unsigned long number)
  2860. {
  2861. char *mailbox = stream->mailbox, buf[1024] = "", *user;
  2862. if (!(user = get_user_by_mailbox(mailbox, buf, sizeof(buf))))
  2863. return;
  2864. update_messages_by_imapuser(user, number);
  2865. }
  2866. static struct ast_vm_user *find_user_realtime_imapuser(const char *imapuser)
  2867. {
  2868. struct ast_variable *var;
  2869. struct ast_vm_user *vmu;
  2870. vmu = ast_calloc(1, sizeof *vmu);
  2871. if (!vmu)
  2872. return NULL;
  2873. populate_defaults(vmu);
  2874. ast_set_flag(vmu, VM_ALLOCED);
  2875. var = ast_load_realtime("voicemail", "imapuser", imapuser, NULL);
  2876. if (var) {
  2877. apply_options_full(vmu, var);
  2878. ast_variables_destroy(var);
  2879. return vmu;
  2880. } else {
  2881. ast_free(vmu);
  2882. return NULL;
  2883. }
  2884. }
  2885. /* Interfaces to C-client */
  2886. void mm_exists(MAILSTREAM * stream, unsigned long number)
  2887. {
  2888. /* mail_ping will callback here if new mail! */
  2889. ast_debug(4, "Entering EXISTS callback for message %ld\n", number);
  2890. if (number == 0) return;
  2891. set_update(stream);
  2892. }
  2893. void mm_expunged(MAILSTREAM * stream, unsigned long number)
  2894. {
  2895. /* mail_ping will callback here if expunged mail! */
  2896. ast_debug(4, "Entering EXPUNGE callback for message %ld\n", number);
  2897. if (number == 0) return;
  2898. set_update(stream);
  2899. }
  2900. void mm_flags(MAILSTREAM * stream, unsigned long number)
  2901. {
  2902. /* mail_ping will callback here if read mail! */
  2903. ast_debug(4, "Entering FLAGS callback for message %ld\n", number);
  2904. if (number == 0) return;
  2905. set_update(stream);
  2906. }
  2907. void mm_notify(MAILSTREAM * stream, char *string, long errflg)
  2908. {
  2909. ast_debug(5, "Entering NOTIFY callback, errflag is %ld, string is %s\n", errflg, string);
  2910. mm_log (string, errflg);
  2911. }
  2912. void mm_list(MAILSTREAM * stream, int delim, char *mailbox, long attributes)
  2913. {
  2914. if (delimiter == '\0') {
  2915. delimiter = delim;
  2916. }
  2917. ast_debug(5, "Delimiter set to %c and mailbox %s\n", delim, mailbox);
  2918. if (attributes & LATT_NOINFERIORS)
  2919. ast_debug(5, "no inferiors\n");
  2920. if (attributes & LATT_NOSELECT)
  2921. ast_debug(5, "no select\n");
  2922. if (attributes & LATT_MARKED)
  2923. ast_debug(5, "marked\n");
  2924. if (attributes & LATT_UNMARKED)
  2925. ast_debug(5, "unmarked\n");
  2926. }
  2927. void mm_lsub(MAILSTREAM * stream, int delim, char *mailbox, long attributes)
  2928. {
  2929. ast_debug(5, "Delimiter set to %c and mailbox %s\n", delim, mailbox);
  2930. if (attributes & LATT_NOINFERIORS)
  2931. ast_debug(5, "no inferiors\n");
  2932. if (attributes & LATT_NOSELECT)
  2933. ast_debug(5, "no select\n");
  2934. if (attributes & LATT_MARKED)
  2935. ast_debug(5, "marked\n");
  2936. if (attributes & LATT_UNMARKED)
  2937. ast_debug(5, "unmarked\n");
  2938. }
  2939. void mm_status(MAILSTREAM * stream, char *mailbox, MAILSTATUS * status)
  2940. {
  2941. ast_log(AST_LOG_NOTICE, " Mailbox %s", mailbox);
  2942. if (status->flags & SA_MESSAGES)
  2943. ast_log(AST_LOG_NOTICE, ", %lu messages", status->messages);
  2944. if (status->flags & SA_RECENT)
  2945. ast_log(AST_LOG_NOTICE, ", %lu recent", status->recent);
  2946. if (status->flags & SA_UNSEEN)
  2947. ast_log(AST_LOG_NOTICE, ", %lu unseen", status->unseen);
  2948. if (status->flags & SA_UIDVALIDITY)
  2949. ast_log(AST_LOG_NOTICE, ", %lu UID validity", status->uidvalidity);
  2950. if (status->flags & SA_UIDNEXT)
  2951. ast_log(AST_LOG_NOTICE, ", %lu next UID", status->uidnext);
  2952. ast_log(AST_LOG_NOTICE, "\n");
  2953. }
  2954. void mm_log(char *string, long errflg)
  2955. {
  2956. switch ((short) errflg) {
  2957. case NIL:
  2958. ast_debug(1, "IMAP Info: %s\n", string);
  2959. break;
  2960. case PARSE:
  2961. case WARN:
  2962. ast_log(AST_LOG_WARNING, "IMAP Warning: %s\n", string);
  2963. break;
  2964. case ERROR:
  2965. ast_log(AST_LOG_ERROR, "IMAP Error: %s\n", string);
  2966. break;
  2967. }
  2968. }
  2969. void mm_dlog(char *string)
  2970. {
  2971. ast_log(AST_LOG_NOTICE, "%s\n", string);
  2972. }
  2973. void mm_login(NETMBX * mb, char *user, char *pwd, long trial)
  2974. {
  2975. struct ast_vm_user *vmu;
  2976. ast_debug(4, "Entering callback mm_login\n");
  2977. ast_copy_string(user, mb->user, MAILTMPLEN);
  2978. /* We should only do this when necessary */
  2979. if (!ast_strlen_zero(authpassword)) {
  2980. ast_copy_string(pwd, authpassword, MAILTMPLEN);
  2981. } else {
  2982. AST_LIST_TRAVERSE(&users, vmu, list) {
  2983. if (!strcasecmp(mb->user, vmu->imapuser)) {
  2984. ast_copy_string(pwd, vmu->imappassword, MAILTMPLEN);
  2985. break;
  2986. }
  2987. }
  2988. if (!vmu) {
  2989. if ((vmu = find_user_realtime_imapuser(mb->user))) {
  2990. ast_copy_string(pwd, vmu->imappassword, MAILTMPLEN);
  2991. free_user(vmu);
  2992. }
  2993. }
  2994. }
  2995. }
  2996. void mm_critical(MAILSTREAM * stream)
  2997. {
  2998. }
  2999. void mm_nocritical(MAILSTREAM * stream)
  3000. {
  3001. }
  3002. long mm_diskerror(MAILSTREAM * stream, long errcode, long serious)
  3003. {
  3004. kill (getpid (), SIGSTOP);
  3005. return NIL;
  3006. }
  3007. void mm_fatal(char *string)
  3008. {
  3009. ast_log(AST_LOG_ERROR, "IMAP access FATAL error: %s\n", string);
  3010. }
  3011. /* C-client callback to handle quota */
  3012. static void mm_parsequota(MAILSTREAM *stream, unsigned char *msg, QUOTALIST *pquota)
  3013. {
  3014. struct vm_state *vms;
  3015. char *mailbox = stream->mailbox, *user;
  3016. char buf[1024] = "";
  3017. unsigned long usage = 0, limit = 0;
  3018. while (pquota) {
  3019. usage = pquota->usage;
  3020. limit = pquota->limit;
  3021. pquota = pquota->next;
  3022. }
  3023. if (!(user = get_user_by_mailbox(mailbox, buf, sizeof(buf))) || (!(vms = get_vm_state_by_imapuser(user, 2)) && !(vms = get_vm_state_by_imapuser(user, 0)))) {
  3024. ast_log(AST_LOG_ERROR, "No state found.\n");
  3025. return;
  3026. }
  3027. ast_debug(3, "User %s usage is %lu, limit is %lu\n", user, usage, limit);
  3028. vms->quota_usage = usage;
  3029. vms->quota_limit = limit;
  3030. }
  3031. static char *get_header_by_tag(char *header, char *tag, char *buf, size_t len)
  3032. {
  3033. char *start, *eol_pnt;
  3034. int taglen;
  3035. if (ast_strlen_zero(header) || ast_strlen_zero(tag))
  3036. return NULL;
  3037. taglen = strlen(tag) + 1;
  3038. if (taglen < 1)
  3039. return NULL;
  3040. if (!(start = strcasestr(header, tag)))
  3041. return NULL;
  3042. /* Since we can be called multiple times we should clear our buffer */
  3043. memset(buf, 0, len);
  3044. ast_copy_string(buf, start+taglen, len);
  3045. if ((eol_pnt = strchr(buf,'\r')) || (eol_pnt = strchr(buf,'\n')))
  3046. *eol_pnt = '\0';
  3047. return buf;
  3048. }
  3049. static char *get_user_by_mailbox(char *mailbox, char *buf, size_t len)
  3050. {
  3051. char *start, *eol_pnt, *quote;
  3052. if (ast_strlen_zero(mailbox))
  3053. return NULL;
  3054. if (!(start = strstr(mailbox, "/user=")))
  3055. return NULL;
  3056. ast_copy_string(buf, start+6, len);
  3057. if (!(quote = strchr(buf, '"'))) {
  3058. if ((eol_pnt = strchr(buf, '/')) || (eol_pnt = strchr(buf, '}'))) {
  3059. *eol_pnt = '\0';
  3060. }
  3061. return buf;
  3062. } else {
  3063. if ((eol_pnt = strchr(quote + 1, '"'))) {
  3064. *eol_pnt = '\0';
  3065. }
  3066. return quote + 1;
  3067. }
  3068. }
  3069. static struct vm_state *create_vm_state_from_user(struct ast_vm_user *vmu)
  3070. {
  3071. struct vm_state *vms_p;
  3072. pthread_once(&ts_vmstate.once, ts_vmstate.key_init);
  3073. if ((vms_p = pthread_getspecific(ts_vmstate.key)) && !strcmp(vms_p->imapuser, vmu->imapuser) && !strcmp(vms_p->username, vmu->mailbox)) {
  3074. return vms_p;
  3075. }
  3076. ast_debug(5, "Adding new vmstate for %s\n", vmu->imapuser);
  3077. /* XXX: Is this correctly freed always? */
  3078. if (!(vms_p = ast_calloc(1, sizeof(*vms_p))))
  3079. return NULL;
  3080. ast_copy_string(vms_p->imapuser, vmu->imapuser, sizeof(vms_p->imapuser));
  3081. ast_copy_string(vms_p->imapfolder, vmu->imapfolder, sizeof(vms_p->imapfolder));
  3082. ast_copy_string(vms_p->imapserver, vmu->imapserver, sizeof(vms_p->imapserver));
  3083. ast_copy_string(vms_p->imapport, vmu->imapport, sizeof(vms_p->imapport));
  3084. ast_copy_string(vms_p->imapflags, vmu->imapflags, sizeof(vms_p->imapflags));
  3085. ast_copy_string(vms_p->username, vmu->mailbox, sizeof(vms_p->username)); /* save for access from interactive entry point */
  3086. ast_copy_string(vms_p->context, vmu->context, sizeof(vms_p->context));
  3087. vms_p->mailstream = NIL; /* save for access from interactive entry point */
  3088. vms_p->imapversion = vmu->imapversion;
  3089. ast_debug(5, "Copied %s to %s\n", vmu->imapuser, vms_p->imapuser);
  3090. vms_p->updated = 1;
  3091. /* set mailbox to INBOX! */
  3092. ast_copy_string(vms_p->curbox, mbox(vmu, 0), sizeof(vms_p->curbox));
  3093. init_vm_state(vms_p);
  3094. vmstate_insert(vms_p);
  3095. return vms_p;
  3096. }
  3097. static struct vm_state *get_vm_state_by_imapuser(const char *user, int interactive)
  3098. {
  3099. struct vmstate *vlist = NULL;
  3100. if (interactive) {
  3101. struct vm_state *vms;
  3102. pthread_once(&ts_vmstate.once, ts_vmstate.key_init);
  3103. vms = pthread_getspecific(ts_vmstate.key);
  3104. return vms;
  3105. }
  3106. AST_LIST_LOCK(&vmstates);
  3107. AST_LIST_TRAVERSE(&vmstates, vlist, list) {
  3108. if (!vlist->vms) {
  3109. ast_debug(3, "error: vms is NULL for %s\n", user);
  3110. continue;
  3111. }
  3112. if (vlist->vms->imapversion != imapversion) {
  3113. continue;
  3114. }
  3115. if (!vlist->vms->imapuser) {
  3116. ast_debug(3, "error: imapuser is NULL for %s\n", user);
  3117. continue;
  3118. }
  3119. if (!strcmp(vlist->vms->imapuser, user) && (interactive == 2 || vlist->vms->interactive == interactive)) {
  3120. AST_LIST_UNLOCK(&vmstates);
  3121. return vlist->vms;
  3122. }
  3123. }
  3124. AST_LIST_UNLOCK(&vmstates);
  3125. ast_debug(3, "%s not found in vmstates\n", user);
  3126. return NULL;
  3127. }
  3128. static struct vm_state *get_vm_state_by_mailbox(const char *mailbox, const char *context, int interactive)
  3129. {
  3130. struct vmstate *vlist = NULL;
  3131. const char *local_context = S_OR(context, "default");
  3132. if (interactive) {
  3133. struct vm_state *vms;
  3134. pthread_once(&ts_vmstate.once, ts_vmstate.key_init);
  3135. vms = pthread_getspecific(ts_vmstate.key);
  3136. return vms;
  3137. }
  3138. AST_LIST_LOCK(&vmstates);
  3139. AST_LIST_TRAVERSE(&vmstates, vlist, list) {
  3140. if (!vlist->vms) {
  3141. ast_debug(3, "error: vms is NULL for %s\n", mailbox);
  3142. continue;
  3143. }
  3144. if (vlist->vms->imapversion != imapversion) {
  3145. continue;
  3146. }
  3147. if (!vlist->vms->username || !vlist->vms->context) {
  3148. ast_debug(3, "error: username is NULL for %s\n", mailbox);
  3149. continue;
  3150. }
  3151. ast_debug(3, "comparing mailbox %s@%s (i=%d) to vmstate mailbox %s@%s (i=%d)\n", mailbox, local_context, interactive, vlist->vms->username, vlist->vms->context, vlist->vms->interactive);
  3152. if (!strcmp(vlist->vms->username, mailbox) && !strcmp(vlist->vms->context, local_context) && vlist->vms->interactive == interactive) {
  3153. ast_debug(3, "Found it!\n");
  3154. AST_LIST_UNLOCK(&vmstates);
  3155. return vlist->vms;
  3156. }
  3157. }
  3158. AST_LIST_UNLOCK(&vmstates);
  3159. ast_debug(3, "%s not found in vmstates\n", mailbox);
  3160. return NULL;
  3161. }
  3162. static void vmstate_insert(struct vm_state *vms)
  3163. {
  3164. struct vmstate *v;
  3165. struct vm_state *altvms;
  3166. /* If interactive, it probably already exists, and we should
  3167. use the one we already have since it is more up to date.
  3168. We can compare the username to find the duplicate */
  3169. if (vms->interactive == 1) {
  3170. altvms = get_vm_state_by_mailbox(vms->username, vms->context, 0);
  3171. if (altvms) {
  3172. ast_debug(3, "Duplicate mailbox %s, copying message info...\n", vms->username);
  3173. vms->newmessages = altvms->newmessages;
  3174. vms->oldmessages = altvms->oldmessages;
  3175. vms->vmArrayIndex = altvms->vmArrayIndex;
  3176. /* XXX: no msgArray copying? */
  3177. vms->lastmsg = altvms->lastmsg;
  3178. vms->curmsg = altvms->curmsg;
  3179. /* get a pointer to the persistent store */
  3180. vms->persist_vms = altvms;
  3181. /* Reuse the mailstream? */
  3182. #ifdef REALLY_FAST_EVEN_IF_IT_MEANS_RESOURCE_LEAKS
  3183. vms->mailstream = altvms->mailstream;
  3184. #else
  3185. vms->mailstream = NIL;
  3186. #endif
  3187. }
  3188. return;
  3189. }
  3190. if (!(v = ast_calloc(1, sizeof(*v))))
  3191. return;
  3192. v->vms = vms;
  3193. ast_debug(3, "Inserting vm_state for user:%s, mailbox %s\n", vms->imapuser, vms->username);
  3194. AST_LIST_LOCK(&vmstates);
  3195. AST_LIST_INSERT_TAIL(&vmstates, v, list);
  3196. AST_LIST_UNLOCK(&vmstates);
  3197. }
  3198. static void vmstate_delete(struct vm_state *vms)
  3199. {
  3200. struct vmstate *vc = NULL;
  3201. struct vm_state *altvms = NULL;
  3202. /* If interactive, we should copy pertinent info
  3203. back to the persistent state (to make update immediate) */
  3204. if (vms->interactive == 1 && (altvms = vms->persist_vms)) {
  3205. ast_debug(3, "Duplicate mailbox %s, copying message info...\n", vms->username);
  3206. altvms->newmessages = vms->newmessages;
  3207. altvms->oldmessages = vms->oldmessages;
  3208. altvms->updated = 1;
  3209. vms->mailstream = mail_close(vms->mailstream);
  3210. /* Interactive states are not stored within the persistent list */
  3211. return;
  3212. }
  3213. ast_debug(3, "Removing vm_state for user:%s, mailbox %s\n", vms->imapuser, vms->username);
  3214. AST_LIST_LOCK(&vmstates);
  3215. AST_LIST_TRAVERSE_SAFE_BEGIN(&vmstates, vc, list) {
  3216. if (vc->vms == vms) {
  3217. AST_LIST_REMOVE_CURRENT(list);
  3218. break;
  3219. }
  3220. }
  3221. AST_LIST_TRAVERSE_SAFE_END
  3222. AST_LIST_UNLOCK(&vmstates);
  3223. if (vc) {
  3224. ast_mutex_destroy(&vc->vms->lock);
  3225. ast_free(vc->vms->msgArray);
  3226. vc->vms->msgArray = NULL;
  3227. vc->vms->msg_array_max = 0;
  3228. /* XXX: is no one supposed to free vms itself? */
  3229. ast_free(vc);
  3230. } else {
  3231. ast_log(AST_LOG_ERROR, "No vmstate found for user:%s, mailbox %s\n", vms->imapuser, vms->username);
  3232. }
  3233. }
  3234. static void set_update(MAILSTREAM * stream)
  3235. {
  3236. struct vm_state *vms;
  3237. char *mailbox = stream->mailbox, *user;
  3238. char buf[1024] = "";
  3239. if (!(user = get_user_by_mailbox(mailbox, buf, sizeof(buf))) || !(vms = get_vm_state_by_imapuser(user, 0))) {
  3240. if (user && option_debug > 2)
  3241. ast_log(AST_LOG_WARNING, "User %s mailbox not found for update.\n", user);
  3242. return;
  3243. }
  3244. ast_debug(3, "User %s mailbox set for update.\n", user);
  3245. vms->updated = 1; /* Set updated flag since mailbox changed */
  3246. }
  3247. static void init_vm_state(struct vm_state *vms)
  3248. {
  3249. vms->msg_array_max = VMSTATE_MAX_MSG_ARRAY;
  3250. vms->msgArray = ast_calloc(vms->msg_array_max, sizeof(long));
  3251. if (!vms->msgArray) {
  3252. /* Out of mem? This can't be good. */
  3253. vms->msg_array_max = 0;
  3254. }
  3255. vms->vmArrayIndex = 0;
  3256. ast_mutex_init(&vms->lock);
  3257. }
  3258. static int save_body(BODY *body, struct vm_state *vms, char *section, char *format, int is_intro)
  3259. {
  3260. char *body_content;
  3261. char *body_decoded;
  3262. char *fn = is_intro ? vms->introfn : vms->fn;
  3263. unsigned long len;
  3264. unsigned long newlen;
  3265. char filename[256];
  3266. if (!body || body == NIL)
  3267. return -1;
  3268. ast_mutex_lock(&vms->lock);
  3269. body_content = mail_fetchbody(vms->mailstream, vms->msgArray[vms->curmsg], section, &len);
  3270. ast_mutex_unlock(&vms->lock);
  3271. if (body_content != NIL) {
  3272. snprintf(filename, sizeof(filename), "%s.%s", fn, format);
  3273. /* ast_debug(1, body_content); */
  3274. body_decoded = rfc822_base64((unsigned char *) body_content, len, &newlen);
  3275. /* If the body of the file is empty, return an error */
  3276. if (!newlen) {
  3277. return -1;
  3278. }
  3279. write_file(filename, (char *) body_decoded, newlen);
  3280. } else {
  3281. ast_debug(5, "Body of message is NULL.\n");
  3282. return -1;
  3283. }
  3284. return 0;
  3285. }
  3286. /*!
  3287. * \brief Get delimiter via mm_list callback
  3288. * \param vms The voicemail state object
  3289. * \param stream
  3290. *
  3291. * Determines the delimiter character that is used by the underlying IMAP based mail store.
  3292. */
  3293. /* MUTEX should already be held */
  3294. static void get_mailbox_delimiter(struct vm_state *vms, MAILSTREAM *stream) {
  3295. char tmp[50];
  3296. snprintf(tmp, sizeof(tmp), "{%s}", S_OR(vms->imapserver, imapserver));
  3297. mail_list(stream, tmp, "*");
  3298. }
  3299. /*!
  3300. * \brief Check Quota for user
  3301. * \param vms a pointer to a vm_state struct, will use the mailstream property of this.
  3302. * \param mailbox the mailbox to check the quota for.
  3303. *
  3304. * Calls imap_getquotaroot, which will populate its results into the vm_state vms input structure.
  3305. */
  3306. static void check_quota(struct vm_state *vms, char *mailbox) {
  3307. ast_mutex_lock(&vms->lock);
  3308. mail_parameters(NULL, SET_QUOTA, (void *) mm_parsequota);
  3309. ast_debug(3, "Mailbox name set to: %s, about to check quotas\n", mailbox);
  3310. if (vms && vms->mailstream != NULL) {
  3311. imap_getquotaroot(vms->mailstream, mailbox);
  3312. } else {
  3313. ast_log(AST_LOG_WARNING, "Mailstream not available for mailbox: %s\n", mailbox);
  3314. }
  3315. ast_mutex_unlock(&vms->lock);
  3316. }
  3317. #endif /* IMAP_STORAGE */
  3318. /*! \brief Lock file path
  3319. * only return failure if ast_lock_path returns 'timeout',
  3320. * not if the path does not exist or any other reason
  3321. */
  3322. static int vm_lock_path(const char *path)
  3323. {
  3324. switch (ast_lock_path(path)) {
  3325. case AST_LOCK_TIMEOUT:
  3326. return -1;
  3327. default:
  3328. return 0;
  3329. }
  3330. }
  3331. #define MSG_ID_LEN 256
  3332. /* Used to attach a unique identifier to an msg_id */
  3333. static int msg_id_incrementor;
  3334. /*!
  3335. * \brief Sets the destination string to a uniquely identifying msg_id string
  3336. * \param dst pointer to a character buffer that should contain MSG_ID_LEN characters.
  3337. */
  3338. static void generate_msg_id(char *dst);
  3339. #ifdef ODBC_STORAGE
  3340. struct generic_prepare_struct {
  3341. char *sql;
  3342. int argc;
  3343. char **argv;
  3344. };
  3345. static SQLHSTMT generic_prepare(struct odbc_obj *obj, void *data)
  3346. {
  3347. struct generic_prepare_struct *gps = data;
  3348. int res, i;
  3349. SQLHSTMT stmt;
  3350. res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
  3351. if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
  3352. ast_log(AST_LOG_WARNING, "SQL Alloc Handle failed!\n");
  3353. return NULL;
  3354. }
  3355. res = SQLPrepare(stmt, (unsigned char *) gps->sql, SQL_NTS);
  3356. if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
  3357. ast_log(AST_LOG_WARNING, "SQL Prepare failed![%s]\n", gps->sql);
  3358. SQLFreeHandle(SQL_HANDLE_STMT, stmt);
  3359. return NULL;
  3360. }
  3361. for (i = 0; i < gps->argc; i++)
  3362. SQLBindParameter(stmt, i + 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(gps->argv[i]), 0, gps->argv[i], 0, NULL);
  3363. return stmt;
  3364. }
  3365. static void odbc_update_msg_id(char *dir, int msg_num, char *msg_id)
  3366. {
  3367. SQLHSTMT stmt;
  3368. char sql[PATH_MAX];
  3369. struct odbc_obj *obj;
  3370. char msg_num_str[20];
  3371. char *argv[] = { msg_id, dir, msg_num_str };
  3372. struct generic_prepare_struct gps = { .sql = sql, .argc = 3, .argv = argv };
  3373. obj = ast_odbc_request_obj(odbc_database, 0);
  3374. if (!obj) {
  3375. ast_log(LOG_WARNING, "Unable to update message ID for message %d in %s\n", msg_num, dir);
  3376. return;
  3377. }
  3378. snprintf(msg_num_str, sizeof(msg_num_str), "%d", msg_num);
  3379. snprintf(sql, sizeof(sql), "UPDATE %s SET msg_id=? WHERE dir=? AND msgnum=?", odbc_table);
  3380. stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
  3381. if (!stmt) {
  3382. ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
  3383. } else {
  3384. SQLFreeHandle(SQL_HANDLE_STMT, stmt);
  3385. }
  3386. ast_odbc_release_obj(obj);
  3387. return;
  3388. }
  3389. /*!
  3390. * \brief Retrieves a file from an ODBC data store.
  3391. * \param dir the path to the file to be retrieved.
  3392. * \param msgnum the message number, such as within a mailbox folder.
  3393. *
  3394. * This method is used by the RETRIEVE macro when mailboxes are stored in an ODBC back end.
  3395. * The purpose is to get the message from the database store to the local file system, so that the message may be played, or the information file may be read.
  3396. *
  3397. * The file is looked up by invoking a SQL on the odbc_table (default 'voicemessages') using the dir and msgnum input parameters.
  3398. * The output is the message information file with the name msgnum and the extension .txt
  3399. * and the message file with the extension of its format, in the directory with base file name of the msgnum.
  3400. *
  3401. * \return 0 on success, -1 on error.
  3402. */
  3403. static int retrieve_file(char *dir, int msgnum)
  3404. {
  3405. int x = 0;
  3406. int res;
  3407. int fd = -1;
  3408. size_t fdlen = 0;
  3409. void *fdm = MAP_FAILED;
  3410. SQLSMALLINT colcount = 0;
  3411. SQLHSTMT stmt;
  3412. char sql[PATH_MAX];
  3413. char fmt[80]="";
  3414. char *c;
  3415. char coltitle[256];
  3416. SQLSMALLINT collen;
  3417. SQLSMALLINT datatype;
  3418. SQLSMALLINT decimaldigits;
  3419. SQLSMALLINT nullable;
  3420. SQLULEN colsize;
  3421. SQLLEN colsize2;
  3422. FILE *f = NULL;
  3423. char rowdata[80];
  3424. char fn[PATH_MAX];
  3425. char full_fn[PATH_MAX];
  3426. char msgnums[80];
  3427. char *argv[] = { dir, msgnums };
  3428. struct generic_prepare_struct gps = { .sql = sql, .argc = 2, .argv = argv };
  3429. struct odbc_obj *obj;
  3430. obj = ast_odbc_request_obj(odbc_database, 0);
  3431. if (obj) {
  3432. ast_copy_string(fmt, vmfmts, sizeof(fmt));
  3433. c = strchr(fmt, '|');
  3434. if (c)
  3435. *c = '\0';
  3436. if (!strcasecmp(fmt, "wav49"))
  3437. strcpy(fmt, "WAV");
  3438. snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
  3439. if (msgnum > -1)
  3440. make_file(fn, sizeof(fn), dir, msgnum);
  3441. else
  3442. ast_copy_string(fn, dir, sizeof(fn));
  3443. /* Create the information file */
  3444. snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
  3445. if (!(f = fopen(full_fn, "w+"))) {
  3446. ast_log(AST_LOG_WARNING, "Failed to open/create '%s'\n", full_fn);
  3447. goto yuck;
  3448. }
  3449. snprintf(full_fn, sizeof(full_fn), "%s.%s", fn, fmt);
  3450. snprintf(sql, sizeof(sql), "SELECT * FROM %s WHERE dir=? AND msgnum=?", odbc_table);
  3451. stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
  3452. if (!stmt) {
  3453. ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
  3454. ast_odbc_release_obj(obj);
  3455. goto yuck;
  3456. }
  3457. res = SQLFetch(stmt);
  3458. if (res == SQL_NO_DATA) {
  3459. SQLFreeHandle (SQL_HANDLE_STMT, stmt);
  3460. ast_odbc_release_obj(obj);
  3461. goto yuck;
  3462. } else if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
  3463. ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
  3464. SQLFreeHandle (SQL_HANDLE_STMT, stmt);
  3465. ast_odbc_release_obj(obj);
  3466. goto yuck;
  3467. }
  3468. fd = open(full_fn, O_RDWR | O_CREAT | O_TRUNC, VOICEMAIL_FILE_MODE);
  3469. if (fd < 0) {
  3470. ast_log(AST_LOG_WARNING, "Failed to write '%s': %s\n", full_fn, strerror(errno));
  3471. SQLFreeHandle (SQL_HANDLE_STMT, stmt);
  3472. ast_odbc_release_obj(obj);
  3473. goto yuck;
  3474. }
  3475. res = SQLNumResultCols(stmt, &colcount);
  3476. if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
  3477. ast_log(AST_LOG_WARNING, "SQL Column Count error!\n[%s]\n\n", sql);
  3478. SQLFreeHandle (SQL_HANDLE_STMT, stmt);
  3479. ast_odbc_release_obj(obj);
  3480. goto yuck;
  3481. }
  3482. if (f)
  3483. fprintf(f, "[message]\n");
  3484. for (x = 0; x < colcount; x++) {
  3485. rowdata[0] = '\0';
  3486. colsize = 0;
  3487. collen = sizeof(coltitle);
  3488. res = SQLDescribeCol(stmt, x + 1, (unsigned char *) coltitle, sizeof(coltitle), &collen,
  3489. &datatype, &colsize, &decimaldigits, &nullable);
  3490. if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
  3491. ast_log(AST_LOG_WARNING, "SQL Describe Column error!\n[%s]\n\n", sql);
  3492. SQLFreeHandle (SQL_HANDLE_STMT, stmt);
  3493. ast_odbc_release_obj(obj);
  3494. goto yuck;
  3495. }
  3496. if (!strcasecmp(coltitle, "recording")) {
  3497. off_t offset;
  3498. res = SQLGetData(stmt, x + 1, SQL_BINARY, rowdata, 0, &colsize2);
  3499. fdlen = colsize2;
  3500. if (fd > -1) {
  3501. char tmp[1]="";
  3502. lseek(fd, fdlen - 1, SEEK_SET);
  3503. if (write(fd, tmp, 1) != 1) {
  3504. close(fd);
  3505. fd = -1;
  3506. continue;
  3507. }
  3508. /* Read out in small chunks */
  3509. for (offset = 0; offset < colsize2; offset += CHUNKSIZE) {
  3510. if ((fdm = mmap(NULL, CHUNKSIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, offset)) == MAP_FAILED) {
  3511. ast_log(AST_LOG_WARNING, "Could not mmap the output file: %s (%d)\n", strerror(errno), errno);
  3512. SQLFreeHandle(SQL_HANDLE_STMT, stmt);
  3513. ast_odbc_release_obj(obj);
  3514. goto yuck;
  3515. } else {
  3516. res = SQLGetData(stmt, x + 1, SQL_BINARY, fdm, CHUNKSIZE, NULL);
  3517. munmap(fdm, CHUNKSIZE);
  3518. if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
  3519. ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
  3520. unlink(full_fn);
  3521. SQLFreeHandle(SQL_HANDLE_STMT, stmt);
  3522. ast_odbc_release_obj(obj);
  3523. goto yuck;
  3524. }
  3525. }
  3526. }
  3527. if (truncate(full_fn, fdlen) < 0) {
  3528. ast_log(LOG_WARNING, "Unable to truncate '%s': %s\n", full_fn, strerror(errno));
  3529. }
  3530. }
  3531. } else {
  3532. res = SQLGetData(stmt, x + 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
  3533. if ((res == SQL_NULL_DATA) && (!strcasecmp(coltitle, "msg_id"))) {
  3534. char msg_id[MSG_ID_LEN];
  3535. generate_msg_id(msg_id);
  3536. snprintf(rowdata, sizeof(rowdata), "%s", msg_id);
  3537. odbc_update_msg_id(dir, msgnum, msg_id);
  3538. } else if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
  3539. ast_log(AST_LOG_WARNING, "SQL Get Data error! coltitle=%s\n[%s]\n\n", coltitle, sql);
  3540. SQLFreeHandle (SQL_HANDLE_STMT, stmt);
  3541. ast_odbc_release_obj(obj);
  3542. goto yuck;
  3543. }
  3544. if (strcasecmp(coltitle, "msgnum") && strcasecmp(coltitle, "dir") && f)
  3545. fprintf(f, "%s=%s\n", coltitle, rowdata);
  3546. }
  3547. }
  3548. SQLFreeHandle (SQL_HANDLE_STMT, stmt);
  3549. ast_odbc_release_obj(obj);
  3550. } else
  3551. ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
  3552. yuck:
  3553. if (f)
  3554. fclose(f);
  3555. if (fd > -1)
  3556. close(fd);
  3557. return x - 1;
  3558. }
  3559. /*!
  3560. * \brief Determines the highest message number in use for a given user and mailbox folder.
  3561. * \param vmu
  3562. * \param dir the folder the mailbox folder to look for messages. Used to construct the SQL where clause.
  3563. *
  3564. * This method is used when mailboxes are stored in an ODBC back end.
  3565. * Typical use to set the msgnum would be to take the value returned from this method and add one to it.
  3566. *
  3567. * \return the value of zero or greater to indicate the last message index in use, -1 to indicate none.
  3568. */
  3569. static int last_message_index(struct ast_vm_user *vmu, char *dir)
  3570. {
  3571. int x = 0;
  3572. int res;
  3573. SQLHSTMT stmt;
  3574. char sql[PATH_MAX];
  3575. char rowdata[20];
  3576. char *argv[] = { dir };
  3577. struct generic_prepare_struct gps = { .sql = sql, .argc = 1, .argv = argv };
  3578. struct odbc_obj *obj;
  3579. obj = ast_odbc_request_obj(odbc_database, 0);
  3580. if (obj) {
  3581. snprintf(sql, sizeof(sql), "SELECT msgnum FROM %s WHERE dir=? order by msgnum desc", odbc_table);
  3582. stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
  3583. if (!stmt) {
  3584. ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
  3585. ast_odbc_release_obj(obj);
  3586. goto yuck;
  3587. }
  3588. res = SQLFetch(stmt);
  3589. if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
  3590. if (res == SQL_NO_DATA) {
  3591. ast_log(AST_LOG_DEBUG, "Directory '%s' has no messages and therefore no index was retrieved.\n", dir);
  3592. } else {
  3593. ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
  3594. }
  3595. SQLFreeHandle (SQL_HANDLE_STMT, stmt);
  3596. ast_odbc_release_obj(obj);
  3597. goto yuck;
  3598. }
  3599. res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
  3600. if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
  3601. ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
  3602. SQLFreeHandle (SQL_HANDLE_STMT, stmt);
  3603. ast_odbc_release_obj(obj);
  3604. goto yuck;
  3605. }
  3606. if (sscanf(rowdata, "%30d", &x) != 1)
  3607. ast_log(AST_LOG_WARNING, "Failed to read message index!\n");
  3608. SQLFreeHandle (SQL_HANDLE_STMT, stmt);
  3609. ast_odbc_release_obj(obj);
  3610. return x;
  3611. } else
  3612. ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
  3613. yuck:
  3614. return x - 1;
  3615. }
  3616. /*!
  3617. * \brief Determines if the specified message exists.
  3618. * \param dir the folder the mailbox folder to look for messages.
  3619. * \param msgnum the message index to query for.
  3620. *
  3621. * This method is used when mailboxes are stored in an ODBC back end.
  3622. *
  3623. * \return greater than zero if the message exists, zero when the message does not exist or on error.
  3624. */
  3625. static int message_exists(char *dir, int msgnum)
  3626. {
  3627. int x = 0;
  3628. int res;
  3629. SQLHSTMT stmt;
  3630. char sql[PATH_MAX];
  3631. char rowdata[20];
  3632. char msgnums[20];
  3633. char *argv[] = { dir, msgnums };
  3634. struct generic_prepare_struct gps = { .sql = sql, .argc = 2, .argv = argv };
  3635. struct odbc_obj *obj;
  3636. obj = ast_odbc_request_obj(odbc_database, 0);
  3637. if (obj) {
  3638. snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
  3639. snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir=? AND msgnum=?", odbc_table);
  3640. stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
  3641. if (!stmt) {
  3642. ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
  3643. ast_odbc_release_obj(obj);
  3644. goto yuck;
  3645. }
  3646. res = SQLFetch(stmt);
  3647. if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
  3648. ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
  3649. SQLFreeHandle (SQL_HANDLE_STMT, stmt);
  3650. ast_odbc_release_obj(obj);
  3651. goto yuck;
  3652. }
  3653. res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
  3654. if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
  3655. ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
  3656. SQLFreeHandle (SQL_HANDLE_STMT, stmt);
  3657. ast_odbc_release_obj(obj);
  3658. goto yuck;
  3659. }
  3660. if (sscanf(rowdata, "%30d", &x) != 1)
  3661. ast_log(AST_LOG_WARNING, "Failed to read message count!\n");
  3662. SQLFreeHandle (SQL_HANDLE_STMT, stmt);
  3663. ast_odbc_release_obj(obj);
  3664. } else
  3665. ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
  3666. yuck:
  3667. return x;
  3668. }
  3669. /*!
  3670. * \brief returns the number of messages found.
  3671. * \param vmu
  3672. * \param dir the folder the mailbox folder to look for messages. Used to construct the SQL where clause.
  3673. *
  3674. * This method is used when mailboxes are stored in an ODBC back end.
  3675. *
  3676. * \return The count of messages being zero or more, less than zero on error.
  3677. */
  3678. static int count_messages(struct ast_vm_user *vmu, char *dir)
  3679. {
  3680. int x = 0;
  3681. int res;
  3682. SQLHSTMT stmt;
  3683. char sql[PATH_MAX];
  3684. char rowdata[20];
  3685. char *argv[] = { dir };
  3686. struct generic_prepare_struct gps = { .sql = sql, .argc = 1, .argv = argv };
  3687. struct odbc_obj *obj;
  3688. obj = ast_odbc_request_obj(odbc_database, 0);
  3689. if (obj) {
  3690. snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir=?", odbc_table);
  3691. stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
  3692. if (!stmt) {
  3693. ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
  3694. ast_odbc_release_obj(obj);
  3695. goto yuck;
  3696. }
  3697. res = SQLFetch(stmt);
  3698. if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
  3699. ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
  3700. SQLFreeHandle (SQL_HANDLE_STMT, stmt);
  3701. ast_odbc_release_obj(obj);
  3702. goto yuck;
  3703. }
  3704. res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
  3705. if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
  3706. ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
  3707. SQLFreeHandle (SQL_HANDLE_STMT, stmt);
  3708. ast_odbc_release_obj(obj);
  3709. goto yuck;
  3710. }
  3711. if (sscanf(rowdata, "%30d", &x) != 1)
  3712. ast_log(AST_LOG_WARNING, "Failed to read message count!\n");
  3713. SQLFreeHandle (SQL_HANDLE_STMT, stmt);
  3714. ast_odbc_release_obj(obj);
  3715. return x;
  3716. } else
  3717. ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
  3718. yuck:
  3719. return x - 1;
  3720. }
  3721. /*!
  3722. * \brief Deletes a message from the mailbox folder.
  3723. * \param sdir The mailbox folder to work in.
  3724. * \param smsg The message index to be deleted.
  3725. *
  3726. * This method is used when mailboxes are stored in an ODBC back end.
  3727. * The specified message is directly deleted from the database 'voicemessages' table.
  3728. *
  3729. * \return the value greater than zero on success to indicate the number of messages, less than zero on error.
  3730. */
  3731. static void delete_file(const char *sdir, int smsg)
  3732. {
  3733. SQLHSTMT stmt;
  3734. char sql[PATH_MAX];
  3735. char msgnums[20];
  3736. char *argv[] = { NULL, msgnums };
  3737. struct generic_prepare_struct gps = { .sql = sql, .argc = 2, .argv = argv };
  3738. struct odbc_obj *obj;
  3739. argv[0] = ast_strdupa(sdir);
  3740. obj = ast_odbc_request_obj(odbc_database, 0);
  3741. if (obj) {
  3742. snprintf(msgnums, sizeof(msgnums), "%d", smsg);
  3743. snprintf(sql, sizeof(sql), "DELETE FROM %s WHERE dir=? AND msgnum=?", odbc_table);
  3744. stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
  3745. if (!stmt)
  3746. ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
  3747. else
  3748. SQLFreeHandle (SQL_HANDLE_STMT, stmt);
  3749. ast_odbc_release_obj(obj);
  3750. } else
  3751. ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
  3752. return;
  3753. }
  3754. /*!
  3755. * \brief Copies a voicemail from one mailbox to another.
  3756. * \param sdir the folder for which to look for the message to be copied.
  3757. * \param smsg the index of the message to be copied.
  3758. * \param ddir the destination folder to copy the message into.
  3759. * \param dmsg the index to be used for the copied message.
  3760. * \param dmailboxuser The user who owns the mailbox tha contains the destination folder.
  3761. * \param dmailboxcontext The context for the destination user.
  3762. *
  3763. * This method is used for the COPY macro when mailboxes are stored in an ODBC back end.
  3764. */
  3765. static void copy_file(char *sdir, int smsg, char *ddir, int dmsg, char *dmailboxuser, char *dmailboxcontext)
  3766. {
  3767. SQLHSTMT stmt;
  3768. char sql[512];
  3769. char msgnums[20];
  3770. char msgnumd[20];
  3771. char msg_id[MSG_ID_LEN];
  3772. struct odbc_obj *obj;
  3773. char *argv[] = { ddir, msgnumd, msg_id, dmailboxuser, dmailboxcontext, sdir, msgnums };
  3774. struct generic_prepare_struct gps = { .sql = sql, .argc = 7, .argv = argv };
  3775. generate_msg_id(msg_id);
  3776. delete_file(ddir, dmsg);
  3777. obj = ast_odbc_request_obj(odbc_database, 0);
  3778. if (obj) {
  3779. snprintf(msgnums, sizeof(msgnums), "%d", smsg);
  3780. snprintf(msgnumd, sizeof(msgnumd), "%d", dmsg);
  3781. snprintf(sql, sizeof(sql), "INSERT INTO %s (dir, msgnum, msg_id, context, macrocontext, callerid, origtime, duration, recording, flag, mailboxuser, mailboxcontext) SELECT ?,?,?,context,macrocontext,callerid,origtime,duration,recording,flag,?,? FROM %s WHERE dir=? AND msgnum=?", odbc_table, odbc_table);
  3782. stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
  3783. if (!stmt)
  3784. ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s] (You probably don't have MySQL 4.1 or later installed)\n\n", sql);
  3785. else
  3786. SQLFreeHandle(SQL_HANDLE_STMT, stmt);
  3787. ast_odbc_release_obj(obj);
  3788. } else
  3789. ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
  3790. return;
  3791. }
  3792. struct insert_data {
  3793. char *sql;
  3794. const char *dir;
  3795. const char *msgnums;
  3796. void *data;
  3797. SQLLEN datalen;
  3798. SQLLEN indlen;
  3799. const char *context;
  3800. const char *macrocontext;
  3801. const char *callerid;
  3802. const char *origtime;
  3803. const char *duration;
  3804. const char *mailboxuser;
  3805. const char *mailboxcontext;
  3806. const char *category;
  3807. const char *flag;
  3808. const char *msg_id;
  3809. };
  3810. static SQLHSTMT insert_data_cb(struct odbc_obj *obj, void *vdata)
  3811. {
  3812. struct insert_data *data = vdata;
  3813. int res;
  3814. SQLHSTMT stmt;
  3815. res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
  3816. if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
  3817. ast_log(AST_LOG_WARNING, "SQL Alloc Handle failed!\n");
  3818. SQLFreeHandle(SQL_HANDLE_STMT, stmt);
  3819. return NULL;
  3820. }
  3821. SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->dir), 0, (void *) data->dir, 0, NULL);
  3822. SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->msgnums), 0, (void *) data->msgnums, 0, NULL);
  3823. SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_BINARY, SQL_LONGVARBINARY, data->datalen, 0, (void *) data->data, data->datalen, &data->indlen);
  3824. SQLBindParameter(stmt, 4, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->context), 0, (void *) data->context, 0, NULL);
  3825. SQLBindParameter(stmt, 5, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->macrocontext), 0, (void *) data->macrocontext, 0, NULL);
  3826. SQLBindParameter(stmt, 6, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->callerid), 0, (void *) data->callerid, 0, NULL);
  3827. SQLBindParameter(stmt, 7, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->origtime), 0, (void *) data->origtime, 0, NULL);
  3828. SQLBindParameter(stmt, 8, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->duration), 0, (void *) data->duration, 0, NULL);
  3829. SQLBindParameter(stmt, 9, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->mailboxuser), 0, (void *) data->mailboxuser, 0, NULL);
  3830. SQLBindParameter(stmt, 10, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->mailboxcontext), 0, (void *) data->mailboxcontext, 0, NULL);
  3831. SQLBindParameter(stmt, 11, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->flag), 0, (void *) data->flag, 0, NULL);
  3832. SQLBindParameter(stmt, 12, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->msg_id), 0, (void *) data->msg_id, 0, NULL);
  3833. if (!ast_strlen_zero(data->category)) {
  3834. SQLBindParameter(stmt, 13, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->category), 0, (void *) data->category, 0, NULL);
  3835. }
  3836. res = SQLExecDirect(stmt, (unsigned char *) data->sql, SQL_NTS);
  3837. if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
  3838. ast_log(AST_LOG_WARNING, "SQL Direct Execute failed!\n");
  3839. SQLFreeHandle(SQL_HANDLE_STMT, stmt);
  3840. return NULL;
  3841. }
  3842. return stmt;
  3843. }
  3844. /*!
  3845. * \brief Stores a voicemail into the database.
  3846. * \param dir the folder the mailbox folder to store the message.
  3847. * \param mailboxuser the user owning the mailbox folder.
  3848. * \param mailboxcontext
  3849. * \param msgnum the message index for the message to be stored.
  3850. *
  3851. * This method is used when mailboxes are stored in an ODBC back end.
  3852. * The message sound file and information file is looked up on the file system.
  3853. * A SQL query is invoked to store the message into the (MySQL) database.
  3854. *
  3855. * \return the zero on success -1 on error.
  3856. */
  3857. static int store_file(const char *dir, const char *mailboxuser, const char *mailboxcontext, int msgnum)
  3858. {
  3859. int res = 0;
  3860. int fd = -1;
  3861. void *fdm = MAP_FAILED;
  3862. off_t fdlen = -1;
  3863. SQLHSTMT stmt;
  3864. char sql[PATH_MAX];
  3865. char msgnums[20];
  3866. char fn[PATH_MAX];
  3867. char full_fn[PATH_MAX];
  3868. char fmt[80]="";
  3869. char *c;
  3870. struct ast_config *cfg = NULL;
  3871. struct odbc_obj *obj;
  3872. struct insert_data idata = { .sql = sql, .msgnums = msgnums, .dir = dir, .mailboxuser = mailboxuser, .mailboxcontext = mailboxcontext,
  3873. .context = "", .macrocontext = "", .callerid = "", .origtime = "", .duration = "", .category = "", .flag = "", .msg_id = "" };
  3874. struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
  3875. delete_file(dir, msgnum);
  3876. if (!(obj = ast_odbc_request_obj(odbc_database, 0))) {
  3877. ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
  3878. return -1;
  3879. }
  3880. do {
  3881. ast_copy_string(fmt, vmfmts, sizeof(fmt));
  3882. c = strchr(fmt, '|');
  3883. if (c)
  3884. *c = '\0';
  3885. if (!strcasecmp(fmt, "wav49"))
  3886. strcpy(fmt, "WAV");
  3887. snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
  3888. if (msgnum > -1)
  3889. make_file(fn, sizeof(fn), dir, msgnum);
  3890. else
  3891. ast_copy_string(fn, dir, sizeof(fn));
  3892. snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
  3893. cfg = ast_config_load(full_fn, config_flags);
  3894. snprintf(full_fn, sizeof(full_fn), "%s.%s", fn, fmt);
  3895. fd = open(full_fn, O_RDWR);
  3896. if (fd < 0) {
  3897. ast_log(AST_LOG_WARNING, "Open of sound file '%s' failed: %s\n", full_fn, strerror(errno));
  3898. res = -1;
  3899. break;
  3900. }
  3901. if (valid_config(cfg)) {
  3902. if (!(idata.context = ast_variable_retrieve(cfg, "message", "context"))) {
  3903. idata.context = "";
  3904. }
  3905. if (!(idata.macrocontext = ast_variable_retrieve(cfg, "message", "macrocontext"))) {
  3906. idata.macrocontext = "";
  3907. }
  3908. if (!(idata.callerid = ast_variable_retrieve(cfg, "message", "callerid"))) {
  3909. idata.callerid = "";
  3910. }
  3911. if (!(idata.origtime = ast_variable_retrieve(cfg, "message", "origtime"))) {
  3912. idata.origtime = "";
  3913. }
  3914. if (!(idata.duration = ast_variable_retrieve(cfg, "message", "duration"))) {
  3915. idata.duration = "";
  3916. }
  3917. if (!(idata.category = ast_variable_retrieve(cfg, "message", "category"))) {
  3918. idata.category = "";
  3919. }
  3920. if (!(idata.flag = ast_variable_retrieve(cfg, "message", "flag"))) {
  3921. idata.flag = "";
  3922. }
  3923. if (!(idata.msg_id = ast_variable_retrieve(cfg, "message", "msg_id"))) {
  3924. idata.msg_id = "";
  3925. }
  3926. }
  3927. fdlen = lseek(fd, 0, SEEK_END);
  3928. if (fdlen < 0 || lseek(fd, 0, SEEK_SET) < 0) {
  3929. ast_log(AST_LOG_WARNING, "Failed to process sound file '%s': %s\n", full_fn, strerror(errno));
  3930. res = -1;
  3931. break;
  3932. }
  3933. fdm = mmap(NULL, fdlen, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
  3934. if (fdm == MAP_FAILED) {
  3935. ast_log(AST_LOG_WARNING, "Memory map failed for sound file '%s'!\n", full_fn);
  3936. res = -1;
  3937. break;
  3938. }
  3939. idata.data = fdm;
  3940. idata.datalen = idata.indlen = fdlen;
  3941. if (!ast_strlen_zero(idata.category))
  3942. snprintf(sql, sizeof(sql), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration,mailboxuser,mailboxcontext,flag,msg_id,category) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?)", odbc_table);
  3943. else
  3944. snprintf(sql, sizeof(sql), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration,mailboxuser,mailboxcontext,flag,msg_id) VALUES (?,?,?,?,?,?,?,?,?,?,?,?)", odbc_table);
  3945. if ((stmt = ast_odbc_direct_execute(obj, insert_data_cb, &idata))) {
  3946. SQLFreeHandle (SQL_HANDLE_STMT, stmt);
  3947. } else {
  3948. ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
  3949. res = -1;
  3950. }
  3951. } while (0);
  3952. if (obj) {
  3953. ast_odbc_release_obj(obj);
  3954. }
  3955. if (valid_config(cfg))
  3956. ast_config_destroy(cfg);
  3957. if (fdm != MAP_FAILED)
  3958. munmap(fdm, fdlen);
  3959. if (fd > -1)
  3960. close(fd);
  3961. return res;
  3962. }
  3963. /*!
  3964. * \brief Renames a message in a mailbox folder.
  3965. * \param sdir The folder of the message to be renamed.
  3966. * \param smsg The index of the message to be renamed.
  3967. * \param mailboxuser The user to become the owner of the message after it is renamed. Usually this will be the same as the original owner.
  3968. * \param mailboxcontext The context to be set for the message. Usually this will be the same as the original context.
  3969. * \param ddir The destination folder for the message to be renamed into
  3970. * \param dmsg The destination message for the message to be renamed.
  3971. *
  3972. * This method is used by the RENAME macro when mailboxes are stored in an ODBC back end.
  3973. * The is usually used to resequence the messages in the mailbox, such as to delete messag index 0, it would be called successively to slide all the other messages down one index.
  3974. * But in theory, because the SQL query performs an update on (dir, msgnum, mailboxuser, mailboxcontext) in the database, it should be possible to have the message relocated to another mailbox or context as well.
  3975. */
  3976. static void rename_file(char *sdir, int smsg, char *mailboxuser, char *mailboxcontext, char *ddir, int dmsg)
  3977. {
  3978. SQLHSTMT stmt;
  3979. char sql[PATH_MAX];
  3980. char msgnums[20];
  3981. char msgnumd[20];
  3982. struct odbc_obj *obj;
  3983. char *argv[] = { ddir, msgnumd, mailboxuser, mailboxcontext, sdir, msgnums };
  3984. struct generic_prepare_struct gps = { .sql = sql, .argc = 6, .argv = argv };
  3985. delete_file(ddir, dmsg);
  3986. obj = ast_odbc_request_obj(odbc_database, 0);
  3987. if (obj) {
  3988. snprintf(msgnums, sizeof(msgnums), "%d", smsg);
  3989. snprintf(msgnumd, sizeof(msgnumd), "%d", dmsg);
  3990. snprintf(sql, sizeof(sql), "UPDATE %s SET dir=?, msgnum=?, mailboxuser=?, mailboxcontext=? WHERE dir=? AND msgnum=?", odbc_table);
  3991. stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
  3992. if (!stmt)
  3993. ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
  3994. else
  3995. SQLFreeHandle(SQL_HANDLE_STMT, stmt);
  3996. ast_odbc_release_obj(obj);
  3997. } else
  3998. ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
  3999. return;
  4000. }
  4001. /*!
  4002. * \brief Removes a voicemail message file.
  4003. * \param dir the path to the message file.
  4004. * \param msgnum the unique number for the message within the mailbox.
  4005. *
  4006. * Removes the message content file and the information file.
  4007. * This method is used by the DISPOSE macro when mailboxes are stored in an ODBC back end.
  4008. * Typical use is to clean up after a RETRIEVE operation.
  4009. * Note that this does not remove the message from the mailbox folders, to do that we would use delete_file().
  4010. * \return zero on success, -1 on error.
  4011. */
  4012. static int remove_file(char *dir, int msgnum)
  4013. {
  4014. char fn[PATH_MAX];
  4015. char full_fn[PATH_MAX];
  4016. char msgnums[80];
  4017. if (msgnum > -1) {
  4018. snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
  4019. make_file(fn, sizeof(fn), dir, msgnum);
  4020. } else
  4021. ast_copy_string(fn, dir, sizeof(fn));
  4022. ast_filedelete(fn, NULL);
  4023. snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
  4024. unlink(full_fn);
  4025. return 0;
  4026. }
  4027. #else
  4028. #ifndef IMAP_STORAGE
  4029. /*!
  4030. * \brief Find all .txt files - even if they are not in sequence from 0000.
  4031. * \param vmu
  4032. * \param dir
  4033. *
  4034. * This method is used when mailboxes are stored on the filesystem. (not ODBC and not IMAP).
  4035. *
  4036. * \return the count of messages, zero or more.
  4037. */
  4038. static int count_messages(struct ast_vm_user *vmu, char *dir)
  4039. {
  4040. int vmcount = 0;
  4041. DIR *vmdir = NULL;
  4042. struct dirent *vment = NULL;
  4043. if (vm_lock_path(dir))
  4044. return ERROR_LOCK_PATH;
  4045. if ((vmdir = opendir(dir))) {
  4046. while ((vment = readdir(vmdir))) {
  4047. if (strlen(vment->d_name) > 7 && !strncmp(vment->d_name + 7, ".txt", 4)) {
  4048. vmcount++;
  4049. }
  4050. }
  4051. closedir(vmdir);
  4052. }
  4053. ast_unlock_path(dir);
  4054. return vmcount;
  4055. }
  4056. /*!
  4057. * \brief Renames a message in a mailbox folder.
  4058. * \param sfn The path to the mailbox information and data file to be renamed.
  4059. * \param dfn The path for where the message data and information files will be renamed to.
  4060. *
  4061. * This method is used by the RENAME macro when mailboxes are stored on the filesystem. (not ODBC and not IMAP).
  4062. */
  4063. static void rename_file(char *sfn, char *dfn)
  4064. {
  4065. char stxt[PATH_MAX];
  4066. char dtxt[PATH_MAX];
  4067. ast_filerename(sfn, dfn, NULL);
  4068. snprintf(stxt, sizeof(stxt), "%s.txt", sfn);
  4069. snprintf(dtxt, sizeof(dtxt), "%s.txt", dfn);
  4070. if (ast_check_realtime("voicemail_data")) {
  4071. ast_update_realtime("voicemail_data", "filename", sfn, "filename", dfn, SENTINEL);
  4072. }
  4073. rename(stxt, dtxt);
  4074. }
  4075. /*!
  4076. * \brief Determines the highest message number in use for a given user and mailbox folder.
  4077. * \param vmu
  4078. * \param dir the folder the mailbox folder to look for messages. Used to construct the SQL where clause.
  4079. *
  4080. * This method is used when mailboxes are stored on the filesystem. (not ODBC and not IMAP).
  4081. * Typical use to set the msgnum would be to take the value returned from this method and add one to it.
  4082. *
  4083. * \note Should always be called with a lock already set on dir.
  4084. * \return the value of zero or greaterto indicate the last message index in use, -1 to indicate none.
  4085. */
  4086. static int last_message_index(struct ast_vm_user *vmu, char *dir)
  4087. {
  4088. int x;
  4089. unsigned char map[MAXMSGLIMIT] = "";
  4090. DIR *msgdir;
  4091. struct dirent *msgdirent;
  4092. int msgdirint;
  4093. char extension[4];
  4094. int stopcount = 0;
  4095. /* Reading the entire directory into a file map scales better than
  4096. * doing a stat repeatedly on a predicted sequence. I suspect this
  4097. * is partially due to stat(2) internally doing a readdir(2) itself to
  4098. * find each file. */
  4099. if (!(msgdir = opendir(dir))) {
  4100. return -1;
  4101. }
  4102. while ((msgdirent = readdir(msgdir))) {
  4103. if (sscanf(msgdirent->d_name, "msg%30d.%3s", &msgdirint, extension) == 2 && !strcmp(extension, "txt") && msgdirint < MAXMSGLIMIT) {
  4104. map[msgdirint] = 1;
  4105. stopcount++;
  4106. ast_debug(4, "%s map[%d] = %d, count = %d\n", dir, msgdirint, map[msgdirint], stopcount);
  4107. }
  4108. }
  4109. closedir(msgdir);
  4110. for (x = 0; x < vmu->maxmsg; x++) {
  4111. if (map[x] == 1) {
  4112. stopcount--;
  4113. } else if (map[x] == 0 && !stopcount) {
  4114. break;
  4115. }
  4116. }
  4117. return x - 1;
  4118. }
  4119. #endif /* #ifndef IMAP_STORAGE */
  4120. #endif /* #else of #ifdef ODBC_STORAGE */
  4121. #ifndef IMAP_STORAGE
  4122. /*!
  4123. * \brief Utility function to copy a file.
  4124. * \param infile The path to the file to be copied. The file must be readable, it is opened in read only mode.
  4125. * \param outfile The path for which to copy the file to. The directory permissions must allow the creation (or truncation) of the file, and allow for opening the file in write only mode.
  4126. *
  4127. * When the compiler option HARDLINK_WHEN_POSSIBLE is set, the copy operation will attempt to use the hard link facility instead of copy the file (to save disk space). If the link operation fails, it falls back to the copy operation.
  4128. * The copy operation copies up to 4096 bytes at once.
  4129. *
  4130. * \return zero on success, -1 on error.
  4131. */
  4132. static int copy(char *infile, char *outfile)
  4133. {
  4134. int ifd;
  4135. int ofd;
  4136. int res;
  4137. int len;
  4138. char buf[4096];
  4139. #ifdef HARDLINK_WHEN_POSSIBLE
  4140. /* Hard link if possible; saves disk space & is faster */
  4141. if (link(infile, outfile)) {
  4142. #endif
  4143. if ((ifd = open(infile, O_RDONLY)) < 0) {
  4144. ast_log(AST_LOG_WARNING, "Unable to open %s in read-only mode: %s\n", infile, strerror(errno));
  4145. return -1;
  4146. }
  4147. if ((ofd = open(outfile, O_WRONLY | O_TRUNC | O_CREAT, VOICEMAIL_FILE_MODE)) < 0) {
  4148. ast_log(AST_LOG_WARNING, "Unable to open %s in write-only mode: %s\n", outfile, strerror(errno));
  4149. close(ifd);
  4150. return -1;
  4151. }
  4152. do {
  4153. len = read(ifd, buf, sizeof(buf));
  4154. if (len < 0) {
  4155. ast_log(AST_LOG_WARNING, "Read failed on %s: %s\n", infile, strerror(errno));
  4156. close(ifd);
  4157. close(ofd);
  4158. unlink(outfile);
  4159. } else if (len) {
  4160. res = write(ofd, buf, len);
  4161. if (errno == ENOMEM || errno == ENOSPC || res != len) {
  4162. ast_log(AST_LOG_WARNING, "Write failed on %s (%d of %d): %s\n", outfile, res, len, strerror(errno));
  4163. close(ifd);
  4164. close(ofd);
  4165. unlink(outfile);
  4166. }
  4167. }
  4168. } while (len);
  4169. close(ifd);
  4170. close(ofd);
  4171. return 0;
  4172. #ifdef HARDLINK_WHEN_POSSIBLE
  4173. } else {
  4174. /* Hard link succeeded */
  4175. return 0;
  4176. }
  4177. #endif
  4178. }
  4179. /*!
  4180. * \brief Copies a voicemail information (envelope) file.
  4181. * \param frompath
  4182. * \param topath
  4183. *
  4184. * Every voicemail has the data (.wav) file, and the information file.
  4185. * This function performs the file system copying of the information file for a voicemail, handling the internal fields and their values.
  4186. * This is used by the COPY macro when not using IMAP storage.
  4187. */
  4188. static void copy_plain_file(char *frompath, char *topath)
  4189. {
  4190. char frompath2[PATH_MAX], topath2[PATH_MAX];
  4191. struct ast_variable *tmp,*var = NULL;
  4192. const char *origmailbox = NULL, *context = NULL, *macrocontext = NULL, *exten = NULL, *priority = NULL, *callerchan = NULL, *callerid = NULL, *origdate = NULL, *origtime = NULL, *category = NULL, *duration = NULL;
  4193. ast_filecopy(frompath, topath, NULL);
  4194. snprintf(frompath2, sizeof(frompath2), "%s.txt", frompath);
  4195. snprintf(topath2, sizeof(topath2), "%s.txt", topath);
  4196. if (ast_check_realtime("voicemail_data")) {
  4197. var = ast_load_realtime("voicemail_data", "filename", frompath, SENTINEL);
  4198. /* This cycle converts ast_variable linked list, to va_list list of arguments, may be there is a better way to do it? */
  4199. for (tmp = var; tmp; tmp = tmp->next) {
  4200. if (!strcasecmp(tmp->name, "origmailbox")) {
  4201. origmailbox = tmp->value;
  4202. } else if (!strcasecmp(tmp->name, "context")) {
  4203. context = tmp->value;
  4204. } else if (!strcasecmp(tmp->name, "macrocontext")) {
  4205. macrocontext = tmp->value;
  4206. } else if (!strcasecmp(tmp->name, "exten")) {
  4207. exten = tmp->value;
  4208. } else if (!strcasecmp(tmp->name, "priority")) {
  4209. priority = tmp->value;
  4210. } else if (!strcasecmp(tmp->name, "callerchan")) {
  4211. callerchan = tmp->value;
  4212. } else if (!strcasecmp(tmp->name, "callerid")) {
  4213. callerid = tmp->value;
  4214. } else if (!strcasecmp(tmp->name, "origdate")) {
  4215. origdate = tmp->value;
  4216. } else if (!strcasecmp(tmp->name, "origtime")) {
  4217. origtime = tmp->value;
  4218. } else if (!strcasecmp(tmp->name, "category")) {
  4219. category = tmp->value;
  4220. } else if (!strcasecmp(tmp->name, "duration")) {
  4221. duration = tmp->value;
  4222. }
  4223. }
  4224. ast_store_realtime("voicemail_data", "filename", topath, "origmailbox", origmailbox, "context", context, "macrocontext", macrocontext, "exten", exten, "priority", priority, "callerchan", callerchan, "callerid", callerid, "origdate", origdate, "origtime", origtime, "category", category, "duration", duration, SENTINEL);
  4225. }
  4226. copy(frompath2, topath2);
  4227. ast_variables_destroy(var);
  4228. }
  4229. #endif
  4230. /*!
  4231. * \brief Removes the voicemail sound and information file.
  4232. * \param file The path to the sound file. This will be the the folder and message index, without the extension.
  4233. *
  4234. * This is used by the DELETE macro when voicemails are stored on the file system.
  4235. *
  4236. * \return zero on success, -1 on error.
  4237. */
  4238. static int vm_delete(char *file)
  4239. {
  4240. char *txt;
  4241. int txtsize = 0;
  4242. txtsize = (strlen(file) + 5)*sizeof(char);
  4243. txt = ast_alloca(txtsize);
  4244. /* Sprintf here would safe because we alloca'd exactly the right length,
  4245. * but trying to eliminate all sprintf's anyhow
  4246. */
  4247. if (ast_check_realtime("voicemail_data")) {
  4248. ast_destroy_realtime("voicemail_data", "filename", file, SENTINEL);
  4249. }
  4250. snprintf(txt, txtsize, "%s.txt", file);
  4251. unlink(txt);
  4252. return ast_filedelete(file, NULL);
  4253. }
  4254. /*!
  4255. * \brief utility used by inchar(), for base_encode()
  4256. */
  4257. static int inbuf(struct baseio *bio, FILE *fi)
  4258. {
  4259. int l;
  4260. if (bio->ateof)
  4261. return 0;
  4262. if ((l = fread(bio->iobuf, 1, BASEMAXINLINE, fi)) <= 0) {
  4263. if (ferror(fi))
  4264. return -1;
  4265. bio->ateof = 1;
  4266. return 0;
  4267. }
  4268. bio->iolen = l;
  4269. bio->iocp = 0;
  4270. return 1;
  4271. }
  4272. /*!
  4273. * \brief utility used by base_encode()
  4274. */
  4275. static int inchar(struct baseio *bio, FILE *fi)
  4276. {
  4277. if (bio->iocp>=bio->iolen) {
  4278. if (!inbuf(bio, fi))
  4279. return EOF;
  4280. }
  4281. return bio->iobuf[bio->iocp++];
  4282. }
  4283. /*!
  4284. * \brief utility used by base_encode()
  4285. */
  4286. static int ochar(struct baseio *bio, int c, FILE *so)
  4287. {
  4288. if (bio->linelength >= BASELINELEN) {
  4289. if (fputs(ENDL, so) == EOF) {
  4290. return -1;
  4291. }
  4292. bio->linelength = 0;
  4293. }
  4294. if (putc(((unsigned char) c), so) == EOF) {
  4295. return -1;
  4296. }
  4297. bio->linelength++;
  4298. return 1;
  4299. }
  4300. /*!
  4301. * \brief Performs a base 64 encode algorithm on the contents of a File
  4302. * \param filename The path to the file to be encoded. Must be readable, file is opened in read mode.
  4303. * \param so A FILE handle to the output file to receive the base 64 encoded contents of the input file, identified by filename.
  4304. *
  4305. * TODO: shouldn't this (and the above 3 support functions) be put into some kind of external utility location, such as funcs/func_base64.c ?
  4306. *
  4307. * \return zero on success, -1 on error.
  4308. */
  4309. static int base_encode(char *filename, FILE *so)
  4310. {
  4311. static const unsigned char dtable[] = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K',
  4312. 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
  4313. 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0',
  4314. '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'};
  4315. int i, hiteof = 0;
  4316. FILE *fi;
  4317. struct baseio bio;
  4318. memset(&bio, 0, sizeof(bio));
  4319. bio.iocp = BASEMAXINLINE;
  4320. if (!(fi = fopen(filename, "rb"))) {
  4321. ast_log(AST_LOG_WARNING, "Failed to open file: %s: %s\n", filename, strerror(errno));
  4322. return -1;
  4323. }
  4324. while (!hiteof){
  4325. unsigned char igroup[3], ogroup[4];
  4326. int c, n;
  4327. memset(igroup, 0, sizeof(igroup));
  4328. for (n = 0; n < 3; n++) {
  4329. if ((c = inchar(&bio, fi)) == EOF) {
  4330. hiteof = 1;
  4331. break;
  4332. }
  4333. igroup[n] = (unsigned char) c;
  4334. }
  4335. if (n > 0) {
  4336. ogroup[0]= dtable[igroup[0] >> 2];
  4337. ogroup[1]= dtable[((igroup[0] & 3) << 4) | (igroup[1] >> 4)];
  4338. ogroup[2]= dtable[((igroup[1] & 0xF) << 2) | (igroup[2] >> 6)];
  4339. ogroup[3]= dtable[igroup[2] & 0x3F];
  4340. if (n < 3) {
  4341. ogroup[3] = '=';
  4342. if (n < 2)
  4343. ogroup[2] = '=';
  4344. }
  4345. for (i = 0; i < 4; i++)
  4346. ochar(&bio, ogroup[i], so);
  4347. }
  4348. }
  4349. fclose(fi);
  4350. if (fputs(ENDL, so) == EOF) {
  4351. return 0;
  4352. }
  4353. return 1;
  4354. }
  4355. static void prep_email_sub_vars(struct ast_channel *ast, struct ast_vm_user *vmu, int msgnum, char *context, char *mailbox, const char *fromfolder, char *cidnum, char *cidname, char *dur, char *date, const char *category, const char *flag)
  4356. {
  4357. char callerid[256];
  4358. char num[12];
  4359. char fromdir[256], fromfile[256];
  4360. struct ast_config *msg_cfg;
  4361. const char *origcallerid, *origtime;
  4362. char origcidname[80], origcidnum[80], origdate[80];
  4363. int inttime;
  4364. struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
  4365. /* Prepare variables for substitution in email body and subject */
  4366. pbx_builtin_setvar_helper(ast, "VM_NAME", vmu->fullname);
  4367. pbx_builtin_setvar_helper(ast, "VM_DUR", dur);
  4368. snprintf(num, sizeof(num), "%d", msgnum);
  4369. pbx_builtin_setvar_helper(ast, "VM_MSGNUM", num);
  4370. pbx_builtin_setvar_helper(ast, "VM_CONTEXT", context);
  4371. pbx_builtin_setvar_helper(ast, "VM_MAILBOX", mailbox);
  4372. pbx_builtin_setvar_helper(ast, "VM_CALLERID", (!ast_strlen_zero(cidname) || !ast_strlen_zero(cidnum)) ?
  4373. ast_callerid_merge(callerid, sizeof(callerid), cidname, cidnum, NULL) : "an unknown caller");
  4374. pbx_builtin_setvar_helper(ast, "VM_CIDNAME", (!ast_strlen_zero(cidname) ? cidname : "an unknown caller"));
  4375. pbx_builtin_setvar_helper(ast, "VM_CIDNUM", (!ast_strlen_zero(cidnum) ? cidnum : "an unknown caller"));
  4376. pbx_builtin_setvar_helper(ast, "VM_DATE", date);
  4377. pbx_builtin_setvar_helper(ast, "VM_CATEGORY", category ? ast_strdupa(category) : "no category");
  4378. pbx_builtin_setvar_helper(ast, "VM_FLAG", flag);
  4379. /* Retrieve info from VM attribute file */
  4380. make_dir(fromdir, sizeof(fromdir), vmu->context, vmu->mailbox, fromfolder);
  4381. make_file(fromfile, sizeof(fromfile), fromdir, msgnum - 1);
  4382. if (strlen(fromfile) < sizeof(fromfile) - 5) {
  4383. strcat(fromfile, ".txt");
  4384. }
  4385. if (!(msg_cfg = ast_config_load(fromfile, config_flags)) || !(valid_config(msg_cfg))) {
  4386. ast_debug(1, "Config load for message text file '%s' failed\n", fromfile);
  4387. return;
  4388. }
  4389. if ((origcallerid = ast_variable_retrieve(msg_cfg, "message", "callerid"))) {
  4390. pbx_builtin_setvar_helper(ast, "ORIG_VM_CALLERID", origcallerid);
  4391. ast_callerid_split(origcallerid, origcidname, sizeof(origcidname), origcidnum, sizeof(origcidnum));
  4392. pbx_builtin_setvar_helper(ast, "ORIG_VM_CIDNAME", origcidname);
  4393. pbx_builtin_setvar_helper(ast, "ORIG_VM_CIDNUM", origcidnum);
  4394. }
  4395. if ((origtime = ast_variable_retrieve(msg_cfg, "message", "origtime")) && sscanf(origtime, "%30d", &inttime) == 1) {
  4396. struct timeval tv = { inttime, };
  4397. struct ast_tm tm;
  4398. ast_localtime(&tv, &tm, NULL);
  4399. ast_strftime_locale(origdate, sizeof(origdate), emaildateformat, &tm, S_OR(vmu->locale, NULL));
  4400. pbx_builtin_setvar_helper(ast, "ORIG_VM_DATE", origdate);
  4401. }
  4402. ast_config_destroy(msg_cfg);
  4403. }
  4404. /*!
  4405. * \brief Wraps a character sequence in double quotes, escaping occurences of quotes within the string.
  4406. * \param from The string to work with.
  4407. * \param buf The buffer into which to write the modified quoted string.
  4408. * \param maxlen Always zero, but see \see ast_str
  4409. *
  4410. * \return The destination string with quotes wrapped on it (the to field).
  4411. */
  4412. static const char *ast_str_quote(struct ast_str **buf, ssize_t maxlen, const char *from)
  4413. {
  4414. const char *ptr;
  4415. /* We're only ever passing 0 to maxlen, so short output isn't possible */
  4416. ast_str_set(buf, maxlen, "\"");
  4417. for (ptr = from; *ptr; ptr++) {
  4418. if (*ptr == '"' || *ptr == '\\') {
  4419. ast_str_append(buf, maxlen, "\\%c", *ptr);
  4420. } else {
  4421. ast_str_append(buf, maxlen, "%c", *ptr);
  4422. }
  4423. }
  4424. ast_str_append(buf, maxlen, "\"");
  4425. return ast_str_buffer(*buf);
  4426. }
  4427. /*! \brief
  4428. * fill in *tm for current time according to the proper timezone, if any.
  4429. * \return tm so it can be used as a function argument.
  4430. */
  4431. static const struct ast_tm *vmu_tm(const struct ast_vm_user *vmu, struct ast_tm *tm)
  4432. {
  4433. const struct vm_zone *z = NULL;
  4434. struct timeval t = ast_tvnow();
  4435. /* Does this user have a timezone specified? */
  4436. if (!ast_strlen_zero(vmu->zonetag)) {
  4437. /* Find the zone in the list */
  4438. AST_LIST_LOCK(&zones);
  4439. AST_LIST_TRAVERSE(&zones, z, list) {
  4440. if (!strcmp(z->name, vmu->zonetag))
  4441. break;
  4442. }
  4443. AST_LIST_UNLOCK(&zones);
  4444. }
  4445. ast_localtime(&t, tm, z ? z->timezone : NULL);
  4446. return tm;
  4447. }
  4448. /*!\brief Check if the string would need encoding within the MIME standard, to
  4449. * avoid confusing certain mail software that expects messages to be 7-bit
  4450. * clean.
  4451. */
  4452. static int check_mime(const char *str)
  4453. {
  4454. for (; *str; str++) {
  4455. if (*str > 126 || *str < 32 || strchr("()<>@,:;/\"[]?.=", *str)) {
  4456. return 1;
  4457. }
  4458. }
  4459. return 0;
  4460. }
  4461. /*!\brief Encode a string according to the MIME rules for encoding strings
  4462. * that are not 7-bit clean or contain control characters.
  4463. *
  4464. * Additionally, if the encoded string would exceed the MIME limit of 76
  4465. * characters per line, then the encoding will be broken up into multiple
  4466. * sections, separated by a space character, in order to facilitate
  4467. * breaking up the associated header across multiple lines.
  4468. *
  4469. * \param end An expandable buffer for holding the result
  4470. * \param maxlen Always zero, but see \see ast_str
  4471. * \param start A string to be encoded
  4472. * \param preamble The length of the first line already used for this string,
  4473. * to ensure that each line maintains a maximum length of 76 chars.
  4474. * \param postamble the length of any additional characters appended to the
  4475. * line, used to ensure proper field wrapping.
  4476. * \retval The encoded string.
  4477. */
  4478. static const char *ast_str_encode_mime(struct ast_str **end, ssize_t maxlen, const char *start, size_t preamble, size_t postamble)
  4479. {
  4480. struct ast_str *tmp = ast_str_alloca(80);
  4481. int first_section = 1;
  4482. ast_str_reset(*end);
  4483. ast_str_set(&tmp, -1, "=?%s?Q?", charset);
  4484. for (; *start; start++) {
  4485. int need_encoding = 0;
  4486. if (*start < 33 || *start > 126 || strchr("()<>@,:;/\"[]?.=_", *start)) {
  4487. need_encoding = 1;
  4488. }
  4489. if ((first_section && need_encoding && preamble + ast_str_strlen(tmp) > 70) ||
  4490. (first_section && !need_encoding && preamble + ast_str_strlen(tmp) > 72) ||
  4491. (!first_section && need_encoding && ast_str_strlen(tmp) > 70) ||
  4492. (!first_section && !need_encoding && ast_str_strlen(tmp) > 72)) {
  4493. /* Start new line */
  4494. ast_str_append(end, maxlen, "%s%s?=", first_section ? "" : " ", ast_str_buffer(tmp));
  4495. ast_str_set(&tmp, -1, "=?%s?Q?", charset);
  4496. first_section = 0;
  4497. }
  4498. if (need_encoding && *start == ' ') {
  4499. ast_str_append(&tmp, -1, "_");
  4500. } else if (need_encoding) {
  4501. ast_str_append(&tmp, -1, "=%hhX", *start);
  4502. } else {
  4503. ast_str_append(&tmp, -1, "%c", *start);
  4504. }
  4505. }
  4506. ast_str_append(end, maxlen, "%s%s?=%s", first_section ? "" : " ", ast_str_buffer(tmp), ast_str_strlen(tmp) + postamble > 74 ? " " : "");
  4507. return ast_str_buffer(*end);
  4508. }
  4509. /*!
  4510. * \brief Creates the email file to be sent to indicate a new voicemail exists for a user.
  4511. * \param p The output file to generate the email contents into.
  4512. * \param srcemail The email address to send the email to, presumably the email address for the owner of the mailbox.
  4513. * \param vmu The voicemail user who is sending the voicemail.
  4514. * \param msgnum The message index in the mailbox folder.
  4515. * \param context
  4516. * \param mailbox The voicemail box to read the voicemail to be notified in this email.
  4517. * \param fromfolder
  4518. * \param cidnum The caller ID number.
  4519. * \param cidname The caller ID name.
  4520. * \param attach the name of the sound file to be attached to the email, if attach_user_voicemail == 1.
  4521. * \param attach2
  4522. * \param format The message sound file format. i.e. .wav
  4523. * \param duration The time of the message content, in seconds.
  4524. * \param attach_user_voicemail if 1, the sound file is attached to the email.
  4525. * \param chan
  4526. * \param category
  4527. * \param imap if == 1, indicates the target folder for the email notification to be sent to will be an IMAP mailstore. This causes additional mailbox headers to be set, which would facilitate searching for the email in the destination IMAP folder.
  4528. * \param flag, msg_id
  4529. *
  4530. * The email body, and base 64 encoded attachement (if any) are stored to the file identified by *p. This method does not actually send the email. That is done by invoking the configure 'mailcmd' and piping this generated file into it, or with the sendemail() function.
  4531. */
  4532. static void make_email_file(FILE *p,
  4533. char *srcemail,
  4534. struct ast_vm_user *vmu,
  4535. int msgnum,
  4536. char *context,
  4537. char *mailbox,
  4538. const char *fromfolder,
  4539. char *cidnum,
  4540. char *cidname,
  4541. char *attach,
  4542. char *attach2,
  4543. char *format,
  4544. int duration,
  4545. int attach_user_voicemail,
  4546. struct ast_channel *chan,
  4547. const char *category,
  4548. int imap,
  4549. const char *flag,
  4550. const char *msg_id)
  4551. {
  4552. char date[256];
  4553. char host[MAXHOSTNAMELEN] = "";
  4554. char who[256];
  4555. char bound[256];
  4556. char dur[256];
  4557. struct ast_tm tm;
  4558. char enc_cidnum[256] = "", enc_cidname[256] = "";
  4559. struct ast_str *str1 = ast_str_create(16), *str2 = ast_str_create(16);
  4560. char *greeting_attachment;
  4561. char filename[256];
  4562. int first_line;
  4563. char *emailsbuf;
  4564. char *email;
  4565. if (!str1 || !str2) {
  4566. ast_free(str1);
  4567. ast_free(str2);
  4568. return;
  4569. }
  4570. if (cidnum) {
  4571. strip_control_and_high(cidnum, enc_cidnum, sizeof(enc_cidnum));
  4572. }
  4573. if (cidname) {
  4574. strip_control_and_high(cidname, enc_cidname, sizeof(enc_cidname));
  4575. }
  4576. gethostname(host, sizeof(host) - 1);
  4577. if (strchr(srcemail, '@')) {
  4578. ast_copy_string(who, srcemail, sizeof(who));
  4579. } else {
  4580. snprintf(who, sizeof(who), "%s@%s", srcemail, host);
  4581. }
  4582. greeting_attachment = strrchr(ast_strdupa(attach), '/');
  4583. if (greeting_attachment) {
  4584. *greeting_attachment++ = '\0';
  4585. }
  4586. snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
  4587. ast_strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", vmu_tm(vmu, &tm));
  4588. fprintf(p, "Date: %s" ENDL, date);
  4589. /* Set date format for voicemail mail */
  4590. ast_strftime_locale(date, sizeof(date), emaildateformat, &tm, S_OR(vmu->locale, NULL));
  4591. if (!ast_strlen_zero(fromstring)) {
  4592. struct ast_channel *ast;
  4593. if ((ast = ast_dummy_channel_alloc())) {
  4594. char *ptr;
  4595. prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, enc_cidnum, enc_cidname, dur, date, category, flag);
  4596. ast_str_substitute_variables(&str1, 0, ast, fromstring);
  4597. if (check_mime(ast_str_buffer(str1))) {
  4598. first_line = 1;
  4599. ast_str_encode_mime(&str2, 0, ast_str_buffer(str1), strlen("From: "), strlen(who) + 3);
  4600. while ((ptr = strchr(ast_str_buffer(str2), ' '))) {
  4601. *ptr = '\0';
  4602. fprintf(p, "%s %s" ENDL, first_line ? "From:" : "", ast_str_buffer(str2));
  4603. first_line = 0;
  4604. /* Substring is smaller, so this will never grow */
  4605. ast_str_set(&str2, 0, "%s", ptr + 1);
  4606. }
  4607. fprintf(p, "%s %s <%s>" ENDL, first_line ? "From:" : "", ast_str_buffer(str2), who);
  4608. } else {
  4609. fprintf(p, "From: %s <%s>" ENDL, ast_str_quote(&str2, 0, ast_str_buffer(str1)), who);
  4610. }
  4611. ast = ast_channel_unref(ast);
  4612. } else {
  4613. ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
  4614. }
  4615. } else {
  4616. fprintf(p, "From: Asterisk PBX <%s>" ENDL, who);
  4617. }
  4618. emailsbuf = ast_strdupa(vmu->email);
  4619. fprintf(p, "To:");
  4620. first_line = 1;
  4621. while ((email = strsep(&emailsbuf, "|"))) {
  4622. char *next = emailsbuf;
  4623. if (check_mime(vmu->fullname)) {
  4624. char *ptr;
  4625. ast_str_encode_mime(&str2, 0, vmu->fullname, first_line ? strlen("To: ") : 0, strlen(email) + 3 + (next ? strlen(",") : 0));
  4626. while ((ptr = strchr(ast_str_buffer(str2), ' '))) {
  4627. *ptr = '\0';
  4628. fprintf(p, " %s" ENDL, ast_str_buffer(str2));
  4629. /* Substring is smaller, so this will never grow */
  4630. ast_str_set(&str2, 0, "%s", ptr + 1);
  4631. }
  4632. fprintf(p, " %s <%s>%s" ENDL, ast_str_buffer(str2), email, next ? "," : "");
  4633. } else {
  4634. fprintf(p, " %s <%s>%s" ENDL, ast_str_quote(&str2, 0, vmu->fullname), email, next ? "," : "");
  4635. }
  4636. first_line = 0;
  4637. }
  4638. if (!ast_strlen_zero(emailsubject) || !ast_strlen_zero(vmu->emailsubject)) {
  4639. char *e_subj = !ast_strlen_zero(vmu->emailsubject) ? vmu->emailsubject : emailsubject;
  4640. struct ast_channel *ast;
  4641. if ((ast = ast_dummy_channel_alloc())) {
  4642. prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, category, flag);
  4643. ast_str_substitute_variables(&str1, 0, ast, e_subj);
  4644. if (check_mime(ast_str_buffer(str1))) {
  4645. char *ptr;
  4646. first_line = 1;
  4647. ast_str_encode_mime(&str2, 0, ast_str_buffer(str1), strlen("Subject: "), 0);
  4648. while ((ptr = strchr(ast_str_buffer(str2), ' '))) {
  4649. *ptr = '\0';
  4650. fprintf(p, "%s %s" ENDL, first_line ? "Subject:" : "", ast_str_buffer(str2));
  4651. first_line = 0;
  4652. /* Substring is smaller, so this will never grow */
  4653. ast_str_set(&str2, 0, "%s", ptr + 1);
  4654. }
  4655. fprintf(p, "%s %s" ENDL, first_line ? "Subject:" : "", ast_str_buffer(str2));
  4656. } else {
  4657. fprintf(p, "Subject: %s" ENDL, ast_str_buffer(str1));
  4658. }
  4659. ast = ast_channel_unref(ast);
  4660. } else {
  4661. ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
  4662. }
  4663. } else if (ast_test_flag((&globalflags), VM_PBXSKIP)) {
  4664. if (ast_strlen_zero(flag)) {
  4665. fprintf(p, "Subject: New message %d in mailbox %s" ENDL, msgnum + 1, mailbox);
  4666. } else {
  4667. fprintf(p, "Subject: New %s message %d in mailbox %s" ENDL, flag, msgnum + 1, mailbox);
  4668. }
  4669. } else {
  4670. if (ast_strlen_zero(flag)) {
  4671. fprintf(p, "Subject: [PBX]: New message %d in mailbox %s" ENDL, msgnum + 1, mailbox);
  4672. } else {
  4673. fprintf(p, "Subject: [PBX]: New %s message %d in mailbox %s" ENDL, flag, msgnum + 1, mailbox);
  4674. }
  4675. }
  4676. fprintf(p, "Message-ID: <Asterisk-%d-%u-%s-%d@%s>" ENDL, msgnum + 1,
  4677. (unsigned int) ast_random(), mailbox, (int) getpid(), host);
  4678. if (imap) {
  4679. /* additional information needed for IMAP searching */
  4680. fprintf(p, "X-Asterisk-VM-Message-Num: %d" ENDL, msgnum + 1);
  4681. /* fprintf(p, "X-Asterisk-VM-Orig-Mailbox: %s" ENDL, ext); */
  4682. fprintf(p, "X-Asterisk-VM-Server-Name: %s" ENDL, fromstring);
  4683. fprintf(p, "X-Asterisk-VM-Context: %s" ENDL, context);
  4684. #ifdef IMAP_STORAGE
  4685. fprintf(p, "X-Asterisk-VM-Extension: %s" ENDL, (!ast_strlen_zero(vmu->imapvmshareid) ? vmu->imapvmshareid : mailbox));
  4686. #else
  4687. fprintf(p, "X-Asterisk-VM-Extension: %s" ENDL, mailbox);
  4688. #endif
  4689. /* flag added for Urgent */
  4690. fprintf(p, "X-Asterisk-VM-Flag: %s" ENDL, flag);
  4691. fprintf(p, "X-Asterisk-VM-Priority: %d" ENDL, chan ? ast_channel_priority(chan) : 0);
  4692. fprintf(p, "X-Asterisk-VM-Caller-channel: %s" ENDL, chan ? ast_channel_name(chan) : "");
  4693. fprintf(p, "X-Asterisk-VM-Caller-ID-Num: %s" ENDL, enc_cidnum);
  4694. fprintf(p, "X-Asterisk-VM-Caller-ID-Name: %s" ENDL, enc_cidname);
  4695. fprintf(p, "X-Asterisk-VM-Duration: %d" ENDL, duration);
  4696. if (!ast_strlen_zero(category)) {
  4697. fprintf(p, "X-Asterisk-VM-Category: %s" ENDL, category);
  4698. } else {
  4699. fprintf(p, "X-Asterisk-VM-Category: " ENDL);
  4700. }
  4701. fprintf(p, "X-Asterisk-VM-Message-Type: %s" ENDL, msgnum > -1 ? "Message" : greeting_attachment);
  4702. fprintf(p, "X-Asterisk-VM-Orig-date: %s" ENDL, date);
  4703. fprintf(p, "X-Asterisk-VM-Orig-time: %ld" ENDL, (long) time(NULL));
  4704. fprintf(p, "X-Asterisk-VM-Message-ID: %s" ENDL, msg_id);
  4705. }
  4706. if (!ast_strlen_zero(cidnum)) {
  4707. fprintf(p, "X-Asterisk-CallerID: %s" ENDL, enc_cidnum);
  4708. }
  4709. if (!ast_strlen_zero(cidname)) {
  4710. fprintf(p, "X-Asterisk-CallerIDName: %s" ENDL, enc_cidname);
  4711. }
  4712. fprintf(p, "MIME-Version: 1.0" ENDL);
  4713. if (attach_user_voicemail) {
  4714. /* Something unique. */
  4715. snprintf(bound, sizeof(bound), "----voicemail_%d%s%d%u", msgnum + 1, mailbox,
  4716. (int) getpid(), (unsigned int) ast_random());
  4717. fprintf(p, "Content-Type: multipart/mixed; boundary=\"%s\"" ENDL, bound);
  4718. fprintf(p, ENDL ENDL "This is a multi-part message in MIME format." ENDL ENDL);
  4719. fprintf(p, "--%s" ENDL, bound);
  4720. }
  4721. fprintf(p, "Content-Type: text/plain; charset=%s" ENDL "Content-Transfer-Encoding: 8bit" ENDL ENDL, charset);
  4722. if (emailbody || vmu->emailbody) {
  4723. char* e_body = vmu->emailbody ? vmu->emailbody : emailbody;
  4724. struct ast_channel *ast;
  4725. if ((ast = ast_dummy_channel_alloc())) {
  4726. prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, category, flag);
  4727. ast_str_substitute_variables(&str1, 0, ast, e_body);
  4728. #ifdef IMAP_STORAGE
  4729. {
  4730. /* Convert body to native line terminators for IMAP backend */
  4731. char *line = ast_str_buffer(str1), *next;
  4732. do {
  4733. /* Terminate line before outputting it to the file */
  4734. if ((next = strchr(line, '\n'))) {
  4735. *next++ = '\0';
  4736. }
  4737. fprintf(p, "%s" ENDL, line);
  4738. line = next;
  4739. } while (!ast_strlen_zero(line));
  4740. }
  4741. #else
  4742. fprintf(p, "%s" ENDL, ast_str_buffer(str1));
  4743. #endif
  4744. ast = ast_channel_unref(ast);
  4745. } else {
  4746. ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
  4747. }
  4748. } else if (msgnum > -1) {
  4749. if (strcmp(vmu->mailbox, mailbox)) {
  4750. /* Forwarded type */
  4751. struct ast_config *msg_cfg;
  4752. const char *v;
  4753. int inttime;
  4754. char fromdir[256], fromfile[256], origdate[80] = "", origcallerid[80] = "";
  4755. struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
  4756. /* Retrieve info from VM attribute file */
  4757. make_dir(fromdir, sizeof(fromdir), vmu->context, vmu->mailbox, fromfolder);
  4758. make_file(fromfile, sizeof(fromfile), fromdir, msgnum);
  4759. if (strlen(fromfile) < sizeof(fromfile) - 5) {
  4760. strcat(fromfile, ".txt");
  4761. }
  4762. if ((msg_cfg = ast_config_load(fromfile, config_flags)) && valid_config(msg_cfg)) {
  4763. if ((v = ast_variable_retrieve(msg_cfg, "message", "callerid"))) {
  4764. ast_copy_string(origcallerid, v, sizeof(origcallerid));
  4765. }
  4766. /* You might be tempted to do origdate, except that a) it's in the wrong
  4767. * format, and b) it's missing for IMAP recordings. */
  4768. if ((v = ast_variable_retrieve(msg_cfg, "message", "origtime")) && sscanf(v, "%30d", &inttime) == 1) {
  4769. struct timeval tv = { inttime, };
  4770. struct ast_tm tm;
  4771. ast_localtime(&tv, &tm, NULL);
  4772. ast_strftime_locale(origdate, sizeof(origdate), emaildateformat, &tm, S_OR(vmu->locale, NULL));
  4773. }
  4774. fprintf(p, "Dear %s:" ENDL ENDL "\tJust wanted to let you know you were just forwarded"
  4775. " a %s long message (number %d)" ENDL "in mailbox %s from %s, on %s" ENDL
  4776. "(originally sent by %s on %s)" ENDL "so you might want to check it when you get a"
  4777. " chance. Thanks!" ENDL ENDL "\t\t\t\t--Asterisk" ENDL ENDL, vmu->fullname, dur,
  4778. msgnum + 1, mailbox, (cidname ? cidname : (cidnum ? cidnum : "an unknown caller")),
  4779. date, origcallerid, origdate);
  4780. ast_config_destroy(msg_cfg);
  4781. } else {
  4782. goto plain_message;
  4783. }
  4784. } else {
  4785. plain_message:
  4786. fprintf(p, "Dear %s:" ENDL ENDL "\tJust wanted to let you know you were just left a "
  4787. "%s long message (number %d)" ENDL "in mailbox %s from %s, on %s so you might" ENDL
  4788. "want to check it when you get a chance. Thanks!" ENDL ENDL "\t\t\t\t--Asterisk"
  4789. ENDL ENDL, vmu->fullname, dur, msgnum + 1, mailbox,
  4790. (cidname ? cidname : (cidnum ? cidnum : "an unknown caller")), date);
  4791. }
  4792. } else {
  4793. fprintf(p, "This message is to let you know that your greeting was changed on %s." ENDL
  4794. "Please do not delete this message, lest your greeting vanish with it." ENDL ENDL, date);
  4795. }
  4796. if (imap || attach_user_voicemail) {
  4797. if (!ast_strlen_zero(attach2)) {
  4798. snprintf(filename, sizeof(filename), "msg%04d.%s", msgnum, format);
  4799. ast_debug(5, "creating second attachment filename %s\n", filename);
  4800. add_email_attachment(p, vmu, format, attach, greeting_attachment, mailbox, bound, filename, 0, msgnum);
  4801. snprintf(filename, sizeof(filename), "msgintro%04d.%s", msgnum, format);
  4802. ast_debug(5, "creating attachment filename %s\n", filename);
  4803. add_email_attachment(p, vmu, format, attach2, greeting_attachment, mailbox, bound, filename, 1, msgnum);
  4804. } else {
  4805. snprintf(filename, sizeof(filename), "msg%04d.%s", msgnum, format);
  4806. ast_debug(5, "creating attachment filename %s, no second attachment.\n", filename);
  4807. add_email_attachment(p, vmu, format, attach, greeting_attachment, mailbox, bound, filename, 1, msgnum);
  4808. }
  4809. }
  4810. ast_free(str1);
  4811. ast_free(str2);
  4812. }
  4813. static int add_email_attachment(FILE *p, struct ast_vm_user *vmu, char *format, char *attach, char *greeting_attachment, char *mailbox, char *bound, char *filename, int last, int msgnum)
  4814. {
  4815. char tmpdir[256], newtmp[256];
  4816. char fname[256];
  4817. char tmpcmd[256];
  4818. int tmpfd = -1;
  4819. int soxstatus = 0;
  4820. /* Eww. We want formats to tell us their own MIME type */
  4821. char *ctype = (!strcasecmp(format, "ogg")) ? "application/" : "audio/x-";
  4822. if (vmu->volgain < -.001 || vmu->volgain > .001) {
  4823. create_dirpath(tmpdir, sizeof(tmpdir), vmu->context, vmu->mailbox, "tmp");
  4824. snprintf(newtmp, sizeof(newtmp), "%s/XXXXXX", tmpdir);
  4825. tmpfd = mkstemp(newtmp);
  4826. chmod(newtmp, VOICEMAIL_FILE_MODE & ~my_umask);
  4827. ast_debug(3, "newtmp: %s\n", newtmp);
  4828. if (tmpfd > -1) {
  4829. snprintf(tmpcmd, sizeof(tmpcmd), "sox -v %.4f %s.%s %s.%s", vmu->volgain, attach, format, newtmp, format);
  4830. if ((soxstatus = ast_safe_system(tmpcmd)) == 0) {
  4831. attach = newtmp;
  4832. ast_debug(3, "VOLGAIN: Stored at: %s.%s - Level: %.4f - Mailbox: %s\n", attach, format, vmu->volgain, mailbox);
  4833. } else {
  4834. ast_log(LOG_WARNING, "Sox failed to re-encode %s.%s: %s (have you installed support for all sox file formats?)\n", attach, format,
  4835. soxstatus == 1 ? "Problem with command line options" : "An error occurred during file processing");
  4836. ast_log(LOG_WARNING, "Voicemail attachment will have no volume gain.\n");
  4837. }
  4838. }
  4839. }
  4840. fprintf(p, "--%s" ENDL, bound);
  4841. if (msgnum > -1)
  4842. fprintf(p, "Content-Type: %s%s; name=\"%s\"" ENDL, ctype, format, filename);
  4843. else
  4844. fprintf(p, "Content-Type: %s%s; name=\"%s.%s\"" ENDL, ctype, format, greeting_attachment, format);
  4845. fprintf(p, "Content-Transfer-Encoding: base64" ENDL);
  4846. fprintf(p, "Content-Description: Voicemail sound attachment." ENDL);
  4847. if (msgnum > -1)
  4848. fprintf(p, "Content-Disposition: attachment; filename=\"%s\"" ENDL ENDL, filename);
  4849. else
  4850. fprintf(p, "Content-Disposition: attachment; filename=\"%s.%s\"" ENDL ENDL, greeting_attachment, format);
  4851. snprintf(fname, sizeof(fname), "%s.%s", attach, format);
  4852. base_encode(fname, p);
  4853. if (last)
  4854. fprintf(p, ENDL ENDL "--%s--" ENDL "." ENDL, bound);
  4855. if (tmpfd > -1) {
  4856. if (soxstatus == 0) {
  4857. unlink(fname);
  4858. }
  4859. close(tmpfd);
  4860. unlink(newtmp);
  4861. }
  4862. return 0;
  4863. }
  4864. static int sendmail(char *srcemail,
  4865. struct ast_vm_user *vmu,
  4866. int msgnum,
  4867. char *context,
  4868. char *mailbox,
  4869. const char *fromfolder,
  4870. char *cidnum,
  4871. char *cidname,
  4872. char *attach,
  4873. char *attach2,
  4874. char *format,
  4875. int duration,
  4876. int attach_user_voicemail,
  4877. struct ast_channel *chan,
  4878. const char *category,
  4879. const char *flag,
  4880. const char *msg_id)
  4881. {
  4882. FILE *p = NULL;
  4883. char tmp[80] = "/tmp/astmail-XXXXXX";
  4884. char tmp2[256];
  4885. char *stringp;
  4886. if (vmu && ast_strlen_zero(vmu->email)) {
  4887. ast_log(AST_LOG_WARNING, "E-mail address missing for mailbox [%s]. E-mail will not be sent.\n", vmu->mailbox);
  4888. return(0);
  4889. }
  4890. /* Mail only the first format */
  4891. format = ast_strdupa(format);
  4892. stringp = format;
  4893. strsep(&stringp, "|");
  4894. if (!strcmp(format, "wav49"))
  4895. format = "WAV";
  4896. ast_debug(3, "Attaching file '%s', format '%s', uservm is '%d', global is %u\n", attach, format, attach_user_voicemail, ast_test_flag((&globalflags), VM_ATTACH));
  4897. /* Make a temporary file instead of piping directly to sendmail, in case the mail
  4898. command hangs */
  4899. if ((p = vm_mkftemp(tmp)) == NULL) {
  4900. ast_log(AST_LOG_WARNING, "Unable to launch '%s' (can't create temporary file)\n", mailcmd);
  4901. return -1;
  4902. } else {
  4903. make_email_file(p, srcemail, vmu, msgnum, context, mailbox, fromfolder, cidnum, cidname, attach, attach2, format, duration, attach_user_voicemail, chan, category, 0, flag, msg_id);
  4904. fclose(p);
  4905. snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
  4906. ast_safe_system(tmp2);
  4907. ast_debug(1, "Sent mail to %s with command '%s'\n", vmu->email, mailcmd);
  4908. }
  4909. return 0;
  4910. }
  4911. static int sendpage(char *srcemail, char *pager, int msgnum, char *context, char *mailbox, const char *fromfolder, char *cidnum, char *cidname, int duration, struct ast_vm_user *vmu, const char *category, const char *flag)
  4912. {
  4913. char enc_cidnum[256], enc_cidname[256];
  4914. char date[256];
  4915. char host[MAXHOSTNAMELEN] = "";
  4916. char who[256];
  4917. char dur[PATH_MAX];
  4918. char tmp[80] = "/tmp/astmail-XXXXXX";
  4919. char tmp2[PATH_MAX];
  4920. struct ast_tm tm;
  4921. FILE *p;
  4922. struct ast_str *str1 = ast_str_create(16), *str2 = ast_str_create(16);
  4923. if (!str1 || !str2) {
  4924. ast_free(str1);
  4925. ast_free(str2);
  4926. return -1;
  4927. }
  4928. if (cidnum) {
  4929. strip_control_and_high(cidnum, enc_cidnum, sizeof(enc_cidnum));
  4930. }
  4931. if (cidname) {
  4932. strip_control_and_high(cidname, enc_cidname, sizeof(enc_cidname));
  4933. }
  4934. if ((p = vm_mkftemp(tmp)) == NULL) {
  4935. ast_log(AST_LOG_WARNING, "Unable to launch '%s' (can't create temporary file)\n", mailcmd);
  4936. ast_free(str1);
  4937. ast_free(str2);
  4938. return -1;
  4939. }
  4940. gethostname(host, sizeof(host)-1);
  4941. if (strchr(srcemail, '@')) {
  4942. ast_copy_string(who, srcemail, sizeof(who));
  4943. } else {
  4944. snprintf(who, sizeof(who), "%s@%s", srcemail, host);
  4945. }
  4946. snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
  4947. ast_strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", vmu_tm(vmu, &tm));
  4948. fprintf(p, "Date: %s\n", date);
  4949. /* Reformat for custom pager format */
  4950. ast_strftime_locale(date, sizeof(date), pagerdateformat, vmu_tm(vmu, &tm), S_OR(vmu->locale, NULL));
  4951. if (!ast_strlen_zero(pagerfromstring)) {
  4952. struct ast_channel *ast;
  4953. if ((ast = ast_dummy_channel_alloc())) {
  4954. char *ptr;
  4955. prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, enc_cidnum, enc_cidname, dur, date, category, flag);
  4956. ast_str_substitute_variables(&str1, 0, ast, pagerfromstring);
  4957. if (check_mime(ast_str_buffer(str1))) {
  4958. int first_line = 1;
  4959. ast_str_encode_mime(&str2, 0, ast_str_buffer(str1), strlen("From: "), strlen(who) + 3);
  4960. while ((ptr = strchr(ast_str_buffer(str2), ' '))) {
  4961. *ptr = '\0';
  4962. fprintf(p, "%s %s" ENDL, first_line ? "From:" : "", ast_str_buffer(str2));
  4963. first_line = 0;
  4964. /* Substring is smaller, so this will never grow */
  4965. ast_str_set(&str2, 0, "%s", ptr + 1);
  4966. }
  4967. fprintf(p, "%s %s <%s>" ENDL, first_line ? "From:" : "", ast_str_buffer(str2), who);
  4968. } else {
  4969. fprintf(p, "From: %s <%s>" ENDL, ast_str_quote(&str2, 0, ast_str_buffer(str1)), who);
  4970. }
  4971. ast = ast_channel_unref(ast);
  4972. } else {
  4973. ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
  4974. }
  4975. } else {
  4976. fprintf(p, "From: Asterisk PBX <%s>" ENDL, who);
  4977. }
  4978. if (check_mime(vmu->fullname)) {
  4979. int first_line = 1;
  4980. char *ptr;
  4981. ast_str_encode_mime(&str2, 0, vmu->fullname, strlen("To: "), strlen(pager) + 3);
  4982. while ((ptr = strchr(ast_str_buffer(str2), ' '))) {
  4983. *ptr = '\0';
  4984. fprintf(p, "%s %s" ENDL, first_line ? "To:" : "", ast_str_buffer(str2));
  4985. first_line = 0;
  4986. /* Substring is smaller, so this will never grow */
  4987. ast_str_set(&str2, 0, "%s", ptr + 1);
  4988. }
  4989. fprintf(p, "%s %s <%s>" ENDL, first_line ? "To:" : "", ast_str_buffer(str2), pager);
  4990. } else {
  4991. fprintf(p, "To: %s <%s>" ENDL, ast_str_quote(&str2, 0, vmu->fullname), pager);
  4992. }
  4993. if (!ast_strlen_zero(pagersubject)) {
  4994. struct ast_channel *ast;
  4995. if ((ast = ast_dummy_channel_alloc())) {
  4996. prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, category, flag);
  4997. ast_str_substitute_variables(&str1, 0, ast, pagersubject);
  4998. if (check_mime(ast_str_buffer(str1))) {
  4999. int first_line = 1;
  5000. char *ptr;
  5001. ast_str_encode_mime(&str2, 0, ast_str_buffer(str1), strlen("Subject: "), 0);
  5002. while ((ptr = strchr(ast_str_buffer(str2), ' '))) {
  5003. *ptr = '\0';
  5004. fprintf(p, "%s %s" ENDL, first_line ? "Subject:" : "", ast_str_buffer(str2));
  5005. first_line = 0;
  5006. /* Substring is smaller, so this will never grow */
  5007. ast_str_set(&str2, 0, "%s", ptr + 1);
  5008. }
  5009. fprintf(p, "%s %s" ENDL, first_line ? "Subject:" : "", ast_str_buffer(str2));
  5010. } else {
  5011. fprintf(p, "Subject: %s" ENDL, ast_str_buffer(str1));
  5012. }
  5013. ast = ast_channel_unref(ast);
  5014. } else {
  5015. ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
  5016. }
  5017. } else {
  5018. if (ast_strlen_zero(flag)) {
  5019. fprintf(p, "Subject: New VM\n\n");
  5020. } else {
  5021. fprintf(p, "Subject: New %s VM\n\n", flag);
  5022. }
  5023. }
  5024. if (pagerbody) {
  5025. struct ast_channel *ast;
  5026. if ((ast = ast_dummy_channel_alloc())) {
  5027. prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, category, flag);
  5028. ast_str_substitute_variables(&str1, 0, ast, pagerbody);
  5029. fprintf(p, "%s" ENDL, ast_str_buffer(str1));
  5030. ast = ast_channel_unref(ast);
  5031. } else {
  5032. ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
  5033. }
  5034. } else {
  5035. fprintf(p, "New %s long %s msg in box %s\n"
  5036. "from %s, on %s", dur, flag, mailbox, (cidname ? cidname : (cidnum ? cidnum : "unknown")), date);
  5037. }
  5038. fclose(p);
  5039. snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
  5040. ast_safe_system(tmp2);
  5041. ast_debug(1, "Sent page to %s with command '%s'\n", pager, mailcmd);
  5042. ast_free(str1);
  5043. ast_free(str2);
  5044. return 0;
  5045. }
  5046. /*!
  5047. * \brief Gets the current date and time, as formatted string.
  5048. * \param s The buffer to hold the output formatted date.
  5049. * \param len the length of the buffer. Used to prevent buffer overflow in ast_strftime.
  5050. *
  5051. * The date format string used is "%a %b %e %r UTC %Y".
  5052. *
  5053. * \return zero on success, -1 on error.
  5054. */
  5055. static int get_date(char *s, int len)
  5056. {
  5057. struct ast_tm tm;
  5058. struct timeval t = ast_tvnow();
  5059. ast_localtime(&t, &tm, "UTC");
  5060. return ast_strftime(s, len, "%a %b %e %r UTC %Y", &tm);
  5061. }
  5062. static int invent_message(struct ast_channel *chan, char *context, char *ext, int busy, char *ecodes)
  5063. {
  5064. int res;
  5065. char fn[PATH_MAX];
  5066. char dest[PATH_MAX];
  5067. snprintf(fn, sizeof(fn), "%s%s/%s/greet", VM_SPOOL_DIR, context, ext);
  5068. if ((res = create_dirpath(dest, sizeof(dest), context, ext, ""))) {
  5069. ast_log(AST_LOG_WARNING, "Failed to make directory(%s)\n", fn);
  5070. return -1;
  5071. }
  5072. RETRIEVE(fn, -1, ext, context);
  5073. if (ast_fileexists(fn, NULL, NULL) > 0) {
  5074. res = ast_stream_and_wait(chan, fn, ecodes);
  5075. if (res) {
  5076. DISPOSE(fn, -1);
  5077. return res;
  5078. }
  5079. } else {
  5080. /* Dispose just in case */
  5081. DISPOSE(fn, -1);
  5082. res = ast_stream_and_wait(chan, "vm-theperson", ecodes);
  5083. if (res)
  5084. return res;
  5085. res = ast_say_digit_str(chan, ext, ecodes, ast_channel_language(chan));
  5086. if (res)
  5087. return res;
  5088. }
  5089. res = ast_stream_and_wait(chan, busy ? "vm-isonphone" : "vm-isunavail", ecodes);
  5090. return res;
  5091. }
  5092. static void free_zone(struct vm_zone *z)
  5093. {
  5094. ast_free(z);
  5095. }
  5096. #ifdef ODBC_STORAGE
  5097. static int inboxcount2(const char *mailbox, int *urgentmsgs, int *newmsgs, int *oldmsgs)
  5098. {
  5099. int x = -1;
  5100. int res;
  5101. SQLHSTMT stmt = NULL;
  5102. char sql[PATH_MAX];
  5103. char rowdata[20];
  5104. char tmp[PATH_MAX] = "";
  5105. struct odbc_obj *obj = NULL;
  5106. char *context;
  5107. struct generic_prepare_struct gps = { .sql = sql, .argc = 0 };
  5108. if (newmsgs)
  5109. *newmsgs = 0;
  5110. if (oldmsgs)
  5111. *oldmsgs = 0;
  5112. if (urgentmsgs)
  5113. *urgentmsgs = 0;
  5114. /* If no mailbox, return immediately */
  5115. if (ast_strlen_zero(mailbox))
  5116. return 0;
  5117. ast_copy_string(tmp, mailbox, sizeof(tmp));
  5118. if (strchr(mailbox, ' ') || strchr(mailbox, ',')) {
  5119. int u, n, o;
  5120. char *next, *remaining = tmp;
  5121. while ((next = strsep(&remaining, " ,"))) {
  5122. if (inboxcount2(next, urgentmsgs ? &u : NULL, &n, &o)) {
  5123. return -1;
  5124. }
  5125. if (urgentmsgs) {
  5126. *urgentmsgs += u;
  5127. }
  5128. if (newmsgs) {
  5129. *newmsgs += n;
  5130. }
  5131. if (oldmsgs) {
  5132. *oldmsgs += o;
  5133. }
  5134. }
  5135. return 0;
  5136. }
  5137. context = strchr(tmp, '@');
  5138. if (context) {
  5139. *context = '\0';
  5140. context++;
  5141. } else
  5142. context = "default";
  5143. if ((obj = ast_odbc_request_obj(odbc_database, 0))) {
  5144. do {
  5145. if (newmsgs) {
  5146. snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, tmp, "INBOX");
  5147. if (!(stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps))) {
  5148. ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
  5149. break;
  5150. }
  5151. res = SQLFetch(stmt);
  5152. if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
  5153. ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
  5154. break;
  5155. }
  5156. res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
  5157. if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
  5158. ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
  5159. break;
  5160. }
  5161. *newmsgs = atoi(rowdata);
  5162. SQLFreeHandle (SQL_HANDLE_STMT, stmt);
  5163. }
  5164. if (oldmsgs) {
  5165. snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, tmp, "Old");
  5166. if (!(stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps))) {
  5167. ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
  5168. break;
  5169. }
  5170. res = SQLFetch(stmt);
  5171. if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
  5172. ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
  5173. break;
  5174. }
  5175. res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
  5176. if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
  5177. ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
  5178. break;
  5179. }
  5180. SQLFreeHandle(SQL_HANDLE_STMT, stmt);
  5181. *oldmsgs = atoi(rowdata);
  5182. }
  5183. if (urgentmsgs) {
  5184. snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, tmp, "Urgent");
  5185. if (!(stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps))) {
  5186. ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
  5187. break;
  5188. }
  5189. res = SQLFetch(stmt);
  5190. if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
  5191. ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
  5192. break;
  5193. }
  5194. res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
  5195. if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
  5196. ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
  5197. break;
  5198. }
  5199. *urgentmsgs = atoi(rowdata);
  5200. }
  5201. x = 0;
  5202. } while (0);
  5203. } else {
  5204. ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
  5205. }
  5206. if (stmt) {
  5207. SQLFreeHandle (SQL_HANDLE_STMT, stmt);
  5208. }
  5209. if (obj) {
  5210. ast_odbc_release_obj(obj);
  5211. }
  5212. return x;
  5213. }
  5214. /*!
  5215. * \brief Gets the number of messages that exist in a mailbox folder.
  5216. * \param mailbox_id
  5217. * \param folder
  5218. *
  5219. * This method is used when ODBC backend is used.
  5220. * \return The number of messages in this mailbox folder (zero or more).
  5221. */
  5222. static int messagecount(const char *mailbox_id, const char *folder)
  5223. {
  5224. struct odbc_obj *obj = NULL;
  5225. char *context;
  5226. char *mailbox;
  5227. int nummsgs = 0;
  5228. int res;
  5229. SQLHSTMT stmt = NULL;
  5230. char sql[PATH_MAX];
  5231. char rowdata[20];
  5232. struct generic_prepare_struct gps = { .sql = sql, .argc = 0 };
  5233. /* If no mailbox, return immediately */
  5234. if (ast_strlen_zero(mailbox_id)
  5235. || separate_mailbox(ast_strdupa(mailbox_id), &mailbox, &context)) {
  5236. return 0;
  5237. }
  5238. if (ast_strlen_zero(folder)) {
  5239. folder = "INBOX";
  5240. }
  5241. obj = ast_odbc_request_obj(odbc_database, 0);
  5242. if (obj) {
  5243. if (!strcmp(folder, "INBOX")) {
  5244. snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/INBOX' OR dir = '%s%s/%s/Urgent'", odbc_table, VM_SPOOL_DIR, context, mailbox, VM_SPOOL_DIR, context, mailbox);
  5245. } else {
  5246. snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, mailbox, folder);
  5247. }
  5248. stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
  5249. if (!stmt) {
  5250. ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
  5251. goto yuck;
  5252. }
  5253. res = SQLFetch(stmt);
  5254. if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
  5255. ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
  5256. SQLFreeHandle (SQL_HANDLE_STMT, stmt);
  5257. goto yuck;
  5258. }
  5259. res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
  5260. if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
  5261. ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
  5262. SQLFreeHandle (SQL_HANDLE_STMT, stmt);
  5263. goto yuck;
  5264. }
  5265. nummsgs = atoi(rowdata);
  5266. SQLFreeHandle (SQL_HANDLE_STMT, stmt);
  5267. } else
  5268. ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
  5269. yuck:
  5270. if (obj)
  5271. ast_odbc_release_obj(obj);
  5272. return nummsgs;
  5273. }
  5274. /**
  5275. * \brief Determines if the given folder has messages.
  5276. * \param mailbox The @ delimited string for user@context. If no context is found, uses 'default' for the context.
  5277. *
  5278. * This function is used when the mailbox is stored in an ODBC back end.
  5279. * This invokes the messagecount(). Here we are interested in the presence of messages (> 0) only, not the actual count.
  5280. * \return 1 if the folder has one or more messages. zero otherwise.
  5281. */
  5282. static int has_voicemail(const char *mailboxes, const char *folder)
  5283. {
  5284. char *parse;
  5285. char *mailbox;
  5286. parse = ast_strdupa(mailboxes);
  5287. while ((mailbox = strsep(&parse, ",&"))) {
  5288. if (messagecount(mailbox, folder)) {
  5289. return 1;
  5290. }
  5291. }
  5292. return 0;
  5293. }
  5294. #endif
  5295. #ifndef IMAP_STORAGE
  5296. /*!
  5297. * \brief Copies a message from one mailbox to another.
  5298. * \param chan
  5299. * \param vmu
  5300. * \param imbox
  5301. * \param msgnum
  5302. * \param duration
  5303. * \param recip
  5304. * \param fmt
  5305. * \param dir
  5306. * \param flag, dest_folder
  5307. *
  5308. * This is only used by file storage based mailboxes.
  5309. *
  5310. * \return zero on success, -1 on error.
  5311. */
  5312. static int copy_message(struct ast_channel *chan, struct ast_vm_user *vmu, int imbox, int msgnum, long duration, struct ast_vm_user *recip, char *fmt, char *dir, const char *flag, const char *dest_folder)
  5313. {
  5314. char fromdir[PATH_MAX], todir[PATH_MAX], frompath[PATH_MAX], topath[PATH_MAX];
  5315. const char *frombox = mbox(vmu, imbox);
  5316. const char *userfolder;
  5317. int recipmsgnum;
  5318. int res = 0;
  5319. ast_log(AST_LOG_NOTICE, "Copying message from %s@%s to %s@%s\n", vmu->mailbox, vmu->context, recip->mailbox, recip->context);
  5320. if (!ast_strlen_zero(flag) && !strcmp(flag, "Urgent")) { /* If urgent, copy to Urgent folder */
  5321. userfolder = "Urgent";
  5322. } else if (!ast_strlen_zero(dest_folder)) {
  5323. userfolder = dest_folder;
  5324. } else {
  5325. userfolder = "INBOX";
  5326. }
  5327. create_dirpath(todir, sizeof(todir), recip->context, recip->mailbox, userfolder);
  5328. if (!dir)
  5329. make_dir(fromdir, sizeof(fromdir), vmu->context, vmu->mailbox, frombox);
  5330. else
  5331. ast_copy_string(fromdir, dir, sizeof(fromdir));
  5332. make_file(frompath, sizeof(frompath), fromdir, msgnum);
  5333. make_dir(todir, sizeof(todir), recip->context, recip->mailbox, userfolder);
  5334. if (vm_lock_path(todir))
  5335. return ERROR_LOCK_PATH;
  5336. recipmsgnum = last_message_index(recip, todir) + 1;
  5337. if (recipmsgnum < recip->maxmsg - (imbox ? 0 : inprocess_count(vmu->mailbox, vmu->context, 0))) {
  5338. make_file(topath, sizeof(topath), todir, recipmsgnum);
  5339. #ifndef ODBC_STORAGE
  5340. if (EXISTS(fromdir, msgnum, frompath, chan ? ast_channel_language(chan) : "")) {
  5341. COPY(fromdir, msgnum, todir, recipmsgnum, recip->mailbox, recip->context, frompath, topath);
  5342. } else {
  5343. #endif
  5344. /* If we are prepending a message for ODBC, then the message already
  5345. * exists in the database, but we want to force copying from the
  5346. * filesystem (since only the FS contains the prepend). */
  5347. copy_plain_file(frompath, topath);
  5348. STORE(todir, recip->mailbox, recip->context, recipmsgnum, chan, recip, fmt, duration, NULL, NULL, NULL);
  5349. vm_delete(topath);
  5350. #ifndef ODBC_STORAGE
  5351. }
  5352. #endif
  5353. } else {
  5354. ast_log(AST_LOG_ERROR, "Recipient mailbox %s@%s is full\n", recip->mailbox, recip->context);
  5355. res = -1;
  5356. }
  5357. ast_unlock_path(todir);
  5358. if (chan) {
  5359. struct ast_party_caller *caller = ast_channel_caller(chan);
  5360. notify_new_message(chan, recip, NULL, recipmsgnum, duration, fmt,
  5361. S_COR(caller->id.number.valid, caller->id.number.str, NULL),
  5362. S_COR(caller->id.name.valid, caller->id.name.str, NULL),
  5363. flag);
  5364. }
  5365. return res;
  5366. }
  5367. #endif
  5368. #if !(defined(IMAP_STORAGE) || defined(ODBC_STORAGE))
  5369. static int messagecount(const char *mailbox_id, const char *folder)
  5370. {
  5371. char *context;
  5372. char *mailbox;
  5373. if (ast_strlen_zero(mailbox_id)
  5374. || separate_mailbox(ast_strdupa(mailbox_id), &mailbox, &context)) {
  5375. return 0;
  5376. }
  5377. return __has_voicemail(context, mailbox, folder, 0) + (folder && strcmp(folder, "INBOX") ? 0 : __has_voicemail(context, mailbox, "Urgent", 0));
  5378. }
  5379. static int __has_voicemail(const char *context, const char *mailbox, const char *folder, int shortcircuit)
  5380. {
  5381. DIR *dir;
  5382. struct dirent *de;
  5383. char fn[256];
  5384. int ret = 0;
  5385. /* If no mailbox, return immediately */
  5386. if (ast_strlen_zero(mailbox))
  5387. return 0;
  5388. if (ast_strlen_zero(folder))
  5389. folder = "INBOX";
  5390. if (ast_strlen_zero(context))
  5391. context = "default";
  5392. snprintf(fn, sizeof(fn), "%s%s/%s/%s", VM_SPOOL_DIR, context, mailbox, folder);
  5393. if (!(dir = opendir(fn)))
  5394. return 0;
  5395. while ((de = readdir(dir))) {
  5396. if (!strncasecmp(de->d_name, "msg", 3)) {
  5397. if (shortcircuit) {
  5398. ret = 1;
  5399. break;
  5400. } else if (!strncasecmp(de->d_name + 8, "txt", 3)) {
  5401. ret++;
  5402. }
  5403. }
  5404. }
  5405. closedir(dir);
  5406. return ret;
  5407. }
  5408. /**
  5409. * \brief Determines if the given folder has messages.
  5410. * \param mailbox The \@ delimited string for user\@context. If no context is found, uses 'default' for the context.
  5411. * \param folder the folder to look in
  5412. *
  5413. * This function is used when the mailbox is stored in a filesystem back end.
  5414. * This invokes the __has_voicemail(). Here we are interested in the presence of messages (> 0) only, not the actual count.
  5415. * \return 1 if the folder has one or more messages. zero otherwise.
  5416. */
  5417. static int has_voicemail(const char *mailbox, const char *folder)
  5418. {
  5419. char tmp[256], *tmp2 = tmp, *box, *context;
  5420. ast_copy_string(tmp, mailbox, sizeof(tmp));
  5421. if (ast_strlen_zero(folder)) {
  5422. folder = "INBOX";
  5423. }
  5424. while ((box = strsep(&tmp2, ",&"))) {
  5425. if ((context = strchr(box, '@')))
  5426. *context++ = '\0';
  5427. else
  5428. context = "default";
  5429. if (__has_voicemail(context, box, folder, 1))
  5430. return 1;
  5431. /* If we are checking INBOX, we should check Urgent as well */
  5432. if (!strcmp(folder, "INBOX") && __has_voicemail(context, box, "Urgent", 1)) {
  5433. return 1;
  5434. }
  5435. }
  5436. return 0;
  5437. }
  5438. static int inboxcount2(const char *mailbox, int *urgentmsgs, int *newmsgs, int *oldmsgs)
  5439. {
  5440. char tmp[256];
  5441. char *context;
  5442. /* If no mailbox, return immediately */
  5443. if (ast_strlen_zero(mailbox))
  5444. return 0;
  5445. if (newmsgs)
  5446. *newmsgs = 0;
  5447. if (oldmsgs)
  5448. *oldmsgs = 0;
  5449. if (urgentmsgs)
  5450. *urgentmsgs = 0;
  5451. if (strchr(mailbox, ',')) {
  5452. int tmpnew, tmpold, tmpurgent;
  5453. char *mb, *cur;
  5454. ast_copy_string(tmp, mailbox, sizeof(tmp));
  5455. mb = tmp;
  5456. while ((cur = strsep(&mb, ", "))) {
  5457. if (!ast_strlen_zero(cur)) {
  5458. if (inboxcount2(cur, urgentmsgs ? &tmpurgent : NULL, newmsgs ? &tmpnew : NULL, oldmsgs ? &tmpold : NULL))
  5459. return -1;
  5460. else {
  5461. if (newmsgs)
  5462. *newmsgs += tmpnew;
  5463. if (oldmsgs)
  5464. *oldmsgs += tmpold;
  5465. if (urgentmsgs)
  5466. *urgentmsgs += tmpurgent;
  5467. }
  5468. }
  5469. }
  5470. return 0;
  5471. }
  5472. ast_copy_string(tmp, mailbox, sizeof(tmp));
  5473. if ((context = strchr(tmp, '@')))
  5474. *context++ = '\0';
  5475. else
  5476. context = "default";
  5477. if (newmsgs)
  5478. *newmsgs = __has_voicemail(context, tmp, "INBOX", 0);
  5479. if (oldmsgs)
  5480. *oldmsgs = __has_voicemail(context, tmp, "Old", 0);
  5481. if (urgentmsgs)
  5482. *urgentmsgs = __has_voicemail(context, tmp, "Urgent", 0);
  5483. return 0;
  5484. }
  5485. #endif
  5486. /* Exactly the same function for file-based, ODBC-based, and IMAP-based, so why create 3 different copies? */
  5487. static int inboxcount(const char *mailbox, int *newmsgs, int *oldmsgs)
  5488. {
  5489. int urgentmsgs = 0;
  5490. int res = inboxcount2(mailbox, &urgentmsgs, newmsgs, oldmsgs);
  5491. if (newmsgs) {
  5492. *newmsgs += urgentmsgs;
  5493. }
  5494. return res;
  5495. }
  5496. static void run_externnotify(char *context, char *extension, const char *flag)
  5497. {
  5498. char arguments[255];
  5499. char ext_context[256] = "";
  5500. int newvoicemails = 0, oldvoicemails = 0, urgentvoicemails = 0;
  5501. struct ast_smdi_mwi_message *mwi_msg;
  5502. if (!ast_strlen_zero(context))
  5503. snprintf(ext_context, sizeof(ext_context), "%s@%s", extension, context);
  5504. else
  5505. ast_copy_string(ext_context, extension, sizeof(ext_context));
  5506. if (smdi_iface) {
  5507. if (ast_app_has_voicemail(ext_context, NULL))
  5508. ast_smdi_mwi_set(smdi_iface, extension);
  5509. else
  5510. ast_smdi_mwi_unset(smdi_iface, extension);
  5511. if ((mwi_msg = ast_smdi_mwi_message_wait_station(smdi_iface, SMDI_MWI_WAIT_TIMEOUT, extension))) {
  5512. ast_log(AST_LOG_ERROR, "Error executing SMDI MWI change for %s\n", extension);
  5513. if (!strncmp(mwi_msg->cause, "INV", 3))
  5514. ast_log(AST_LOG_ERROR, "Invalid MWI extension: %s\n", mwi_msg->fwd_st);
  5515. else if (!strncmp(mwi_msg->cause, "BLK", 3))
  5516. ast_log(AST_LOG_WARNING, "MWI light was already on or off for %s\n", mwi_msg->fwd_st);
  5517. ast_log(AST_LOG_WARNING, "The switch reported '%s'\n", mwi_msg->cause);
  5518. ao2_ref(mwi_msg, -1);
  5519. } else {
  5520. ast_debug(1, "Successfully executed SMDI MWI change for %s\n", extension);
  5521. }
  5522. }
  5523. if (!ast_strlen_zero(externnotify)) {
  5524. if (inboxcount2(ext_context, &urgentvoicemails, &newvoicemails, &oldvoicemails)) {
  5525. ast_log(AST_LOG_ERROR, "Problem in calculating number of voicemail messages available for extension %s\n", extension);
  5526. } else {
  5527. snprintf(arguments, sizeof(arguments), "%s %s %s %d %d %d &",
  5528. externnotify, S_OR(context, "\"\""),
  5529. extension, newvoicemails,
  5530. oldvoicemails, urgentvoicemails);
  5531. ast_debug(1, "Executing %s\n", arguments);
  5532. ast_safe_system(arguments);
  5533. }
  5534. }
  5535. }
  5536. /*!
  5537. * \brief Variables used for saving a voicemail.
  5538. *
  5539. * This includes the record gain, mode flags, and the exit context of the chanel that was used for leaving the voicemail.
  5540. */
  5541. struct leave_vm_options {
  5542. unsigned int flags;
  5543. signed char record_gain;
  5544. char *exitcontext;
  5545. };
  5546. static void generate_msg_id(char *dst)
  5547. {
  5548. /* msg id is time of msg_id generation plus an incrementing value
  5549. * called each time a new msg_id is generated. This should achieve uniqueness,
  5550. * but only in single system solutions.
  5551. */
  5552. unsigned int unique_counter = ast_atomic_fetchadd_int(&msg_id_incrementor, +1);
  5553. snprintf(dst, MSG_ID_LEN, "%ld-%08x", (long) time(NULL), unique_counter);
  5554. }
  5555. /*!
  5556. * \internal
  5557. * \brief Creates a voicemail based on a specified file to a mailbox.
  5558. * \param recdata A vm_recording_data containing filename and voicemail txt info.
  5559. * \retval -1 failure
  5560. * \retval 0 success
  5561. *
  5562. * This is installed to the app.h voicemail functions and accommodates all voicemail
  5563. * storage methods. It should probably be broken out along with leave_voicemail at
  5564. * some point in the future.
  5565. *
  5566. * This function currently only works for a single recipient and only uses the format
  5567. * specified in recording_ext.
  5568. */
  5569. static int msg_create_from_file(struct ast_vm_recording_data *recdata)
  5570. {
  5571. /* voicemail recipient structure */
  5572. struct ast_vm_user *recipient; /* points to svm once it's been created */
  5573. struct ast_vm_user svm; /* struct storing the voicemail recipient */
  5574. /* File paths */
  5575. char tmpdir[PATH_MAX]; /* directory temp files are stored in */
  5576. char tmptxtfile[PATH_MAX]; /* tmp file for voicemail txt file */
  5577. char desttxtfile[PATH_MAX]; /* final destination for txt file */
  5578. char tmpaudiofile[PATH_MAX]; /* tmp file where audio is stored */
  5579. char dir[PATH_MAX]; /* destination for tmp files on completion */
  5580. char destination[PATH_MAX]; /* destination with msgXXXX. Basically <dir>/msgXXXX */
  5581. /* stuff that only seems to be needed for IMAP */
  5582. #ifdef IMAP_STORAGE
  5583. struct vm_state *vms = NULL;
  5584. char ext_context[256] = "";
  5585. char *fmt = ast_strdupa(recdata->recording_ext);
  5586. int newmsgs = 0;
  5587. int oldmsgs = 0;
  5588. #endif
  5589. /* miscellaneous operational variables */
  5590. int res = 0; /* Used to store error codes from functions */
  5591. int txtdes /* File descriptor for the text file used to write the voicemail info */;
  5592. FILE *txt; /* FILE pointer to text file used to write the voicemail info */
  5593. char date[256]; /* string used to hold date of the voicemail (only used for ODBC) */
  5594. int msgnum; /* the 4 digit number designated to the voicemail */
  5595. int duration = 0; /* Length of the audio being recorded in seconds */
  5596. struct ast_filestream *recording_fs; /*used to read the recording to get duration data */
  5597. /* We aren't currently doing anything with category, since it comes from a channel variable and
  5598. * this function doesn't use channels, but this function could add that as an argument later. */
  5599. const char *category = NULL; /* pointless for now */
  5600. char msg_id[MSG_ID_LEN];
  5601. /* Start by checking to see if the file actually exists... */
  5602. if (!(ast_fileexists(recdata->recording_file, recdata->recording_ext, NULL))) {
  5603. ast_log(LOG_ERROR, "File: %s not found.\n", recdata->recording_file);
  5604. return -1;
  5605. }
  5606. if (!(recipient = find_user(&svm, recdata->context, recdata->mailbox))) {
  5607. ast_log(LOG_ERROR, "No entry in voicemail config file for '%s@%s'\n", recdata->mailbox, recdata->context);
  5608. return -1;
  5609. }
  5610. /* determine duration in seconds */
  5611. if ((recording_fs = ast_readfile(recdata->recording_file, recdata->recording_ext, NULL, 0, 0, VOICEMAIL_DIR_MODE))) {
  5612. if (!ast_seekstream(recording_fs, 0, SEEK_END)) {
  5613. long framelength = ast_tellstream(recording_fs);
  5614. int sample_rate = ast_ratestream(recording_fs);
  5615. if (sample_rate) {
  5616. duration = (int) (framelength / sample_rate);
  5617. } else {
  5618. ast_log(LOG_ERROR,"Unable to determine sample rate of recording %s\n", recdata->recording_file);
  5619. }
  5620. }
  5621. ast_closestream(recording_fs);
  5622. }
  5623. /* If the duration was below the minimum duration for the user, let's just drop the whole thing now */
  5624. if (duration < recipient->minsecs) {
  5625. ast_log(LOG_NOTICE, "Copying recording to voicemail %s@%s skipped because duration was shorter than "
  5626. "minmessage of recipient\n", recdata->mailbox, recdata->context);
  5627. return -1;
  5628. }
  5629. /* Note that this number must be dropped back to a net sum of zero before returning from this function */
  5630. if ((res = create_dirpath(tmpdir, sizeof(tmpdir), recipient->context, recdata->mailbox, "tmp"))) {
  5631. ast_log(LOG_ERROR, "Failed to make directory.\n");
  5632. }
  5633. snprintf(tmptxtfile, sizeof(tmptxtfile), "%s/XXXXXX", tmpdir);
  5634. txtdes = mkstemp(tmptxtfile);
  5635. if (txtdes < 0) {
  5636. chmod(tmptxtfile, VOICEMAIL_FILE_MODE & ~my_umask);
  5637. /* Something screwed up. Abort. */
  5638. ast_log(AST_LOG_ERROR, "Unable to create message file: %s\n", strerror(errno));
  5639. free_user(recipient);
  5640. return -1;
  5641. }
  5642. /* Store information */
  5643. txt = fdopen(txtdes, "w+");
  5644. if (txt) {
  5645. generate_msg_id(msg_id);
  5646. get_date(date, sizeof(date));
  5647. fprintf(txt,
  5648. ";\n"
  5649. "; Message Information file\n"
  5650. ";\n"
  5651. "[message]\n"
  5652. "origmailbox=%s\n"
  5653. "context=%s\n"
  5654. "macrocontext=%s\n"
  5655. "exten=%s\n"
  5656. "rdnis=Unknown\n"
  5657. "priority=%d\n"
  5658. "callerchan=%s\n"
  5659. "callerid=%s\n"
  5660. "origdate=%s\n"
  5661. "origtime=%ld\n"
  5662. "category=%s\n"
  5663. "msg_id=%s\n"
  5664. "flag=\n" /* flags not supported in copy from file yet */
  5665. "duration=%d\n", /* Don't have any reliable way to get duration of file. */
  5666. recdata->mailbox,
  5667. S_OR(recdata->call_context, ""),
  5668. S_OR(recdata->call_macrocontext, ""),
  5669. S_OR(recdata->call_extension, ""),
  5670. recdata->call_priority,
  5671. S_OR(recdata->call_callerchan, "Unknown"),
  5672. S_OR(recdata->call_callerid, "Unknown"),
  5673. date, (long) time(NULL),
  5674. S_OR(category, ""),
  5675. msg_id,
  5676. duration);
  5677. /* Since we are recording from a file, we shouldn't need to do anything else with
  5678. * this txt file */
  5679. fclose(txt);
  5680. } else {
  5681. ast_log(LOG_WARNING, "Error opening text file for output\n");
  5682. if (ast_check_realtime("voicemail_data")) {
  5683. ast_destroy_realtime("voicemail_data", "filename", tmptxtfile, SENTINEL);
  5684. }
  5685. free_user(recipient);
  5686. return -1;
  5687. }
  5688. /* At this point, the actual creation of a voicemail message should be finished.
  5689. * Now we just need to copy the files being recorded into the receiving folder. */
  5690. create_dirpath(dir, sizeof(dir), recipient->context, recipient->mailbox, recdata->folder);
  5691. #ifdef IMAP_STORAGE
  5692. /* make recipient info into an inboxcount friendly string */
  5693. snprintf(ext_context, sizeof(ext_context), "%s@%s", recipient->mailbox, recipient->context);
  5694. /* Is ext a mailbox? */
  5695. /* must open stream for this user to get info! */
  5696. res = inboxcount(ext_context, &newmsgs, &oldmsgs);
  5697. if (res < 0) {
  5698. ast_log(LOG_NOTICE, "Can not leave voicemail, unable to count messages\n");
  5699. free_user(recipient);
  5700. unlink(tmptxtfile);
  5701. return -1;
  5702. }
  5703. if (!(vms = get_vm_state_by_mailbox(recipient->mailbox, recipient->context, 0))) {
  5704. /* It is possible under certain circumstances that inboxcount did not
  5705. * create a vm_state when it was needed. This is a catchall which will
  5706. * rarely be used.
  5707. */
  5708. if (!(vms = create_vm_state_from_user(recipient))) {
  5709. ast_log(LOG_ERROR, "Couldn't allocate necessary space\n");
  5710. free_user(recipient);
  5711. unlink(tmptxtfile);
  5712. return -1;
  5713. }
  5714. }
  5715. vms->newmessages++;
  5716. /* here is a big difference! We add one to it later */
  5717. msgnum = newmsgs + oldmsgs;
  5718. ast_debug(3, "Messagecount set to %d\n", msgnum);
  5719. snprintf(destination, sizeof(destination), "%simap/msg%s%04d", VM_SPOOL_DIR, recipient->mailbox, msgnum);
  5720. /* Check to see if we have enough room in the mailbox. If not, spit out an error and end
  5721. * Note that imap_check_limits raises inprocess_count if successful */
  5722. if ((res = imap_check_limits(NULL, vms, recipient, msgnum))) {
  5723. ast_log(LOG_NOTICE, "Didn't copy to voicemail. Mailbox for %s@%s is full.\n", recipient->mailbox, recipient->context);
  5724. inprocess_count(recipient->mailbox, recipient->context, -1);
  5725. free_user(recipient);
  5726. unlink(tmptxtfile);
  5727. return -1;
  5728. }
  5729. #else
  5730. /* Check to see if the mailbox is full for ODBC/File storage */
  5731. ast_debug(3, "mailbox = %d : inprocess = %d\n", count_messages(recipient, dir),
  5732. inprocess_count(recipient->mailbox, recipient->context, 0));
  5733. if (count_messages(recipient, dir) > recipient->maxmsg - inprocess_count(recipient->mailbox, recipient->context, +1)) {
  5734. ast_log(AST_LOG_WARNING, "Didn't copy to voicemail. Mailbox for %s@%s is full.\n", recipient->mailbox, recipient->context);
  5735. inprocess_count(recipient->mailbox, recipient->context, -1);
  5736. free_user(recipient);
  5737. unlink(tmptxtfile);
  5738. return -1;
  5739. }
  5740. msgnum = last_message_index(recipient, dir) + 1;
  5741. #endif
  5742. /* Lock the directory receiving the voicemail since we want it to still exist when we attempt to copy the voicemail.
  5743. * We need to unlock it before we return. */
  5744. if (vm_lock_path(dir)) {
  5745. ast_log(LOG_ERROR, "Couldn't lock directory %s. Voicemail will be lost.\n", dir);
  5746. /* Delete files */
  5747. ast_filedelete(tmptxtfile, NULL);
  5748. unlink(tmptxtfile);
  5749. free_user(recipient);
  5750. return -1;
  5751. }
  5752. make_file(destination, sizeof(destination), dir, msgnum);
  5753. make_file(tmpaudiofile, sizeof(tmpaudiofile), tmpdir, msgnum);
  5754. if (ast_filecopy(recdata->recording_file, tmpaudiofile, recdata->recording_ext)) {
  5755. ast_log(LOG_ERROR, "Audio file failed to copy to tmp dir. Probably low disk space.\n");
  5756. inprocess_count(recipient->mailbox, recipient->context, -1);
  5757. ast_unlock_path(dir);
  5758. free_user(recipient);
  5759. unlink(tmptxtfile);
  5760. return -1;
  5761. }
  5762. /* Alright, try to copy to the destination folder now. */
  5763. if (ast_filerename(tmpaudiofile, destination, recdata->recording_ext)) {
  5764. ast_log(LOG_ERROR, "Audio file failed to move to destination directory. Permissions/Overlap?\n");
  5765. inprocess_count(recipient->mailbox, recipient->context, -1);
  5766. ast_unlock_path(dir);
  5767. free_user(recipient);
  5768. unlink(tmptxtfile);
  5769. return -1;
  5770. }
  5771. snprintf(desttxtfile, sizeof(desttxtfile), "%s.txt", destination);
  5772. rename(tmptxtfile, desttxtfile);
  5773. if (chmod(desttxtfile, VOICEMAIL_FILE_MODE) < 0) {
  5774. ast_log(AST_LOG_ERROR, "Couldn't set permissions on voicemail text file %s: %s", desttxtfile, strerror(errno));
  5775. }
  5776. ast_unlock_path(dir);
  5777. inprocess_count(recipient->mailbox, recipient->context, -1);
  5778. /* If we copied something, we should store it either to ODBC or IMAP if we are using those. The STORE macro allows us
  5779. * to do both with one line and is also safe to use with file storage mode. Also, if we are using ODBC, now is a good
  5780. * time to create the voicemail database entry. */
  5781. if (ast_fileexists(destination, NULL, NULL) > 0) {
  5782. if (ast_check_realtime("voicemail_data")) {
  5783. get_date(date, sizeof(date));
  5784. ast_store_realtime("voicemail_data",
  5785. "origmailbox", recdata->mailbox,
  5786. "context", S_OR(recdata->context, ""),
  5787. "macrocontext", S_OR(recdata->call_macrocontext, ""),
  5788. "exten", S_OR(recdata->call_extension, ""),
  5789. "priority", recdata->call_priority,
  5790. "callerchan", S_OR(recdata->call_callerchan, "Unknown"),
  5791. "callerid", S_OR(recdata->call_callerid, "Unknown"),
  5792. "origdate", date,
  5793. "origtime", time(NULL),
  5794. "category", S_OR(category, ""),
  5795. "filename", tmptxtfile,
  5796. "duration", duration,
  5797. SENTINEL);
  5798. }
  5799. STORE(dir, recipient->mailbox, recipient->context, msgnum, NULL, recipient, fmt, 0, vms, "", msg_id);
  5800. notify_new_state(recipient);
  5801. }
  5802. free_user(recipient);
  5803. unlink(tmptxtfile);
  5804. return 0;
  5805. }
  5806. /*!
  5807. * \brief Prompts the user and records a voicemail to a mailbox.
  5808. * \param chan
  5809. * \param ext
  5810. * \param options OPT_BUSY_GREETING, OPT_UNAVAIL_GREETING
  5811. *
  5812. *
  5813. *
  5814. * \return zero on success, -1 on error.
  5815. */
  5816. static int leave_voicemail(struct ast_channel *chan, char *ext, struct leave_vm_options *options)
  5817. {
  5818. #ifdef IMAP_STORAGE
  5819. int newmsgs, oldmsgs;
  5820. #else
  5821. char urgdir[PATH_MAX];
  5822. #endif
  5823. char txtfile[PATH_MAX];
  5824. char tmptxtfile[PATH_MAX];
  5825. struct vm_state *vms = NULL;
  5826. char callerid[256];
  5827. FILE *txt;
  5828. char date[256];
  5829. int txtdes;
  5830. int res = 0;
  5831. int msgnum;
  5832. int duration = 0;
  5833. int sound_duration = 0;
  5834. int ausemacro = 0;
  5835. int ousemacro = 0;
  5836. int ouseexten = 0;
  5837. char tmpdur[16];
  5838. char priority[16];
  5839. char origtime[16];
  5840. char dir[PATH_MAX];
  5841. char tmpdir[PATH_MAX];
  5842. char fn[PATH_MAX];
  5843. char prefile[PATH_MAX] = "";
  5844. char tempfile[PATH_MAX] = "";
  5845. char ext_context[256] = "";
  5846. char fmt[80];
  5847. char *context;
  5848. char ecodes[17] = "#";
  5849. struct ast_str *tmp = ast_str_create(16);
  5850. char *tmpptr;
  5851. struct ast_vm_user *vmu;
  5852. struct ast_vm_user svm;
  5853. const char *category = NULL;
  5854. const char *code;
  5855. const char *alldtmf = "0123456789ABCD*#";
  5856. char flag[80];
  5857. if (!tmp) {
  5858. return -1;
  5859. }
  5860. ast_str_set(&tmp, 0, "%s", ext);
  5861. ext = ast_str_buffer(tmp);
  5862. if ((context = strchr(ext, '@'))) {
  5863. *context++ = '\0';
  5864. tmpptr = strchr(context, '&');
  5865. } else {
  5866. tmpptr = strchr(ext, '&');
  5867. }
  5868. if (tmpptr)
  5869. *tmpptr++ = '\0';
  5870. ast_channel_lock(chan);
  5871. if ((category = pbx_builtin_getvar_helper(chan, "VM_CATEGORY"))) {
  5872. category = ast_strdupa(category);
  5873. }
  5874. ast_channel_unlock(chan);
  5875. if (ast_test_flag(options, OPT_MESSAGE_Urgent)) {
  5876. ast_copy_string(flag, "Urgent", sizeof(flag));
  5877. } else if (ast_test_flag(options, OPT_MESSAGE_PRIORITY)) {
  5878. ast_copy_string(flag, "PRIORITY", sizeof(flag));
  5879. } else {
  5880. flag[0] = '\0';
  5881. }
  5882. ast_debug(3, "Before find_user\n");
  5883. if (!(vmu = find_user(&svm, context, ext))) {
  5884. ast_log(AST_LOG_WARNING, "No entry in voicemail config file for '%s'\n", ext);
  5885. pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
  5886. ast_free(tmp);
  5887. return res;
  5888. }
  5889. /* Setup pre-file if appropriate */
  5890. if (strcmp(vmu->context, "default"))
  5891. snprintf(ext_context, sizeof(ext_context), "%s@%s", ext, vmu->context);
  5892. else
  5893. ast_copy_string(ext_context, vmu->mailbox, sizeof(ext_context));
  5894. /* Set the path to the prefile. Will be one of
  5895. VM_SPOOL_DIRcontext/ext/busy
  5896. VM_SPOOL_DIRcontext/ext/unavail
  5897. Depending on the flag set in options.
  5898. */
  5899. if (ast_test_flag(options, OPT_BUSY_GREETING)) {
  5900. snprintf(prefile, sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, ext);
  5901. } else if (ast_test_flag(options, OPT_UNAVAIL_GREETING)) {
  5902. snprintf(prefile, sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, ext);
  5903. }
  5904. /* Set the path to the tmpfile as
  5905. VM_SPOOL_DIR/context/ext/temp
  5906. and attempt to create the folder structure.
  5907. */
  5908. snprintf(tempfile, sizeof(tempfile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, ext);
  5909. if ((res = create_dirpath(tmpdir, sizeof(tmpdir), vmu->context, ext, "tmp"))) {
  5910. ast_log(AST_LOG_WARNING, "Failed to make directory (%s)\n", tempfile);
  5911. ast_free(tmp);
  5912. return -1;
  5913. }
  5914. RETRIEVE(tempfile, -1, vmu->mailbox, vmu->context);
  5915. if (ast_fileexists(tempfile, NULL, NULL) > 0)
  5916. ast_copy_string(prefile, tempfile, sizeof(prefile));
  5917. DISPOSE(tempfile, -1);
  5918. /* It's easier just to try to make it than to check for its existence */
  5919. #ifndef IMAP_STORAGE
  5920. create_dirpath(dir, sizeof(dir), vmu->context, ext, "INBOX");
  5921. #else
  5922. snprintf(dir, sizeof(dir), "%simap", VM_SPOOL_DIR);
  5923. if (mkdir(dir, VOICEMAIL_DIR_MODE) && errno != EEXIST) {
  5924. ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dir, strerror(errno));
  5925. }
  5926. #endif
  5927. /* Check current or macro-calling context for special extensions */
  5928. if (ast_test_flag(vmu, VM_OPERATOR)) {
  5929. if (!ast_strlen_zero(vmu->exit)) {
  5930. if (ast_exists_extension(chan, vmu->exit, "o", 1,
  5931. S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL))) {
  5932. strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
  5933. ouseexten = 1;
  5934. }
  5935. } else if (ast_exists_extension(chan, ast_channel_context(chan), "o", 1,
  5936. S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL))) {
  5937. strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
  5938. ouseexten = 1;
  5939. } else if (!ast_strlen_zero(ast_channel_macrocontext(chan))
  5940. && ast_exists_extension(chan, ast_channel_macrocontext(chan), "o", 1,
  5941. S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL))) {
  5942. strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
  5943. ousemacro = 1;
  5944. }
  5945. }
  5946. if (!ast_strlen_zero(vmu->exit)) {
  5947. if (ast_exists_extension(chan, vmu->exit, "a", 1,
  5948. S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL))) {
  5949. strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
  5950. }
  5951. } else if (ast_exists_extension(chan, ast_channel_context(chan), "a", 1,
  5952. S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL))) {
  5953. strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
  5954. } else if (!ast_strlen_zero(ast_channel_macrocontext(chan))
  5955. && ast_exists_extension(chan, ast_channel_macrocontext(chan), "a", 1,
  5956. S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL))) {
  5957. strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
  5958. ausemacro = 1;
  5959. }
  5960. if (ast_test_flag(options, OPT_DTMFEXIT)) {
  5961. for (code = alldtmf; *code; code++) {
  5962. char e[2] = "";
  5963. e[0] = *code;
  5964. if (strchr(ecodes, e[0]) == NULL
  5965. && ast_canmatch_extension(chan,
  5966. (!ast_strlen_zero(options->exitcontext) ? options->exitcontext : ast_channel_context(chan)),
  5967. e, 1, S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL))) {
  5968. strncat(ecodes, e, sizeof(ecodes) - strlen(ecodes) - 1);
  5969. }
  5970. }
  5971. }
  5972. /* Play the beginning intro if desired */
  5973. if (!ast_strlen_zero(prefile)) {
  5974. #ifdef ODBC_STORAGE
  5975. int success =
  5976. #endif
  5977. RETRIEVE(prefile, -1, ext, context);
  5978. if (ast_fileexists(prefile, NULL, NULL) > 0) {
  5979. if (ast_streamfile(chan, prefile, ast_channel_language(chan)) > -1)
  5980. res = ast_waitstream(chan, ecodes);
  5981. #ifdef ODBC_STORAGE
  5982. if (success == -1) {
  5983. /* We couldn't retrieve the file from the database, but we found it on the file system. Let's put it in the database. */
  5984. ast_debug(1, "Greeting not retrieved from database, but found in file storage. Inserting into database\n");
  5985. store_file(prefile, vmu->mailbox, vmu->context, -1);
  5986. }
  5987. #endif
  5988. } else {
  5989. ast_debug(1, "%s doesn't exist, doing what we can\n", prefile);
  5990. res = invent_message(chan, vmu->context, ext, ast_test_flag(options, OPT_BUSY_GREETING), ecodes);
  5991. }
  5992. DISPOSE(prefile, -1);
  5993. if (res < 0) {
  5994. ast_debug(1, "Hang up during prefile playback\n");
  5995. free_user(vmu);
  5996. pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
  5997. ast_free(tmp);
  5998. return -1;
  5999. }
  6000. }
  6001. if (res == '#') {
  6002. /* On a '#' we skip the instructions */
  6003. ast_set_flag(options, OPT_SILENT);
  6004. res = 0;
  6005. }
  6006. /* If maxmsg is zero, act as a "greetings only" voicemail: Exit successfully without recording */
  6007. if (vmu->maxmsg == 0) {
  6008. ast_debug(3, "Greetings only VM (maxmsg=0), Skipping voicemail recording\n");
  6009. pbx_builtin_setvar_helper(chan, "VMSTATUS", "SUCCESS");
  6010. goto leave_vm_out;
  6011. }
  6012. if (!res && !ast_test_flag(options, OPT_SILENT)) {
  6013. res = ast_stream_and_wait(chan, INTRO, ecodes);
  6014. if (res == '#') {
  6015. ast_set_flag(options, OPT_SILENT);
  6016. res = 0;
  6017. }
  6018. }
  6019. if (res > 0)
  6020. ast_stopstream(chan);
  6021. /* Check for a '*' here in case the caller wants to escape from voicemail to something
  6022. other than the operator -- an automated attendant or mailbox login for example */
  6023. if (res == '*') {
  6024. ast_channel_exten_set(chan, "a");
  6025. if (!ast_strlen_zero(vmu->exit)) {
  6026. ast_channel_context_set(chan, vmu->exit);
  6027. } else if (ausemacro && !ast_strlen_zero(ast_channel_macrocontext(chan))) {
  6028. ast_channel_context_set(chan, ast_channel_macrocontext(chan));
  6029. }
  6030. ast_channel_priority_set(chan, 0);
  6031. free_user(vmu);
  6032. pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
  6033. ast_free(tmp);
  6034. return 0;
  6035. }
  6036. /* Check for a '0' here */
  6037. if (ast_test_flag(vmu, VM_OPERATOR) && res == '0') {
  6038. transfer:
  6039. if (ouseexten || ousemacro) {
  6040. ast_channel_exten_set(chan, "o");
  6041. if (!ast_strlen_zero(vmu->exit)) {
  6042. ast_channel_context_set(chan, vmu->exit);
  6043. } else if (ousemacro && !ast_strlen_zero(ast_channel_macrocontext(chan))) {
  6044. ast_channel_context_set(chan, ast_channel_macrocontext(chan));
  6045. }
  6046. ast_play_and_wait(chan, "transfer");
  6047. ast_channel_priority_set(chan, 0);
  6048. free_user(vmu);
  6049. pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
  6050. }
  6051. ast_free(tmp);
  6052. return OPERATOR_EXIT;
  6053. }
  6054. /* Allow all other digits to exit Voicemail and return to the dialplan */
  6055. if (ast_test_flag(options, OPT_DTMFEXIT) && res > 0) {
  6056. if (!ast_strlen_zero(options->exitcontext)) {
  6057. ast_channel_context_set(chan, options->exitcontext);
  6058. }
  6059. free_user(vmu);
  6060. ast_free(tmp);
  6061. pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
  6062. return res;
  6063. }
  6064. if (res < 0) {
  6065. free_user(vmu);
  6066. pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
  6067. ast_free(tmp);
  6068. return -1;
  6069. }
  6070. /* The meat of recording the message... All the announcements and beeps have been played*/
  6071. ast_copy_string(fmt, vmfmts, sizeof(fmt));
  6072. if (!ast_strlen_zero(fmt)) {
  6073. char msg_id[MSG_ID_LEN] = "";
  6074. msgnum = 0;
  6075. #ifdef IMAP_STORAGE
  6076. /* Is ext a mailbox? */
  6077. /* must open stream for this user to get info! */
  6078. res = inboxcount(ext_context, &newmsgs, &oldmsgs);
  6079. if (res < 0) {
  6080. ast_log(AST_LOG_NOTICE, "Can not leave voicemail, unable to count messages\n");
  6081. ast_free(tmp);
  6082. return -1;
  6083. }
  6084. if (!(vms = get_vm_state_by_mailbox(ext, context, 0))) {
  6085. /* It is possible under certain circumstances that inboxcount did not
  6086. * create a vm_state when it was needed. This is a catchall which will
  6087. * rarely be used.
  6088. */
  6089. if (!(vms = create_vm_state_from_user(vmu))) {
  6090. ast_log(AST_LOG_ERROR, "Couldn't allocate necessary space\n");
  6091. ast_free(tmp);
  6092. return -1;
  6093. }
  6094. }
  6095. vms->newmessages++;
  6096. /* here is a big difference! We add one to it later */
  6097. msgnum = newmsgs + oldmsgs;
  6098. ast_debug(3, "Messagecount set to %d\n", msgnum);
  6099. snprintf(fn, sizeof(fn), "%simap/msg%s%04d", VM_SPOOL_DIR, vmu->mailbox, msgnum);
  6100. /* set variable for compatibility */
  6101. pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", "IMAP_STORAGE");
  6102. if ((res = imap_check_limits(chan, vms, vmu, msgnum))) {
  6103. goto leave_vm_out;
  6104. }
  6105. #else
  6106. if (count_messages(vmu, dir) >= vmu->maxmsg - inprocess_count(vmu->mailbox, vmu->context, +1)) {
  6107. res = ast_streamfile(chan, "vm-mailboxfull", ast_channel_language(chan));
  6108. if (!res)
  6109. res = ast_waitstream(chan, "");
  6110. ast_log(AST_LOG_WARNING, "No more messages possible\n");
  6111. pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
  6112. inprocess_count(vmu->mailbox, vmu->context, -1);
  6113. goto leave_vm_out;
  6114. }
  6115. #endif
  6116. snprintf(tmptxtfile, sizeof(tmptxtfile), "%s/XXXXXX", tmpdir);
  6117. txtdes = mkstemp(tmptxtfile);
  6118. chmod(tmptxtfile, VOICEMAIL_FILE_MODE & ~my_umask);
  6119. if (txtdes < 0) {
  6120. res = ast_streamfile(chan, "vm-mailboxfull", ast_channel_language(chan));
  6121. if (!res)
  6122. res = ast_waitstream(chan, "");
  6123. ast_log(AST_LOG_ERROR, "Unable to create message file: %s\n", strerror(errno));
  6124. pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
  6125. inprocess_count(vmu->mailbox, vmu->context, -1);
  6126. goto leave_vm_out;
  6127. }
  6128. /* Now play the beep once we have the message number for our next message. */
  6129. if (res >= 0) {
  6130. /* Unless we're *really* silent, try to send the beep */
  6131. res = ast_stream_and_wait(chan, "beep", "");
  6132. }
  6133. /* Store information in real-time storage */
  6134. if (ast_check_realtime("voicemail_data")) {
  6135. snprintf(priority, sizeof(priority), "%d", ast_channel_priority(chan));
  6136. snprintf(origtime, sizeof(origtime), "%ld", (long) time(NULL));
  6137. get_date(date, sizeof(date));
  6138. ast_callerid_merge(callerid, sizeof(callerid),
  6139. S_COR(ast_channel_caller(chan)->id.name.valid, ast_channel_caller(chan)->id.name.str, NULL),
  6140. S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL),
  6141. "Unknown");
  6142. ast_store_realtime("voicemail_data",
  6143. "origmailbox", ext,
  6144. "context", ast_channel_context(chan),
  6145. "macrocontext", ast_channel_macrocontext(chan),
  6146. "exten", ast_channel_exten(chan),
  6147. "priority", priority,
  6148. "callerchan", ast_channel_name(chan),
  6149. "callerid", callerid,
  6150. "origdate", date,
  6151. "origtime", origtime,
  6152. "category", S_OR(category, ""),
  6153. "filename", tmptxtfile,
  6154. SENTINEL);
  6155. }
  6156. /* Store information */
  6157. txt = fdopen(txtdes, "w+");
  6158. if (txt) {
  6159. generate_msg_id(msg_id);
  6160. get_date(date, sizeof(date));
  6161. ast_callerid_merge(callerid, sizeof(callerid),
  6162. S_COR(ast_channel_caller(chan)->id.name.valid, ast_channel_caller(chan)->id.name.str, NULL),
  6163. S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL),
  6164. "Unknown");
  6165. fprintf(txt,
  6166. ";\n"
  6167. "; Message Information file\n"
  6168. ";\n"
  6169. "[message]\n"
  6170. "origmailbox=%s\n"
  6171. "context=%s\n"
  6172. "macrocontext=%s\n"
  6173. "exten=%s\n"
  6174. "rdnis=%s\n"
  6175. "priority=%d\n"
  6176. "callerchan=%s\n"
  6177. "callerid=%s\n"
  6178. "origdate=%s\n"
  6179. "origtime=%ld\n"
  6180. "category=%s\n"
  6181. "msg_id=%s\n",
  6182. ext,
  6183. ast_channel_context(chan),
  6184. ast_channel_macrocontext(chan),
  6185. ast_channel_exten(chan),
  6186. S_COR(ast_channel_redirecting(chan)->from.number.valid,
  6187. ast_channel_redirecting(chan)->from.number.str, "unknown"),
  6188. ast_channel_priority(chan),
  6189. ast_channel_name(chan),
  6190. callerid,
  6191. date, (long) time(NULL),
  6192. category ? category : "",
  6193. msg_id);
  6194. } else {
  6195. ast_log(AST_LOG_WARNING, "Error opening text file for output\n");
  6196. inprocess_count(vmu->mailbox, vmu->context, -1);
  6197. if (ast_check_realtime("voicemail_data")) {
  6198. ast_destroy_realtime("voicemail_data", "filename", tmptxtfile, SENTINEL);
  6199. }
  6200. res = ast_streamfile(chan, "vm-mailboxfull", ast_channel_language(chan));
  6201. goto leave_vm_out;
  6202. }
  6203. res = play_record_review(chan, NULL, tmptxtfile, vmu->maxsecs, fmt, 1, vmu, &duration, &sound_duration, NULL, options->record_gain, vms, flag, msg_id);
  6204. if (txt) {
  6205. fprintf(txt, "flag=%s\n", flag);
  6206. if (sound_duration < vmu->minsecs) {
  6207. fclose(txt);
  6208. ast_verb(3, "Recording was %d seconds long but needs to be at least %d - abandoning\n", sound_duration, vmu->minsecs);
  6209. ast_filedelete(tmptxtfile, NULL);
  6210. unlink(tmptxtfile);
  6211. if (ast_check_realtime("voicemail_data")) {
  6212. ast_destroy_realtime("voicemail_data", "filename", tmptxtfile, SENTINEL);
  6213. }
  6214. inprocess_count(vmu->mailbox, vmu->context, -1);
  6215. } else {
  6216. fprintf(txt, "duration=%d\n", duration);
  6217. fclose(txt);
  6218. if (vm_lock_path(dir)) {
  6219. ast_log(AST_LOG_ERROR, "Couldn't lock directory %s. Voicemail will be lost.\n", dir);
  6220. /* Delete files */
  6221. ast_filedelete(tmptxtfile, NULL);
  6222. unlink(tmptxtfile);
  6223. inprocess_count(vmu->mailbox, vmu->context, -1);
  6224. } else if (ast_fileexists(tmptxtfile, NULL, NULL) <= 0) {
  6225. ast_debug(1, "The recorded media file is gone, so we should remove the .txt file too!\n");
  6226. unlink(tmptxtfile);
  6227. ast_unlock_path(dir);
  6228. inprocess_count(vmu->mailbox, vmu->context, -1);
  6229. if (ast_check_realtime("voicemail_data")) {
  6230. ast_destroy_realtime("voicemail_data", "filename", tmptxtfile, SENTINEL);
  6231. }
  6232. } else {
  6233. #ifndef IMAP_STORAGE
  6234. msgnum = last_message_index(vmu, dir) + 1;
  6235. #endif
  6236. make_file(fn, sizeof(fn), dir, msgnum);
  6237. /* assign a variable with the name of the voicemail file */
  6238. #ifndef IMAP_STORAGE
  6239. pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", fn);
  6240. #else
  6241. pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", "IMAP_STORAGE");
  6242. #endif
  6243. snprintf(txtfile, sizeof(txtfile), "%s.txt", fn);
  6244. ast_filerename(tmptxtfile, fn, NULL);
  6245. rename(tmptxtfile, txtfile);
  6246. inprocess_count(vmu->mailbox, vmu->context, -1);
  6247. /* Properly set permissions on voicemail text descriptor file.
  6248. Unfortunately mkstemp() makes this file 0600 on most unix systems. */
  6249. if (chmod(txtfile, VOICEMAIL_FILE_MODE) < 0)
  6250. ast_log(AST_LOG_ERROR, "Couldn't set permissions on voicemail text file %s: %s", txtfile, strerror(errno));
  6251. ast_unlock_path(dir);
  6252. if (ast_check_realtime("voicemail_data")) {
  6253. snprintf(tmpdur, sizeof(tmpdur), "%d", duration);
  6254. ast_update_realtime("voicemail_data", "filename", tmptxtfile, "filename", fn, "duration", tmpdur, SENTINEL);
  6255. }
  6256. /* We must store the file first, before copying the message, because
  6257. * ODBC storage does the entire copy with SQL.
  6258. */
  6259. if (ast_fileexists(fn, NULL, NULL) > 0) {
  6260. STORE(dir, vmu->mailbox, vmu->context, msgnum, chan, vmu, fmt, duration, vms, flag, msg_id);
  6261. }
  6262. /* Are there to be more recipients of this message? */
  6263. while (tmpptr) {
  6264. struct ast_vm_user recipu, *recip;
  6265. char *exten, *cntx;
  6266. exten = strsep(&tmpptr, "&");
  6267. cntx = strchr(exten, '@');
  6268. if (cntx) {
  6269. *cntx = '\0';
  6270. cntx++;
  6271. }
  6272. if ((recip = find_user(&recipu, cntx, exten))) {
  6273. copy_message(chan, vmu, 0, msgnum, duration, recip, fmt, dir, flag, NULL);
  6274. free_user(recip);
  6275. }
  6276. }
  6277. #ifndef IMAP_STORAGE
  6278. if (!ast_strlen_zero(flag) && !strcmp(flag, "Urgent")) { /* If this is an Urgent message */
  6279. /* Move the message from INBOX to Urgent folder if this is urgent! */
  6280. char sfn[PATH_MAX];
  6281. char dfn[PATH_MAX];
  6282. int x;
  6283. /* It's easier just to try to make it than to check for its existence */
  6284. create_dirpath(urgdir, sizeof(urgdir), vmu->context, ext, "Urgent");
  6285. x = last_message_index(vmu, urgdir) + 1;
  6286. make_file(sfn, sizeof(sfn), dir, msgnum);
  6287. make_file(dfn, sizeof(dfn), urgdir, x);
  6288. ast_debug(5, "Created an Urgent message, moving file from %s to %s.\n", sfn, dfn);
  6289. RENAME(dir, msgnum, vmu->mailbox, vmu->context, urgdir, x, sfn, dfn);
  6290. /* Notification must happen for this new message in Urgent folder, not INBOX */
  6291. ast_copy_string(fn, dfn, sizeof(fn));
  6292. msgnum = x;
  6293. }
  6294. #endif
  6295. /* Notification needs to happen after the copy, though. */
  6296. if (ast_fileexists(fn, NULL, NULL)) {
  6297. #ifdef IMAP_STORAGE
  6298. notify_new_message(chan, vmu, vms, msgnum, duration, fmt,
  6299. S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL),
  6300. S_COR(ast_channel_caller(chan)->id.name.valid, ast_channel_caller(chan)->id.name.str, NULL),
  6301. flag);
  6302. #else
  6303. notify_new_message(chan, vmu, NULL, msgnum, duration, fmt,
  6304. S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL),
  6305. S_COR(ast_channel_caller(chan)->id.name.valid, ast_channel_caller(chan)->id.name.str, NULL),
  6306. flag);
  6307. #endif
  6308. }
  6309. /* Disposal needs to happen after the optional move and copy */
  6310. if (ast_fileexists(fn, NULL, NULL)) {
  6311. DISPOSE(dir, msgnum);
  6312. }
  6313. }
  6314. }
  6315. } else {
  6316. inprocess_count(vmu->mailbox, vmu->context, -1);
  6317. }
  6318. if (res == '0') {
  6319. goto transfer;
  6320. } else if (res > 0 && res != 't')
  6321. res = 0;
  6322. if (sound_duration < vmu->minsecs)
  6323. /* XXX We should really give a prompt too short/option start again, with leave_vm_out called only after a timeout XXX */
  6324. pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
  6325. else
  6326. pbx_builtin_setvar_helper(chan, "VMSTATUS", "SUCCESS");
  6327. } else
  6328. ast_log(AST_LOG_WARNING, "No format for saving voicemail?\n");
  6329. leave_vm_out:
  6330. free_user(vmu);
  6331. #ifdef IMAP_STORAGE
  6332. /* expunge message - use UID Expunge if supported on IMAP server*/
  6333. ast_debug(3, "*** Checking if we can expunge, expungeonhangup set to %d\n", expungeonhangup);
  6334. if (expungeonhangup == 1) {
  6335. ast_mutex_lock(&vms->lock);
  6336. #ifdef HAVE_IMAP_TK2006
  6337. if (LEVELUIDPLUS (vms->mailstream)) {
  6338. mail_expunge_full(vms->mailstream, NIL, EX_UID);
  6339. } else
  6340. #endif
  6341. mail_expunge(vms->mailstream);
  6342. ast_mutex_unlock(&vms->lock);
  6343. }
  6344. #endif
  6345. ast_free(tmp);
  6346. return res;
  6347. }
  6348. #if !defined(IMAP_STORAGE)
  6349. static int resequence_mailbox(struct ast_vm_user *vmu, char *dir, int stopcount)
  6350. {
  6351. /* we know the actual number of messages, so stop process when number is hit */
  6352. int x, dest;
  6353. char sfn[PATH_MAX];
  6354. char dfn[PATH_MAX];
  6355. if (vm_lock_path(dir)) {
  6356. return ERROR_LOCK_PATH;
  6357. }
  6358. for (x = 0, dest = 0; dest != stopcount && x < vmu->maxmsg + 10; x++) {
  6359. make_file(sfn, sizeof(sfn), dir, x);
  6360. if (EXISTS(dir, x, sfn, NULL)) {
  6361. if (x != dest) {
  6362. make_file(dfn, sizeof(dfn), dir, dest);
  6363. RENAME(dir, x, vmu->mailbox, vmu->context, dir, dest, sfn, dfn);
  6364. }
  6365. dest++;
  6366. }
  6367. }
  6368. ast_unlock_path(dir);
  6369. return dest;
  6370. }
  6371. #endif
  6372. static int say_and_wait(struct ast_channel *chan, int num, const char *language)
  6373. {
  6374. int d;
  6375. d = ast_say_number(chan, num, AST_DIGIT_ANY, language, NULL);
  6376. return d;
  6377. }
  6378. static int save_to_folder(struct ast_vm_user *vmu, struct vm_state *vms, int msg, int box, int *newmsg, int move)
  6379. {
  6380. #ifdef IMAP_STORAGE
  6381. /* we must use mbox(x) folder names, and copy the message there */
  6382. /* simple. huh? */
  6383. char sequence[10];
  6384. char mailbox[256];
  6385. int res;
  6386. /* get the real IMAP message number for this message */
  6387. snprintf(sequence, sizeof(sequence), "%ld", vms->msgArray[msg]);
  6388. ast_debug(3, "Copying sequence %s to mailbox %s\n", sequence, mbox(vmu, box));
  6389. ast_mutex_lock(&vms->lock);
  6390. /* if save to Old folder, put in INBOX as read */
  6391. if (box == OLD_FOLDER) {
  6392. mail_setflag(vms->mailstream, sequence, "\\Seen");
  6393. mail_clearflag(vms->mailstream, sequence, "\\Unseen");
  6394. } else if (box == NEW_FOLDER) {
  6395. mail_setflag(vms->mailstream, sequence, "\\Unseen");
  6396. mail_clearflag(vms->mailstream, sequence, "\\Seen");
  6397. }
  6398. if (!strcasecmp(mbox(vmu, NEW_FOLDER), vms->curbox) && (box == NEW_FOLDER || box == OLD_FOLDER)) {
  6399. ast_mutex_unlock(&vms->lock);
  6400. return 0;
  6401. }
  6402. /* Create the folder if it don't exist */
  6403. imap_mailbox_name(mailbox, sizeof(mailbox), vms, box, 1); /* Get the full mailbox name */
  6404. ast_debug(5, "Checking if folder exists: %s\n", mailbox);
  6405. if (mail_create(vms->mailstream, mailbox) == NIL)
  6406. ast_debug(5, "Folder exists.\n");
  6407. else
  6408. ast_log(AST_LOG_NOTICE, "Folder %s created!\n", mbox(vmu, box));
  6409. if (move) {
  6410. res = !mail_move(vms->mailstream, sequence, (char *) mbox(vmu, box));
  6411. } else {
  6412. res = !mail_copy(vms->mailstream, sequence, (char *) mbox(vmu, box));
  6413. }
  6414. ast_mutex_unlock(&vms->lock);
  6415. return res;
  6416. #else
  6417. char *dir = vms->curdir;
  6418. char *username = vms->username;
  6419. char *context = vmu->context;
  6420. char sfn[PATH_MAX];
  6421. char dfn[PATH_MAX];
  6422. char ddir[PATH_MAX];
  6423. const char *dbox = mbox(vmu, box);
  6424. int x, i;
  6425. create_dirpath(ddir, sizeof(ddir), context, username, dbox);
  6426. if (vm_lock_path(ddir))
  6427. return ERROR_LOCK_PATH;
  6428. x = last_message_index(vmu, ddir) + 1;
  6429. if (box == 10 && x >= vmu->maxdeletedmsg) { /* "Deleted" folder*/
  6430. x--;
  6431. for (i = 1; i <= x; i++) {
  6432. /* Push files down a "slot". The oldest file (msg0000) will be deleted. */
  6433. make_file(sfn, sizeof(sfn), ddir, i);
  6434. make_file(dfn, sizeof(dfn), ddir, i - 1);
  6435. if (EXISTS(ddir, i, sfn, NULL)) {
  6436. RENAME(ddir, i, vmu->mailbox, vmu->context, ddir, i - 1, sfn, dfn);
  6437. } else
  6438. break;
  6439. }
  6440. } else {
  6441. if (x >= vmu->maxmsg) {
  6442. ast_unlock_path(ddir);
  6443. return -1;
  6444. }
  6445. }
  6446. make_file(sfn, sizeof(sfn), dir, msg);
  6447. make_file(dfn, sizeof(dfn), ddir, x);
  6448. if (strcmp(sfn, dfn)) {
  6449. COPY(dir, msg, ddir, x, username, context, sfn, dfn);
  6450. }
  6451. ast_unlock_path(ddir);
  6452. if (newmsg) {
  6453. *newmsg = x;
  6454. }
  6455. return 0;
  6456. #endif
  6457. }
  6458. static int adsi_logo(unsigned char *buf)
  6459. {
  6460. int bytes = 0;
  6461. bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, "Comedian Mail", "");
  6462. bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, "(C)2002-2006 Digium, Inc.", "");
  6463. return bytes;
  6464. }
  6465. static int adsi_load_vmail(struct ast_channel *chan, int *useadsi)
  6466. {
  6467. unsigned char buf[256];
  6468. int bytes = 0;
  6469. int x;
  6470. char num[5];
  6471. *useadsi = 0;
  6472. bytes += ast_adsi_data_mode(buf + bytes);
  6473. ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
  6474. bytes = 0;
  6475. bytes += adsi_logo(buf);
  6476. bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
  6477. #ifdef DISPLAY
  6478. bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " .", "");
  6479. #endif
  6480. bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
  6481. bytes += ast_adsi_data_mode(buf + bytes);
  6482. ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
  6483. if (ast_adsi_begin_download(chan, addesc, adsifdn, adsisec, adsiver)) {
  6484. bytes = 0;
  6485. bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Cancelled.", "");
  6486. bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
  6487. bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
  6488. bytes += ast_adsi_voice_mode(buf + bytes, 0);
  6489. ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
  6490. return 0;
  6491. }
  6492. #ifdef DISPLAY
  6493. /* Add a dot */
  6494. bytes = 0;
  6495. bytes += ast_adsi_logo(buf);
  6496. bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
  6497. bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " ..", "");
  6498. bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
  6499. ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
  6500. #endif
  6501. bytes = 0;
  6502. bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 0, "Listen", "Listen", "1", 1);
  6503. bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 1, "Folder", "Folder", "2", 1);
  6504. bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 2, "Advanced", "Advnced", "3", 1);
  6505. bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Options", "Options", "0", 1);
  6506. bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 4, "Help", "Help", "*", 1);
  6507. bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 5, "Exit", "Exit", "#", 1);
  6508. ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
  6509. #ifdef DISPLAY
  6510. /* Add another dot */
  6511. bytes = 0;
  6512. bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " ...", "");
  6513. bytes += ast_adsi_voice_mode(buf + bytes, 0);
  6514. bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
  6515. ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
  6516. #endif
  6517. bytes = 0;
  6518. /* These buttons we load but don't use yet */
  6519. bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 6, "Previous", "Prev", "4", 1);
  6520. bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 8, "Repeat", "Repeat", "5", 1);
  6521. bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 7, "Delete", "Delete", "7", 1);
  6522. bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 9, "Next", "Next", "6", 1);
  6523. bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 10, "Save", "Save", "9", 1);
  6524. bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 11, "Undelete", "Restore", "7", 1);
  6525. ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
  6526. #ifdef DISPLAY
  6527. /* Add another dot */
  6528. bytes = 0;
  6529. bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " ....", "");
  6530. bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
  6531. ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
  6532. #endif
  6533. bytes = 0;
  6534. for (x = 0; x < 5; x++) {
  6535. snprintf(num, sizeof(num), "%d", x);
  6536. bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + x, mbox(NULL, x), mbox(NULL, x), num, 1);
  6537. }
  6538. bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + 5, "Cancel", "Cancel", "#", 1);
  6539. ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
  6540. #ifdef DISPLAY
  6541. /* Add another dot */
  6542. bytes = 0;
  6543. bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " .....", "");
  6544. bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
  6545. ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
  6546. #endif
  6547. if (ast_adsi_end_download(chan)) {
  6548. bytes = 0;
  6549. bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Download Unsuccessful.", "");
  6550. bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
  6551. bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
  6552. bytes += ast_adsi_voice_mode(buf + bytes, 0);
  6553. ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
  6554. return 0;
  6555. }
  6556. bytes = 0;
  6557. bytes += ast_adsi_download_disconnect(buf + bytes);
  6558. bytes += ast_adsi_voice_mode(buf + bytes, 0);
  6559. ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
  6560. ast_debug(1, "Done downloading scripts...\n");
  6561. #ifdef DISPLAY
  6562. /* Add last dot */
  6563. bytes = 0;
  6564. bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, " ......", "");
  6565. bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
  6566. #endif
  6567. ast_debug(1, "Restarting session...\n");
  6568. bytes = 0;
  6569. /* Load the session now */
  6570. if (ast_adsi_load_session(chan, adsifdn, adsiver, 1) == 1) {
  6571. *useadsi = 1;
  6572. bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Scripts Loaded!", "");
  6573. } else
  6574. bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Failed!", "");
  6575. ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
  6576. return 0;
  6577. }
  6578. static void adsi_begin(struct ast_channel *chan, int *useadsi)
  6579. {
  6580. int x;
  6581. if (!ast_adsi_available(chan))
  6582. return;
  6583. x = ast_adsi_load_session(chan, adsifdn, adsiver, 1);
  6584. if (x < 0)
  6585. return;
  6586. if (!x) {
  6587. if (adsi_load_vmail(chan, useadsi)) {
  6588. ast_log(AST_LOG_WARNING, "Unable to upload voicemail scripts\n");
  6589. return;
  6590. }
  6591. } else
  6592. *useadsi = 1;
  6593. }
  6594. static void adsi_login(struct ast_channel *chan)
  6595. {
  6596. unsigned char buf[256];
  6597. int bytes = 0;
  6598. unsigned char keys[8];
  6599. int x;
  6600. if (!ast_adsi_available(chan))
  6601. return;
  6602. for (x = 0; x < 8; x++)
  6603. keys[x] = 0;
  6604. /* Set one key for next */
  6605. keys[3] = ADSI_KEY_APPS + 3;
  6606. bytes += adsi_logo(buf + bytes);
  6607. bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, " ", "");
  6608. bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, " ", "");
  6609. bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
  6610. bytes += ast_adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Mailbox: ******", "");
  6611. bytes += ast_adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 1, 1, ADSI_JUST_LEFT);
  6612. bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Enter", "Enter", "#", 1);
  6613. bytes += ast_adsi_set_keys(buf + bytes, keys);
  6614. bytes += ast_adsi_voice_mode(buf + bytes, 0);
  6615. ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
  6616. }
  6617. static void adsi_password(struct ast_channel *chan)
  6618. {
  6619. unsigned char buf[256];
  6620. int bytes = 0;
  6621. unsigned char keys[8];
  6622. int x;
  6623. if (!ast_adsi_available(chan))
  6624. return;
  6625. for (x = 0; x < 8; x++)
  6626. keys[x] = 0;
  6627. /* Set one key for next */
  6628. keys[3] = ADSI_KEY_APPS + 3;
  6629. bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
  6630. bytes += ast_adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Password: ******", "");
  6631. bytes += ast_adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 0, 1, ADSI_JUST_LEFT);
  6632. bytes += ast_adsi_set_keys(buf + bytes, keys);
  6633. bytes += ast_adsi_voice_mode(buf + bytes, 0);
  6634. ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
  6635. }
  6636. static void adsi_folders(struct ast_channel *chan, int start, char *label)
  6637. {
  6638. unsigned char buf[256];
  6639. int bytes = 0;
  6640. unsigned char keys[8];
  6641. int x, y;
  6642. if (!ast_adsi_available(chan))
  6643. return;
  6644. for (x = 0; x < 5; x++) {
  6645. y = ADSI_KEY_APPS + 12 + start + x;
  6646. if (y > ADSI_KEY_APPS + 12 + 4)
  6647. y = 0;
  6648. keys[x] = ADSI_KEY_SKT | y;
  6649. }
  6650. keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 17);
  6651. keys[6] = 0;
  6652. keys[7] = 0;
  6653. bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, label, "");
  6654. bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, " ", "");
  6655. bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
  6656. bytes += ast_adsi_set_keys(buf + bytes, keys);
  6657. bytes += ast_adsi_voice_mode(buf + bytes, 0);
  6658. ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
  6659. }
  6660. static void adsi_message(struct ast_channel *chan, struct vm_state *vms)
  6661. {
  6662. int bytes = 0;
  6663. unsigned char buf[256];
  6664. char buf1[256], buf2[256];
  6665. char fn2[PATH_MAX];
  6666. char cid[256] = "";
  6667. char *val;
  6668. char *name, *num;
  6669. char datetime[21] = "";
  6670. FILE *f;
  6671. unsigned char keys[8];
  6672. int x;
  6673. if (!ast_adsi_available(chan))
  6674. return;
  6675. /* Retrieve important info */
  6676. snprintf(fn2, sizeof(fn2), "%s.txt", vms->fn);
  6677. f = fopen(fn2, "r");
  6678. if (f) {
  6679. while (!feof(f)) {
  6680. if (!fgets((char *) buf, sizeof(buf), f)) {
  6681. continue;
  6682. }
  6683. if (!feof(f)) {
  6684. char *stringp = NULL;
  6685. stringp = (char *) buf;
  6686. strsep(&stringp, "=");
  6687. val = strsep(&stringp, "=");
  6688. if (!ast_strlen_zero(val)) {
  6689. if (!strcmp((char *) buf, "callerid"))
  6690. ast_copy_string(cid, val, sizeof(cid));
  6691. if (!strcmp((char *) buf, "origdate"))
  6692. ast_copy_string(datetime, val, sizeof(datetime));
  6693. }
  6694. }
  6695. }
  6696. fclose(f);
  6697. }
  6698. /* New meaning for keys */
  6699. for (x = 0; x < 5; x++)
  6700. keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x);
  6701. keys[6] = 0x0;
  6702. keys[7] = 0x0;
  6703. if (!vms->curmsg) {
  6704. /* No prev key, provide "Folder" instead */
  6705. keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
  6706. }
  6707. if (vms->curmsg >= vms->lastmsg) {
  6708. /* If last message ... */
  6709. if (vms->curmsg) {
  6710. /* but not only message, provide "Folder" instead */
  6711. keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
  6712. bytes += ast_adsi_voice_mode(buf + bytes, 0);
  6713. } else {
  6714. /* Otherwise if only message, leave blank */
  6715. keys[3] = 1;
  6716. }
  6717. }
  6718. if (!ast_strlen_zero(cid)) {
  6719. ast_callerid_parse(cid, &name, &num);
  6720. if (!name)
  6721. name = num;
  6722. } else {
  6723. name = "Unknown Caller";
  6724. }
  6725. /* If deleted, show "undeleted" */
  6726. #ifdef IMAP_STORAGE
  6727. ast_mutex_lock(&vms->lock);
  6728. #endif
  6729. if (vms->deleted[vms->curmsg]) {
  6730. keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11);
  6731. }
  6732. #ifdef IMAP_STORAGE
  6733. ast_mutex_unlock(&vms->lock);
  6734. #endif
  6735. /* Except "Exit" */
  6736. keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 5);
  6737. snprintf(buf1, sizeof(buf1), "%s%s", vms->curbox,
  6738. strcasecmp(vms->curbox, "INBOX") ? " Messages" : "");
  6739. snprintf(buf2, sizeof(buf2), "Message %d of %d", vms->curmsg + 1, vms->lastmsg + 1);
  6740. bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
  6741. bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
  6742. bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, name, "");
  6743. bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, datetime, "");
  6744. bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
  6745. bytes += ast_adsi_set_keys(buf + bytes, keys);
  6746. bytes += ast_adsi_voice_mode(buf + bytes, 0);
  6747. ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
  6748. }
  6749. static void adsi_delete(struct ast_channel *chan, struct vm_state *vms)
  6750. {
  6751. int bytes = 0;
  6752. unsigned char buf[256];
  6753. unsigned char keys[8];
  6754. int x;
  6755. if (!ast_adsi_available(chan))
  6756. return;
  6757. /* New meaning for keys */
  6758. for (x = 0; x < 5; x++)
  6759. keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x);
  6760. keys[6] = 0x0;
  6761. keys[7] = 0x0;
  6762. if (!vms->curmsg) {
  6763. /* No prev key, provide "Folder" instead */
  6764. keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
  6765. }
  6766. if (vms->curmsg >= vms->lastmsg) {
  6767. /* If last message ... */
  6768. if (vms->curmsg) {
  6769. /* but not only message, provide "Folder" instead */
  6770. keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
  6771. } else {
  6772. /* Otherwise if only message, leave blank */
  6773. keys[3] = 1;
  6774. }
  6775. }
  6776. /* If deleted, show "undeleted" */
  6777. #ifdef IMAP_STORAGE
  6778. ast_mutex_lock(&vms->lock);
  6779. #endif
  6780. if (vms->deleted[vms->curmsg]) {
  6781. keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11);
  6782. }
  6783. #ifdef IMAP_STORAGE
  6784. ast_mutex_unlock(&vms->lock);
  6785. #endif
  6786. /* Except "Exit" */
  6787. keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 5);
  6788. bytes += ast_adsi_set_keys(buf + bytes, keys);
  6789. bytes += ast_adsi_voice_mode(buf + bytes, 0);
  6790. ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
  6791. }
  6792. static void adsi_status(struct ast_channel *chan, struct vm_state *vms)
  6793. {
  6794. unsigned char buf[256] = "";
  6795. char buf1[256] = "", buf2[256] = "";
  6796. int bytes = 0;
  6797. unsigned char keys[8];
  6798. int x;
  6799. char *newm = (vms->newmessages == 1) ? "message" : "messages";
  6800. char *oldm = (vms->oldmessages == 1) ? "message" : "messages";
  6801. if (!ast_adsi_available(chan))
  6802. return;
  6803. if (vms->newmessages) {
  6804. snprintf(buf1, sizeof(buf1), "You have %d new", vms->newmessages);
  6805. if (vms->oldmessages) {
  6806. strncat(buf1, " and", sizeof(buf1) - strlen(buf1) - 1);
  6807. snprintf(buf2, sizeof(buf2), "%d old %s.", vms->oldmessages, oldm);
  6808. } else {
  6809. snprintf(buf2, sizeof(buf2), "%s.", newm);
  6810. }
  6811. } else if (vms->oldmessages) {
  6812. snprintf(buf1, sizeof(buf1), "You have %d old", vms->oldmessages);
  6813. snprintf(buf2, sizeof(buf2), "%s.", oldm);
  6814. } else {
  6815. strcpy(buf1, "You have no messages.");
  6816. buf2[0] = ' ';
  6817. buf2[1] = '\0';
  6818. }
  6819. bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
  6820. bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
  6821. bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
  6822. for (x = 0; x < 6; x++)
  6823. keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + x);
  6824. keys[6] = 0;
  6825. keys[7] = 0;
  6826. /* Don't let them listen if there are none */
  6827. if (vms->lastmsg < 0)
  6828. keys[0] = 1;
  6829. bytes += ast_adsi_set_keys(buf + bytes, keys);
  6830. bytes += ast_adsi_voice_mode(buf + bytes, 0);
  6831. ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
  6832. }
  6833. static void adsi_status2(struct ast_channel *chan, struct vm_state *vms)
  6834. {
  6835. unsigned char buf[256] = "";
  6836. char buf1[256] = "", buf2[256] = "";
  6837. int bytes = 0;
  6838. unsigned char keys[8];
  6839. int x;
  6840. char *mess = (vms->lastmsg == 0) ? "message" : "messages";
  6841. if (!ast_adsi_available(chan))
  6842. return;
  6843. /* Original command keys */
  6844. for (x = 0; x < 6; x++)
  6845. keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + x);
  6846. keys[6] = 0;
  6847. keys[7] = 0;
  6848. if ((vms->lastmsg + 1) < 1)
  6849. keys[0] = 0;
  6850. snprintf(buf1, sizeof(buf1), "%s%s has", vms->curbox,
  6851. strcasecmp(vms->curbox, "INBOX") ? " folder" : "");
  6852. if (vms->lastmsg + 1)
  6853. snprintf(buf2, sizeof(buf2), "%d %s.", vms->lastmsg + 1, mess);
  6854. else
  6855. strcpy(buf2, "no messages.");
  6856. bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
  6857. bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
  6858. bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, "", "");
  6859. bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
  6860. bytes += ast_adsi_set_keys(buf + bytes, keys);
  6861. bytes += ast_adsi_voice_mode(buf + bytes, 0);
  6862. ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
  6863. }
  6864. /*
  6865. static void adsi_clear(struct ast_channel *chan)
  6866. {
  6867. char buf[256];
  6868. int bytes=0;
  6869. if (!ast_adsi_available(chan))
  6870. return;
  6871. bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
  6872. bytes += ast_adsi_voice_mode(buf + bytes, 0);
  6873. ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
  6874. }
  6875. */
  6876. static void adsi_goodbye(struct ast_channel *chan)
  6877. {
  6878. unsigned char buf[256];
  6879. int bytes = 0;
  6880. if (!ast_adsi_available(chan))
  6881. return;
  6882. bytes += adsi_logo(buf + bytes);
  6883. bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, " ", "");
  6884. bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Goodbye", "");
  6885. bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
  6886. bytes += ast_adsi_voice_mode(buf + bytes, 0);
  6887. ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
  6888. }
  6889. /*!\brief get_folder: Folder menu
  6890. * Plays "press 1 for INBOX messages" etc.
  6891. * Should possibly be internationalized
  6892. */
  6893. static int get_folder(struct ast_channel *chan, int start)
  6894. {
  6895. int x;
  6896. int d;
  6897. char fn[PATH_MAX];
  6898. d = ast_play_and_wait(chan, "vm-press"); /* "Press" */
  6899. if (d)
  6900. return d;
  6901. for (x = start; x < 5; x++) { /* For all folders */
  6902. if ((d = ast_say_number(chan, x, AST_DIGIT_ANY, ast_channel_language(chan), NULL)))
  6903. return d;
  6904. d = ast_play_and_wait(chan, "vm-for"); /* "for" */
  6905. if (d)
  6906. return d;
  6907. snprintf(fn, sizeof(fn), "vm-%s", mbox(NULL, x)); /* Folder name */
  6908. /* The inbox folder can have its name changed under certain conditions
  6909. * so this checks if the sound file exists for the inbox folder name and
  6910. * if it doesn't, plays the default name instead. */
  6911. if (x == 0) {
  6912. if (ast_fileexists(fn, NULL, NULL)) {
  6913. d = vm_play_folder_name(chan, fn);
  6914. } else {
  6915. ast_verb(4, "Failed to find file %s; falling back to INBOX\n", fn);
  6916. d = vm_play_folder_name(chan, "vm-INBOX");
  6917. }
  6918. } else {
  6919. ast_test_suite_event_notify("PLAYBACK", "Message: folder name %s", fn);
  6920. d = vm_play_folder_name(chan, fn);
  6921. }
  6922. if (d)
  6923. return d;
  6924. d = ast_waitfordigit(chan, 500);
  6925. if (d)
  6926. return d;
  6927. }
  6928. d = ast_play_and_wait(chan, "vm-tocancel"); /* "or pound to cancel" */
  6929. if (d)
  6930. return d;
  6931. d = ast_waitfordigit(chan, 4000);
  6932. return d;
  6933. }
  6934. /* Japanese Syntax */
  6935. static int get_folder_ja(struct ast_channel *chan, int start)
  6936. {
  6937. int x;
  6938. int d;
  6939. char fn[256];
  6940. for (x = start; x< 5; x++) { /* For all folders */
  6941. if ((d = ast_say_number(chan, x, AST_DIGIT_ANY, ast_channel_language(chan), (char *) NULL))) {
  6942. return d;
  6943. }
  6944. snprintf(fn, sizeof(fn), "vm-%s", mbox(NULL, x)); /* Folder name */
  6945. d = vm_play_folder_name(chan, fn);
  6946. if (d) {
  6947. return d;
  6948. }
  6949. d = ast_waitfordigit(chan, 500);
  6950. if (d) {
  6951. return d;
  6952. }
  6953. }
  6954. d = ast_play_and_wait(chan, "vm-tocancel"); /* "or pound to cancel" */
  6955. if (d) {
  6956. return d;
  6957. }
  6958. d = ast_waitfordigit(chan, 4000);
  6959. return d;
  6960. }
  6961. /*!
  6962. * \brief plays a prompt and waits for a keypress.
  6963. * \param chan
  6964. * \param fn the name of the voice prompt file to be played. For example, 'vm-changeto', 'vm-savefolder'
  6965. * \param start Does not appear to be used at this time.
  6966. *
  6967. * This is used by the main menu option to move a message to a folder or to save a message into a folder.
  6968. * After playing the message identified by the fn parameter value, it calls get_folder(), which plays the
  6969. * prompting for the number inputs that correspond to the available folders.
  6970. *
  6971. * \return zero on success, or -1 on error.
  6972. */
  6973. static int get_folder2(struct ast_channel *chan, char *fn, int start)
  6974. {
  6975. int res = 0;
  6976. int loops = 0;
  6977. res = ast_play_and_wait(chan, fn); /* Folder name */
  6978. while (((res < '0') || (res > '9')) &&
  6979. (res != '#') && (res >= 0) &&
  6980. loops < 4) {
  6981. /* res = get_folder(chan, 0); */
  6982. if (!strcasecmp(ast_channel_language(chan),"ja")) { /* Japanese syntax */
  6983. res = get_folder_ja(chan, 0);
  6984. } else { /* Default syntax */
  6985. res = get_folder(chan, 0);
  6986. }
  6987. loops++;
  6988. }
  6989. if (loops == 4) { /* give up */
  6990. ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", '#', '#');
  6991. return '#';
  6992. }
  6993. ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", res, res);
  6994. return res;
  6995. }
  6996. /*!
  6997. * \brief presents the option to prepend to an existing message when forwarding it.
  6998. * \param chan
  6999. * \param vmu
  7000. * \param curdir
  7001. * \param curmsg
  7002. * \param vm_fmts
  7003. * \param context
  7004. * \param record_gain
  7005. * \param duration
  7006. * \param vms
  7007. * \param flag
  7008. *
  7009. * Presents a prompt for 1 to prepend the current message, 2 to forward the message without prepending, or * to return to the main menu.
  7010. *
  7011. * This is invoked from forward_message() when performing a forward operation (option 8 from main menu).
  7012. * \return zero on success, -1 on error.
  7013. */
  7014. static int vm_forwardoptions(struct ast_channel *chan, struct ast_vm_user *vmu, char *curdir, int curmsg, char *vm_fmts,
  7015. char *context, signed char record_gain, long *duration, struct vm_state *vms, char *flag)
  7016. {
  7017. int cmd = 0;
  7018. int retries = 0, prepend_duration = 0, already_recorded = 0;
  7019. char msgfile[PATH_MAX], backup[PATH_MAX], backup_textfile[PATH_MAX];
  7020. char textfile[PATH_MAX];
  7021. struct ast_config *msg_cfg;
  7022. struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
  7023. #ifndef IMAP_STORAGE
  7024. signed char zero_gain = 0;
  7025. #else
  7026. const char *msg_id = NULL;
  7027. #endif
  7028. const char *duration_str;
  7029. /* Must always populate duration correctly */
  7030. make_file(msgfile, sizeof(msgfile), curdir, curmsg);
  7031. strcpy(textfile, msgfile);
  7032. strcpy(backup, msgfile);
  7033. strcpy(backup_textfile, msgfile);
  7034. strncat(textfile, ".txt", sizeof(textfile) - strlen(textfile) - 1);
  7035. strncat(backup, "-bak", sizeof(backup) - strlen(backup) - 1);
  7036. strncat(backup_textfile, "-bak.txt", sizeof(backup_textfile) - strlen(backup_textfile) - 1);
  7037. if ((msg_cfg = ast_config_load(textfile, config_flags)) && valid_config(msg_cfg) && (duration_str = ast_variable_retrieve(msg_cfg, "message", "duration"))) {
  7038. *duration = atoi(duration_str);
  7039. } else {
  7040. *duration = 0;
  7041. }
  7042. while ((cmd >= 0) && (cmd != 't') && (cmd != '*')) {
  7043. if (cmd)
  7044. retries = 0;
  7045. switch (cmd) {
  7046. case '1':
  7047. #ifdef IMAP_STORAGE
  7048. /* Record new intro file */
  7049. if (msg_cfg && msg_cfg != CONFIG_STATUS_FILEINVALID) {
  7050. msg_id = ast_variable_retrieve(msg_cfg, "message", "msg_id");
  7051. }
  7052. make_file(vms->introfn, sizeof(vms->introfn), curdir, curmsg);
  7053. strncat(vms->introfn, "intro", sizeof(vms->introfn));
  7054. ast_play_and_wait(chan, INTRO);
  7055. ast_play_and_wait(chan, "beep");
  7056. cmd = play_record_review(chan, NULL, vms->introfn, vmu->maxsecs, vm_fmts, 1, vmu, (int *) duration, NULL, NULL, record_gain, vms, flag, msg_id);
  7057. if (cmd == -1) {
  7058. break;
  7059. }
  7060. cmd = 't';
  7061. #else
  7062. /* prepend a message to the current message, update the metadata and return */
  7063. make_file(msgfile, sizeof(msgfile), curdir, curmsg);
  7064. strcpy(textfile, msgfile);
  7065. strncat(textfile, ".txt", sizeof(textfile) - 1);
  7066. *duration = 0;
  7067. /* if we can't read the message metadata, stop now */
  7068. if (!valid_config(msg_cfg)) {
  7069. cmd = 0;
  7070. break;
  7071. }
  7072. /* Back up the original file, so we can retry the prepend and restore it after forward. */
  7073. #ifndef IMAP_STORAGE
  7074. if (already_recorded) {
  7075. ast_filecopy(backup, msgfile, NULL);
  7076. copy(backup_textfile, textfile);
  7077. }
  7078. else {
  7079. ast_filecopy(msgfile, backup, NULL);
  7080. copy(textfile, backup_textfile);
  7081. }
  7082. #endif
  7083. already_recorded = 1;
  7084. if (record_gain)
  7085. ast_channel_setoption(chan, AST_OPTION_RXGAIN, &record_gain, sizeof(record_gain), 0);
  7086. cmd = ast_play_and_prepend(chan, NULL, msgfile, 0, vm_fmts, &prepend_duration, NULL, 1, silencethreshold, maxsilence);
  7087. if (cmd == 'S') { /* If we timed out, tell the user it didn't work properly and clean up the files */
  7088. ast_stream_and_wait(chan, vm_pls_try_again, ""); /* this might be removed if a proper vm_prepend_timeout is ever recorded */
  7089. ast_stream_and_wait(chan, vm_prepend_timeout, "");
  7090. ast_filerename(backup, msgfile, NULL);
  7091. }
  7092. if (record_gain)
  7093. ast_channel_setoption(chan, AST_OPTION_RXGAIN, &zero_gain, sizeof(zero_gain), 0);
  7094. if ((duration_str = ast_variable_retrieve(msg_cfg, "message", "duration")))
  7095. *duration = atoi(duration_str);
  7096. if (prepend_duration) {
  7097. struct ast_category *msg_cat;
  7098. /* need enough space for a maximum-length message duration */
  7099. char duration_buf[12];
  7100. *duration += prepend_duration;
  7101. msg_cat = ast_category_get(msg_cfg, "message", NULL);
  7102. snprintf(duration_buf, 11, "%ld", *duration);
  7103. if (!ast_variable_update(msg_cat, "duration", duration_buf, NULL, 0)) {
  7104. ast_config_text_file_save(textfile, msg_cfg, "app_voicemail");
  7105. }
  7106. }
  7107. #endif
  7108. break;
  7109. case '2':
  7110. /* NULL out introfile so we know there is no intro! */
  7111. #ifdef IMAP_STORAGE
  7112. *vms->introfn = '\0';
  7113. #endif
  7114. cmd = 't';
  7115. break;
  7116. case '*':
  7117. cmd = '*';
  7118. break;
  7119. default:
  7120. /* If time_out and return to menu, reset already_recorded */
  7121. already_recorded = 0;
  7122. cmd = ast_play_and_wait(chan, "vm-forwardoptions");
  7123. /* "Press 1 to prepend a message or 2 to forward the message without prepending" */
  7124. if (!cmd) {
  7125. cmd = ast_play_and_wait(chan, "vm-starmain");
  7126. /* "press star to return to the main menu" */
  7127. }
  7128. if (!cmd) {
  7129. cmd = ast_waitfordigit(chan, 6000);
  7130. }
  7131. if (!cmd) {
  7132. retries++;
  7133. }
  7134. if (retries > 3) {
  7135. cmd = '*'; /* Let's cancel this beast */
  7136. }
  7137. ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", cmd, cmd);
  7138. }
  7139. }
  7140. if (valid_config(msg_cfg))
  7141. ast_config_destroy(msg_cfg);
  7142. if (prepend_duration)
  7143. *duration = prepend_duration;
  7144. if (already_recorded && cmd == -1) {
  7145. /* restore original message if prepention cancelled */
  7146. ast_filerename(backup, msgfile, NULL);
  7147. rename(backup_textfile, textfile);
  7148. }
  7149. if (cmd == 't' || cmd == 'S') /* XXX entering this block with a value of 'S' is probably no longer possible. */
  7150. cmd = 0;
  7151. return cmd;
  7152. }
  7153. static void queue_mwi_event(const char *channel_id, const char *box, int urgent, int new, int old)
  7154. {
  7155. char *mailbox;
  7156. char *context;
  7157. if (separate_mailbox(ast_strdupa(box), &mailbox, &context)) {
  7158. return;
  7159. }
  7160. ast_publish_mwi_state_channel(mailbox, context, new + urgent, old, channel_id);
  7161. }
  7162. /*!
  7163. * \brief Sends email notification that a user has a new voicemail waiting for them.
  7164. * \param chan
  7165. * \param vmu
  7166. * \param vms
  7167. * \param msgnum
  7168. * \param duration
  7169. * \param fmt
  7170. * \param cidnum The Caller ID phone number value.
  7171. * \param cidname The Caller ID name value.
  7172. * \param flag
  7173. *
  7174. * \return zero on success, -1 on error.
  7175. */
  7176. static int notify_new_message(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int msgnum, long duration, char *fmt, char *cidnum, char *cidname, const char *flag)
  7177. {
  7178. char todir[PATH_MAX], fn[PATH_MAX], ext_context[PATH_MAX], *stringp;
  7179. int newmsgs = 0, oldmsgs = 0, urgentmsgs = 0;
  7180. const char *category;
  7181. char *myserveremail = serveremail;
  7182. ast_channel_lock(chan);
  7183. if ((category = pbx_builtin_getvar_helper(chan, "VM_CATEGORY"))) {
  7184. category = ast_strdupa(category);
  7185. }
  7186. ast_channel_unlock(chan);
  7187. #ifndef IMAP_STORAGE
  7188. make_dir(todir, sizeof(todir), vmu->context, vmu->mailbox, !ast_strlen_zero(flag) && !strcmp(flag, "Urgent") ? "Urgent" : "INBOX");
  7189. #else
  7190. snprintf(todir, sizeof(todir), "%simap", VM_SPOOL_DIR);
  7191. #endif
  7192. make_file(fn, sizeof(fn), todir, msgnum);
  7193. snprintf(ext_context, sizeof(ext_context), "%s@%s", vmu->mailbox, vmu->context);
  7194. if (!ast_strlen_zero(vmu->attachfmt)) {
  7195. if (strstr(fmt, vmu->attachfmt))
  7196. fmt = vmu->attachfmt;
  7197. else
  7198. ast_log(AST_LOG_WARNING, "Attachment format '%s' is not one of the recorded formats '%s'. Falling back to default format for '%s@%s'.\n", vmu->attachfmt, fmt, vmu->mailbox, vmu->context);
  7199. }
  7200. /* Attach only the first format */
  7201. fmt = ast_strdupa(fmt);
  7202. stringp = fmt;
  7203. strsep(&stringp, "|");
  7204. if (!ast_strlen_zero(vmu->serveremail))
  7205. myserveremail = vmu->serveremail;
  7206. if (!ast_strlen_zero(vmu->email)) {
  7207. int attach_user_voicemail = ast_test_flag(vmu, VM_ATTACH);
  7208. char *msg_id = NULL;
  7209. #ifdef IMAP_STORAGE
  7210. struct ast_config *msg_cfg;
  7211. struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
  7212. char filename[PATH_MAX];
  7213. snprintf(filename, sizeof(filename), "%s.txt", fn);
  7214. msg_cfg = ast_config_load(filename, config_flags);
  7215. if (msg_cfg && msg_cfg != CONFIG_STATUS_FILEINVALID) {
  7216. msg_id = ast_strdupa(ast_variable_retrieve(msg_cfg, "message", "msg_id"));
  7217. ast_config_destroy(msg_cfg);
  7218. }
  7219. #endif
  7220. if (attach_user_voicemail)
  7221. RETRIEVE(todir, msgnum, vmu->mailbox, vmu->context);
  7222. /* XXX possible imap issue, should category be NULL XXX */
  7223. sendmail(myserveremail, vmu, msgnum, vmu->context, vmu->mailbox, mbox(vmu, 0), cidnum, cidname, fn, NULL, fmt, duration, attach_user_voicemail, chan, category, flag, msg_id);
  7224. if (attach_user_voicemail)
  7225. DISPOSE(todir, msgnum);
  7226. }
  7227. if (!ast_strlen_zero(vmu->pager)) {
  7228. sendpage(myserveremail, vmu->pager, msgnum, vmu->context, vmu->mailbox, mbox(vmu, 0), cidnum, cidname, duration, vmu, category, flag);
  7229. }
  7230. if (ast_test_flag(vmu, VM_DELETE))
  7231. DELETE(todir, msgnum, fn, vmu);
  7232. /* Leave voicemail for someone */
  7233. if (ast_app_has_voicemail(ext_context, NULL))
  7234. ast_app_inboxcount2(ext_context, &urgentmsgs, &newmsgs, &oldmsgs);
  7235. queue_mwi_event(ast_channel_uniqueid(chan), ext_context, urgentmsgs, newmsgs, oldmsgs);
  7236. run_externnotify(vmu->context, vmu->mailbox, flag);
  7237. #ifdef IMAP_STORAGE
  7238. vm_delete(fn); /* Delete the file, but not the IMAP message */
  7239. if (ast_test_flag(vmu, VM_DELETE)) { /* Delete the IMAP message if delete = yes */
  7240. vm_imap_delete(NULL, vms->curmsg, vmu);
  7241. vms->newmessages--; /* Fix new message count */
  7242. }
  7243. #endif
  7244. return 0;
  7245. }
  7246. /*!
  7247. * \brief Sends a voicemail message to a mailbox recipient.
  7248. * \param chan
  7249. * \param context
  7250. * \param vms
  7251. * \param sender
  7252. * \param fmt
  7253. * \param is_new_message Used to indicate the mode for which this method was invoked.
  7254. * Will be 0 when called to forward an existing message (option 8)
  7255. * Will be 1 when called to leave a message (option 3->5)
  7256. * \param record_gain
  7257. * \param urgent
  7258. *
  7259. * Reads the destination mailbox(es) from keypad input for CID, or if use_directory feature is enabled, the Directory.
  7260. *
  7261. * When in the leave message mode (is_new_message == 1):
  7262. * - allow the leaving of a message for ourselves. (Will not allow us to forward a message to ourselves, when is_new_message == 0).
  7263. * - attempt to determine the context and and mailbox, and then invoke leave_message() function to record and store the message.
  7264. *
  7265. * When in the forward message mode (is_new_message == 0):
  7266. * - retrieves the current message to be forwarded
  7267. * - copies the original message to a temporary file, so updates to the envelope can be done.
  7268. * - determines the target mailbox and folders
  7269. * - copies the message into the target mailbox, using copy_message() or by generating the message into an email attachment if using imap folders.
  7270. *
  7271. * \return zero on success, -1 on error.
  7272. */
  7273. static int forward_message(struct ast_channel *chan, char *context, struct vm_state *vms, struct ast_vm_user *sender, char *fmt, int is_new_message, signed char record_gain, int urgent)
  7274. {
  7275. #ifdef IMAP_STORAGE
  7276. int todircount = 0;
  7277. struct vm_state *dstvms;
  7278. #endif
  7279. char username[70]="";
  7280. char fn[PATH_MAX]; /* for playback of name greeting */
  7281. char ecodes[16] = "#";
  7282. int res = 0, cmd = 0;
  7283. struct ast_vm_user *receiver = NULL, *vmtmp;
  7284. AST_LIST_HEAD_NOLOCK_STATIC(extensions, ast_vm_user);
  7285. char *stringp;
  7286. const char *s;
  7287. int saved_messages = 0;
  7288. int valid_extensions = 0;
  7289. char *dir;
  7290. int curmsg;
  7291. char urgent_str[7] = "";
  7292. int prompt_played = 0;
  7293. #ifndef IMAP_STORAGE
  7294. char msgfile[PATH_MAX], textfile[PATH_MAX], backup[PATH_MAX], backup_textfile[PATH_MAX];
  7295. #endif
  7296. if (ast_test_flag((&globalflags), VM_FWDURGAUTO)) {
  7297. ast_copy_string(urgent_str, urgent ? "Urgent" : "", sizeof(urgent_str));
  7298. }
  7299. if (vms == NULL) return -1;
  7300. dir = vms->curdir;
  7301. curmsg = vms->curmsg;
  7302. ast_test_suite_event_notify("FORWARD", "Message: entering forward message menu");
  7303. while (!res && !valid_extensions) {
  7304. int use_directory = 0;
  7305. if (ast_test_flag((&globalflags), VM_DIRECFORWARD)) {
  7306. int done = 0;
  7307. int retries = 0;
  7308. cmd = 0;
  7309. while ((cmd >= 0) && !done ){
  7310. if (cmd)
  7311. retries = 0;
  7312. switch (cmd) {
  7313. case '1':
  7314. use_directory = 0;
  7315. done = 1;
  7316. break;
  7317. case '2':
  7318. use_directory = 1;
  7319. done = 1;
  7320. break;
  7321. case '*':
  7322. cmd = 't';
  7323. done = 1;
  7324. break;
  7325. default:
  7326. /* Press 1 to enter an extension press 2 to use the directory */
  7327. cmd = ast_play_and_wait(chan, "vm-forward");
  7328. if (!cmd) {
  7329. cmd = ast_waitfordigit(chan, 3000);
  7330. }
  7331. if (!cmd) {
  7332. retries++;
  7333. }
  7334. if (retries > 3) {
  7335. cmd = 't';
  7336. done = 1;
  7337. }
  7338. ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", cmd, cmd);
  7339. }
  7340. }
  7341. if (cmd < 0 || cmd == 't')
  7342. break;
  7343. }
  7344. if (use_directory) {
  7345. /* use app_directory */
  7346. struct ast_app* directory_app;
  7347. directory_app = pbx_findapp("Directory");
  7348. if (directory_app) {
  7349. char vmcontext[256];
  7350. char *old_context;
  7351. char *old_exten;
  7352. int old_priority;
  7353. /* make backup copies */
  7354. old_context = ast_strdupa(ast_channel_context(chan));
  7355. old_exten = ast_strdupa(ast_channel_exten(chan));
  7356. old_priority = ast_channel_priority(chan);
  7357. /* call the the Directory, changes the channel */
  7358. snprintf(vmcontext, sizeof(vmcontext), "%s,,v", context ? context : "default");
  7359. res = pbx_exec(chan, directory_app, vmcontext);
  7360. ast_copy_string(username, ast_channel_exten(chan), sizeof(username));
  7361. /* restore the old context, exten, and priority */
  7362. ast_channel_context_set(chan, old_context);
  7363. ast_channel_exten_set(chan, old_exten);
  7364. ast_channel_priority_set(chan, old_priority);
  7365. } else {
  7366. ast_log(AST_LOG_WARNING, "Could not find the Directory application, disabling directory_forward\n");
  7367. ast_clear_flag((&globalflags), VM_DIRECFORWARD);
  7368. }
  7369. } else {
  7370. /* Ask for an extension */
  7371. res = ast_streamfile(chan, "vm-extension", ast_channel_language(chan)); /* "extension" */
  7372. prompt_played++;
  7373. if (res || prompt_played > 4)
  7374. break;
  7375. if ((res = ast_readstring(chan, username, sizeof(username) - 1, 2000, 10000, "#")) < 0)
  7376. break;
  7377. }
  7378. /* start all over if no username */
  7379. if (ast_strlen_zero(username))
  7380. continue;
  7381. stringp = username;
  7382. s = strsep(&stringp, "*");
  7383. /* start optimistic */
  7384. valid_extensions = 1;
  7385. while (s) {
  7386. if ((is_new_message == 1 || strcmp(s, sender->mailbox)) && (receiver = find_user(NULL, context, s))) {
  7387. int oldmsgs;
  7388. int newmsgs;
  7389. int capacity;
  7390. if (inboxcount(s, &newmsgs, &oldmsgs)) {
  7391. ast_log(LOG_ERROR, "Problem in calculating number of voicemail messages available for extension %s\n", s);
  7392. /* Shouldn't happen, but allow trying another extension if it does */
  7393. res = ast_play_and_wait(chan, "pbx-invalid");
  7394. valid_extensions = 0;
  7395. break;
  7396. }
  7397. capacity = receiver->maxmsg - inprocess_count(receiver->mailbox, receiver->context, +1);
  7398. if ((newmsgs + oldmsgs) >= capacity) {
  7399. ast_log(LOG_NOTICE, "Mailbox '%s' is full with capacity of %d, prompting for another extension.\n", s, capacity);
  7400. res = ast_play_and_wait(chan, "vm-mailboxfull");
  7401. valid_extensions = 0;
  7402. while ((vmtmp = AST_LIST_REMOVE_HEAD(&extensions, list))) {
  7403. inprocess_count(vmtmp->mailbox, vmtmp->context, -1);
  7404. free_user(vmtmp);
  7405. }
  7406. inprocess_count(receiver->mailbox, receiver->context, -1);
  7407. break;
  7408. }
  7409. AST_LIST_INSERT_HEAD(&extensions, receiver, list);
  7410. } else {
  7411. /* XXX Optimization for the future. When we encounter a single bad extension,
  7412. * bailing out on all of the extensions may not be the way to go. We should
  7413. * probably just bail on that single extension, then allow the user to enter
  7414. * several more. XXX
  7415. */
  7416. while ((receiver = AST_LIST_REMOVE_HEAD(&extensions, list))) {
  7417. free_user(receiver);
  7418. }
  7419. ast_log(LOG_NOTICE, "'%s' is not a valid mailbox\n", s);
  7420. /* "I am sorry, that's not a valid extension. Please try again." */
  7421. res = ast_play_and_wait(chan, "pbx-invalid");
  7422. valid_extensions = 0;
  7423. break;
  7424. }
  7425. /* play name if available, else play extension number */
  7426. snprintf(fn, sizeof(fn), "%s%s/%s/greet", VM_SPOOL_DIR, receiver->context, s);
  7427. RETRIEVE(fn, -1, s, receiver->context);
  7428. if (ast_fileexists(fn, NULL, NULL) > 0) {
  7429. res = ast_stream_and_wait(chan, fn, ecodes);
  7430. if (res) {
  7431. DISPOSE(fn, -1);
  7432. return res;
  7433. }
  7434. } else {
  7435. res = ast_say_digit_str(chan, s, ecodes, ast_channel_language(chan));
  7436. }
  7437. DISPOSE(fn, -1);
  7438. s = strsep(&stringp, "*");
  7439. }
  7440. /* break from the loop of reading the extensions */
  7441. if (valid_extensions)
  7442. break;
  7443. }
  7444. /* check if we're clear to proceed */
  7445. if (AST_LIST_EMPTY(&extensions) || !valid_extensions)
  7446. return res;
  7447. if (is_new_message == 1) {
  7448. struct leave_vm_options leave_options;
  7449. char mailbox[AST_MAX_EXTENSION * 2 + 2];
  7450. snprintf(mailbox, sizeof(mailbox), "%s@%s", username, context);
  7451. /* Send VoiceMail */
  7452. memset(&leave_options, 0, sizeof(leave_options));
  7453. leave_options.record_gain = record_gain;
  7454. cmd = leave_voicemail(chan, mailbox, &leave_options);
  7455. } else {
  7456. /* Forward VoiceMail */
  7457. long duration = 0;
  7458. struct vm_state vmstmp;
  7459. int copy_msg_result = 0;
  7460. #ifdef IMAP_STORAGE
  7461. char filename[PATH_MAX];
  7462. struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
  7463. const char *msg_id = NULL;
  7464. struct ast_config *msg_cfg;
  7465. #endif
  7466. memcpy(&vmstmp, vms, sizeof(vmstmp));
  7467. RETRIEVE(dir, curmsg, sender->mailbox, sender->context);
  7468. #ifdef IMAP_STORAGE
  7469. make_file(filename, sizeof(filename), dir, curmsg);
  7470. strncat(filename, ".txt", sizeof(filename) - strlen(filename) - 1);
  7471. msg_cfg = ast_config_load(filename, config_flags);
  7472. if (msg_cfg && msg_cfg == CONFIG_STATUS_FILEINVALID) {
  7473. msg_id = ast_strdupa(ast_variable_retrieve(msg_cfg, "message", "msg_id"));
  7474. ast_config_destroy(msg_cfg);
  7475. }
  7476. #endif
  7477. cmd = vm_forwardoptions(chan, sender, vmstmp.curdir, curmsg, vmfmts, S_OR(context, "default"), record_gain, &duration, &vmstmp, urgent_str);
  7478. if (!cmd) {
  7479. AST_LIST_TRAVERSE_SAFE_BEGIN(&extensions, vmtmp, list) {
  7480. #ifdef IMAP_STORAGE
  7481. int attach_user_voicemail;
  7482. char *myserveremail = serveremail;
  7483. /* get destination mailbox */
  7484. dstvms = get_vm_state_by_mailbox(vmtmp->mailbox, vmtmp->context, 0);
  7485. if (!dstvms) {
  7486. dstvms = create_vm_state_from_user(vmtmp);
  7487. }
  7488. if (dstvms) {
  7489. init_mailstream(dstvms, 0);
  7490. if (!dstvms->mailstream) {
  7491. ast_log(AST_LOG_ERROR, "IMAP mailstream for %s is NULL\n", vmtmp->mailbox);
  7492. } else {
  7493. copy_msg_result = STORE(vmstmp.curdir, vmtmp->mailbox, vmtmp->context, dstvms->curmsg, chan, vmtmp, fmt, duration, dstvms, urgent_str, msg_id);
  7494. run_externnotify(vmtmp->context, vmtmp->mailbox, urgent_str);
  7495. }
  7496. } else {
  7497. ast_log(AST_LOG_ERROR, "Could not find state information for mailbox %s\n", vmtmp->mailbox);
  7498. }
  7499. if (!ast_strlen_zero(vmtmp->serveremail))
  7500. myserveremail = vmtmp->serveremail;
  7501. attach_user_voicemail = ast_test_flag(vmtmp, VM_ATTACH);
  7502. /* NULL category for IMAP storage */
  7503. sendmail(myserveremail, vmtmp, todircount, vmtmp->context, vmtmp->mailbox,
  7504. dstvms->curbox,
  7505. S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL),
  7506. S_COR(ast_channel_caller(chan)->id.name.valid, ast_channel_caller(chan)->id.name.str, NULL),
  7507. vmstmp.fn, vmstmp.introfn, fmt, duration, attach_user_voicemail, chan,
  7508. NULL, urgent_str, msg_id);
  7509. #else
  7510. copy_msg_result = copy_message(chan, sender, 0, curmsg, duration, vmtmp, fmt, dir, urgent_str, NULL);
  7511. #endif
  7512. saved_messages++;
  7513. AST_LIST_REMOVE_CURRENT(list);
  7514. inprocess_count(vmtmp->mailbox, vmtmp->context, -1);
  7515. free_user(vmtmp);
  7516. if (res)
  7517. break;
  7518. }
  7519. AST_LIST_TRAVERSE_SAFE_END;
  7520. if (saved_messages > 0 && !copy_msg_result) {
  7521. /* give confirmation that the message was saved */
  7522. /* commented out since we can't forward batches yet
  7523. if (saved_messages == 1)
  7524. res = ast_play_and_wait(chan, "vm-message");
  7525. else
  7526. res = ast_play_and_wait(chan, "vm-messages");
  7527. if (!res)
  7528. res = ast_play_and_wait(chan, "vm-saved"); */
  7529. #ifdef IMAP_STORAGE
  7530. /* If forwarded with intro, DON'T PLAY THIS MESSAGE AGAIN! */
  7531. if (ast_strlen_zero(vmstmp.introfn))
  7532. #endif
  7533. res = ast_play_and_wait(chan, "vm-msgforwarded");
  7534. }
  7535. #ifndef IMAP_STORAGE
  7536. else {
  7537. /* with IMAP, mailbox full warning played by imap_check_limits */
  7538. res = ast_play_and_wait(chan, "vm-mailboxfull");
  7539. }
  7540. /* Restore original message without prepended message if backup exists */
  7541. make_file(msgfile, sizeof(msgfile), dir, curmsg);
  7542. strcpy(textfile, msgfile);
  7543. strcpy(backup, msgfile);
  7544. strcpy(backup_textfile, msgfile);
  7545. strncat(textfile, ".txt", sizeof(textfile) - strlen(textfile) - 1);
  7546. strncat(backup, "-bak", sizeof(backup) - strlen(backup) - 1);
  7547. strncat(backup_textfile, "-bak.txt", sizeof(backup_textfile) - strlen(backup_textfile) - 1);
  7548. if (ast_fileexists(backup, NULL, NULL) > 0) {
  7549. ast_filerename(backup, msgfile, NULL);
  7550. rename(backup_textfile, textfile);
  7551. }
  7552. #endif
  7553. }
  7554. DISPOSE(dir, curmsg);
  7555. #ifndef IMAP_STORAGE
  7556. if (cmd) { /* assuming hangup, cleanup backup file */
  7557. make_file(msgfile, sizeof(msgfile), dir, curmsg);
  7558. strcpy(textfile, msgfile);
  7559. strcpy(backup_textfile, msgfile);
  7560. strncat(textfile, ".txt", sizeof(textfile) - strlen(textfile) - 1);
  7561. strncat(backup_textfile, "-bak.txt", sizeof(backup_textfile) - strlen(backup_textfile) - 1);
  7562. rename(backup_textfile, textfile);
  7563. }
  7564. #endif
  7565. }
  7566. /* If anything failed above, we still have this list to free */
  7567. while ((vmtmp = AST_LIST_REMOVE_HEAD(&extensions, list))) {
  7568. inprocess_count(vmtmp->mailbox, vmtmp->context, -1);
  7569. free_user(vmtmp);
  7570. }
  7571. return res ? res : cmd;
  7572. }
  7573. static int wait_file2(struct ast_channel *chan, struct vm_state *vms, char *file)
  7574. {
  7575. int res;
  7576. if ((res = ast_stream_and_wait(chan, file, AST_DIGIT_ANY)) < 0)
  7577. ast_log(AST_LOG_WARNING, "Unable to play message %s\n", file);
  7578. return res;
  7579. }
  7580. static int wait_file(struct ast_channel *chan, struct vm_state *vms, char *file)
  7581. {
  7582. ast_test_suite_event_notify("PLAYVOICE", "Message: Playing %s", file);
  7583. return ast_control_streamfile(chan, file, listen_control_forward_key, listen_control_reverse_key, listen_control_stop_key, listen_control_pause_key, listen_control_restart_key, skipms, NULL);
  7584. }
  7585. static int play_message_category(struct ast_channel *chan, const char *category)
  7586. {
  7587. int res = 0;
  7588. if (!ast_strlen_zero(category))
  7589. res = ast_play_and_wait(chan, category);
  7590. if (res) {
  7591. ast_log(AST_LOG_WARNING, "No sound file for category '%s' was found.\n", category);
  7592. res = 0;
  7593. }
  7594. return res;
  7595. }
  7596. static int play_message_datetime(struct ast_channel *chan, struct ast_vm_user *vmu, const char *origtime, const char *filename)
  7597. {
  7598. int res = 0;
  7599. struct vm_zone *the_zone = NULL;
  7600. time_t t;
  7601. if (ast_get_time_t(origtime, &t, 0, NULL)) {
  7602. ast_log(AST_LOG_WARNING, "Couldn't find origtime in %s\n", filename);
  7603. return 0;
  7604. }
  7605. /* Does this user have a timezone specified? */
  7606. if (!ast_strlen_zero(vmu->zonetag)) {
  7607. /* Find the zone in the list */
  7608. struct vm_zone *z;
  7609. AST_LIST_LOCK(&zones);
  7610. AST_LIST_TRAVERSE(&zones, z, list) {
  7611. if (!strcmp(z->name, vmu->zonetag)) {
  7612. the_zone = z;
  7613. break;
  7614. }
  7615. }
  7616. AST_LIST_UNLOCK(&zones);
  7617. }
  7618. /* No internal variable parsing for now, so we'll comment it out for the time being */
  7619. #if 0
  7620. /* Set the DIFF_* variables */
  7621. ast_localtime(&t, &time_now, NULL);
  7622. tv_now = ast_tvnow();
  7623. ast_localtime(&tv_now, &time_then, NULL);
  7624. /* Day difference */
  7625. if (time_now.tm_year == time_then.tm_year)
  7626. snprintf(temp, sizeof(temp), "%d", time_now.tm_yday);
  7627. else
  7628. snprintf(temp, sizeof(temp), "%d", (time_now.tm_year - time_then.tm_year) * 365 + (time_now.tm_yday - time_then.tm_yday));
  7629. pbx_builtin_setvar_helper(chan, "DIFF_DAY", temp);
  7630. /* Can't think of how other diffs might be helpful, but I'm sure somebody will think of something. */
  7631. #endif
  7632. if (the_zone) {
  7633. res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, ast_channel_language(chan), the_zone->msg_format, the_zone->timezone);
  7634. } else if (!strncasecmp(ast_channel_language(chan), "de", 2)) { /* GERMAN syntax */
  7635. res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, ast_channel_language(chan), "'vm-received' Q 'digits/at' HM", NULL);
  7636. } else if (!strncasecmp(ast_channel_language(chan), "gr", 2)) { /* GREEK syntax */
  7637. res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, ast_channel_language(chan), "'vm-received' q H 'digits/kai' M ", NULL);
  7638. } else if (!strncasecmp(ast_channel_language(chan), "it", 2)) { /* ITALIAN syntax */
  7639. res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, ast_channel_language(chan), "'vm-received' q 'digits/at' 'digits/hours' k 'digits/e' M 'digits/minutes'", NULL);
  7640. } else if (!strcasecmp(ast_channel_language(chan),"ja")) { /* Japanese syntax */
  7641. res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, ast_channel_language(chan), "PHM q 'jp-ni' 'vm-received'", NULL);
  7642. } else if (!strncasecmp(ast_channel_language(chan), "nl", 2)) { /* DUTCH syntax */
  7643. res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, ast_channel_language(chan), "'vm-received' q 'digits/nl-om' HM", NULL);
  7644. } else if (!strncasecmp(ast_channel_language(chan), "no", 2)) { /* NORWEGIAN syntax */
  7645. res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, ast_channel_language(chan), "'vm-received' Q 'digits/at' HM", NULL);
  7646. } else if (!strncasecmp(ast_channel_language(chan), "pl", 2)) { /* POLISH syntax */
  7647. res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, ast_channel_language(chan), "'vm-received' Q HM", NULL);
  7648. } else if (!strncasecmp(ast_channel_language(chan), "pt_BR", 5)) { /* Brazillian PORTUGUESE syntax */
  7649. res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, ast_channel_language(chan), "'vm-received' Ad 'digits/pt-de' B 'digits/pt-de' Y 'digits/pt-as' HM ", NULL);
  7650. } else if (!strncasecmp(ast_channel_language(chan), "se", 2)) { /* SWEDISH syntax */
  7651. res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, ast_channel_language(chan), "'vm-received' dB 'digits/at' k 'and' M", NULL);
  7652. } else if (!strncasecmp(ast_channel_language(chan), "zh", 2)) { /* CHINESE (Taiwan) syntax */
  7653. res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, ast_channel_language(chan), "qR 'vm-received'", NULL);
  7654. } else if (!strncasecmp(ast_channel_language(chan), "vi", 2)) { /* VIETNAMESE syntax */
  7655. res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, ast_channel_language(chan), "'vm-received' A 'digits/day' dB 'digits/year' Y 'digits/at' k 'hours' M 'minutes'", NULL);
  7656. } else {
  7657. res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, ast_channel_language(chan), "'vm-received' q 'digits/at' IMp", NULL);
  7658. }
  7659. #if 0
  7660. pbx_builtin_setvar_helper(chan, "DIFF_DAY", NULL);
  7661. #endif
  7662. return res;
  7663. }
  7664. static int play_message_callerid(struct ast_channel *chan, struct vm_state *vms, char *cid, const char *context, int callback, int saycidnumber)
  7665. {
  7666. int res = 0;
  7667. int i;
  7668. char *callerid, *name;
  7669. char prefile[PATH_MAX] = "";
  7670. /* If voicemail cid is not enabled, or we didn't get cid or context from
  7671. * the attribute file, leave now.
  7672. *
  7673. * TODO Still need to change this so that if this function is called by the
  7674. * message envelope (and someone is explicitly requesting to hear the CID),
  7675. * it does not check to see if CID is enabled in the config file.
  7676. */
  7677. if ((cid == NULL)||(context == NULL))
  7678. return res;
  7679. /* Strip off caller ID number from name */
  7680. ast_debug(1, "VM-CID: composite caller ID received: %s, context: %s\n", cid, context);
  7681. ast_callerid_parse(cid, &name, &callerid);
  7682. if ((!ast_strlen_zero(callerid)) && strcmp(callerid, "Unknown")) {
  7683. /* Check for internal contexts and only */
  7684. /* say extension when the call didn't come from an internal context in the list */
  7685. for (i = 0 ; i < MAX_NUM_CID_CONTEXTS ; i++){
  7686. ast_debug(1, "VM-CID: comparing internalcontext: %s\n", cidinternalcontexts[i]);
  7687. if ((strcmp(cidinternalcontexts[i], context) == 0))
  7688. break;
  7689. }
  7690. if (i != MAX_NUM_CID_CONTEXTS){ /* internal context? */
  7691. if (!res) {
  7692. snprintf(prefile, sizeof(prefile), "%s%s/%s/greet", VM_SPOOL_DIR, context, callerid);
  7693. if (!ast_strlen_zero(prefile)) {
  7694. /* See if we can find a recorded name for this callerid
  7695. * and if found, use that instead of saying number. */
  7696. if (ast_fileexists(prefile, NULL, NULL) > 0) {
  7697. ast_verb(3, "Playing envelope info: CID number '%s' matches mailbox number, playing recorded name\n", callerid);
  7698. if (!callback)
  7699. res = wait_file2(chan, vms, "vm-from");
  7700. res = ast_stream_and_wait(chan, prefile, "");
  7701. } else {
  7702. ast_verb(3, "Playing envelope info: message from '%s'\n", callerid);
  7703. /* Say "from extension" as one saying to sound smoother */
  7704. if (!callback)
  7705. res = wait_file2(chan, vms, "vm-from-extension");
  7706. res = ast_say_digit_str(chan, callerid, "", ast_channel_language(chan));
  7707. }
  7708. }
  7709. }
  7710. } else if (!res) {
  7711. ast_debug(1, "VM-CID: Numeric caller id: (%s)\n", callerid);
  7712. /* If there is a recording for this numeric callerid then play that */
  7713. if (!callback) {
  7714. /* See if we can find a recorded name for this person instead of their extension number */
  7715. snprintf(prefile, sizeof(prefile), "%s/recordings/callerids/%s", ast_config_AST_SPOOL_DIR, callerid);
  7716. if (!saycidnumber && ast_fileexists(prefile, NULL, NULL) > 0) {
  7717. ast_verb(3, "Playing recorded name for CID number '%s' - '%s'\n", callerid,prefile);
  7718. wait_file2(chan, vms, "vm-from");
  7719. res = ast_stream_and_wait(chan, prefile, "");
  7720. ast_verb(3, "Played recorded name result '%d'\n", res);
  7721. } else {
  7722. /* Since this is all nicely figured out, why not say "from phone number" in this case" */
  7723. wait_file2(chan, vms, "vm-from-phonenumber");
  7724. res = ast_say_digit_str(chan, callerid, AST_DIGIT_ANY, ast_channel_language(chan));
  7725. }
  7726. } else {
  7727. res = ast_say_digit_str(chan, callerid, AST_DIGIT_ANY, ast_channel_language(chan));
  7728. }
  7729. }
  7730. } else {
  7731. /* Number unknown */
  7732. ast_debug(1, "VM-CID: From an unknown number\n");
  7733. /* Say "from an unknown caller" as one phrase - it is already recorded by "the voice" anyhow */
  7734. res = wait_file2(chan, vms, "vm-unknown-caller");
  7735. }
  7736. return res;
  7737. }
  7738. static int play_message_duration(struct ast_channel *chan, struct vm_state *vms, const char *duration, int minduration)
  7739. {
  7740. int res = 0;
  7741. int durationm;
  7742. int durations;
  7743. /* Verify that we have a duration for the message */
  7744. if (duration == NULL)
  7745. return res;
  7746. /* Convert from seconds to minutes */
  7747. durations = atoi(duration);
  7748. durationm = (durations / 60);
  7749. ast_debug(1, "VM-Duration: duration is: %d seconds converted to: %d minutes\n", durations, durationm);
  7750. if ((!res) && (durationm >= minduration)) {
  7751. res = wait_file2(chan, vms, "vm-duration");
  7752. /* POLISH syntax */
  7753. if (!strncasecmp(ast_channel_language(chan), "pl", 2)) {
  7754. div_t num = div(durationm, 10);
  7755. if (durationm == 1) {
  7756. res = ast_play_and_wait(chan, "digits/1z");
  7757. res = res ? res : ast_play_and_wait(chan, "vm-minute-ta");
  7758. } else if (num.rem > 1 && num.rem < 5 && num.quot != 1) {
  7759. if (num.rem == 2) {
  7760. if (!num.quot) {
  7761. res = ast_play_and_wait(chan, "digits/2-ie");
  7762. } else {
  7763. res = say_and_wait(chan, durationm - 2 , ast_channel_language(chan));
  7764. res = res ? res : ast_play_and_wait(chan, "digits/2-ie");
  7765. }
  7766. } else {
  7767. res = say_and_wait(chan, durationm, ast_channel_language(chan));
  7768. }
  7769. res = res ? res : ast_play_and_wait(chan, "vm-minute-ty");
  7770. } else {
  7771. res = say_and_wait(chan, durationm, ast_channel_language(chan));
  7772. res = res ? res : ast_play_and_wait(chan, "vm-minute-t");
  7773. }
  7774. /* DEFAULT syntax */
  7775. } else {
  7776. res = ast_say_number(chan, durationm, AST_DIGIT_ANY, ast_channel_language(chan), NULL);
  7777. res = wait_file2(chan, vms, "vm-minutes");
  7778. }
  7779. }
  7780. return res;
  7781. }
  7782. static int play_message(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms)
  7783. {
  7784. int res = 0;
  7785. char filename[256], *cid;
  7786. const char *origtime, *context, *category, *duration, *flag;
  7787. struct ast_config *msg_cfg;
  7788. struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
  7789. vms->starting = 0;
  7790. make_file(vms->fn, sizeof(vms->fn), vms->curdir, vms->curmsg);
  7791. adsi_message(chan, vms);
  7792. if (!vms->curmsg) {
  7793. res = wait_file2(chan, vms, "vm-first"); /* "First" */
  7794. } else if (vms->curmsg == vms->lastmsg) {
  7795. res = wait_file2(chan, vms, "vm-last"); /* "last" */
  7796. }
  7797. snprintf(filename, sizeof(filename), "%s.txt", vms->fn);
  7798. RETRIEVE(vms->curdir, vms->curmsg, vmu->mailbox, vmu->context);
  7799. msg_cfg = ast_config_load(filename, config_flags);
  7800. if (!valid_config(msg_cfg)) {
  7801. ast_log(LOG_WARNING, "No message attribute file?!! (%s)\n", filename);
  7802. return 0;
  7803. }
  7804. flag = ast_variable_retrieve(msg_cfg, "message", "flag");
  7805. /* Play the word urgent if we are listening to urgent messages */
  7806. if (!ast_strlen_zero(flag) && !strcmp(flag, "Urgent")) {
  7807. res = wait_file2(chan, vms, "vm-Urgent"); /* "urgent" */
  7808. }
  7809. if (!res) {
  7810. /* XXX Why are we playing messages above, and then playing the same language-specific stuff here? */
  7811. /* POLISH syntax */
  7812. if (!strncasecmp(ast_channel_language(chan), "pl", 2)) {
  7813. if (vms->curmsg && (vms->curmsg != vms->lastmsg)) {
  7814. int ten, one;
  7815. char nextmsg[256];
  7816. ten = (vms->curmsg + 1) / 10;
  7817. one = (vms->curmsg + 1) % 10;
  7818. if (vms->curmsg < 20) {
  7819. snprintf(nextmsg, sizeof(nextmsg), "digits/n-%d", vms->curmsg + 1);
  7820. res = wait_file2(chan, vms, nextmsg);
  7821. } else {
  7822. snprintf(nextmsg, sizeof(nextmsg), "digits/n-%d", ten * 10);
  7823. res = wait_file2(chan, vms, nextmsg);
  7824. if (one > 0) {
  7825. if (!res) {
  7826. snprintf(nextmsg, sizeof(nextmsg), "digits/n-%d", one);
  7827. res = wait_file2(chan, vms, nextmsg);
  7828. }
  7829. }
  7830. }
  7831. }
  7832. if (!res)
  7833. res = wait_file2(chan, vms, "vm-message");
  7834. /* HEBREW syntax */
  7835. } else if (!strncasecmp(ast_channel_language(chan), "he", 2)) {
  7836. if (!vms->curmsg) {
  7837. res = wait_file2(chan, vms, "vm-message");
  7838. res = wait_file2(chan, vms, "vm-first");
  7839. } else if (vms->curmsg == vms->lastmsg) {
  7840. res = wait_file2(chan, vms, "vm-message");
  7841. res = wait_file2(chan, vms, "vm-last");
  7842. } else {
  7843. res = wait_file2(chan, vms, "vm-message");
  7844. res = wait_file2(chan, vms, "vm-number");
  7845. res = ast_say_number(chan, vms->curmsg + 1, AST_DIGIT_ANY, ast_channel_language(chan), "f");
  7846. }
  7847. /* VIETNAMESE syntax */
  7848. } else if (!strncasecmp(ast_channel_language(chan), "vi", 2)) {
  7849. if (!vms->curmsg) {
  7850. res = wait_file2(chan, vms, "vm-message");
  7851. res = wait_file2(chan, vms, "vm-first");
  7852. } else if (vms->curmsg == vms->lastmsg) {
  7853. res = wait_file2(chan, vms, "vm-message");
  7854. res = wait_file2(chan, vms, "vm-last");
  7855. } else {
  7856. res = wait_file2(chan, vms, "vm-message");
  7857. res = wait_file2(chan, vms, "vm-number");
  7858. res = ast_say_number(chan, vms->curmsg + 1, AST_DIGIT_ANY, ast_channel_language(chan), "f");
  7859. }
  7860. } else {
  7861. if (!strncasecmp(ast_channel_language(chan), "se", 2)) { /* SWEDISH syntax */
  7862. res = wait_file2(chan, vms, "vm-meddelandet"); /* "message" */
  7863. } else { /* DEFAULT syntax */
  7864. res = wait_file2(chan, vms, "vm-message");
  7865. }
  7866. if (vms->curmsg && (vms->curmsg != vms->lastmsg)) {
  7867. if (!res) {
  7868. ast_test_suite_event_notify("PLAYBACK", "Message: message number");
  7869. res = ast_say_number(chan, vms->curmsg + 1, AST_DIGIT_ANY, ast_channel_language(chan), NULL);
  7870. }
  7871. }
  7872. }
  7873. }
  7874. if (!valid_config(msg_cfg)) {
  7875. ast_log(AST_LOG_WARNING, "No message attribute file?!! (%s)\n", filename);
  7876. return 0;
  7877. }
  7878. if (!(origtime = ast_variable_retrieve(msg_cfg, "message", "origtime"))) {
  7879. ast_log(AST_LOG_WARNING, "No origtime?!\n");
  7880. DISPOSE(vms->curdir, vms->curmsg);
  7881. ast_config_destroy(msg_cfg);
  7882. return 0;
  7883. }
  7884. cid = ast_strdupa(ast_variable_retrieve(msg_cfg, "message", "callerid"));
  7885. duration = ast_variable_retrieve(msg_cfg, "message", "duration");
  7886. category = ast_variable_retrieve(msg_cfg, "message", "category");
  7887. context = ast_variable_retrieve(msg_cfg, "message", "context");
  7888. if (!strncasecmp("macro", context, 5)) /* Macro names in contexts are useless for our needs */
  7889. context = ast_variable_retrieve(msg_cfg, "message", "macrocontext");
  7890. if (!res) {
  7891. res = play_message_category(chan, category);
  7892. }
  7893. if ((!res) && (ast_test_flag(vmu, VM_ENVELOPE))) {
  7894. res = play_message_datetime(chan, vmu, origtime, filename);
  7895. }
  7896. if ((!res) && (ast_test_flag(vmu, VM_SAYCID))) {
  7897. res = play_message_callerid(chan, vms, cid, context, 0, 0);
  7898. }
  7899. if ((!res) && (ast_test_flag(vmu, VM_SAYDURATION))) {
  7900. res = play_message_duration(chan, vms, duration, vmu->saydurationm);
  7901. }
  7902. /* Allow pressing '1' to skip envelope / callerid */
  7903. if (res == '1') {
  7904. ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", res, res);
  7905. res = 0;
  7906. }
  7907. ast_config_destroy(msg_cfg);
  7908. if (!res) {
  7909. make_file(vms->fn, sizeof(vms->fn), vms->curdir, vms->curmsg);
  7910. #ifdef IMAP_STORAGE
  7911. ast_mutex_lock(&vms->lock);
  7912. #endif
  7913. vms->heard[vms->curmsg] = 1;
  7914. #ifdef IMAP_STORAGE
  7915. ast_mutex_unlock(&vms->lock);
  7916. /*IMAP storage stores any prepended message from a forward
  7917. * as a separate file from the rest of the message
  7918. */
  7919. if (!ast_strlen_zero(vms->introfn) && ast_fileexists(vms->introfn, NULL, NULL) > 0) {
  7920. wait_file(chan, vms, vms->introfn);
  7921. }
  7922. #endif
  7923. if ((res = wait_file(chan, vms, vms->fn)) < 0) {
  7924. ast_log(AST_LOG_WARNING, "Playback of message %s failed\n", vms->fn);
  7925. res = 0;
  7926. }
  7927. ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", res, res);
  7928. }
  7929. DISPOSE(vms->curdir, vms->curmsg);
  7930. return res;
  7931. }
  7932. #ifdef IMAP_STORAGE
  7933. static int imap_remove_file(char *dir, int msgnum)
  7934. {
  7935. char fn[PATH_MAX];
  7936. char full_fn[PATH_MAX];
  7937. char intro[PATH_MAX] = {0,};
  7938. if (msgnum > -1) {
  7939. make_file(fn, sizeof(fn), dir, msgnum);
  7940. snprintf(intro, sizeof(intro), "%sintro", fn);
  7941. } else
  7942. ast_copy_string(fn, dir, sizeof(fn));
  7943. if ((msgnum < 0 && imapgreetings) || msgnum > -1) {
  7944. ast_filedelete(fn, NULL);
  7945. if (!ast_strlen_zero(intro)) {
  7946. ast_filedelete(intro, NULL);
  7947. }
  7948. snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
  7949. unlink(full_fn);
  7950. }
  7951. return 0;
  7952. }
  7953. static int imap_delete_old_greeting (char *dir, struct vm_state *vms)
  7954. {
  7955. char *file, *filename;
  7956. char *attachment;
  7957. char arg[10];
  7958. int i;
  7959. BODY* body;
  7960. file = strrchr(ast_strdupa(dir), '/');
  7961. if (file) {
  7962. *file++ = '\0';
  7963. } else {
  7964. ast_log(AST_LOG_ERROR, "Failed to procure file name from directory passed. You should never see this.\n");
  7965. return -1;
  7966. }
  7967. ast_mutex_lock(&vms->lock);
  7968. for (i = 0; i < vms->mailstream->nmsgs; i++) {
  7969. mail_fetchstructure(vms->mailstream, i + 1, &body);
  7970. /* We have the body, now we extract the file name of the first attachment. */
  7971. if (body->nested.part->next && body->nested.part->next->body.parameter->value) {
  7972. attachment = ast_strdupa(body->nested.part->next->body.parameter->value);
  7973. } else {
  7974. ast_log(AST_LOG_ERROR, "There is no file attached to this IMAP message.\n");
  7975. ast_mutex_unlock(&vms->lock);
  7976. return -1;
  7977. }
  7978. filename = strsep(&attachment, ".");
  7979. if (!strcmp(filename, file)) {
  7980. sprintf(arg, "%d", i + 1);
  7981. mail_setflag(vms->mailstream, arg, "\\DELETED");
  7982. }
  7983. }
  7984. mail_expunge(vms->mailstream);
  7985. ast_mutex_unlock(&vms->lock);
  7986. return 0;
  7987. }
  7988. #elif !defined(IMAP_STORAGE)
  7989. static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu, int box)
  7990. {
  7991. int count_msg, last_msg;
  7992. ast_copy_string(vms->curbox, mbox(vmu, box), sizeof(vms->curbox));
  7993. /* Rename the member vmbox HERE so that we don't try to return before
  7994. * we know what's going on.
  7995. */
  7996. snprintf(vms->vmbox, sizeof(vms->vmbox), "vm-%s", vms->curbox);
  7997. /* Faster to make the directory than to check if it exists. */
  7998. create_dirpath(vms->curdir, sizeof(vms->curdir), vmu->context, vms->username, vms->curbox);
  7999. /* traverses directory using readdir (or select query for ODBC) */
  8000. count_msg = count_messages(vmu, vms->curdir);
  8001. if (count_msg < 0) {
  8002. return count_msg;
  8003. } else {
  8004. vms->lastmsg = count_msg - 1;
  8005. }
  8006. if (vm_allocate_dh(vms, vmu, count_msg)) {
  8007. return -1;
  8008. }
  8009. /*
  8010. The following test is needed in case sequencing gets messed up.
  8011. There appears to be more than one way to mess up sequence, so
  8012. we will not try to find all of the root causes--just fix it when
  8013. detected.
  8014. */
  8015. if (vm_lock_path(vms->curdir)) {
  8016. ast_log(AST_LOG_ERROR, "Could not open mailbox %s: mailbox is locked\n", vms->curdir);
  8017. return ERROR_LOCK_PATH;
  8018. }
  8019. /* for local storage, checks directory for messages up to maxmsg limit */
  8020. last_msg = last_message_index(vmu, vms->curdir);
  8021. ast_unlock_path(vms->curdir);
  8022. if (last_msg < -1) {
  8023. return last_msg;
  8024. } else if (vms->lastmsg != last_msg) {
  8025. ast_log(LOG_NOTICE, "Resequencing Mailbox: %s, expected %d but found %d message(s) in box with max threshold of %d.\n", vms->curdir, last_msg + 1, vms->lastmsg + 1, vmu->maxmsg);
  8026. resequence_mailbox(vmu, vms->curdir, count_msg);
  8027. }
  8028. return 0;
  8029. }
  8030. #endif
  8031. static int close_mailbox(struct vm_state *vms, struct ast_vm_user *vmu)
  8032. {
  8033. int x = 0;
  8034. int last_msg_idx = 0;
  8035. #ifndef IMAP_STORAGE
  8036. int res = 0, nummsg;
  8037. char fn2[PATH_MAX];
  8038. #endif
  8039. if (vms->lastmsg <= -1) {
  8040. goto done;
  8041. }
  8042. vms->curmsg = -1;
  8043. #ifndef IMAP_STORAGE
  8044. /* Get the deleted messages fixed */
  8045. if (vm_lock_path(vms->curdir)) {
  8046. return ERROR_LOCK_PATH;
  8047. }
  8048. /* update count as message may have arrived while we've got mailbox open */
  8049. last_msg_idx = last_message_index(vmu, vms->curdir);
  8050. if (last_msg_idx != vms->lastmsg) {
  8051. ast_log(AST_LOG_NOTICE, "%d messages received after mailbox opened.\n", last_msg_idx - vms->lastmsg);
  8052. }
  8053. /* must check up to last detected message, just in case it is erroneously greater than maxmsg */
  8054. for (x = 0; x < last_msg_idx + 1; x++) {
  8055. if (!vms->deleted[x] && ((strcasecmp(vms->curbox, "INBOX") && strcasecmp(vms->curbox, "Urgent")) || !vms->heard[x] || (vms->heard[x] && !ast_test_flag(vmu, VM_MOVEHEARD)))) {
  8056. /* Save this message. It's not in INBOX or hasn't been heard */
  8057. make_file(vms->fn, sizeof(vms->fn), vms->curdir, x);
  8058. if (!EXISTS(vms->curdir, x, vms->fn, NULL)) {
  8059. break;
  8060. }
  8061. vms->curmsg++;
  8062. make_file(fn2, sizeof(fn2), vms->curdir, vms->curmsg);
  8063. if (strcmp(vms->fn, fn2)) {
  8064. RENAME(vms->curdir, x, vmu->mailbox, vmu->context, vms->curdir, vms->curmsg, vms->fn, fn2);
  8065. }
  8066. } else if ((!strcasecmp(vms->curbox, "INBOX") || !strcasecmp(vms->curbox, "Urgent")) && vms->heard[x] && ast_test_flag(vmu, VM_MOVEHEARD) && !vms->deleted[x]) {
  8067. /* Move to old folder before deleting */
  8068. res = save_to_folder(vmu, vms, x, 1, NULL, 0);
  8069. if (res == ERROR_LOCK_PATH) {
  8070. /* If save failed do not delete the message */
  8071. ast_log(AST_LOG_WARNING, "Save failed. Not moving message: %s.\n", res == ERROR_LOCK_PATH ? "unable to lock path" : "destination folder full");
  8072. vms->deleted[x] = 0;
  8073. vms->heard[x] = 0;
  8074. --x;
  8075. }
  8076. } else if (vms->deleted[x] && vmu->maxdeletedmsg) {
  8077. /* Move to deleted folder */
  8078. res = save_to_folder(vmu, vms, x, 10, NULL, 0);
  8079. if (res == ERROR_LOCK_PATH) {
  8080. /* If save failed do not delete the message */
  8081. vms->deleted[x] = 0;
  8082. vms->heard[x] = 0;
  8083. --x;
  8084. }
  8085. } else if (vms->deleted[x] && ast_check_realtime("voicemail_data")) {
  8086. /* If realtime storage enabled - we should explicitly delete this message,
  8087. cause RENAME() will overwrite files, but will keep duplicate records in RT-storage */
  8088. make_file(vms->fn, sizeof(vms->fn), vms->curdir, x);
  8089. if (EXISTS(vms->curdir, x, vms->fn, NULL)) {
  8090. DELETE(vms->curdir, x, vms->fn, vmu);
  8091. }
  8092. }
  8093. }
  8094. /* Delete ALL remaining messages */
  8095. nummsg = x - 1;
  8096. for (x = vms->curmsg + 1; x <= nummsg; x++) {
  8097. make_file(vms->fn, sizeof(vms->fn), vms->curdir, x);
  8098. if (EXISTS(vms->curdir, x, vms->fn, NULL)) {
  8099. DELETE(vms->curdir, x, vms->fn, vmu);
  8100. }
  8101. }
  8102. ast_unlock_path(vms->curdir);
  8103. #else /* defined(IMAP_STORAGE) */
  8104. ast_mutex_lock(&vms->lock);
  8105. if (vms->deleted) {
  8106. /* Since we now expunge after each delete, deleting in reverse order
  8107. * ensures that no reordering occurs between each step. */
  8108. last_msg_idx = vms->dh_arraysize;
  8109. for (x = last_msg_idx - 1; x >= 0; x--) {
  8110. if (vms->deleted[x]) {
  8111. ast_debug(3, "IMAP delete of %d\n", x);
  8112. DELETE(vms->curdir, x, vms->fn, vmu);
  8113. }
  8114. }
  8115. }
  8116. #endif
  8117. done:
  8118. if (vms->deleted) {
  8119. ast_free(vms->deleted);
  8120. vms->deleted = NULL;
  8121. }
  8122. if (vms->heard) {
  8123. ast_free(vms->heard);
  8124. vms->heard = NULL;
  8125. }
  8126. vms->dh_arraysize = 0;
  8127. #ifdef IMAP_STORAGE
  8128. ast_mutex_unlock(&vms->lock);
  8129. #endif
  8130. return 0;
  8131. }
  8132. /* In Greek even though we CAN use a syntax like "friends messages"
  8133. * ("filika mynhmata") it is not elegant. This also goes for "work/family messages"
  8134. * ("ergasiaka/oikogeniaka mynhmata"). Therefore it is better to use a reversed
  8135. * syntax for the above three categories which is more elegant.
  8136. */
  8137. static int vm_play_folder_name_gr(struct ast_channel *chan, char *box)
  8138. {
  8139. int cmd;
  8140. char *buf;
  8141. buf = ast_alloca(strlen(box) + 2);
  8142. strcpy(buf, box);
  8143. strcat(buf, "s");
  8144. if (!strcasecmp(box, "vm-INBOX") || !strcasecmp(box, "vm-Old")){
  8145. cmd = ast_play_and_wait(chan, buf); /* "NEA / PALIA" */
  8146. return cmd ? cmd : ast_play_and_wait(chan, "vm-messages"); /* "messages" -> "MYNHMATA" */
  8147. } else {
  8148. cmd = ast_play_and_wait(chan, "vm-messages"); /* "messages" -> "MYNHMATA" */
  8149. return cmd ? cmd : ast_play_and_wait(chan, box); /* friends/family/work... -> "FILWN"/"OIKOGENIAS"/"DOULEIAS"*/
  8150. }
  8151. }
  8152. static int vm_play_folder_name_ja(struct ast_channel *chan, char *box)
  8153. {
  8154. int cmd;
  8155. if (!strcasecmp(box, "vm-INBOX") || !strcasecmp(box, "vm-Old")) {
  8156. cmd = ast_play_and_wait(chan, box);
  8157. return cmd ? cmd : ast_play_and_wait(chan, "vm-messages");
  8158. } else {
  8159. cmd = ast_play_and_wait(chan, box);
  8160. return cmd;
  8161. }
  8162. }
  8163. static int vm_play_folder_name_pl(struct ast_channel *chan, char *box)
  8164. {
  8165. int cmd;
  8166. if (!strcasecmp(box, "vm-INBOX") || !strcasecmp(box, "vm-Old")) {
  8167. if (!strcasecmp(box, "vm-INBOX"))
  8168. cmd = ast_play_and_wait(chan, "vm-new-e");
  8169. else
  8170. cmd = ast_play_and_wait(chan, "vm-old-e");
  8171. return cmd ? cmd : ast_play_and_wait(chan, "vm-messages");
  8172. } else {
  8173. cmd = ast_play_and_wait(chan, "vm-messages");
  8174. return cmd ? cmd : ast_play_and_wait(chan, box);
  8175. }
  8176. }
  8177. static int vm_play_folder_name_ua(struct ast_channel *chan, char *box)
  8178. {
  8179. int cmd;
  8180. if (!strcasecmp(box, "vm-Family") || !strcasecmp(box, "vm-Friends") || !strcasecmp(box, "vm-Work")){
  8181. cmd = ast_play_and_wait(chan, "vm-messages");
  8182. return cmd ? cmd : ast_play_and_wait(chan, box);
  8183. } else {
  8184. cmd = ast_play_and_wait(chan, box);
  8185. return cmd ? cmd : ast_play_and_wait(chan, "vm-messages");
  8186. }
  8187. }
  8188. static int vm_play_folder_name(struct ast_channel *chan, char *box)
  8189. {
  8190. int cmd;
  8191. if ( !strncasecmp(ast_channel_language(chan), "it", 2) ||
  8192. !strncasecmp(ast_channel_language(chan), "es", 2) ||
  8193. !strncasecmp(ast_channel_language(chan), "pt", 2)) { /* Italian, Spanish, or Portuguese syntax */
  8194. cmd = ast_play_and_wait(chan, "vm-messages"); /* "messages */
  8195. return cmd ? cmd : ast_play_and_wait(chan, box);
  8196. } else if (!strncasecmp(ast_channel_language(chan), "gr", 2)) {
  8197. return vm_play_folder_name_gr(chan, box);
  8198. } else if (!strncasecmp(ast_channel_language(chan), "he", 2)) { /* Hebrew syntax */
  8199. return ast_play_and_wait(chan, box);
  8200. } else if (!strncasecmp(ast_channel_language(chan), "ja", 2)) { /* Japanese syntax */
  8201. return vm_play_folder_name_ja(chan, box);
  8202. } else if (!strncasecmp(ast_channel_language(chan), "pl", 2)) {
  8203. return vm_play_folder_name_pl(chan, box);
  8204. } else if (!strncasecmp(ast_channel_language(chan), "ua", 2)) { /* Ukrainian syntax */
  8205. return vm_play_folder_name_ua(chan, box);
  8206. } else if (!strncasecmp(ast_channel_language(chan), "vi", 2)) {
  8207. return ast_play_and_wait(chan, box);
  8208. } else { /* Default English */
  8209. cmd = ast_play_and_wait(chan, box);
  8210. return cmd ? cmd : ast_play_and_wait(chan, "vm-messages"); /* "messages */
  8211. }
  8212. }
  8213. /* GREEK SYNTAX
  8214. In greek the plural for old/new is
  8215. different so we need the following files
  8216. We also need vm-denExeteMynhmata because
  8217. this syntax is different.
  8218. -> vm-Olds.wav : "Palia"
  8219. -> vm-INBOXs.wav : "Nea"
  8220. -> vm-denExeteMynhmata : "den exete mynhmata"
  8221. */
  8222. static int vm_intro_gr(struct ast_channel *chan, struct vm_state *vms)
  8223. {
  8224. int res = 0;
  8225. if (vms->newmessages) {
  8226. res = ast_play_and_wait(chan, "vm-youhave");
  8227. if (!res)
  8228. res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, ast_channel_language(chan), NULL);
  8229. if (!res) {
  8230. if (vms->newmessages == 1) {
  8231. res = ast_play_and_wait(chan, "vm-INBOX");
  8232. if (!res)
  8233. res = ast_play_and_wait(chan, "vm-message");
  8234. } else {
  8235. res = ast_play_and_wait(chan, "vm-INBOXs");
  8236. if (!res)
  8237. res = ast_play_and_wait(chan, "vm-messages");
  8238. }
  8239. }
  8240. } else if (vms->oldmessages){
  8241. res = ast_play_and_wait(chan, "vm-youhave");
  8242. if (!res)
  8243. res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, ast_channel_language(chan), NULL);
  8244. if (vms->oldmessages == 1){
  8245. res = ast_play_and_wait(chan, "vm-Old");
  8246. if (!res)
  8247. res = ast_play_and_wait(chan, "vm-message");
  8248. } else {
  8249. res = ast_play_and_wait(chan, "vm-Olds");
  8250. if (!res)
  8251. res = ast_play_and_wait(chan, "vm-messages");
  8252. }
  8253. } else if (!vms->oldmessages && !vms->newmessages)
  8254. res = ast_play_and_wait(chan, "vm-denExeteMynhmata");
  8255. return res;
  8256. }
  8257. /* Version of vm_intro() designed to work for many languages.
  8258. *
  8259. * It is hoped that this function can prevent the proliferation of
  8260. * language-specific vm_intro() functions and in time replace the language-
  8261. * specific functions which already exist. An examination of the language-
  8262. * specific functions revealed that they all corrected the same deficiencies
  8263. * in vm_intro_en() (which was the default function). Namely:
  8264. *
  8265. * 1) The vm-Old and vm-INBOX sound files were overloaded. The English
  8266. * wording of the voicemail greeting hides this problem. For example,
  8267. * vm-INBOX contains only the word "new". This means that both of these
  8268. * sequences produce valid utterances:
  8269. * * vm-youhave digit/1 vm-INBOX vm-message (you have one new message)
  8270. * * vm-press digit/1 vm-for vm-INBOX vm-messages (press 1 for new messages)
  8271. * However, if we rerecord vm-INBOX to say "the new" (which is unavoidable
  8272. * in many languages) the first utterance becomes "you have 1 the new message".
  8273. * 2) The function contains hardcoded rules for pluralizing the word "message".
  8274. * These rules are correct for English, but not for many other languages.
  8275. * 3) No attempt is made to pluralize the adjectives ("old" and "new") as
  8276. * required in many languages.
  8277. * 4) The gender of the word for "message" is not specified. This is a problem
  8278. * because in many languages the gender of the number in phrases such
  8279. * as "you have one new message" must match the gender of the word
  8280. * meaning "message".
  8281. *
  8282. * Fixing these problems for each new language has meant duplication of effort.
  8283. * This new function solves the problems in the following general ways:
  8284. * 1) Add new sound files vm-new and vm-old. These can be linked to vm-INBOX
  8285. * and vm-Old respectively for those languages where it makes sense.
  8286. * 2) Call ast_say_counted_noun() to put the proper gender and number prefix
  8287. * on vm-message.
  8288. * 3) Call ast_say_counted_adjective() to put the proper gender and number
  8289. * prefix on vm-new and vm-old (none for English).
  8290. * 4) Pass the gender of the language's word for "message" as an agument to
  8291. * this function which is can in turn pass on to the functions which
  8292. * say numbers and put endings on nounds and adjectives.
  8293. *
  8294. * All languages require these messages:
  8295. * vm-youhave "You have..."
  8296. * vm-and "and"
  8297. * vm-no "no" (in the sense of "none", as in "you have no messages")
  8298. *
  8299. * To use it for English, you will need these additional sound files:
  8300. * vm-new "new"
  8301. * vm-message "message", singular
  8302. * vm-messages "messages", plural
  8303. *
  8304. * If you use it for Russian and other slavic languages, you will need these additional sound files:
  8305. *
  8306. * vm-newn "novoye" (singular, neuter)
  8307. * vm-newx "novikh" (counting plural form, genative plural)
  8308. * vm-message "sobsheniye" (singular form)
  8309. * vm-messagex1 "sobsheniya" (first counting plural form, genative singular)
  8310. * vm-messagex2 "sobsheniy" (second counting plural form, genative plural)
  8311. * digits/1n "odno" (neuter singular for phrases such as "one message" or "thirty one messages")
  8312. * digits/2n "dva" (neuter singular)
  8313. */
  8314. static int vm_intro_multilang(struct ast_channel *chan, struct vm_state *vms, const char message_gender[])
  8315. {
  8316. int res;
  8317. int lastnum = 0;
  8318. res = ast_play_and_wait(chan, "vm-youhave");
  8319. if (!res && vms->newmessages) {
  8320. lastnum = vms->newmessages;
  8321. if (!(res = ast_say_number(chan, lastnum, AST_DIGIT_ANY, ast_channel_language(chan), message_gender))) {
  8322. res = ast_say_counted_adjective(chan, lastnum, "vm-new", message_gender);
  8323. }
  8324. if (!res && vms->oldmessages) {
  8325. res = ast_play_and_wait(chan, "vm-and");
  8326. }
  8327. }
  8328. if (!res && vms->oldmessages) {
  8329. lastnum = vms->oldmessages;
  8330. if (!(res = ast_say_number(chan, lastnum, AST_DIGIT_ANY, ast_channel_language(chan), message_gender))) {
  8331. res = ast_say_counted_adjective(chan, lastnum, "vm-old", message_gender);
  8332. }
  8333. }
  8334. if (!res) {
  8335. if (lastnum == 0) {
  8336. res = ast_play_and_wait(chan, "vm-no");
  8337. }
  8338. if (!res) {
  8339. res = ast_say_counted_noun(chan, lastnum, "vm-message");
  8340. }
  8341. }
  8342. return res;
  8343. }
  8344. /* Default Hebrew syntax */
  8345. static int vm_intro_he(struct ast_channel *chan, struct vm_state *vms)
  8346. {
  8347. int res = 0;
  8348. /* Introduce messages they have */
  8349. if (!res) {
  8350. if ((vms->newmessages) || (vms->oldmessages)) {
  8351. res = ast_play_and_wait(chan, "vm-youhave");
  8352. }
  8353. /*
  8354. * The word "shtei" refers to the number 2 in hebrew when performing a count
  8355. * of elements. In Hebrew, there are 6 forms of enumerating the number 2 for
  8356. * an element, this is one of them.
  8357. */
  8358. if (vms->newmessages) {
  8359. if (!res) {
  8360. if (vms->newmessages == 1) {
  8361. res = ast_play_and_wait(chan, "vm-INBOX1");
  8362. } else {
  8363. if (vms->newmessages == 2) {
  8364. res = ast_play_and_wait(chan, "vm-shtei");
  8365. } else {
  8366. res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, ast_channel_language(chan), "f");
  8367. }
  8368. res = ast_play_and_wait(chan, "vm-INBOX");
  8369. }
  8370. }
  8371. if (vms->oldmessages && !res) {
  8372. res = ast_play_and_wait(chan, "vm-and");
  8373. if (vms->oldmessages == 1) {
  8374. res = ast_play_and_wait(chan, "vm-Old1");
  8375. } else {
  8376. if (vms->oldmessages == 2) {
  8377. res = ast_play_and_wait(chan, "vm-shtei");
  8378. } else {
  8379. res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, ast_channel_language(chan), "f");
  8380. }
  8381. res = ast_play_and_wait(chan, "vm-Old");
  8382. }
  8383. }
  8384. }
  8385. if (!res && vms->oldmessages && !vms->newmessages) {
  8386. if (!res) {
  8387. if (vms->oldmessages == 1) {
  8388. res = ast_play_and_wait(chan, "vm-Old1");
  8389. } else {
  8390. if (vms->oldmessages == 2) {
  8391. res = ast_play_and_wait(chan, "vm-shtei");
  8392. } else {
  8393. res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, ast_channel_language(chan), "f");
  8394. }
  8395. res = ast_play_and_wait(chan, "vm-Old");
  8396. }
  8397. }
  8398. }
  8399. if (!res) {
  8400. if (!vms->oldmessages && !vms->newmessages) {
  8401. if (!res) {
  8402. res = ast_play_and_wait(chan, "vm-nomessages");
  8403. }
  8404. }
  8405. }
  8406. }
  8407. return res;
  8408. }
  8409. /* Japanese syntax */
  8410. static int vm_intro_ja(struct ast_channel *chan,struct vm_state *vms)
  8411. {
  8412. /* Introduce messages they have */
  8413. int res;
  8414. if (vms->newmessages) {
  8415. res = ast_play_and_wait(chan, "vm-INBOX");
  8416. if (!res)
  8417. res = ast_play_and_wait(chan, "vm-message");
  8418. if (!res)
  8419. res = ast_play_and_wait(chan, "jp-ga");
  8420. if (!res)
  8421. res = say_and_wait(chan, vms->newmessages, ast_channel_language(chan));
  8422. if (vms->oldmessages && !res)
  8423. res = ast_play_and_wait(chan, "silence/1");
  8424. }
  8425. if (vms->oldmessages) {
  8426. res = ast_play_and_wait(chan, "vm-Old");
  8427. if (!res)
  8428. res = ast_play_and_wait(chan, "vm-message");
  8429. if (!res)
  8430. res = ast_play_and_wait(chan, "jp-ga");
  8431. if (!res)
  8432. res = say_and_wait(chan, vms->oldmessages, ast_channel_language(chan));
  8433. }
  8434. if (!vms->oldmessages && !vms->newmessages) {
  8435. res = ast_play_and_wait(chan, "vm-messages");
  8436. if (!res)
  8437. res = ast_play_and_wait(chan, "jp-wa");
  8438. if (!res)
  8439. res = ast_play_and_wait(chan, "jp-arimasen");
  8440. }
  8441. else {
  8442. res = ast_play_and_wait(chan, "jp-arimasu");
  8443. }
  8444. return res;
  8445. } /* Japanese */
  8446. /* Default English syntax */
  8447. static int vm_intro_en(struct ast_channel *chan, struct vm_state *vms)
  8448. {
  8449. int res;
  8450. /* Introduce messages they have */
  8451. res = ast_play_and_wait(chan, "vm-youhave");
  8452. if (!res) {
  8453. if (vms->urgentmessages) {
  8454. res = say_and_wait(chan, vms->urgentmessages, ast_channel_language(chan));
  8455. if (!res)
  8456. res = ast_play_and_wait(chan, "vm-Urgent");
  8457. if ((vms->oldmessages || vms->newmessages) && !res) {
  8458. res = ast_play_and_wait(chan, "vm-and");
  8459. } else if (!res) {
  8460. if (vms->urgentmessages == 1)
  8461. res = ast_play_and_wait(chan, "vm-message");
  8462. else
  8463. res = ast_play_and_wait(chan, "vm-messages");
  8464. }
  8465. }
  8466. if (vms->newmessages) {
  8467. res = say_and_wait(chan, vms->newmessages, ast_channel_language(chan));
  8468. if (!res)
  8469. res = ast_play_and_wait(chan, "vm-INBOX");
  8470. if (vms->oldmessages && !res)
  8471. res = ast_play_and_wait(chan, "vm-and");
  8472. else if (!res) {
  8473. if (vms->newmessages == 1)
  8474. res = ast_play_and_wait(chan, "vm-message");
  8475. else
  8476. res = ast_play_and_wait(chan, "vm-messages");
  8477. }
  8478. }
  8479. if (!res && vms->oldmessages) {
  8480. res = say_and_wait(chan, vms->oldmessages, ast_channel_language(chan));
  8481. if (!res)
  8482. res = ast_play_and_wait(chan, "vm-Old");
  8483. if (!res) {
  8484. if (vms->oldmessages == 1)
  8485. res = ast_play_and_wait(chan, "vm-message");
  8486. else
  8487. res = ast_play_and_wait(chan, "vm-messages");
  8488. }
  8489. }
  8490. if (!res) {
  8491. if (!vms->urgentmessages && !vms->oldmessages && !vms->newmessages) {
  8492. res = ast_play_and_wait(chan, "vm-no");
  8493. if (!res)
  8494. res = ast_play_and_wait(chan, "vm-messages");
  8495. }
  8496. }
  8497. }
  8498. return res;
  8499. }
  8500. /* ITALIAN syntax */
  8501. static int vm_intro_it(struct ast_channel *chan, struct vm_state *vms)
  8502. {
  8503. /* Introduce messages they have */
  8504. int res;
  8505. if (!vms->oldmessages && !vms->newmessages &&!vms->urgentmessages)
  8506. res = ast_play_and_wait(chan, "vm-no") ||
  8507. ast_play_and_wait(chan, "vm-message");
  8508. else
  8509. res = ast_play_and_wait(chan, "vm-youhave");
  8510. if (!res && vms->newmessages) {
  8511. res = (vms->newmessages == 1) ?
  8512. ast_play_and_wait(chan, "digits/un") ||
  8513. ast_play_and_wait(chan, "vm-nuovo") ||
  8514. ast_play_and_wait(chan, "vm-message") :
  8515. /* 2 or more new messages */
  8516. say_and_wait(chan, vms->newmessages, ast_channel_language(chan)) ||
  8517. ast_play_and_wait(chan, "vm-nuovi") ||
  8518. ast_play_and_wait(chan, "vm-messages");
  8519. if (!res && vms->oldmessages)
  8520. res = ast_play_and_wait(chan, "vm-and");
  8521. }
  8522. if (!res && vms->oldmessages) {
  8523. res = (vms->oldmessages == 1) ?
  8524. ast_play_and_wait(chan, "digits/un") ||
  8525. ast_play_and_wait(chan, "vm-vecchio") ||
  8526. ast_play_and_wait(chan, "vm-message") :
  8527. /* 2 or more old messages */
  8528. say_and_wait(chan, vms->oldmessages, ast_channel_language(chan)) ||
  8529. ast_play_and_wait(chan, "vm-vecchi") ||
  8530. ast_play_and_wait(chan, "vm-messages");
  8531. }
  8532. return res;
  8533. }
  8534. /* POLISH syntax */
  8535. static int vm_intro_pl(struct ast_channel *chan, struct vm_state *vms)
  8536. {
  8537. /* Introduce messages they have */
  8538. int res;
  8539. div_t num;
  8540. if (!vms->oldmessages && !vms->newmessages) {
  8541. res = ast_play_and_wait(chan, "vm-no");
  8542. res = res ? res : ast_play_and_wait(chan, "vm-messages");
  8543. return res;
  8544. } else {
  8545. res = ast_play_and_wait(chan, "vm-youhave");
  8546. }
  8547. if (vms->newmessages) {
  8548. num = div(vms->newmessages, 10);
  8549. if (vms->newmessages == 1) {
  8550. res = ast_play_and_wait(chan, "digits/1-a");
  8551. res = res ? res : ast_play_and_wait(chan, "vm-new-a");
  8552. res = res ? res : ast_play_and_wait(chan, "vm-message");
  8553. } else if (num.rem > 1 && num.rem < 5 && num.quot != 1) {
  8554. if (num.rem == 2) {
  8555. if (!num.quot) {
  8556. res = ast_play_and_wait(chan, "digits/2-ie");
  8557. } else {
  8558. res = say_and_wait(chan, vms->newmessages - 2 , ast_channel_language(chan));
  8559. res = res ? res : ast_play_and_wait(chan, "digits/2-ie");
  8560. }
  8561. } else {
  8562. res = say_and_wait(chan, vms->newmessages, ast_channel_language(chan));
  8563. }
  8564. res = res ? res : ast_play_and_wait(chan, "vm-new-e");
  8565. res = res ? res : ast_play_and_wait(chan, "vm-messages");
  8566. } else {
  8567. res = say_and_wait(chan, vms->newmessages, ast_channel_language(chan));
  8568. res = res ? res : ast_play_and_wait(chan, "vm-new-ych");
  8569. res = res ? res : ast_play_and_wait(chan, "vm-messages");
  8570. }
  8571. if (!res && vms->oldmessages)
  8572. res = ast_play_and_wait(chan, "vm-and");
  8573. }
  8574. if (!res && vms->oldmessages) {
  8575. num = div(vms->oldmessages, 10);
  8576. if (vms->oldmessages == 1) {
  8577. res = ast_play_and_wait(chan, "digits/1-a");
  8578. res = res ? res : ast_play_and_wait(chan, "vm-old-a");
  8579. res = res ? res : ast_play_and_wait(chan, "vm-message");
  8580. } else if (num.rem > 1 && num.rem < 5 && num.quot != 1) {
  8581. if (num.rem == 2) {
  8582. if (!num.quot) {
  8583. res = ast_play_and_wait(chan, "digits/2-ie");
  8584. } else {
  8585. res = say_and_wait(chan, vms->oldmessages - 2 , ast_channel_language(chan));
  8586. res = res ? res : ast_play_and_wait(chan, "digits/2-ie");
  8587. }
  8588. } else {
  8589. res = say_and_wait(chan, vms->oldmessages, ast_channel_language(chan));
  8590. }
  8591. res = res ? res : ast_play_and_wait(chan, "vm-old-e");
  8592. res = res ? res : ast_play_and_wait(chan, "vm-messages");
  8593. } else {
  8594. res = say_and_wait(chan, vms->oldmessages, ast_channel_language(chan));
  8595. res = res ? res : ast_play_and_wait(chan, "vm-old-ych");
  8596. res = res ? res : ast_play_and_wait(chan, "vm-messages");
  8597. }
  8598. }
  8599. return res;
  8600. }
  8601. /* SWEDISH syntax */
  8602. static int vm_intro_se(struct ast_channel *chan, struct vm_state *vms)
  8603. {
  8604. /* Introduce messages they have */
  8605. int res;
  8606. res = ast_play_and_wait(chan, "vm-youhave");
  8607. if (res)
  8608. return res;
  8609. if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
  8610. res = ast_play_and_wait(chan, "vm-no");
  8611. res = res ? res : ast_play_and_wait(chan, "vm-messages");
  8612. return res;
  8613. }
  8614. if (vms->newmessages) {
  8615. if (vms->newmessages == 1) {
  8616. res = ast_play_and_wait(chan, "digits/ett");
  8617. res = res ? res : ast_play_and_wait(chan, "vm-nytt");
  8618. res = res ? res : ast_play_and_wait(chan, "vm-message");
  8619. } else {
  8620. res = say_and_wait(chan, vms->newmessages, ast_channel_language(chan));
  8621. res = res ? res : ast_play_and_wait(chan, "vm-nya");
  8622. res = res ? res : ast_play_and_wait(chan, "vm-messages");
  8623. }
  8624. if (!res && vms->oldmessages)
  8625. res = ast_play_and_wait(chan, "vm-and");
  8626. }
  8627. if (!res && vms->oldmessages) {
  8628. if (vms->oldmessages == 1) {
  8629. res = ast_play_and_wait(chan, "digits/ett");
  8630. res = res ? res : ast_play_and_wait(chan, "vm-gammalt");
  8631. res = res ? res : ast_play_and_wait(chan, "vm-message");
  8632. } else {
  8633. res = say_and_wait(chan, vms->oldmessages, ast_channel_language(chan));
  8634. res = res ? res : ast_play_and_wait(chan, "vm-gamla");
  8635. res = res ? res : ast_play_and_wait(chan, "vm-messages");
  8636. }
  8637. }
  8638. return res;
  8639. }
  8640. /* NORWEGIAN syntax */
  8641. static int vm_intro_no(struct ast_channel *chan, struct vm_state *vms)
  8642. {
  8643. /* Introduce messages they have */
  8644. int res;
  8645. res = ast_play_and_wait(chan, "vm-youhave");
  8646. if (res)
  8647. return res;
  8648. if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
  8649. res = ast_play_and_wait(chan, "vm-no");
  8650. res = res ? res : ast_play_and_wait(chan, "vm-messages");
  8651. return res;
  8652. }
  8653. if (vms->newmessages) {
  8654. if (vms->newmessages == 1) {
  8655. res = ast_play_and_wait(chan, "digits/1");
  8656. res = res ? res : ast_play_and_wait(chan, "vm-ny");
  8657. res = res ? res : ast_play_and_wait(chan, "vm-message");
  8658. } else {
  8659. res = say_and_wait(chan, vms->newmessages, ast_channel_language(chan));
  8660. res = res ? res : ast_play_and_wait(chan, "vm-nye");
  8661. res = res ? res : ast_play_and_wait(chan, "vm-messages");
  8662. }
  8663. if (!res && vms->oldmessages)
  8664. res = ast_play_and_wait(chan, "vm-and");
  8665. }
  8666. if (!res && vms->oldmessages) {
  8667. if (vms->oldmessages == 1) {
  8668. res = ast_play_and_wait(chan, "digits/1");
  8669. res = res ? res : ast_play_and_wait(chan, "vm-gamel");
  8670. res = res ? res : ast_play_and_wait(chan, "vm-message");
  8671. } else {
  8672. res = say_and_wait(chan, vms->oldmessages, ast_channel_language(chan));
  8673. res = res ? res : ast_play_and_wait(chan, "vm-gamle");
  8674. res = res ? res : ast_play_and_wait(chan, "vm-messages");
  8675. }
  8676. }
  8677. return res;
  8678. }
  8679. /* GERMAN syntax */
  8680. static int vm_intro_de(struct ast_channel *chan, struct vm_state *vms)
  8681. {
  8682. /* Introduce messages they have */
  8683. int res;
  8684. res = ast_play_and_wait(chan, "vm-youhave");
  8685. if (!res) {
  8686. if (vms->newmessages) {
  8687. if (vms->newmessages == 1)
  8688. res = ast_play_and_wait(chan, "digits/1F");
  8689. else
  8690. res = say_and_wait(chan, vms->newmessages, ast_channel_language(chan));
  8691. if (!res)
  8692. res = ast_play_and_wait(chan, "vm-INBOX");
  8693. if (vms->oldmessages && !res)
  8694. res = ast_play_and_wait(chan, "vm-and");
  8695. else if (!res) {
  8696. if (vms->newmessages == 1)
  8697. res = ast_play_and_wait(chan, "vm-message");
  8698. else
  8699. res = ast_play_and_wait(chan, "vm-messages");
  8700. }
  8701. }
  8702. if (!res && vms->oldmessages) {
  8703. if (vms->oldmessages == 1)
  8704. res = ast_play_and_wait(chan, "digits/1F");
  8705. else
  8706. res = say_and_wait(chan, vms->oldmessages, ast_channel_language(chan));
  8707. if (!res)
  8708. res = ast_play_and_wait(chan, "vm-Old");
  8709. if (!res) {
  8710. if (vms->oldmessages == 1)
  8711. res = ast_play_and_wait(chan, "vm-message");
  8712. else
  8713. res = ast_play_and_wait(chan, "vm-messages");
  8714. }
  8715. }
  8716. if (!res) {
  8717. if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
  8718. res = ast_play_and_wait(chan, "vm-no");
  8719. if (!res)
  8720. res = ast_play_and_wait(chan, "vm-messages");
  8721. }
  8722. }
  8723. }
  8724. return res;
  8725. }
  8726. /* SPANISH syntax */
  8727. static int vm_intro_es(struct ast_channel *chan, struct vm_state *vms)
  8728. {
  8729. /* Introduce messages they have */
  8730. int res;
  8731. if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
  8732. res = ast_play_and_wait(chan, "vm-youhaveno");
  8733. if (!res)
  8734. res = ast_play_and_wait(chan, "vm-messages");
  8735. } else {
  8736. res = ast_play_and_wait(chan, "vm-youhave");
  8737. }
  8738. if (!res) {
  8739. if (vms->newmessages) {
  8740. if (!res) {
  8741. if (vms->newmessages == 1) {
  8742. res = ast_play_and_wait(chan, "digits/1M");
  8743. if (!res)
  8744. res = ast_play_and_wait(chan, "vm-message");
  8745. if (!res)
  8746. res = ast_play_and_wait(chan, "vm-INBOXs");
  8747. } else {
  8748. res = say_and_wait(chan, vms->newmessages, ast_channel_language(chan));
  8749. if (!res)
  8750. res = ast_play_and_wait(chan, "vm-messages");
  8751. if (!res)
  8752. res = ast_play_and_wait(chan, "vm-INBOX");
  8753. }
  8754. }
  8755. if (vms->oldmessages && !res)
  8756. res = ast_play_and_wait(chan, "vm-and");
  8757. }
  8758. if (vms->oldmessages) {
  8759. if (!res) {
  8760. if (vms->oldmessages == 1) {
  8761. res = ast_play_and_wait(chan, "digits/1M");
  8762. if (!res)
  8763. res = ast_play_and_wait(chan, "vm-message");
  8764. if (!res)
  8765. res = ast_play_and_wait(chan, "vm-Olds");
  8766. } else {
  8767. res = say_and_wait(chan, vms->oldmessages, ast_channel_language(chan));
  8768. if (!res)
  8769. res = ast_play_and_wait(chan, "vm-messages");
  8770. if (!res)
  8771. res = ast_play_and_wait(chan, "vm-Old");
  8772. }
  8773. }
  8774. }
  8775. }
  8776. return res;
  8777. }
  8778. /* BRAZILIAN PORTUGUESE syntax */
  8779. static int vm_intro_pt_BR(struct ast_channel *chan, struct vm_state *vms) {
  8780. /* Introduce messages they have */
  8781. int res;
  8782. if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
  8783. res = ast_play_and_wait(chan, "vm-nomessages");
  8784. return res;
  8785. } else {
  8786. res = ast_play_and_wait(chan, "vm-youhave");
  8787. }
  8788. if (vms->newmessages) {
  8789. if (!res)
  8790. res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, ast_channel_language(chan), "f");
  8791. if (vms->newmessages == 1) {
  8792. if (!res)
  8793. res = ast_play_and_wait(chan, "vm-message");
  8794. if (!res)
  8795. res = ast_play_and_wait(chan, "vm-INBOXs");
  8796. } else {
  8797. if (!res)
  8798. res = ast_play_and_wait(chan, "vm-messages");
  8799. if (!res)
  8800. res = ast_play_and_wait(chan, "vm-INBOX");
  8801. }
  8802. if (vms->oldmessages && !res)
  8803. res = ast_play_and_wait(chan, "vm-and");
  8804. }
  8805. if (vms->oldmessages) {
  8806. if (!res)
  8807. res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, ast_channel_language(chan), "f");
  8808. if (vms->oldmessages == 1) {
  8809. if (!res)
  8810. res = ast_play_and_wait(chan, "vm-message");
  8811. if (!res)
  8812. res = ast_play_and_wait(chan, "vm-Olds");
  8813. } else {
  8814. if (!res)
  8815. res = ast_play_and_wait(chan, "vm-messages");
  8816. if (!res)
  8817. res = ast_play_and_wait(chan, "vm-Old");
  8818. }
  8819. }
  8820. return res;
  8821. }
  8822. /* FRENCH syntax */
  8823. static int vm_intro_fr(struct ast_channel *chan, struct vm_state *vms)
  8824. {
  8825. /* Introduce messages they have */
  8826. int res;
  8827. res = ast_play_and_wait(chan, "vm-youhave");
  8828. if (!res) {
  8829. if (vms->newmessages) {
  8830. res = say_and_wait(chan, vms->newmessages, ast_channel_language(chan));
  8831. if (!res)
  8832. res = ast_play_and_wait(chan, "vm-INBOX");
  8833. if (vms->oldmessages && !res)
  8834. res = ast_play_and_wait(chan, "vm-and");
  8835. else if (!res) {
  8836. if (vms->newmessages == 1)
  8837. res = ast_play_and_wait(chan, "vm-message");
  8838. else
  8839. res = ast_play_and_wait(chan, "vm-messages");
  8840. }
  8841. }
  8842. if (!res && vms->oldmessages) {
  8843. res = say_and_wait(chan, vms->oldmessages, ast_channel_language(chan));
  8844. if (!res)
  8845. res = ast_play_and_wait(chan, "vm-Old");
  8846. if (!res) {
  8847. if (vms->oldmessages == 1)
  8848. res = ast_play_and_wait(chan, "vm-message");
  8849. else
  8850. res = ast_play_and_wait(chan, "vm-messages");
  8851. }
  8852. }
  8853. if (!res) {
  8854. if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
  8855. res = ast_play_and_wait(chan, "vm-no");
  8856. if (!res)
  8857. res = ast_play_and_wait(chan, "vm-messages");
  8858. }
  8859. }
  8860. }
  8861. return res;
  8862. }
  8863. /* DUTCH syntax */
  8864. static int vm_intro_nl(struct ast_channel *chan, struct vm_state *vms)
  8865. {
  8866. /* Introduce messages they have */
  8867. int res;
  8868. res = ast_play_and_wait(chan, "vm-youhave");
  8869. if (!res) {
  8870. if (vms->newmessages) {
  8871. res = say_and_wait(chan, vms->newmessages, ast_channel_language(chan));
  8872. if (!res) {
  8873. if (vms->newmessages == 1)
  8874. res = ast_play_and_wait(chan, "vm-INBOXs");
  8875. else
  8876. res = ast_play_and_wait(chan, "vm-INBOX");
  8877. }
  8878. if (vms->oldmessages && !res)
  8879. res = ast_play_and_wait(chan, "vm-and");
  8880. else if (!res) {
  8881. if (vms->newmessages == 1)
  8882. res = ast_play_and_wait(chan, "vm-message");
  8883. else
  8884. res = ast_play_and_wait(chan, "vm-messages");
  8885. }
  8886. }
  8887. if (!res && vms->oldmessages) {
  8888. res = say_and_wait(chan, vms->oldmessages, ast_channel_language(chan));
  8889. if (!res) {
  8890. if (vms->oldmessages == 1)
  8891. res = ast_play_and_wait(chan, "vm-Olds");
  8892. else
  8893. res = ast_play_and_wait(chan, "vm-Old");
  8894. }
  8895. if (!res) {
  8896. if (vms->oldmessages == 1)
  8897. res = ast_play_and_wait(chan, "vm-message");
  8898. else
  8899. res = ast_play_and_wait(chan, "vm-messages");
  8900. }
  8901. }
  8902. if (!res) {
  8903. if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
  8904. res = ast_play_and_wait(chan, "vm-no");
  8905. if (!res)
  8906. res = ast_play_and_wait(chan, "vm-messages");
  8907. }
  8908. }
  8909. }
  8910. return res;
  8911. }
  8912. /* PORTUGUESE syntax */
  8913. static int vm_intro_pt(struct ast_channel *chan, struct vm_state *vms)
  8914. {
  8915. /* Introduce messages they have */
  8916. int res;
  8917. res = ast_play_and_wait(chan, "vm-youhave");
  8918. if (!res) {
  8919. if (vms->newmessages) {
  8920. res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, ast_channel_language(chan), "f");
  8921. if (!res) {
  8922. if (vms->newmessages == 1) {
  8923. res = ast_play_and_wait(chan, "vm-message");
  8924. if (!res)
  8925. res = ast_play_and_wait(chan, "vm-INBOXs");
  8926. } else {
  8927. res = ast_play_and_wait(chan, "vm-messages");
  8928. if (!res)
  8929. res = ast_play_and_wait(chan, "vm-INBOX");
  8930. }
  8931. }
  8932. if (vms->oldmessages && !res)
  8933. res = ast_play_and_wait(chan, "vm-and");
  8934. }
  8935. if (!res && vms->oldmessages) {
  8936. res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, ast_channel_language(chan), "f");
  8937. if (!res) {
  8938. if (vms->oldmessages == 1) {
  8939. res = ast_play_and_wait(chan, "vm-message");
  8940. if (!res)
  8941. res = ast_play_and_wait(chan, "vm-Olds");
  8942. } else {
  8943. res = ast_play_and_wait(chan, "vm-messages");
  8944. if (!res)
  8945. res = ast_play_and_wait(chan, "vm-Old");
  8946. }
  8947. }
  8948. }
  8949. if (!res) {
  8950. if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
  8951. res = ast_play_and_wait(chan, "vm-no");
  8952. if (!res)
  8953. res = ast_play_and_wait(chan, "vm-messages");
  8954. }
  8955. }
  8956. }
  8957. return res;
  8958. }
  8959. /* CZECH syntax */
  8960. /* in czech there must be declension of word new and message
  8961. * czech : english : czech : english
  8962. * --------------------------------------------------------
  8963. * vm-youhave : you have
  8964. * vm-novou : one new : vm-zpravu : message
  8965. * vm-nove : 2-4 new : vm-zpravy : messages
  8966. * vm-novych : 5-infinite new : vm-zprav : messages
  8967. * vm-starou : one old
  8968. * vm-stare : 2-4 old
  8969. * vm-starych : 5-infinite old
  8970. * jednu : one - falling 4.
  8971. * vm-no : no ( no messages )
  8972. */
  8973. static int vm_intro_cs(struct ast_channel *chan, struct vm_state *vms)
  8974. {
  8975. int res;
  8976. res = ast_play_and_wait(chan, "vm-youhave");
  8977. if (!res) {
  8978. if (vms->newmessages) {
  8979. if (vms->newmessages == 1) {
  8980. res = ast_play_and_wait(chan, "digits/jednu");
  8981. } else {
  8982. res = say_and_wait(chan, vms->newmessages, ast_channel_language(chan));
  8983. }
  8984. if (!res) {
  8985. if (vms->newmessages == 1)
  8986. res = ast_play_and_wait(chan, "vm-novou");
  8987. if ((vms->newmessages) > 1 && (vms->newmessages < 5))
  8988. res = ast_play_and_wait(chan, "vm-nove");
  8989. if (vms->newmessages > 4)
  8990. res = ast_play_and_wait(chan, "vm-novych");
  8991. }
  8992. if (vms->oldmessages && !res)
  8993. res = ast_play_and_wait(chan, "vm-and");
  8994. else if (!res) {
  8995. if (vms->newmessages == 1)
  8996. res = ast_play_and_wait(chan, "vm-zpravu");
  8997. if ((vms->newmessages) > 1 && (vms->newmessages < 5))
  8998. res = ast_play_and_wait(chan, "vm-zpravy");
  8999. if (vms->newmessages > 4)
  9000. res = ast_play_and_wait(chan, "vm-zprav");
  9001. }
  9002. }
  9003. if (!res && vms->oldmessages) {
  9004. res = say_and_wait(chan, vms->oldmessages, ast_channel_language(chan));
  9005. if (!res) {
  9006. if (vms->oldmessages == 1)
  9007. res = ast_play_and_wait(chan, "vm-starou");
  9008. if ((vms->oldmessages) > 1 && (vms->oldmessages < 5))
  9009. res = ast_play_and_wait(chan, "vm-stare");
  9010. if (vms->oldmessages > 4)
  9011. res = ast_play_and_wait(chan, "vm-starych");
  9012. }
  9013. if (!res) {
  9014. if (vms->oldmessages == 1)
  9015. res = ast_play_and_wait(chan, "vm-zpravu");
  9016. if ((vms->oldmessages) > 1 && (vms->oldmessages < 5))
  9017. res = ast_play_and_wait(chan, "vm-zpravy");
  9018. if (vms->oldmessages > 4)
  9019. res = ast_play_and_wait(chan, "vm-zprav");
  9020. }
  9021. }
  9022. if (!res) {
  9023. if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
  9024. res = ast_play_and_wait(chan, "vm-no");
  9025. if (!res)
  9026. res = ast_play_and_wait(chan, "vm-zpravy");
  9027. }
  9028. }
  9029. }
  9030. return res;
  9031. }
  9032. /* CHINESE (Taiwan) syntax */
  9033. static int vm_intro_zh(struct ast_channel *chan, struct vm_state *vms)
  9034. {
  9035. int res;
  9036. /* Introduce messages they have */
  9037. res = ast_play_and_wait(chan, "vm-you");
  9038. if (!res && vms->newmessages) {
  9039. res = ast_play_and_wait(chan, "vm-have");
  9040. if (!res)
  9041. res = say_and_wait(chan, vms->newmessages, ast_channel_language(chan));
  9042. if (!res)
  9043. res = ast_play_and_wait(chan, "vm-tong");
  9044. if (!res)
  9045. res = ast_play_and_wait(chan, "vm-INBOX");
  9046. if (vms->oldmessages && !res)
  9047. res = ast_play_and_wait(chan, "vm-and");
  9048. else if (!res)
  9049. res = ast_play_and_wait(chan, "vm-messages");
  9050. }
  9051. if (!res && vms->oldmessages) {
  9052. res = ast_play_and_wait(chan, "vm-have");
  9053. if (!res)
  9054. res = say_and_wait(chan, vms->oldmessages, ast_channel_language(chan));
  9055. if (!res)
  9056. res = ast_play_and_wait(chan, "vm-tong");
  9057. if (!res)
  9058. res = ast_play_and_wait(chan, "vm-Old");
  9059. if (!res)
  9060. res = ast_play_and_wait(chan, "vm-messages");
  9061. }
  9062. if (!res && !vms->oldmessages && !vms->newmessages) {
  9063. res = ast_play_and_wait(chan, "vm-haveno");
  9064. if (!res)
  9065. res = ast_play_and_wait(chan, "vm-messages");
  9066. }
  9067. return res;
  9068. }
  9069. /* Vietnamese syntax */
  9070. static int vm_intro_vi(struct ast_channel *chan, struct vm_state *vms)
  9071. {
  9072. int res;
  9073. /* Introduce messages they have */
  9074. res = ast_play_and_wait(chan, "vm-youhave");
  9075. if (!res) {
  9076. if (vms->newmessages) {
  9077. res = say_and_wait(chan, vms->newmessages, ast_channel_language(chan));
  9078. if (!res)
  9079. res = ast_play_and_wait(chan, "vm-INBOX");
  9080. if (vms->oldmessages && !res)
  9081. res = ast_play_and_wait(chan, "vm-and");
  9082. }
  9083. if (!res && vms->oldmessages) {
  9084. res = say_and_wait(chan, vms->oldmessages, ast_channel_language(chan));
  9085. if (!res)
  9086. res = ast_play_and_wait(chan, "vm-Old");
  9087. }
  9088. if (!res) {
  9089. if (!vms->oldmessages && !vms->newmessages) {
  9090. res = ast_play_and_wait(chan, "vm-no");
  9091. if (!res)
  9092. res = ast_play_and_wait(chan, "vm-message");
  9093. }
  9094. }
  9095. }
  9096. return res;
  9097. }
  9098. static int vm_intro(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms)
  9099. {
  9100. char prefile[256];
  9101. /* Notify the user that the temp greeting is set and give them the option to remove it */
  9102. snprintf(prefile, sizeof(prefile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, vms->username);
  9103. if (ast_test_flag(vmu, VM_TEMPGREETWARN)) {
  9104. RETRIEVE(prefile, -1, vmu->mailbox, vmu->context);
  9105. if (ast_fileexists(prefile, NULL, NULL) > 0) {
  9106. ast_play_and_wait(chan, "vm-tempgreetactive");
  9107. }
  9108. DISPOSE(prefile, -1);
  9109. }
  9110. /* Play voicemail intro - syntax is different for different languages */
  9111. if (0) {
  9112. return 0;
  9113. } else if (!strncasecmp(ast_channel_language(chan), "cs", 2)) { /* CZECH syntax */
  9114. return vm_intro_cs(chan, vms);
  9115. } else if (!strncasecmp(ast_channel_language(chan), "cz", 2)) { /* deprecated CZECH syntax */
  9116. static int deprecation_warning = 0;
  9117. if (deprecation_warning++ % 10 == 0) {
  9118. ast_log(LOG_WARNING, "cz is not a standard language code. Please switch to using cs instead.\n");
  9119. }
  9120. return vm_intro_cs(chan, vms);
  9121. } else if (!strncasecmp(ast_channel_language(chan), "de", 2)) { /* GERMAN syntax */
  9122. return vm_intro_de(chan, vms);
  9123. } else if (!strncasecmp(ast_channel_language(chan), "es", 2)) { /* SPANISH syntax */
  9124. return vm_intro_es(chan, vms);
  9125. } else if (!strncasecmp(ast_channel_language(chan), "fr", 2)) { /* FRENCH syntax */
  9126. return vm_intro_fr(chan, vms);
  9127. } else if (!strncasecmp(ast_channel_language(chan), "gr", 2)) { /* GREEK syntax */
  9128. return vm_intro_gr(chan, vms);
  9129. } else if (!strncasecmp(ast_channel_language(chan), "he", 2)) { /* HEBREW syntax */
  9130. return vm_intro_he(chan, vms);
  9131. } else if (!strncasecmp(ast_channel_language(chan), "it", 2)) { /* ITALIAN syntax */
  9132. return vm_intro_it(chan, vms);
  9133. } else if (!strncasecmp(ast_channel_language(chan), "ja", 2)) { /* JAPANESE syntax */
  9134. return vm_intro_ja(chan, vms);
  9135. } else if (!strncasecmp(ast_channel_language(chan), "nl", 2)) { /* DUTCH syntax */
  9136. return vm_intro_nl(chan, vms);
  9137. } else if (!strncasecmp(ast_channel_language(chan), "no", 2)) { /* NORWEGIAN syntax */
  9138. return vm_intro_no(chan, vms);
  9139. } else if (!strncasecmp(ast_channel_language(chan), "pl", 2)) { /* POLISH syntax */
  9140. return vm_intro_pl(chan, vms);
  9141. } else if (!strncasecmp(ast_channel_language(chan), "pt_BR", 5)) { /* BRAZILIAN PORTUGUESE syntax */
  9142. return vm_intro_pt_BR(chan, vms);
  9143. } else if (!strncasecmp(ast_channel_language(chan), "pt", 2)) { /* PORTUGUESE syntax */
  9144. return vm_intro_pt(chan, vms);
  9145. } else if (!strncasecmp(ast_channel_language(chan), "ru", 2)) { /* RUSSIAN syntax */
  9146. return vm_intro_multilang(chan, vms, "n");
  9147. } else if (!strncasecmp(ast_channel_language(chan), "se", 2)) { /* SWEDISH syntax */
  9148. return vm_intro_se(chan, vms);
  9149. } else if (!strncasecmp(ast_channel_language(chan), "ua", 2)) { /* UKRAINIAN syntax */
  9150. return vm_intro_multilang(chan, vms, "n");
  9151. } else if (!strncasecmp(ast_channel_language(chan), "vi", 2)) { /* VIETNAMESE syntax */
  9152. return vm_intro_vi(chan, vms);
  9153. } else if (!strncasecmp(ast_channel_language(chan), "zh", 2)) { /* CHINESE (Taiwan) syntax */
  9154. return vm_intro_zh(chan, vms);
  9155. } else { /* Default to ENGLISH */
  9156. return vm_intro_en(chan, vms);
  9157. }
  9158. }
  9159. static int vm_instructions_en(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int skipadvanced, int in_urgent)
  9160. {
  9161. int res = 0;
  9162. /* Play instructions and wait for new command */
  9163. while (!res) {
  9164. if (vms->starting) {
  9165. if (vms->lastmsg > -1) {
  9166. if (skipadvanced)
  9167. res = ast_play_and_wait(chan, "vm-onefor-full");
  9168. else
  9169. res = ast_play_and_wait(chan, "vm-onefor");
  9170. if (!res)
  9171. res = vm_play_folder_name(chan, vms->vmbox);
  9172. }
  9173. if (!res) {
  9174. if (skipadvanced)
  9175. res = ast_play_and_wait(chan, "vm-opts-full");
  9176. else
  9177. res = ast_play_and_wait(chan, "vm-opts");
  9178. }
  9179. } else {
  9180. /* Added for additional help */
  9181. if (skipadvanced) {
  9182. res = ast_play_and_wait(chan, "vm-onefor-full");
  9183. if (!res)
  9184. res = vm_play_folder_name(chan, vms->vmbox);
  9185. res = ast_play_and_wait(chan, "vm-opts-full");
  9186. }
  9187. /* Logic:
  9188. * If the current message is not the first OR
  9189. * if we're listening to the first new message and there are
  9190. * also urgent messages, then prompt for navigation to the
  9191. * previous message
  9192. */
  9193. if (vms->curmsg || (!in_urgent && vms->urgentmessages > 0) || (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms->lastmsg > 0)) {
  9194. res = ast_play_and_wait(chan, "vm-prev");
  9195. }
  9196. if (!res && !skipadvanced)
  9197. res = ast_play_and_wait(chan, "vm-advopts");
  9198. if (!res)
  9199. res = ast_play_and_wait(chan, "vm-repeat");
  9200. /* Logic:
  9201. * If we're not listening to the last message OR
  9202. * we're listening to the last urgent message and there are
  9203. * also new non-urgent messages, then prompt for navigation
  9204. * to the next message
  9205. */
  9206. if (!res && ((vms->curmsg != vms->lastmsg) || (in_urgent && vms->newmessages > 0) ||
  9207. (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms->lastmsg > 0) )) {
  9208. res = ast_play_and_wait(chan, "vm-next");
  9209. }
  9210. if (!res) {
  9211. int curmsg_deleted;
  9212. #ifdef IMAP_STORAGE
  9213. ast_mutex_lock(&vms->lock);
  9214. #endif
  9215. curmsg_deleted = vms->deleted[vms->curmsg];
  9216. #ifdef IMAP_STORAGE
  9217. ast_mutex_unlock(&vms->lock);
  9218. #endif
  9219. if (!curmsg_deleted) {
  9220. res = ast_play_and_wait(chan, "vm-delete");
  9221. } else {
  9222. res = ast_play_and_wait(chan, "vm-undelete");
  9223. }
  9224. if (!res) {
  9225. res = ast_play_and_wait(chan, "vm-toforward");
  9226. }
  9227. if (!res) {
  9228. res = ast_play_and_wait(chan, "vm-savemessage");
  9229. }
  9230. }
  9231. }
  9232. if (!res) {
  9233. res = ast_play_and_wait(chan, "vm-helpexit");
  9234. }
  9235. if (!res)
  9236. res = ast_waitfordigit(chan, 6000);
  9237. if (!res) {
  9238. vms->repeats++;
  9239. if (vms->repeats > 2) {
  9240. res = 't';
  9241. }
  9242. }
  9243. }
  9244. return res;
  9245. }
  9246. static int vm_instructions_ja(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int skipadvanced, int in_urgent)
  9247. {
  9248. int res = 0;
  9249. /* Play instructions and wait for new command */
  9250. while (!res) {
  9251. if (vms->starting) {
  9252. if (vms->lastmsg > -1) {
  9253. res = vm_play_folder_name(chan, vms->vmbox);
  9254. if (!res)
  9255. res = ast_play_and_wait(chan, "jp-wa");
  9256. if (!res)
  9257. res = ast_play_and_wait(chan, "digits/1");
  9258. if (!res)
  9259. res = ast_play_and_wait(chan, "jp-wo");
  9260. if (!res)
  9261. res = ast_play_and_wait(chan, "silence/1");
  9262. }
  9263. if (!res)
  9264. res = ast_play_and_wait(chan, "vm-opts");
  9265. } else {
  9266. /* Added for additional help */
  9267. if (skipadvanced) {
  9268. res = vm_play_folder_name(chan, vms->vmbox);
  9269. if (!res)
  9270. res = ast_play_and_wait(chan, "jp-wa");
  9271. if (!res)
  9272. res = ast_play_and_wait(chan, "digits/1");
  9273. if (!res)
  9274. res = ast_play_and_wait(chan, "jp-wo");
  9275. if (!res)
  9276. res = ast_play_and_wait(chan, "silence/1");
  9277. res = ast_play_and_wait(chan, "vm-opts-full");
  9278. }
  9279. /* Logic:
  9280. * If the current message is not the first OR
  9281. * if we're listening to the first new message and there are
  9282. * also urgent messages, then prompt for navigation to the
  9283. * previous message
  9284. */
  9285. if (vms->curmsg || (!in_urgent && vms->urgentmessages > 0) || (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms->lastmsg > 0)) {
  9286. res = ast_play_and_wait(chan, "vm-prev");
  9287. }
  9288. if (!res && !skipadvanced)
  9289. res = ast_play_and_wait(chan, "vm-advopts");
  9290. if (!res)
  9291. res = ast_play_and_wait(chan, "vm-repeat");
  9292. /* Logic:
  9293. * If we're not listening to the last message OR
  9294. * we're listening to the last urgent message and there are
  9295. * also new non-urgent messages, then prompt for navigation
  9296. * to the next message
  9297. */
  9298. if (!res && ((vms->curmsg != vms->lastmsg) || (in_urgent && vms->newmessages > 0) ||
  9299. (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms->lastmsg > 0) )) {
  9300. res = ast_play_and_wait(chan, "vm-next");
  9301. }
  9302. if (!res) {
  9303. int curmsg_deleted;
  9304. #ifdef IMAP_STORAGE
  9305. ast_mutex_lock(&vms->lock);
  9306. #endif
  9307. curmsg_deleted = vms->deleted[vms->curmsg];
  9308. #ifdef IMAP_STORAGE
  9309. ast_mutex_unlock(&vms->lock);
  9310. #endif
  9311. if (!curmsg_deleted) {
  9312. res = ast_play_and_wait(chan, "vm-delete");
  9313. } else {
  9314. res = ast_play_and_wait(chan, "vm-undelete");
  9315. }
  9316. if (!res) {
  9317. res = ast_play_and_wait(chan, "vm-toforward");
  9318. }
  9319. if (!res) {
  9320. res = ast_play_and_wait(chan, "vm-savemessage");
  9321. }
  9322. }
  9323. }
  9324. if (!res) {
  9325. res = ast_play_and_wait(chan, "vm-helpexit");
  9326. }
  9327. if (!res)
  9328. res = ast_waitfordigit(chan, 6000);
  9329. if (!res) {
  9330. vms->repeats++;
  9331. if (vms->repeats > 2) {
  9332. res = 't';
  9333. }
  9334. }
  9335. }
  9336. return res;
  9337. }
  9338. static int vm_instructions_zh(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int skipadvanced, int in_urgent)
  9339. {
  9340. int res = 0;
  9341. /* Play instructions and wait for new command */
  9342. while (!res) {
  9343. if (vms->lastmsg > -1) {
  9344. res = ast_play_and_wait(chan, "vm-listen");
  9345. if (!res)
  9346. res = vm_play_folder_name(chan, vms->vmbox);
  9347. if (!res)
  9348. res = ast_play_and_wait(chan, "press");
  9349. if (!res)
  9350. res = ast_play_and_wait(chan, "digits/1");
  9351. }
  9352. if (!res)
  9353. res = ast_play_and_wait(chan, "vm-opts");
  9354. if (!res) {
  9355. vms->starting = 0;
  9356. return vm_instructions_en(chan, vmu, vms, skipadvanced, in_urgent);
  9357. }
  9358. }
  9359. return res;
  9360. }
  9361. static int vm_instructions(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int skipadvanced, int in_urgent)
  9362. {
  9363. if (!strncasecmp(ast_channel_language(chan), "ja", 2)) { /* Japanese syntax */
  9364. return vm_instructions_ja(chan, vmu, vms, skipadvanced, in_urgent);
  9365. } else if (vms->starting && !strncasecmp(ast_channel_language(chan), "zh", 2)) { /* CHINESE (Taiwan) syntax */
  9366. return vm_instructions_zh(chan, vmu, vms, skipadvanced, in_urgent);
  9367. } else { /* Default to ENGLISH */
  9368. return vm_instructions_en(chan, vmu, vms, skipadvanced, in_urgent);
  9369. }
  9370. }
  9371. static int vm_newuser(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain)
  9372. {
  9373. int cmd = 0;
  9374. int duration = 0;
  9375. int tries = 0;
  9376. char newpassword[80] = "";
  9377. char newpassword2[80] = "";
  9378. char prefile[PATH_MAX] = "";
  9379. unsigned char buf[256];
  9380. int bytes = 0;
  9381. ast_test_suite_event_notify("NEWUSER", "Message: entering new user state");
  9382. if (ast_adsi_available(chan)) {
  9383. bytes += adsi_logo(buf + bytes);
  9384. bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "New User Setup", "");
  9385. bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Not Done", "");
  9386. bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
  9387. bytes += ast_adsi_voice_mode(buf + bytes, 0);
  9388. ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
  9389. }
  9390. /* If forcename is set, have the user record their name */
  9391. if (ast_test_flag(vmu, VM_FORCENAME)) {
  9392. snprintf(prefile, sizeof(prefile), "%s%s/%s/greet", VM_SPOOL_DIR, vmu->context, vms->username);
  9393. if (ast_fileexists(prefile, NULL, NULL) < 1) {
  9394. cmd = play_record_review(chan, "vm-rec-name", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL, NULL);
  9395. if (cmd < 0 || cmd == 't' || cmd == '#')
  9396. return cmd;
  9397. }
  9398. }
  9399. /* If forcegreetings is set, have the user record their greetings */
  9400. if (ast_test_flag(vmu, VM_FORCEGREET)) {
  9401. snprintf(prefile, sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, vms->username);
  9402. if (ast_fileexists(prefile, NULL, NULL) < 1) {
  9403. cmd = play_record_review(chan, "vm-rec-unv", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL, NULL);
  9404. if (cmd < 0 || cmd == 't' || cmd == '#')
  9405. return cmd;
  9406. }
  9407. snprintf(prefile, sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, vms->username);
  9408. if (ast_fileexists(prefile, NULL, NULL) < 1) {
  9409. cmd = play_record_review(chan, "vm-rec-busy", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL, NULL);
  9410. if (cmd < 0 || cmd == 't' || cmd == '#')
  9411. return cmd;
  9412. }
  9413. }
  9414. /*
  9415. * Change the password last since new users will be able to skip over any steps this one comes before
  9416. * by hanging up and calling back to voicemail main since the password is used to verify new user status.
  9417. */
  9418. for (;;) {
  9419. newpassword[1] = '\0';
  9420. newpassword[0] = cmd = ast_play_and_wait(chan, vm_newpassword);
  9421. if (cmd == '#')
  9422. newpassword[0] = '\0';
  9423. if (cmd < 0 || cmd == 't' || cmd == '#')
  9424. return cmd;
  9425. cmd = ast_readstring(chan, newpassword + strlen(newpassword), sizeof(newpassword) - 1, 2000, 10000, "#");
  9426. if (cmd < 0 || cmd == 't' || cmd == '#')
  9427. return cmd;
  9428. cmd = check_password(vmu, newpassword); /* perform password validation */
  9429. if (cmd != 0) {
  9430. ast_log(AST_LOG_NOTICE, "Invalid password for user %s (%s)\n", vms->username, newpassword);
  9431. cmd = ast_play_and_wait(chan, vm_invalid_password);
  9432. } else {
  9433. newpassword2[1] = '\0';
  9434. newpassword2[0] = cmd = ast_play_and_wait(chan, vm_reenterpassword);
  9435. if (cmd == '#')
  9436. newpassword2[0] = '\0';
  9437. if (cmd < 0 || cmd == 't' || cmd == '#')
  9438. return cmd;
  9439. cmd = ast_readstring(chan, newpassword2 + strlen(newpassword2), sizeof(newpassword2) - 1, 2000, 10000, "#");
  9440. if (cmd < 0 || cmd == 't' || cmd == '#')
  9441. return cmd;
  9442. if (!strcmp(newpassword, newpassword2))
  9443. break;
  9444. ast_log(AST_LOG_NOTICE, "Password mismatch for user %s (%s != %s)\n", vms->username, newpassword, newpassword2);
  9445. cmd = ast_play_and_wait(chan, vm_mismatch);
  9446. }
  9447. if (++tries == 3)
  9448. return -1;
  9449. if (cmd != 0) {
  9450. cmd = ast_play_and_wait(chan, vm_pls_try_again);
  9451. }
  9452. }
  9453. if (pwdchange & PWDCHANGE_INTERNAL)
  9454. vm_change_password(vmu, newpassword);
  9455. if ((pwdchange & PWDCHANGE_EXTERNAL) && !ast_strlen_zero(ext_pass_cmd))
  9456. vm_change_password_shell(vmu, newpassword);
  9457. ast_debug(1, "User %s set password to %s of length %d\n", vms->username, newpassword, (int) strlen(newpassword));
  9458. cmd = ast_play_and_wait(chan, vm_passchanged);
  9459. return cmd;
  9460. }
  9461. static int vm_options(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain)
  9462. {
  9463. int cmd = 0;
  9464. int retries = 0;
  9465. int duration = 0;
  9466. char newpassword[80] = "";
  9467. char newpassword2[80] = "";
  9468. char prefile[PATH_MAX] = "";
  9469. unsigned char buf[256];
  9470. int bytes = 0;
  9471. ast_test_suite_event_notify("VMOPTIONS", "Message: entering mailbox options");
  9472. if (ast_adsi_available(chan)) {
  9473. bytes += adsi_logo(buf + bytes);
  9474. bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Options Menu", "");
  9475. bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Not Done", "");
  9476. bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
  9477. bytes += ast_adsi_voice_mode(buf + bytes, 0);
  9478. ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
  9479. }
  9480. while ((cmd >= 0) && (cmd != 't')) {
  9481. if (cmd)
  9482. retries = 0;
  9483. switch (cmd) {
  9484. case '1': /* Record your unavailable message */
  9485. snprintf(prefile, sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, vms->username);
  9486. cmd = play_record_review(chan, "vm-rec-unv", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL, NULL);
  9487. break;
  9488. case '2': /* Record your busy message */
  9489. snprintf(prefile, sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, vms->username);
  9490. cmd = play_record_review(chan, "vm-rec-busy", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL, NULL);
  9491. break;
  9492. case '3': /* Record greeting */
  9493. snprintf(prefile, sizeof(prefile), "%s%s/%s/greet", VM_SPOOL_DIR, vmu->context, vms->username);
  9494. cmd = play_record_review(chan, "vm-rec-name", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL, NULL);
  9495. break;
  9496. case '4': /* manage the temporary greeting */
  9497. cmd = vm_tempgreeting(chan, vmu, vms, fmtc, record_gain);
  9498. break;
  9499. case '5': /* change password */
  9500. if (vmu->password[0] == '-') {
  9501. cmd = ast_play_and_wait(chan, "vm-no");
  9502. break;
  9503. }
  9504. newpassword[1] = '\0';
  9505. newpassword[0] = cmd = ast_play_and_wait(chan, vm_newpassword);
  9506. if (cmd == '#')
  9507. newpassword[0] = '\0';
  9508. else {
  9509. if (cmd < 0)
  9510. break;
  9511. if ((cmd = ast_readstring(chan, newpassword + strlen(newpassword), sizeof(newpassword) - 1, 2000, 10000, "#")) < 0) {
  9512. break;
  9513. }
  9514. }
  9515. cmd = check_password(vmu, newpassword); /* perform password validation */
  9516. if (cmd != 0) {
  9517. ast_log(AST_LOG_NOTICE, "Invalid password for user %s (%s)\n", vms->username, newpassword);
  9518. cmd = ast_play_and_wait(chan, vm_invalid_password);
  9519. if (!cmd) {
  9520. cmd = ast_play_and_wait(chan, vm_pls_try_again);
  9521. }
  9522. break;
  9523. }
  9524. newpassword2[1] = '\0';
  9525. newpassword2[0] = cmd = ast_play_and_wait(chan, vm_reenterpassword);
  9526. if (cmd == '#')
  9527. newpassword2[0] = '\0';
  9528. else {
  9529. if (cmd < 0)
  9530. break;
  9531. if ((cmd = ast_readstring(chan, newpassword2 + strlen(newpassword2), sizeof(newpassword2) - 1, 2000, 10000, "#")) < 0) {
  9532. break;
  9533. }
  9534. }
  9535. if (strcmp(newpassword, newpassword2)) {
  9536. ast_log(AST_LOG_NOTICE, "Password mismatch for user %s (%s != %s)\n", vms->username, newpassword, newpassword2);
  9537. cmd = ast_play_and_wait(chan, vm_mismatch);
  9538. if (!cmd) {
  9539. cmd = ast_play_and_wait(chan, vm_pls_try_again);
  9540. }
  9541. break;
  9542. }
  9543. if (pwdchange & PWDCHANGE_INTERNAL) {
  9544. vm_change_password(vmu, newpassword);
  9545. }
  9546. if ((pwdchange & PWDCHANGE_EXTERNAL) && !ast_strlen_zero(ext_pass_cmd)) {
  9547. vm_change_password_shell(vmu, newpassword);
  9548. }
  9549. ast_debug(1, "User %s set password to %s of length %d\n",
  9550. vms->username, newpassword, (int) strlen(newpassword));
  9551. cmd = ast_play_and_wait(chan, vm_passchanged);
  9552. break;
  9553. case '*':
  9554. cmd = 't';
  9555. break;
  9556. default:
  9557. cmd = 0;
  9558. snprintf(prefile, sizeof(prefile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, vms->username);
  9559. RETRIEVE(prefile, -1, vmu->mailbox, vmu->context);
  9560. if (ast_fileexists(prefile, NULL, NULL)) {
  9561. cmd = ast_play_and_wait(chan, "vm-tmpexists");
  9562. }
  9563. DISPOSE(prefile, -1);
  9564. if (!cmd) {
  9565. cmd = ast_play_and_wait(chan, "vm-options");
  9566. }
  9567. if (!cmd) {
  9568. cmd = ast_waitfordigit(chan, 6000);
  9569. }
  9570. if (!cmd) {
  9571. retries++;
  9572. }
  9573. if (retries > 3) {
  9574. cmd = 't';
  9575. }
  9576. ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", cmd, cmd);
  9577. }
  9578. }
  9579. if (cmd == 't')
  9580. cmd = 0;
  9581. return cmd;
  9582. }
  9583. /*!
  9584. * \brief The handler for 'record a temporary greeting'.
  9585. * \param chan
  9586. * \param vmu
  9587. * \param vms
  9588. * \param fmtc
  9589. * \param record_gain
  9590. *
  9591. * This is option 4 from the mailbox options menu.
  9592. * This function manages the following promptings:
  9593. * 1: play / record / review the temporary greeting. : invokes play_record_review().
  9594. * 2: remove (delete) the temporary greeting.
  9595. * *: return to the main menu.
  9596. *
  9597. * \return zero on success, -1 on error.
  9598. */
  9599. static int vm_tempgreeting(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain)
  9600. {
  9601. int cmd = 0;
  9602. int retries = 0;
  9603. int duration = 0;
  9604. char prefile[PATH_MAX] = "";
  9605. unsigned char buf[256];
  9606. int bytes = 0;
  9607. if (ast_adsi_available(chan)) {
  9608. bytes += adsi_logo(buf + bytes);
  9609. bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Temp Greeting Menu", "");
  9610. bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Not Done", "");
  9611. bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
  9612. bytes += ast_adsi_voice_mode(buf + bytes, 0);
  9613. ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
  9614. }
  9615. ast_test_suite_event_notify("TEMPGREETING", "Message: entering temp greeting options");
  9616. snprintf(prefile, sizeof(prefile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, vms->username);
  9617. while ((cmd >= 0) && (cmd != 't')) {
  9618. if (cmd)
  9619. retries = 0;
  9620. RETRIEVE(prefile, -1, vmu->mailbox, vmu->context);
  9621. if (ast_fileexists(prefile, NULL, NULL) <= 0) {
  9622. cmd = play_record_review(chan, "vm-rec-temp", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL, NULL);
  9623. if (cmd == -1) {
  9624. break;
  9625. }
  9626. cmd = 't';
  9627. } else {
  9628. switch (cmd) {
  9629. case '1':
  9630. cmd = play_record_review(chan, "vm-rec-temp", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL, NULL);
  9631. break;
  9632. case '2':
  9633. DELETE(prefile, -1, prefile, vmu);
  9634. ast_play_and_wait(chan, "vm-tempremoved");
  9635. cmd = 't';
  9636. break;
  9637. case '*':
  9638. cmd = 't';
  9639. break;
  9640. default:
  9641. cmd = ast_play_and_wait(chan,
  9642. ast_fileexists(prefile, NULL, NULL) > 0 ? /* XXX always true ? */
  9643. "vm-tempgreeting2" : "vm-tempgreeting");
  9644. if (!cmd) {
  9645. cmd = ast_waitfordigit(chan, 6000);
  9646. }
  9647. if (!cmd) {
  9648. retries++;
  9649. }
  9650. if (retries > 3) {
  9651. cmd = 't';
  9652. }
  9653. ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", cmd, cmd);
  9654. }
  9655. }
  9656. DISPOSE(prefile, -1);
  9657. }
  9658. if (cmd == 't')
  9659. cmd = 0;
  9660. return cmd;
  9661. }
  9662. /*!
  9663. * \brief Greek syntax for 'You have N messages' greeting.
  9664. * \param chan
  9665. * \param vms
  9666. * \param vmu
  9667. *
  9668. * \return zero on success, -1 on error.
  9669. */
  9670. static int vm_browse_messages_gr(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
  9671. {
  9672. int cmd = 0;
  9673. if (vms->lastmsg > -1) {
  9674. cmd = play_message(chan, vmu, vms);
  9675. } else {
  9676. cmd = ast_play_and_wait(chan, "vm-youhaveno");
  9677. if (!strcasecmp(vms->vmbox, "vm-INBOX") ||!strcasecmp(vms->vmbox, "vm-Old")){
  9678. if (!cmd) {
  9679. snprintf(vms->fn, sizeof(vms->fn), "vm-%ss", vms->curbox);
  9680. cmd = ast_play_and_wait(chan, vms->fn);
  9681. }
  9682. if (!cmd)
  9683. cmd = ast_play_and_wait(chan, "vm-messages");
  9684. } else {
  9685. if (!cmd)
  9686. cmd = ast_play_and_wait(chan, "vm-messages");
  9687. if (!cmd) {
  9688. snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
  9689. cmd = ast_play_and_wait(chan, vms->fn);
  9690. }
  9691. }
  9692. }
  9693. return cmd;
  9694. }
  9695. /* Hebrew Syntax */
  9696. static int vm_browse_messages_he(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
  9697. {
  9698. int cmd = 0;
  9699. if (vms->lastmsg > -1) {
  9700. cmd = play_message(chan, vmu, vms);
  9701. } else {
  9702. if (!strcasecmp(vms->fn, "INBOX")) {
  9703. cmd = ast_play_and_wait(chan, "vm-nonewmessages");
  9704. } else {
  9705. cmd = ast_play_and_wait(chan, "vm-nomessages");
  9706. }
  9707. }
  9708. return cmd;
  9709. }
  9710. /*!
  9711. * \brief Default English syntax for 'You have N messages' greeting.
  9712. * \param chan
  9713. * \param vms
  9714. * \param vmu
  9715. *
  9716. * \return zero on success, -1 on error.
  9717. */
  9718. static int vm_browse_messages_en(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
  9719. {
  9720. int cmd = 0;
  9721. if (vms->lastmsg > -1) {
  9722. cmd = play_message(chan, vmu, vms);
  9723. } else {
  9724. cmd = ast_play_and_wait(chan, "vm-youhave");
  9725. if (!cmd)
  9726. cmd = ast_play_and_wait(chan, "vm-no");
  9727. if (!cmd) {
  9728. snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
  9729. cmd = ast_play_and_wait(chan, vms->fn);
  9730. }
  9731. if (!cmd)
  9732. cmd = ast_play_and_wait(chan, "vm-messages");
  9733. }
  9734. return cmd;
  9735. }
  9736. /*!
  9737. *\brief Italian syntax for 'You have N messages' greeting.
  9738. * \param chan
  9739. * \param vms
  9740. * \param vmu
  9741. *
  9742. * \return zero on success, -1 on error.
  9743. */
  9744. static int vm_browse_messages_it(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
  9745. {
  9746. int cmd;
  9747. if (vms->lastmsg > -1) {
  9748. cmd = play_message(chan, vmu, vms);
  9749. } else {
  9750. cmd = ast_play_and_wait(chan, "vm-no");
  9751. if (!cmd)
  9752. cmd = ast_play_and_wait(chan, "vm-message");
  9753. if (!cmd) {
  9754. snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
  9755. cmd = ast_play_and_wait(chan, vms->fn);
  9756. }
  9757. }
  9758. return cmd;
  9759. }
  9760. /*!
  9761. * \brief Japanese syntax for 'You have N messages' greeting.
  9762. * \param chan
  9763. * \param vms
  9764. * \param vmu
  9765. *
  9766. * \return zero on success, -1 on error.
  9767. */
  9768. static int vm_browse_messages_ja(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
  9769. {
  9770. int cmd = 0;
  9771. if (vms->lastmsg > -1) {
  9772. cmd = play_message(chan, vmu, vms);
  9773. } else {
  9774. snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
  9775. cmd = ast_play_and_wait(chan, vms->fn);
  9776. if (!cmd)
  9777. cmd = ast_play_and_wait(chan, "vm-messages");
  9778. if (!cmd)
  9779. cmd = ast_play_and_wait(chan, "jp-wa");
  9780. if (!cmd)
  9781. cmd = ast_play_and_wait(chan, "jp-arimasen");
  9782. }
  9783. return cmd;
  9784. }
  9785. /*!
  9786. * \brief Spanish syntax for 'You have N messages' greeting.
  9787. * \param chan
  9788. * \param vms
  9789. * \param vmu
  9790. *
  9791. * \return zero on success, -1 on error.
  9792. */
  9793. static int vm_browse_messages_es(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
  9794. {
  9795. int cmd;
  9796. if (vms->lastmsg > -1) {
  9797. cmd = play_message(chan, vmu, vms);
  9798. } else {
  9799. cmd = ast_play_and_wait(chan, "vm-youhaveno");
  9800. if (!cmd)
  9801. cmd = ast_play_and_wait(chan, "vm-messages");
  9802. if (!cmd) {
  9803. snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
  9804. cmd = ast_play_and_wait(chan, vms->fn);
  9805. }
  9806. }
  9807. return cmd;
  9808. }
  9809. /*!
  9810. * \brief Portuguese syntax for 'You have N messages' greeting.
  9811. * \param chan
  9812. * \param vms
  9813. * \param vmu
  9814. *
  9815. * \return zero on success, -1 on error.
  9816. */
  9817. static int vm_browse_messages_pt(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
  9818. {
  9819. int cmd;
  9820. if (vms->lastmsg > -1) {
  9821. cmd = play_message(chan, vmu, vms);
  9822. } else {
  9823. cmd = ast_play_and_wait(chan, "vm-no");
  9824. if (!cmd) {
  9825. snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
  9826. cmd = ast_play_and_wait(chan, vms->fn);
  9827. }
  9828. if (!cmd)
  9829. cmd = ast_play_and_wait(chan, "vm-messages");
  9830. }
  9831. return cmd;
  9832. }
  9833. /*!
  9834. * \brief Chinese (Taiwan)syntax for 'You have N messages' greeting.
  9835. * \param chan
  9836. * \param vms
  9837. * \param vmu
  9838. *
  9839. * \return zero on success, -1 on error.
  9840. */
  9841. static int vm_browse_messages_zh(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
  9842. {
  9843. int cmd;
  9844. if (vms->lastmsg > -1) {
  9845. cmd = play_message(chan, vmu, vms);
  9846. } else {
  9847. cmd = ast_play_and_wait(chan, "vm-you");
  9848. if (!cmd)
  9849. cmd = ast_play_and_wait(chan, "vm-haveno");
  9850. if (!cmd)
  9851. cmd = ast_play_and_wait(chan, "vm-messages");
  9852. if (!cmd) {
  9853. snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
  9854. cmd = ast_play_and_wait(chan, vms->fn);
  9855. }
  9856. }
  9857. return cmd;
  9858. }
  9859. /*!
  9860. * \brief Vietnamese syntax for 'You have N messages' greeting.
  9861. * \param chan
  9862. * \param vms
  9863. * \param vmu
  9864. *
  9865. * \return zero on success, -1 on error.
  9866. */
  9867. static int vm_browse_messages_vi(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
  9868. {
  9869. int cmd = 0;
  9870. if (vms->lastmsg > -1) {
  9871. cmd = play_message(chan, vmu, vms);
  9872. } else {
  9873. cmd = ast_play_and_wait(chan, "vm-no");
  9874. if (!cmd) {
  9875. snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
  9876. cmd = ast_play_and_wait(chan, vms->fn);
  9877. }
  9878. }
  9879. return cmd;
  9880. }
  9881. /*!
  9882. * \brief Top level method to invoke the language variant vm_browse_messages_XX function.
  9883. * \param chan The channel for the current user. We read the language property from this.
  9884. * \param vms passed into the language-specific vm_browse_messages function.
  9885. * \param vmu passed into the language-specific vm_browse_messages function.
  9886. *
  9887. * The method to be invoked is determined by the value of language code property in the user's channel.
  9888. * The default (when unable to match) is to use english.
  9889. *
  9890. * \return zero on success, -1 on error.
  9891. */
  9892. static int vm_browse_messages(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
  9893. {
  9894. if (!strncasecmp(ast_channel_language(chan), "es", 2)) { /* SPANISH */
  9895. return vm_browse_messages_es(chan, vms, vmu);
  9896. } else if (!strncasecmp(ast_channel_language(chan), "gr", 2)) { /* GREEK */
  9897. return vm_browse_messages_gr(chan, vms, vmu);
  9898. } else if (!strncasecmp(ast_channel_language(chan), "he", 2)) { /* HEBREW */
  9899. return vm_browse_messages_he(chan, vms, vmu);
  9900. } else if (!strncasecmp(ast_channel_language(chan), "it", 2)) { /* ITALIAN */
  9901. return vm_browse_messages_it(chan, vms, vmu);
  9902. } else if (!strncasecmp(ast_channel_language(chan), "ja", 2)) { /* JAPANESE */
  9903. return vm_browse_messages_ja(chan, vms, vmu);
  9904. } else if (!strncasecmp(ast_channel_language(chan), "pt", 2)) { /* PORTUGUESE */
  9905. return vm_browse_messages_pt(chan, vms, vmu);
  9906. } else if (!strncasecmp(ast_channel_language(chan), "vi", 2)) { /* VIETNAMESE */
  9907. return vm_browse_messages_vi(chan, vms, vmu);
  9908. } else if (!strncasecmp(ast_channel_language(chan), "zh", 2)) { /* CHINESE (Taiwan) */
  9909. return vm_browse_messages_zh(chan, vms, vmu);
  9910. } else { /* Default to English syntax */
  9911. return vm_browse_messages_en(chan, vms, vmu);
  9912. }
  9913. }
  9914. static int vm_authenticate(struct ast_channel *chan, char *mailbox, int mailbox_size,
  9915. struct ast_vm_user *res_vmu, const char *context, const char *prefix,
  9916. int skipuser, int max_logins, int silent)
  9917. {
  9918. int useadsi = 0, valid = 0, logretries = 0;
  9919. char password[AST_MAX_EXTENSION]="", *passptr;
  9920. struct ast_vm_user vmus, *vmu = NULL;
  9921. /* If ADSI is supported, setup login screen */
  9922. adsi_begin(chan, &useadsi);
  9923. if (!skipuser && useadsi)
  9924. adsi_login(chan);
  9925. if (!silent && !skipuser && ast_streamfile(chan, "vm-login", ast_channel_language(chan))) {
  9926. ast_log(AST_LOG_WARNING, "Couldn't stream login file\n");
  9927. return -1;
  9928. }
  9929. /* Authenticate them and get their mailbox/password */
  9930. while (!valid && (logretries < max_logins)) {
  9931. /* Prompt for, and read in the username */
  9932. if (!skipuser && ast_readstring(chan, mailbox, mailbox_size - 1, 2000, 10000, "#") < 0) {
  9933. ast_log(AST_LOG_WARNING, "Couldn't read username\n");
  9934. return -1;
  9935. }
  9936. if (ast_strlen_zero(mailbox)) {
  9937. if (ast_channel_caller(chan)->id.number.valid && ast_channel_caller(chan)->id.number.str) {
  9938. ast_copy_string(mailbox, ast_channel_caller(chan)->id.number.str, mailbox_size);
  9939. } else {
  9940. ast_verb(3, "Username not entered\n");
  9941. return -1;
  9942. }
  9943. } else if (mailbox[0] == '*') {
  9944. /* user entered '*' */
  9945. ast_verb(4, "Mailbox begins with '*', attempting jump to extension 'a'\n");
  9946. if (ast_exists_extension(chan, ast_channel_context(chan), "a", 1,
  9947. S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL))) {
  9948. return -1;
  9949. }
  9950. ast_verb(4, "Jump to extension 'a' failed; setting mailbox to NULL\n");
  9951. mailbox[0] = '\0';
  9952. }
  9953. if (useadsi)
  9954. adsi_password(chan);
  9955. if (!ast_strlen_zero(prefix)) {
  9956. char fullusername[80] = "";
  9957. ast_copy_string(fullusername, prefix, sizeof(fullusername));
  9958. strncat(fullusername, mailbox, sizeof(fullusername) - 1 - strlen(fullusername));
  9959. ast_copy_string(mailbox, fullusername, mailbox_size);
  9960. }
  9961. ast_debug(1, "Before find user for mailbox %s\n", mailbox);
  9962. vmu = find_user(&vmus, context, mailbox);
  9963. if (vmu && (vmu->password[0] == '\0' || (vmu->password[0] == '-' && vmu->password[1] == '\0'))) {
  9964. /* saved password is blank, so don't bother asking */
  9965. password[0] = '\0';
  9966. } else {
  9967. if (ast_streamfile(chan, vm_password, ast_channel_language(chan))) {
  9968. ast_log(AST_LOG_WARNING, "Unable to stream password file\n");
  9969. return -1;
  9970. }
  9971. if (ast_readstring(chan, password, sizeof(password) - 1, 2000, 10000, "#") < 0) {
  9972. ast_log(AST_LOG_WARNING, "Unable to read password\n");
  9973. return -1;
  9974. } else if (password[0] == '*') {
  9975. /* user entered '*' */
  9976. ast_verb(4, "Password begins with '*', attempting jump to extension 'a'\n");
  9977. if (ast_exists_extension(chan, ast_channel_context(chan), "a", 1,
  9978. S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL))) {
  9979. mailbox[0] = '*';
  9980. return -1;
  9981. }
  9982. ast_verb(4, "Jump to extension 'a' failed; setting mailbox and user to NULL\n");
  9983. mailbox[0] = '\0';
  9984. /* if the password entered was '*', do not let a user mailbox be created if the extension 'a' is not defined */
  9985. vmu = NULL;
  9986. }
  9987. }
  9988. if (vmu) {
  9989. passptr = vmu->password;
  9990. if (passptr[0] == '-') passptr++;
  9991. }
  9992. if (vmu && !strcmp(passptr, password))
  9993. valid++;
  9994. else {
  9995. ast_verb(3, "Incorrect password '%s' for user '%s' (context = %s)\n", password, mailbox, context ? context : "default");
  9996. if (!ast_strlen_zero(prefix))
  9997. mailbox[0] = '\0';
  9998. }
  9999. logretries++;
  10000. if (!valid) {
  10001. if (skipuser || logretries >= max_logins) {
  10002. if (ast_streamfile(chan, "vm-incorrect", ast_channel_language(chan))) {
  10003. ast_log(AST_LOG_WARNING, "Unable to stream incorrect message\n");
  10004. return -1;
  10005. }
  10006. } else {
  10007. if (useadsi)
  10008. adsi_login(chan);
  10009. if (ast_streamfile(chan, "vm-incorrect-mailbox", ast_channel_language(chan))) {
  10010. ast_log(AST_LOG_WARNING, "Unable to stream incorrect mailbox message\n");
  10011. return -1;
  10012. }
  10013. }
  10014. if (ast_waitstream(chan, "")) /* Channel is hung up */
  10015. return -1;
  10016. }
  10017. }
  10018. if (!valid && (logretries >= max_logins)) {
  10019. ast_stopstream(chan);
  10020. ast_play_and_wait(chan, "vm-goodbye");
  10021. return -1;
  10022. }
  10023. if (vmu && !skipuser) {
  10024. memcpy(res_vmu, vmu, sizeof(struct ast_vm_user));
  10025. }
  10026. return 0;
  10027. }
  10028. static int play_message_by_id_helper(struct ast_channel *chan,
  10029. struct ast_vm_user *vmu,
  10030. struct vm_state *vms,
  10031. const char *msg_id)
  10032. {
  10033. if (message_range_and_existence_check(vms, &msg_id, 1, &vms->curmsg, vmu)) {
  10034. return -1;
  10035. }
  10036. /* Found the msg, so play it back */
  10037. make_file(vms->fn, sizeof(vms->fn), vms->curdir, vms->curmsg);
  10038. make_file(vms->fn, sizeof(vms->fn), vms->curdir, vms->curmsg);
  10039. #ifdef IMAP_STORAGE
  10040. /*IMAP storage stores any prepended message from a forward
  10041. * as a separate file from the rest of the message
  10042. */
  10043. if (!ast_strlen_zero(vms->introfn) && ast_fileexists(vms->introfn, NULL, NULL) > 0) {
  10044. wait_file(chan, vms, vms->introfn);
  10045. }
  10046. #endif
  10047. if ((wait_file(chan, vms, vms->fn)) < 0) {
  10048. ast_log(AST_LOG_WARNING, "Playback of message %s failed\n", vms->fn);
  10049. } else {
  10050. #ifdef IMAP_STORAGE
  10051. ast_mutex_lock(&vms->lock);
  10052. #endif
  10053. vms->heard[vms->curmsg] = 1;
  10054. #ifdef IMAP_STORAGE
  10055. ast_mutex_unlock(&vms->lock);
  10056. #endif
  10057. }
  10058. return 0;
  10059. }
  10060. /*!
  10061. * \brief Finds a message in a specific mailbox by msg_id and plays it to the channel
  10062. *
  10063. * \retval 0 Success
  10064. * \retval -1 Failure
  10065. */
  10066. static int play_message_by_id(struct ast_channel *chan, const char *mailbox, const char *context, const char *msg_id)
  10067. {
  10068. struct vm_state vms;
  10069. struct ast_vm_user *vmu = NULL, vmus;
  10070. int res = 0;
  10071. int open = 0;
  10072. int played = 0;
  10073. int i;
  10074. memset(&vmus, 0, sizeof(vmus));
  10075. memset(&vms, 0, sizeof(vms));
  10076. if (!(vmu = find_user(&vmus, context, mailbox))) {
  10077. goto play_msg_cleanup;
  10078. }
  10079. /* Iterate through every folder, find the msg, and play it */
  10080. for (i = 0; i < ARRAY_LEN(mailbox_folders) && !played; i++) {
  10081. ast_copy_string(vms.username, mailbox, sizeof(vms.username));
  10082. vms.lastmsg = -1;
  10083. /* open the mailbox state */
  10084. if ((res = open_mailbox(&vms, vmu, i)) < 0) {
  10085. ast_log(LOG_WARNING, "Could not open mailbox %s\n", mailbox);
  10086. res = -1;
  10087. goto play_msg_cleanup;
  10088. }
  10089. open = 1;
  10090. /* play msg if it exists in this mailbox */
  10091. if ((vms.lastmsg != -1) && !(play_message_by_id_helper(chan, vmu, &vms, msg_id))) {
  10092. played = 1;
  10093. }
  10094. /* close mailbox */
  10095. if ((res = close_mailbox(&vms, vmu) == ERROR_LOCK_PATH)) {
  10096. res = -1;
  10097. goto play_msg_cleanup;
  10098. }
  10099. open = 0;
  10100. }
  10101. play_msg_cleanup:
  10102. if (!played) {
  10103. res = -1;
  10104. }
  10105. if (vmu && open) {
  10106. close_mailbox(&vms, vmu);
  10107. }
  10108. #ifdef IMAP_STORAGE
  10109. if (vmu) {
  10110. vmstate_delete(&vms);
  10111. }
  10112. #endif
  10113. return res;
  10114. }
  10115. static int vm_playmsgexec(struct ast_channel *chan, const char *data)
  10116. {
  10117. char *parse;
  10118. char *mailbox = NULL;
  10119. char *context = NULL;
  10120. int res;
  10121. AST_DECLARE_APP_ARGS(args,
  10122. AST_APP_ARG(mailbox);
  10123. AST_APP_ARG(msg_id);
  10124. );
  10125. if (ast_channel_state(chan) != AST_STATE_UP) {
  10126. ast_debug(1, "Before ast_answer\n");
  10127. ast_answer(chan);
  10128. }
  10129. if (ast_strlen_zero(data)) {
  10130. return -1;
  10131. }
  10132. parse = ast_strdupa(data);
  10133. AST_STANDARD_APP_ARGS(args, parse);
  10134. if (ast_strlen_zero(args.mailbox) || ast_strlen_zero(args.msg_id)) {
  10135. return -1;
  10136. }
  10137. if ((context = strchr(args.mailbox, '@'))) {
  10138. *context++ = '\0';
  10139. }
  10140. mailbox = args.mailbox;
  10141. res = play_message_by_id(chan, mailbox, context, args.msg_id);
  10142. pbx_builtin_setvar_helper(chan, "VOICEMAIL_PLAYBACKSTATUS", res ? "FAILED" : "SUCCESS");
  10143. return 0;
  10144. }
  10145. static int vm_execmain(struct ast_channel *chan, const char *data)
  10146. {
  10147. /* XXX This is, admittedly, some pretty horrendous code. For some
  10148. reason it just seemed a lot easier to do with GOTO's. I feel
  10149. like I'm back in my GWBASIC days. XXX */
  10150. int res = -1;
  10151. int cmd = 0;
  10152. int valid = 0;
  10153. char prefixstr[80] ="";
  10154. char ext_context[256]="";
  10155. int box;
  10156. int useadsi = 0;
  10157. int skipuser = 0;
  10158. struct vm_state vms;
  10159. struct ast_vm_user *vmu = NULL, vmus;
  10160. char *context = NULL;
  10161. int silentexit = 0;
  10162. struct ast_flags flags = { 0 };
  10163. signed char record_gain = 0;
  10164. int play_auto = 0;
  10165. int play_folder = 0;
  10166. int in_urgent = 0;
  10167. #ifdef IMAP_STORAGE
  10168. int deleted = 0;
  10169. #endif
  10170. /* Add the vm_state to the active list and keep it active */
  10171. memset(&vms, 0, sizeof(vms));
  10172. vms.lastmsg = -1;
  10173. memset(&vmus, 0, sizeof(vmus));
  10174. ast_test_suite_event_notify("START", "Message: vm_execmain started");
  10175. if (ast_channel_state(chan) != AST_STATE_UP) {
  10176. ast_debug(1, "Before ast_answer\n");
  10177. ast_answer(chan);
  10178. }
  10179. if (!ast_strlen_zero(data)) {
  10180. char *opts[OPT_ARG_ARRAY_SIZE];
  10181. char *parse;
  10182. AST_DECLARE_APP_ARGS(args,
  10183. AST_APP_ARG(argv0);
  10184. AST_APP_ARG(argv1);
  10185. );
  10186. parse = ast_strdupa(data);
  10187. AST_STANDARD_APP_ARGS(args, parse);
  10188. if (args.argc == 2) {
  10189. if (ast_app_parse_options(vm_app_options, &flags, opts, args.argv1))
  10190. return -1;
  10191. if (ast_test_flag(&flags, OPT_RECORDGAIN)) {
  10192. int gain;
  10193. if (!ast_strlen_zero(opts[OPT_ARG_RECORDGAIN])) {
  10194. if (sscanf(opts[OPT_ARG_RECORDGAIN], "%30d", &gain) != 1) {
  10195. ast_log(AST_LOG_WARNING, "Invalid value '%s' provided for record gain option\n", opts[OPT_ARG_RECORDGAIN]);
  10196. return -1;
  10197. } else {
  10198. record_gain = (signed char) gain;
  10199. }
  10200. } else {
  10201. ast_log(AST_LOG_WARNING, "Invalid Gain level set with option g\n");
  10202. }
  10203. }
  10204. if (ast_test_flag(&flags, OPT_AUTOPLAY) ) {
  10205. play_auto = 1;
  10206. if (!ast_strlen_zero(opts[OPT_ARG_PLAYFOLDER])) {
  10207. /* See if it is a folder name first */
  10208. if (isdigit(opts[OPT_ARG_PLAYFOLDER][0])) {
  10209. if (sscanf(opts[OPT_ARG_PLAYFOLDER], "%30d", &play_folder) != 1) {
  10210. play_folder = -1;
  10211. }
  10212. } else {
  10213. play_folder = get_folder_by_name(opts[OPT_ARG_PLAYFOLDER]);
  10214. }
  10215. } else {
  10216. ast_log(AST_LOG_WARNING, "Invalid folder set with option a\n");
  10217. }
  10218. if (play_folder > 9 || play_folder < 0) {
  10219. ast_log(AST_LOG_WARNING,
  10220. "Invalid value '%s' provided for folder autoplay option. Defaulting to 'INBOX'\n",
  10221. opts[OPT_ARG_PLAYFOLDER]);
  10222. play_folder = 0;
  10223. }
  10224. }
  10225. } else {
  10226. /* old style options parsing */
  10227. while (*(args.argv0)) {
  10228. if (*(args.argv0) == 's')
  10229. ast_set_flag(&flags, OPT_SILENT);
  10230. else if (*(args.argv0) == 'p')
  10231. ast_set_flag(&flags, OPT_PREPEND_MAILBOX);
  10232. else
  10233. break;
  10234. (args.argv0)++;
  10235. }
  10236. }
  10237. valid = ast_test_flag(&flags, OPT_SILENT);
  10238. if ((context = strchr(args.argv0, '@')))
  10239. *context++ = '\0';
  10240. if (ast_test_flag(&flags, OPT_PREPEND_MAILBOX))
  10241. ast_copy_string(prefixstr, args.argv0, sizeof(prefixstr));
  10242. else
  10243. ast_copy_string(vms.username, args.argv0, sizeof(vms.username));
  10244. if (!ast_strlen_zero(vms.username) && (vmu = find_user(&vmus, context ,vms.username)))
  10245. skipuser++;
  10246. else
  10247. valid = 0;
  10248. }
  10249. if (!valid)
  10250. res = vm_authenticate(chan, vms.username, sizeof(vms.username), &vmus, context, prefixstr, skipuser, maxlogins, 0);
  10251. ast_debug(1, "After vm_authenticate\n");
  10252. if (vms.username[0] == '*') {
  10253. ast_debug(1, "user pressed * in context '%s'\n", ast_channel_context(chan));
  10254. /* user entered '*' */
  10255. if (!ast_goto_if_exists(chan, ast_channel_context(chan), "a", 1)) {
  10256. ast_test_suite_event_notify("REDIRECT", "Message: redirecting user to 'a' extension");
  10257. res = 0; /* prevent hangup */
  10258. goto out;
  10259. }
  10260. }
  10261. if (!res) {
  10262. valid = 1;
  10263. if (!skipuser)
  10264. vmu = &vmus;
  10265. } else {
  10266. res = 0;
  10267. }
  10268. /* If ADSI is supported, setup login screen */
  10269. adsi_begin(chan, &useadsi);
  10270. if (!valid) {
  10271. goto out;
  10272. }
  10273. ast_test_suite_event_notify("AUTHENTICATED", "Message: vm_user authenticated");
  10274. #ifdef IMAP_STORAGE
  10275. pthread_once(&ts_vmstate.once, ts_vmstate.key_init);
  10276. pthread_setspecific(ts_vmstate.key, &vms);
  10277. vms.interactive = 1;
  10278. vms.updated = 1;
  10279. if (vmu)
  10280. ast_copy_string(vms.context, vmu->context, sizeof(vms.context));
  10281. vmstate_insert(&vms);
  10282. init_vm_state(&vms);
  10283. #endif
  10284. /* Set language from config to override channel language */
  10285. if (!ast_strlen_zero(vmu->language)) {
  10286. ast_channel_lock(chan);
  10287. ast_channel_language_set(chan, vmu->language);
  10288. ast_channel_unlock(chan);
  10289. }
  10290. /* Retrieve urgent, old and new message counts */
  10291. ast_debug(1, "Before open_mailbox\n");
  10292. res = open_mailbox(&vms, vmu, OLD_FOLDER); /* Count all messages, even Urgent */
  10293. if (res < 0)
  10294. goto out;
  10295. vms.oldmessages = vms.lastmsg + 1;
  10296. ast_debug(1, "Number of old messages: %d\n", vms.oldmessages);
  10297. /* check INBOX */
  10298. res = open_mailbox(&vms, vmu, NEW_FOLDER);
  10299. if (res < 0)
  10300. goto out;
  10301. vms.newmessages = vms.lastmsg + 1;
  10302. ast_debug(1, "Number of new messages: %d\n", vms.newmessages);
  10303. /* Start in Urgent */
  10304. in_urgent = 1;
  10305. res = open_mailbox(&vms, vmu, 11); /*11 is the Urgent folder */
  10306. if (res < 0)
  10307. goto out;
  10308. vms.urgentmessages = vms.lastmsg + 1;
  10309. ast_debug(1, "Number of urgent messages: %d\n", vms.urgentmessages);
  10310. /* Select proper mailbox FIRST!! */
  10311. if (play_auto) {
  10312. ast_test_suite_event_notify("AUTOPLAY", "Message: auto-playing messages");
  10313. if (vms.urgentmessages) {
  10314. in_urgent = 1;
  10315. res = open_mailbox(&vms, vmu, 11);
  10316. } else {
  10317. in_urgent = 0;
  10318. res = open_mailbox(&vms, vmu, play_folder);
  10319. }
  10320. if (res < 0)
  10321. goto out;
  10322. /* If there are no new messages, inform the user and hangup */
  10323. if (vms.lastmsg == -1) {
  10324. in_urgent = 0;
  10325. cmd = vm_browse_messages(chan, &vms, vmu);
  10326. res = 0;
  10327. goto out;
  10328. }
  10329. } else {
  10330. if (!vms.newmessages && !vms.urgentmessages && vms.oldmessages) {
  10331. /* If we only have old messages start here */
  10332. res = open_mailbox(&vms, vmu, OLD_FOLDER); /* Count all messages, even Urgent */
  10333. in_urgent = 0;
  10334. play_folder = 1;
  10335. if (res < 0)
  10336. goto out;
  10337. } else if (!vms.urgentmessages && vms.newmessages) {
  10338. /* If we have new messages but none are urgent */
  10339. in_urgent = 0;
  10340. res = open_mailbox(&vms, vmu, NEW_FOLDER);
  10341. if (res < 0)
  10342. goto out;
  10343. }
  10344. }
  10345. if (useadsi)
  10346. adsi_status(chan, &vms);
  10347. res = 0;
  10348. /* Check to see if this is a new user */
  10349. if (!strcasecmp(vmu->mailbox, vmu->password) &&
  10350. (ast_test_flag(vmu, VM_FORCENAME | VM_FORCEGREET))) {
  10351. if (ast_play_and_wait(chan, "vm-newuser") == -1)
  10352. ast_log(AST_LOG_WARNING, "Couldn't stream new user file\n");
  10353. cmd = vm_newuser(chan, vmu, &vms, vmfmts, record_gain);
  10354. if ((cmd == 't') || (cmd == '#')) {
  10355. /* Timeout */
  10356. ast_test_suite_event_notify("TIMEOUT", "Message: response from user timed out");
  10357. res = 0;
  10358. goto out;
  10359. } else if (cmd < 0) {
  10360. /* Hangup */
  10361. ast_test_suite_event_notify("HANGUP", "Message: hangup detected");
  10362. res = -1;
  10363. goto out;
  10364. }
  10365. }
  10366. #ifdef IMAP_STORAGE
  10367. ast_debug(3, "Checking quotas: comparing %u to %u\n", vms.quota_usage, vms.quota_limit);
  10368. if (vms.quota_limit && vms.quota_usage >= vms.quota_limit) {
  10369. ast_debug(1, "*** QUOTA EXCEEDED!!\n");
  10370. cmd = ast_play_and_wait(chan, "vm-mailboxfull");
  10371. }
  10372. ast_debug(3, "Checking quotas: User has %d messages and limit is %d.\n", (vms.newmessages + vms.oldmessages), vmu->maxmsg);
  10373. if ((vms.newmessages + vms.oldmessages) >= vmu->maxmsg) {
  10374. ast_log(AST_LOG_WARNING, "No more messages possible. User has %d messages and limit is %d.\n", (vms.newmessages + vms.oldmessages), vmu->maxmsg);
  10375. cmd = ast_play_and_wait(chan, "vm-mailboxfull");
  10376. }
  10377. #endif
  10378. ast_test_suite_event_notify("INTRO", "Message: playing intro menu");
  10379. if (play_auto) {
  10380. cmd = '1';
  10381. } else {
  10382. cmd = vm_intro(chan, vmu, &vms);
  10383. }
  10384. ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", cmd, cmd);
  10385. vms.repeats = 0;
  10386. vms.starting = 1;
  10387. while ((cmd > -1) && (cmd != 't') && (cmd != '#')) {
  10388. /* Run main menu */
  10389. switch (cmd) {
  10390. case '1': /* First message */
  10391. vms.curmsg = 0;
  10392. /* Fall through */
  10393. case '5': /* Play current message */
  10394. ast_test_suite_event_notify("BROWSE", "Message: browsing message %d\r\nVoicemail: %d", vms.curmsg, vms.curmsg);
  10395. cmd = vm_browse_messages(chan, &vms, vmu);
  10396. break;
  10397. case '2': /* Change folders */
  10398. ast_test_suite_event_notify("CHANGEFOLDER", "Message: browsing to a different folder");
  10399. if (useadsi)
  10400. adsi_folders(chan, 0, "Change to folder...");
  10401. cmd = get_folder2(chan, "vm-changeto", 0);
  10402. ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", cmd, cmd);
  10403. if (cmd == '#') {
  10404. cmd = 0;
  10405. } else if (cmd > 0) {
  10406. cmd = cmd - '0';
  10407. res = close_mailbox(&vms, vmu);
  10408. if (res == ERROR_LOCK_PATH)
  10409. goto out;
  10410. /* If folder is not urgent, set in_urgent to zero! */
  10411. if (cmd != 11) in_urgent = 0;
  10412. res = open_mailbox(&vms, vmu, cmd);
  10413. if (res < 0)
  10414. goto out;
  10415. play_folder = cmd;
  10416. cmd = 0;
  10417. }
  10418. if (useadsi)
  10419. adsi_status2(chan, &vms);
  10420. if (!cmd) {
  10421. cmd = vm_play_folder_name(chan, vms.vmbox);
  10422. }
  10423. vms.starting = 1;
  10424. vms.curmsg = 0;
  10425. break;
  10426. case '3': /* Advanced options */
  10427. ast_test_suite_event_notify("ADVOPTIONS", "Message: entering advanced options menu");
  10428. cmd = 0;
  10429. vms.repeats = 0;
  10430. while ((cmd > -1) && (cmd != 't') && (cmd != '#')) {
  10431. switch (cmd) {
  10432. case '1': /* Reply */
  10433. if (vms.lastmsg > -1 && !vms.starting) {
  10434. cmd = advanced_options(chan, vmu, &vms, vms.curmsg, 1, record_gain);
  10435. if (cmd == ERROR_LOCK_PATH || cmd == OPERATOR_EXIT) {
  10436. res = cmd;
  10437. goto out;
  10438. }
  10439. } else {
  10440. cmd = ast_play_and_wait(chan, "vm-sorry");
  10441. }
  10442. cmd = 't';
  10443. break;
  10444. case '2': /* Callback */
  10445. if (!vms.starting)
  10446. ast_verb(3, "Callback Requested\n");
  10447. if (!ast_strlen_zero(vmu->callback) && vms.lastmsg > -1 && !vms.starting) {
  10448. cmd = advanced_options(chan, vmu, &vms, vms.curmsg, 2, record_gain);
  10449. if (cmd == 9) {
  10450. silentexit = 1;
  10451. goto out;
  10452. } else if (cmd == ERROR_LOCK_PATH) {
  10453. res = cmd;
  10454. goto out;
  10455. }
  10456. } else {
  10457. cmd = ast_play_and_wait(chan, "vm-sorry");
  10458. }
  10459. cmd = 't';
  10460. break;
  10461. case '3': /* Envelope */
  10462. if (vms.lastmsg > -1 && !vms.starting) {
  10463. cmd = advanced_options(chan, vmu, &vms, vms.curmsg, 3, record_gain);
  10464. if (cmd == ERROR_LOCK_PATH) {
  10465. res = cmd;
  10466. goto out;
  10467. }
  10468. } else {
  10469. cmd = ast_play_and_wait(chan, "vm-sorry");
  10470. }
  10471. cmd = 't';
  10472. break;
  10473. case '4': /* Dialout */
  10474. if (!ast_strlen_zero(vmu->dialout)) {
  10475. cmd = dialout(chan, vmu, NULL, vmu->dialout);
  10476. if (cmd == 9) {
  10477. silentexit = 1;
  10478. goto out;
  10479. }
  10480. } else {
  10481. cmd = ast_play_and_wait(chan, "vm-sorry");
  10482. }
  10483. cmd = 't';
  10484. break;
  10485. case '5': /* Leave VoiceMail */
  10486. if (ast_test_flag(vmu, VM_SVMAIL)) {
  10487. cmd = forward_message(chan, context, &vms, vmu, vmfmts, 1, record_gain, 0);
  10488. if (cmd == ERROR_LOCK_PATH || cmd == OPERATOR_EXIT) {
  10489. res = cmd;
  10490. goto out;
  10491. }
  10492. } else {
  10493. cmd = ast_play_and_wait(chan, "vm-sorry");
  10494. }
  10495. cmd = 't';
  10496. break;
  10497. case '*': /* Return to main menu */
  10498. cmd = 't';
  10499. break;
  10500. default:
  10501. cmd = 0;
  10502. if (!vms.starting) {
  10503. cmd = ast_play_and_wait(chan, "vm-toreply");
  10504. }
  10505. if (!ast_strlen_zero(vmu->callback) && !vms.starting && !cmd) {
  10506. cmd = ast_play_and_wait(chan, "vm-tocallback");
  10507. }
  10508. if (!cmd && !vms.starting) {
  10509. cmd = ast_play_and_wait(chan, "vm-tohearenv");
  10510. }
  10511. if (!ast_strlen_zero(vmu->dialout) && !cmd) {
  10512. cmd = ast_play_and_wait(chan, "vm-tomakecall");
  10513. }
  10514. if (ast_test_flag(vmu, VM_SVMAIL) && !cmd) {
  10515. cmd = ast_play_and_wait(chan, "vm-leavemsg");
  10516. }
  10517. if (!cmd) {
  10518. cmd = ast_play_and_wait(chan, "vm-starmain");
  10519. }
  10520. if (!cmd) {
  10521. cmd = ast_waitfordigit(chan, 6000);
  10522. }
  10523. if (!cmd) {
  10524. vms.repeats++;
  10525. }
  10526. if (vms.repeats > 3) {
  10527. cmd = 't';
  10528. }
  10529. ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", cmd, cmd);
  10530. }
  10531. }
  10532. if (cmd == 't') {
  10533. cmd = 0;
  10534. vms.repeats = 0;
  10535. }
  10536. break;
  10537. case '4': /* Go to the previous message */
  10538. ast_test_suite_event_notify("PREVMSG", "Message: browsing message %d\r\nVoicemail: %d", vms.curmsg - 1, vms.curmsg - 1);
  10539. if (vms.curmsg > 0) {
  10540. vms.curmsg--;
  10541. cmd = play_message(chan, vmu, &vms);
  10542. } else {
  10543. /* Check if we were listening to new
  10544. messages. If so, go to Urgent messages
  10545. instead of saying "no more messages"
  10546. */
  10547. if (in_urgent == 0 && vms.urgentmessages > 0) {
  10548. /* Check for Urgent messages */
  10549. in_urgent = 1;
  10550. res = close_mailbox(&vms, vmu);
  10551. if (res == ERROR_LOCK_PATH)
  10552. goto out;
  10553. res = open_mailbox(&vms, vmu, 11); /* Open Urgent folder */
  10554. if (res < 0)
  10555. goto out;
  10556. ast_debug(1, "No more new messages, opened INBOX and got %d Urgent messages\n", vms.lastmsg + 1);
  10557. vms.curmsg = vms.lastmsg;
  10558. if (vms.lastmsg < 0) {
  10559. cmd = ast_play_and_wait(chan, "vm-nomore");
  10560. }
  10561. } else if (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms.lastmsg > 0) {
  10562. vms.curmsg = vms.lastmsg;
  10563. cmd = play_message(chan, vmu, &vms);
  10564. } else {
  10565. cmd = ast_play_and_wait(chan, "vm-nomore");
  10566. }
  10567. }
  10568. break;
  10569. case '6': /* Go to the next message */
  10570. ast_test_suite_event_notify("PREVMSG", "Message: browsing message %d\r\nVoicemail: %d", vms.curmsg + 1, vms.curmsg + 1);
  10571. if (vms.curmsg < vms.lastmsg) {
  10572. vms.curmsg++;
  10573. cmd = play_message(chan, vmu, &vms);
  10574. } else {
  10575. if (in_urgent && vms.newmessages > 0) {
  10576. /* Check if we were listening to urgent
  10577. * messages. If so, go to regular new messages
  10578. * instead of saying "no more messages"
  10579. */
  10580. in_urgent = 0;
  10581. res = close_mailbox(&vms, vmu);
  10582. if (res == ERROR_LOCK_PATH)
  10583. goto out;
  10584. res = open_mailbox(&vms, vmu, NEW_FOLDER);
  10585. if (res < 0)
  10586. goto out;
  10587. ast_debug(1, "No more urgent messages, opened INBOX and got %d new messages\n", vms.lastmsg + 1);
  10588. vms.curmsg = -1;
  10589. if (vms.lastmsg < 0) {
  10590. cmd = ast_play_and_wait(chan, "vm-nomore");
  10591. }
  10592. } else if (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms.lastmsg > 0) {
  10593. vms.curmsg = 0;
  10594. cmd = play_message(chan, vmu, &vms);
  10595. } else {
  10596. cmd = ast_play_and_wait(chan, "vm-nomore");
  10597. }
  10598. }
  10599. break;
  10600. case '7': /* Delete the current message */
  10601. if (vms.curmsg >= 0 && vms.curmsg <= vms.lastmsg) {
  10602. vms.deleted[vms.curmsg] = !vms.deleted[vms.curmsg];
  10603. if (useadsi)
  10604. adsi_delete(chan, &vms);
  10605. if (vms.deleted[vms.curmsg]) {
  10606. if (play_folder == 0) {
  10607. if (in_urgent) {
  10608. vms.urgentmessages--;
  10609. } else {
  10610. vms.newmessages--;
  10611. }
  10612. }
  10613. else if (play_folder == 1)
  10614. vms.oldmessages--;
  10615. cmd = ast_play_and_wait(chan, "vm-deleted");
  10616. } else {
  10617. if (play_folder == 0) {
  10618. if (in_urgent) {
  10619. vms.urgentmessages++;
  10620. } else {
  10621. vms.newmessages++;
  10622. }
  10623. }
  10624. else if (play_folder == 1)
  10625. vms.oldmessages++;
  10626. cmd = ast_play_and_wait(chan, "vm-undeleted");
  10627. }
  10628. if (ast_test_flag(vmu, VM_SKIPAFTERCMD)) {
  10629. if (vms.curmsg < vms.lastmsg) {
  10630. vms.curmsg++;
  10631. cmd = play_message(chan, vmu, &vms);
  10632. } else if (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms.lastmsg > 0) {
  10633. vms.curmsg = 0;
  10634. cmd = play_message(chan, vmu, &vms);
  10635. } else {
  10636. /* Check if we were listening to urgent
  10637. messages. If so, go to regular new messages
  10638. instead of saying "no more messages"
  10639. */
  10640. if (in_urgent == 1) {
  10641. /* Check for new messages */
  10642. in_urgent = 0;
  10643. res = close_mailbox(&vms, vmu);
  10644. if (res == ERROR_LOCK_PATH)
  10645. goto out;
  10646. res = open_mailbox(&vms, vmu, NEW_FOLDER);
  10647. if (res < 0)
  10648. goto out;
  10649. ast_debug(1, "No more urgent messages, opened INBOX and got %d new messages\n", vms.lastmsg + 1);
  10650. vms.curmsg = -1;
  10651. if (vms.lastmsg < 0) {
  10652. cmd = ast_play_and_wait(chan, "vm-nomore");
  10653. }
  10654. } else {
  10655. cmd = ast_play_and_wait(chan, "vm-nomore");
  10656. }
  10657. }
  10658. }
  10659. } else /* Delete not valid if we haven't selected a message */
  10660. cmd = 0;
  10661. #ifdef IMAP_STORAGE
  10662. deleted = 1;
  10663. #endif
  10664. break;
  10665. case '8': /* Forward the current message */
  10666. if (vms.lastmsg > -1) {
  10667. cmd = forward_message(chan, context, &vms, vmu, vmfmts, 0, record_gain, in_urgent);
  10668. if (cmd == ERROR_LOCK_PATH) {
  10669. res = cmd;
  10670. goto out;
  10671. }
  10672. } else {
  10673. /* Check if we were listening to urgent
  10674. messages. If so, go to regular new messages
  10675. instead of saying "no more messages"
  10676. */
  10677. if (in_urgent == 1 && vms.newmessages > 0) {
  10678. /* Check for new messages */
  10679. in_urgent = 0;
  10680. res = close_mailbox(&vms, vmu);
  10681. if (res == ERROR_LOCK_PATH)
  10682. goto out;
  10683. res = open_mailbox(&vms, vmu, NEW_FOLDER);
  10684. if (res < 0)
  10685. goto out;
  10686. ast_debug(1, "No more urgent messages, opened INBOX and got %d new messages\n", vms.lastmsg + 1);
  10687. vms.curmsg = -1;
  10688. if (vms.lastmsg < 0) {
  10689. cmd = ast_play_and_wait(chan, "vm-nomore");
  10690. }
  10691. } else {
  10692. cmd = ast_play_and_wait(chan, "vm-nomore");
  10693. }
  10694. }
  10695. break;
  10696. case '9': /* Save message to folder */
  10697. ast_test_suite_event_notify("SAVEMSG", "Message: saving message %d\r\nVoicemail: %d", vms.curmsg, vms.curmsg);
  10698. if (vms.curmsg < 0 || vms.curmsg > vms.lastmsg) {
  10699. /* No message selected */
  10700. cmd = 0;
  10701. break;
  10702. }
  10703. if (useadsi)
  10704. adsi_folders(chan, 1, "Save to folder...");
  10705. cmd = get_folder2(chan, "vm-savefolder", 1);
  10706. ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", cmd, cmd);
  10707. box = 0; /* Shut up compiler */
  10708. if (cmd == '#') {
  10709. cmd = 0;
  10710. break;
  10711. } else if (cmd > 0) {
  10712. box = cmd = cmd - '0';
  10713. cmd = save_to_folder(vmu, &vms, vms.curmsg, cmd, NULL, 0);
  10714. if (cmd == ERROR_LOCK_PATH) {
  10715. res = cmd;
  10716. goto out;
  10717. #ifndef IMAP_STORAGE
  10718. } else if (!cmd) {
  10719. vms.deleted[vms.curmsg] = 1;
  10720. #endif
  10721. } else {
  10722. vms.deleted[vms.curmsg] = 0;
  10723. vms.heard[vms.curmsg] = 0;
  10724. }
  10725. }
  10726. make_file(vms.fn, sizeof(vms.fn), vms.curdir, vms.curmsg);
  10727. if (useadsi)
  10728. adsi_message(chan, &vms);
  10729. snprintf(vms.fn, sizeof(vms.fn), "vm-%s", mbox(vmu, box));
  10730. if (!cmd) {
  10731. cmd = ast_play_and_wait(chan, "vm-message");
  10732. if (!cmd)
  10733. cmd = say_and_wait(chan, vms.curmsg + 1, ast_channel_language(chan));
  10734. if (!cmd)
  10735. cmd = ast_play_and_wait(chan, "vm-savedto");
  10736. if (!cmd)
  10737. cmd = vm_play_folder_name(chan, vms.fn);
  10738. } else {
  10739. cmd = ast_play_and_wait(chan, "vm-mailboxfull");
  10740. }
  10741. if (ast_test_flag((&globalflags), VM_SKIPAFTERCMD)) {
  10742. if (vms.curmsg < vms.lastmsg) {
  10743. vms.curmsg++;
  10744. cmd = play_message(chan, vmu, &vms);
  10745. } else if (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms.lastmsg > 0) {
  10746. vms.curmsg = 0;
  10747. cmd = play_message(chan, vmu, &vms);
  10748. } else {
  10749. /* Check if we were listening to urgent
  10750. messages. If so, go to regular new messages
  10751. instead of saying "no more messages"
  10752. */
  10753. if (in_urgent == 1 && vms.newmessages > 0) {
  10754. /* Check for new messages */
  10755. in_urgent = 0;
  10756. res = close_mailbox(&vms, vmu);
  10757. if (res == ERROR_LOCK_PATH)
  10758. goto out;
  10759. res = open_mailbox(&vms, vmu, NEW_FOLDER);
  10760. if (res < 0)
  10761. goto out;
  10762. ast_debug(1, "No more urgent messages, opened INBOX and got %d new messages\n", vms.lastmsg + 1);
  10763. vms.curmsg = -1;
  10764. if (vms.lastmsg < 0) {
  10765. cmd = ast_play_and_wait(chan, "vm-nomore");
  10766. }
  10767. } else {
  10768. cmd = ast_play_and_wait(chan, "vm-nomore");
  10769. }
  10770. }
  10771. }
  10772. break;
  10773. case '*': /* Help */
  10774. if (!vms.starting) {
  10775. if (!strncasecmp(ast_channel_language(chan), "ja", 2)) {
  10776. cmd = vm_play_folder_name(chan, vms.vmbox);
  10777. if (!cmd)
  10778. cmd = ast_play_and_wait(chan, "jp-wa");
  10779. if (!cmd)
  10780. cmd = ast_play_and_wait(chan, "digits/1");
  10781. if (!cmd)
  10782. cmd = ast_play_and_wait(chan, "jp-wo");
  10783. if (!cmd)
  10784. cmd = ast_play_and_wait(chan, "silence/1");
  10785. if (!cmd)
  10786. cmd = ast_play_and_wait(chan, "vm-opts");
  10787. if (!cmd)
  10788. cmd = vm_instructions(chan, vmu, &vms, 1, in_urgent);
  10789. break;
  10790. }
  10791. cmd = ast_play_and_wait(chan, "vm-onefor");
  10792. if (!strncasecmp(ast_channel_language(chan), "he", 2)) {
  10793. cmd = ast_play_and_wait(chan, "vm-for");
  10794. }
  10795. if (!cmd)
  10796. cmd = vm_play_folder_name(chan, vms.vmbox);
  10797. if (!cmd)
  10798. cmd = ast_play_and_wait(chan, "vm-opts");
  10799. if (!cmd)
  10800. cmd = vm_instructions(chan, vmu, &vms, 1, in_urgent);
  10801. } else
  10802. cmd = 0;
  10803. break;
  10804. case '0': /* Mailbox options */
  10805. cmd = vm_options(chan, vmu, &vms, vmfmts, record_gain);
  10806. if (useadsi)
  10807. adsi_status(chan, &vms);
  10808. break;
  10809. default: /* Nothing */
  10810. ast_test_suite_event_notify("PLAYBACK", "Message: instructions");
  10811. cmd = vm_instructions(chan, vmu, &vms, 0, in_urgent);
  10812. break;
  10813. }
  10814. }
  10815. if ((cmd == 't') || (cmd == '#')) {
  10816. /* Timeout */
  10817. res = 0;
  10818. } else {
  10819. /* Hangup */
  10820. res = -1;
  10821. }
  10822. out:
  10823. if (res > -1) {
  10824. ast_stopstream(chan);
  10825. adsi_goodbye(chan);
  10826. if (valid && res != OPERATOR_EXIT) {
  10827. if (silentexit)
  10828. res = ast_play_and_wait(chan, "vm-dialout");
  10829. else
  10830. res = ast_play_and_wait(chan, "vm-goodbye");
  10831. }
  10832. if ((valid && res > 0) || res == OPERATOR_EXIT) {
  10833. res = 0;
  10834. }
  10835. if (useadsi)
  10836. ast_adsi_unload_session(chan);
  10837. }
  10838. if (vmu)
  10839. close_mailbox(&vms, vmu);
  10840. if (valid) {
  10841. int new = 0, old = 0, urgent = 0;
  10842. snprintf(ext_context, sizeof(ext_context), "%s@%s", vms.username, vmu->context);
  10843. /* Urgent flag not passwd to externnotify here */
  10844. run_externnotify(vmu->context, vmu->mailbox, NULL);
  10845. ast_app_inboxcount2(ext_context, &urgent, &new, &old);
  10846. queue_mwi_event(ast_channel_uniqueid(chan), ext_context, urgent, new, old);
  10847. }
  10848. #ifdef IMAP_STORAGE
  10849. /* expunge message - use UID Expunge if supported on IMAP server*/
  10850. ast_debug(3, "*** Checking if we can expunge, deleted set to %d, expungeonhangup set to %d\n", deleted, expungeonhangup);
  10851. if (vmu && deleted == 1 && expungeonhangup == 1 && vms.mailstream != NULL) {
  10852. ast_mutex_lock(&vms.lock);
  10853. #ifdef HAVE_IMAP_TK2006
  10854. if (LEVELUIDPLUS (vms.mailstream)) {
  10855. mail_expunge_full(vms.mailstream, NIL, EX_UID);
  10856. } else
  10857. #endif
  10858. mail_expunge(vms.mailstream);
  10859. ast_mutex_unlock(&vms.lock);
  10860. }
  10861. /* before we delete the state, we should copy pertinent info
  10862. * back to the persistent model */
  10863. if (vmu) {
  10864. vmstate_delete(&vms);
  10865. }
  10866. #endif
  10867. if (vmu)
  10868. free_user(vmu);
  10869. #ifdef IMAP_STORAGE
  10870. pthread_setspecific(ts_vmstate.key, NULL);
  10871. #endif
  10872. return res;
  10873. }
  10874. static int vm_exec(struct ast_channel *chan, const char *data)
  10875. {
  10876. int res = 0;
  10877. char *tmp;
  10878. struct leave_vm_options leave_options;
  10879. struct ast_flags flags = { 0 };
  10880. char *opts[OPT_ARG_ARRAY_SIZE];
  10881. AST_DECLARE_APP_ARGS(args,
  10882. AST_APP_ARG(argv0);
  10883. AST_APP_ARG(argv1);
  10884. );
  10885. memset(&leave_options, 0, sizeof(leave_options));
  10886. if (ast_channel_state(chan) != AST_STATE_UP)
  10887. ast_answer(chan);
  10888. if (!ast_strlen_zero(data)) {
  10889. tmp = ast_strdupa(data);
  10890. AST_STANDARD_APP_ARGS(args, tmp);
  10891. if (args.argc == 2) {
  10892. if (ast_app_parse_options(vm_app_options, &flags, opts, args.argv1))
  10893. return -1;
  10894. ast_copy_flags(&leave_options, &flags, OPT_SILENT | OPT_BUSY_GREETING | OPT_UNAVAIL_GREETING | OPT_MESSAGE_Urgent | OPT_MESSAGE_PRIORITY | OPT_DTMFEXIT);
  10895. if (ast_test_flag(&flags, OPT_RECORDGAIN)) {
  10896. int gain;
  10897. if (sscanf(opts[OPT_ARG_RECORDGAIN], "%30d", &gain) != 1) {
  10898. ast_log(AST_LOG_WARNING, "Invalid value '%s' provided for record gain option\n", opts[OPT_ARG_RECORDGAIN]);
  10899. return -1;
  10900. } else {
  10901. leave_options.record_gain = (signed char) gain;
  10902. }
  10903. }
  10904. if (ast_test_flag(&flags, OPT_DTMFEXIT)) {
  10905. if (!ast_strlen_zero(opts[OPT_ARG_DTMFEXIT]))
  10906. leave_options.exitcontext = opts[OPT_ARG_DTMFEXIT];
  10907. }
  10908. }
  10909. } else {
  10910. char temp[256];
  10911. res = ast_app_getdata(chan, "vm-whichbox", temp, sizeof(temp) - 1, 0);
  10912. if (res < 0)
  10913. return res;
  10914. if (ast_strlen_zero(temp))
  10915. return 0;
  10916. args.argv0 = ast_strdupa(temp);
  10917. }
  10918. res = leave_voicemail(chan, args.argv0, &leave_options);
  10919. if (res == 't') {
  10920. ast_play_and_wait(chan, "vm-goodbye");
  10921. res = 0;
  10922. }
  10923. if (res == OPERATOR_EXIT) {
  10924. res = 0;
  10925. }
  10926. if (res == ERROR_LOCK_PATH) {
  10927. ast_log(AST_LOG_ERROR, "Could not leave voicemail. The path is already locked.\n");
  10928. pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
  10929. res = 0;
  10930. }
  10931. return res;
  10932. }
  10933. static int add_message_id(struct ast_config *msg_cfg, char *dir, int msg, char *filename, char *id, size_t id_size, struct ast_vm_user *vmu, int folder)
  10934. {
  10935. struct ast_variable *var;
  10936. struct ast_category *cat;
  10937. generate_msg_id(id);
  10938. var = ast_variable_new("msg_id", id, "");
  10939. if (!var) {
  10940. return -1;
  10941. }
  10942. cat = ast_category_get(msg_cfg, "message", NULL);
  10943. if (!cat) {
  10944. ast_log(LOG_ERROR, "Voicemail data file %s/%d.txt has no [message] category?\n", dir, msg);
  10945. ast_variables_destroy(var);
  10946. return -1;
  10947. }
  10948. ast_variable_append(cat, var);
  10949. if (ast_config_text_file_save(filename, msg_cfg, "app_voicemail")) {
  10950. ast_log(LOG_WARNING, "Unable to update %s to have a message ID\n", filename);
  10951. return -1;
  10952. }
  10953. UPDATE_MSG_ID(dir, msg, id, vmu, msg_cfg, folder);
  10954. return 0;
  10955. }
  10956. static struct ast_vm_user *find_or_create(const char *context, const char *box)
  10957. {
  10958. struct ast_vm_user *vmu;
  10959. if (!ast_strlen_zero(box) && box[0] == '*') {
  10960. ast_log(LOG_WARNING, "Mailbox %s in context %s begins with '*' character. The '*' character,"
  10961. "\n\twhen it is the first character in a mailbox or password, is used to jump to a"
  10962. "\n\tpredefined extension 'a'. A mailbox or password beginning with '*' is not valid"
  10963. "\n\tand will be ignored.\n", box, context);
  10964. return NULL;
  10965. }
  10966. AST_LIST_TRAVERSE(&users, vmu, list) {
  10967. if (ast_test_flag((&globalflags), VM_SEARCH) && !strcasecmp(box, vmu->mailbox)) {
  10968. if (strcasecmp(vmu->context, context)) {
  10969. ast_log(LOG_WARNING, "\nIt has been detected that you have defined mailbox '%s' in separate\
  10970. \n\tcontexts and that you have the 'searchcontexts' option on. This type of\
  10971. \n\tconfiguration creates an ambiguity that you likely do not want. Please\
  10972. \n\tamend your voicemail.conf file to avoid this situation.\n", box);
  10973. }
  10974. ast_log(LOG_WARNING, "Ignoring duplicated mailbox %s\n", box);
  10975. return NULL;
  10976. }
  10977. if (!strcasecmp(context, vmu->context) && !strcasecmp(box, vmu->mailbox)) {
  10978. ast_log(LOG_WARNING, "Ignoring duplicated mailbox %s in context %s\n", box, context);
  10979. return NULL;
  10980. }
  10981. }
  10982. if (!(vmu = ast_calloc(1, sizeof(*vmu))))
  10983. return NULL;
  10984. ast_copy_string(vmu->context, context, sizeof(vmu->context));
  10985. ast_copy_string(vmu->mailbox, box, sizeof(vmu->mailbox));
  10986. AST_LIST_INSERT_TAIL(&users, vmu, list);
  10987. return vmu;
  10988. }
  10989. static int append_mailbox(const char *context, const char *box, const char *data)
  10990. {
  10991. /* Assumes lock is already held */
  10992. char *tmp;
  10993. char *stringp;
  10994. char *s;
  10995. struct ast_vm_user *vmu;
  10996. char *mailbox_full;
  10997. int new = 0, old = 0, urgent = 0;
  10998. char secretfn[PATH_MAX] = "";
  10999. tmp = ast_strdupa(data);
  11000. if (!(vmu = find_or_create(context, box)))
  11001. return -1;
  11002. populate_defaults(vmu);
  11003. stringp = tmp;
  11004. if ((s = strsep(&stringp, ","))) {
  11005. if (!ast_strlen_zero(s) && s[0] == '*') {
  11006. ast_log(LOG_WARNING, "Invalid password detected for mailbox %s. The password"
  11007. "\n\tmust be reset in voicemail.conf.\n", box);
  11008. }
  11009. /* assign password regardless of validity to prevent NULL password from being assigned */
  11010. ast_copy_string(vmu->password, s, sizeof(vmu->password));
  11011. }
  11012. if (stringp && (s = strsep(&stringp, ","))) {
  11013. ast_copy_string(vmu->fullname, s, sizeof(vmu->fullname));
  11014. }
  11015. if (stringp && (s = strsep(&stringp, ","))) {
  11016. vmu->email = ast_strdup(s);
  11017. }
  11018. if (stringp && (s = strsep(&stringp, ","))) {
  11019. ast_copy_string(vmu->pager, s, sizeof(vmu->pager));
  11020. }
  11021. if (stringp && (s = strsep(&stringp, ","))) {
  11022. apply_options(vmu, s);
  11023. }
  11024. switch (vmu->passwordlocation) {
  11025. case OPT_PWLOC_SPOOLDIR:
  11026. snprintf(secretfn, sizeof(secretfn), "%s%s/%s/secret.conf", VM_SPOOL_DIR, vmu->context, vmu->mailbox);
  11027. read_password_from_file(secretfn, vmu->password, sizeof(vmu->password));
  11028. }
  11029. mailbox_full = ast_alloca(strlen(box) + strlen(context) + 1);
  11030. strcpy(mailbox_full, box);
  11031. strcat(mailbox_full, "@");
  11032. strcat(mailbox_full, context);
  11033. inboxcount2(mailbox_full, &urgent, &new, &old);
  11034. queue_mwi_event(NULL, mailbox_full, urgent, new, old);
  11035. return 0;
  11036. }
  11037. AST_TEST_DEFINE(test_voicemail_vmuser)
  11038. {
  11039. int res = 0;
  11040. struct ast_vm_user *vmu;
  11041. /* language parameter seems to only be used for display in manager action */
  11042. static const char options_string[] = "attach=yes|attachfmt=wav49|"
  11043. "serveremail=someguy@digium.com|tz=central|delete=yes|saycid=yes|"
  11044. "sendvoicemail=yes|review=yes|tempgreetwarn=yes|messagewrap=yes|operator=yes|"
  11045. "envelope=yes|moveheard=yes|sayduration=yes|saydurationm=5|forcename=yes|"
  11046. "forcegreetings=yes|callback=somecontext|dialout=somecontext2|"
  11047. "exitcontext=somecontext3|minsecs=10|maxsecs=100|nextaftercmd=yes|"
  11048. "backupdeleted=50|volgain=1.3|passwordlocation=spooldir|emailbody="
  11049. "Dear ${VM_NAME}:\n\n\tYou were just left a ${VM_DUR} long message|emailsubject="
  11050. "[PBX]: New message \\\\${VM_MSGNUM}\\\\ in mailbox ${VM_MAILBOX}";
  11051. #ifdef IMAP_STORAGE
  11052. static const char option_string2[] = "imapuser=imapuser|imappassword=imappasswd|"
  11053. "imapfolder=INBOX|imapvmshareid=6000|imapserver=imapserver|imapport=1234|imapflags=flagged";
  11054. #endif
  11055. switch (cmd) {
  11056. case TEST_INIT:
  11057. info->name = "vmuser";
  11058. info->category = "/apps/app_voicemail/";
  11059. info->summary = "Vmuser unit test";
  11060. info->description =
  11061. "This tests passing all supported parameters to apply_options, the voicemail user config parser";
  11062. return AST_TEST_NOT_RUN;
  11063. case TEST_EXECUTE:
  11064. break;
  11065. }
  11066. if (!(vmu = ast_calloc(1, sizeof(*vmu)))) {
  11067. return AST_TEST_NOT_RUN;
  11068. }
  11069. populate_defaults(vmu);
  11070. ast_set_flag(vmu, VM_ALLOCED);
  11071. apply_options(vmu, options_string);
  11072. if (!ast_test_flag(vmu, VM_ATTACH)) {
  11073. ast_test_status_update(test, "Parse failure for attach option\n");
  11074. res = 1;
  11075. }
  11076. if (strcasecmp(vmu->attachfmt, "wav49")) {
  11077. ast_test_status_update(test, "Parse failure for attachftm option\n");
  11078. res = 1;
  11079. }
  11080. if (strcasecmp(vmu->serveremail, "someguy@digium.com")) {
  11081. ast_test_status_update(test, "Parse failure for serveremail option\n");
  11082. res = 1;
  11083. }
  11084. if (!vmu->emailsubject || strcasecmp(vmu->emailsubject, "[PBX]: New message \\${VM_MSGNUM}\\ in mailbox ${VM_MAILBOX}")) {
  11085. ast_test_status_update(test, "Parse failure for emailsubject option\n");
  11086. res = 1;
  11087. }
  11088. if (!vmu->emailbody || strcasecmp(vmu->emailbody, "Dear ${VM_NAME}:\n\n\tYou were just left a ${VM_DUR} long message")) {
  11089. ast_test_status_update(test, "Parse failure for emailbody option\n");
  11090. res = 1;
  11091. }
  11092. if (strcasecmp(vmu->zonetag, "central")) {
  11093. ast_test_status_update(test, "Parse failure for tz option\n");
  11094. res = 1;
  11095. }
  11096. if (!ast_test_flag(vmu, VM_DELETE)) {
  11097. ast_test_status_update(test, "Parse failure for delete option\n");
  11098. res = 1;
  11099. }
  11100. if (!ast_test_flag(vmu, VM_SAYCID)) {
  11101. ast_test_status_update(test, "Parse failure for saycid option\n");
  11102. res = 1;
  11103. }
  11104. if (!ast_test_flag(vmu, VM_SVMAIL)) {
  11105. ast_test_status_update(test, "Parse failure for sendvoicemail option\n");
  11106. res = 1;
  11107. }
  11108. if (!ast_test_flag(vmu, VM_REVIEW)) {
  11109. ast_test_status_update(test, "Parse failure for review option\n");
  11110. res = 1;
  11111. }
  11112. if (!ast_test_flag(vmu, VM_TEMPGREETWARN)) {
  11113. ast_test_status_update(test, "Parse failure for tempgreetwarm option\n");
  11114. res = 1;
  11115. }
  11116. if (!ast_test_flag(vmu, VM_MESSAGEWRAP)) {
  11117. ast_test_status_update(test, "Parse failure for messagewrap option\n");
  11118. res = 1;
  11119. }
  11120. if (!ast_test_flag(vmu, VM_OPERATOR)) {
  11121. ast_test_status_update(test, "Parse failure for operator option\n");
  11122. res = 1;
  11123. }
  11124. if (!ast_test_flag(vmu, VM_ENVELOPE)) {
  11125. ast_test_status_update(test, "Parse failure for envelope option\n");
  11126. res = 1;
  11127. }
  11128. if (!ast_test_flag(vmu, VM_MOVEHEARD)) {
  11129. ast_test_status_update(test, "Parse failure for moveheard option\n");
  11130. res = 1;
  11131. }
  11132. if (!ast_test_flag(vmu, VM_SAYDURATION)) {
  11133. ast_test_status_update(test, "Parse failure for sayduration option\n");
  11134. res = 1;
  11135. }
  11136. if (vmu->saydurationm != 5) {
  11137. ast_test_status_update(test, "Parse failure for saydurationm option\n");
  11138. res = 1;
  11139. }
  11140. if (!ast_test_flag(vmu, VM_FORCENAME)) {
  11141. ast_test_status_update(test, "Parse failure for forcename option\n");
  11142. res = 1;
  11143. }
  11144. if (!ast_test_flag(vmu, VM_FORCEGREET)) {
  11145. ast_test_status_update(test, "Parse failure for forcegreetings option\n");
  11146. res = 1;
  11147. }
  11148. if (strcasecmp(vmu->callback, "somecontext")) {
  11149. ast_test_status_update(test, "Parse failure for callbacks option\n");
  11150. res = 1;
  11151. }
  11152. if (strcasecmp(vmu->dialout, "somecontext2")) {
  11153. ast_test_status_update(test, "Parse failure for dialout option\n");
  11154. res = 1;
  11155. }
  11156. if (strcasecmp(vmu->exit, "somecontext3")) {
  11157. ast_test_status_update(test, "Parse failure for exitcontext option\n");
  11158. res = 1;
  11159. }
  11160. if (vmu->minsecs != 10) {
  11161. ast_test_status_update(test, "Parse failure for minsecs option\n");
  11162. res = 1;
  11163. }
  11164. if (vmu->maxsecs != 100) {
  11165. ast_test_status_update(test, "Parse failure for maxsecs option\n");
  11166. res = 1;
  11167. }
  11168. if (!ast_test_flag(vmu, VM_SKIPAFTERCMD)) {
  11169. ast_test_status_update(test, "Parse failure for nextaftercmd option\n");
  11170. res = 1;
  11171. }
  11172. if (vmu->maxdeletedmsg != 50) {
  11173. ast_test_status_update(test, "Parse failure for backupdeleted option\n");
  11174. res = 1;
  11175. }
  11176. if (vmu->volgain != 1.3) {
  11177. ast_test_status_update(test, "Parse failure for volgain option\n");
  11178. res = 1;
  11179. }
  11180. if (vmu->passwordlocation != OPT_PWLOC_SPOOLDIR) {
  11181. ast_test_status_update(test, "Parse failure for passwordlocation option\n");
  11182. res = 1;
  11183. }
  11184. #ifdef IMAP_STORAGE
  11185. apply_options(vmu, option_string2);
  11186. if (strcasecmp(vmu->imapuser, "imapuser")) {
  11187. ast_test_status_update(test, "Parse failure for imapuser option\n");
  11188. res = 1;
  11189. }
  11190. if (strcasecmp(vmu->imappassword, "imappasswd")) {
  11191. ast_test_status_update(test, "Parse failure for imappasswd option\n");
  11192. res = 1;
  11193. }
  11194. if (strcasecmp(vmu->imapfolder, "INBOX")) {
  11195. ast_test_status_update(test, "Parse failure for imapfolder option\n");
  11196. res = 1;
  11197. }
  11198. if (strcasecmp(vmu->imapvmshareid, "6000")) {
  11199. ast_test_status_update(test, "Parse failure for imapvmshareid option\n");
  11200. res = 1;
  11201. }
  11202. if (strcasecmp(vmu->imapserver, "imapserver")) {
  11203. ast_test_status_update(test, "Parse failure for imapserver option\n");
  11204. res = 1;
  11205. }
  11206. if (strcasecmp(vmu->imapport, "1234")) {
  11207. ast_test_status_update(test, "Parse failure for imapport option\n");
  11208. res = 1;
  11209. }
  11210. if (strcasecmp(vmu->imapflags, "flagged")) {
  11211. ast_test_status_update(test, "Parse failure for imapflags option\n");
  11212. res = 1;
  11213. }
  11214. #endif
  11215. free_user(vmu);
  11216. return res ? AST_TEST_FAIL : AST_TEST_PASS;
  11217. }
  11218. static int vm_box_exists(struct ast_channel *chan, const char *data)
  11219. {
  11220. struct ast_vm_user svm;
  11221. char *context, *box;
  11222. AST_DECLARE_APP_ARGS(args,
  11223. AST_APP_ARG(mbox);
  11224. AST_APP_ARG(options);
  11225. );
  11226. static int dep_warning = 0;
  11227. if (ast_strlen_zero(data)) {
  11228. ast_log(AST_LOG_ERROR, "MailboxExists requires an argument: (vmbox[@context][|options])\n");
  11229. return -1;
  11230. }
  11231. if (!dep_warning) {
  11232. dep_warning = 1;
  11233. ast_log(AST_LOG_WARNING, "MailboxExists is deprecated. Please use ${VM_INFO(%s,exists)} instead.\n", data);
  11234. }
  11235. box = ast_strdupa(data);
  11236. AST_STANDARD_APP_ARGS(args, box);
  11237. if (args.options) {
  11238. }
  11239. if ((context = strchr(args.mbox, '@'))) {
  11240. *context = '\0';
  11241. context++;
  11242. }
  11243. if (find_user(&svm, context, args.mbox)) {
  11244. pbx_builtin_setvar_helper(chan, "VMBOXEXISTSSTATUS", "SUCCESS");
  11245. } else
  11246. pbx_builtin_setvar_helper(chan, "VMBOXEXISTSSTATUS", "FAILED");
  11247. return 0;
  11248. }
  11249. static int acf_mailbox_exists(struct ast_channel *chan, const char *cmd, char *args, char *buf, size_t len)
  11250. {
  11251. struct ast_vm_user svm;
  11252. AST_DECLARE_APP_ARGS(arg,
  11253. AST_APP_ARG(mbox);
  11254. AST_APP_ARG(context);
  11255. );
  11256. static int dep_warning = 0;
  11257. AST_NONSTANDARD_APP_ARGS(arg, args, '@');
  11258. if (ast_strlen_zero(arg.mbox)) {
  11259. ast_log(LOG_ERROR, "MAILBOX_EXISTS requires an argument (<mailbox>[@<context>])\n");
  11260. return -1;
  11261. }
  11262. if (!dep_warning) {
  11263. dep_warning = 1;
  11264. ast_log(AST_LOG_WARNING, "MAILBOX_EXISTS is deprecated. Please use ${VM_INFO(%s,exists)} instead.\n", args);
  11265. }
  11266. ast_copy_string(buf, find_user(&svm, ast_strlen_zero(arg.context) ? "default" : arg.context, arg.mbox) ? "1" : "0", len);
  11267. return 0;
  11268. }
  11269. static int acf_vm_info(struct ast_channel *chan, const char *cmd, char *args, char *buf, size_t len)
  11270. {
  11271. struct ast_vm_user svm;
  11272. struct ast_vm_user *vmu = NULL;
  11273. char *parse;
  11274. char *mailbox;
  11275. char *context;
  11276. int res = 0;
  11277. AST_DECLARE_APP_ARGS(arg,
  11278. AST_APP_ARG(mailbox_context);
  11279. AST_APP_ARG(attribute);
  11280. AST_APP_ARG(folder);
  11281. );
  11282. buf[0] = '\0';
  11283. if (ast_strlen_zero(args)) {
  11284. ast_log(LOG_ERROR, "VM_INFO requires an argument (<mailbox>[@<context>],attribute[,folder])\n");
  11285. return -1;
  11286. }
  11287. parse = ast_strdupa(args);
  11288. AST_STANDARD_APP_ARGS(arg, parse);
  11289. if (ast_strlen_zero(arg.mailbox_context)
  11290. || ast_strlen_zero(arg.attribute)
  11291. || separate_mailbox(ast_strdupa(arg.mailbox_context), &mailbox, &context)) {
  11292. ast_log(LOG_ERROR, "VM_INFO requires an argument (<mailbox>[@<context>],attribute[,folder])\n");
  11293. return -1;
  11294. }
  11295. vmu = find_user(&svm, context, mailbox);
  11296. if (!strncasecmp(arg.attribute, "exists", 5)) {
  11297. ast_copy_string(buf, vmu ? "1" : "0", len);
  11298. return 0;
  11299. }
  11300. if (vmu) {
  11301. if (!strncasecmp(arg.attribute, "password", 8)) {
  11302. ast_copy_string(buf, vmu->password, len);
  11303. } else if (!strncasecmp(arg.attribute, "fullname", 8)) {
  11304. ast_copy_string(buf, vmu->fullname, len);
  11305. } else if (!strncasecmp(arg.attribute, "email", 5)) {
  11306. ast_copy_string(buf, vmu->email, len);
  11307. } else if (!strncasecmp(arg.attribute, "pager", 5)) {
  11308. ast_copy_string(buf, vmu->pager, len);
  11309. } else if (!strncasecmp(arg.attribute, "language", 8)) {
  11310. ast_copy_string(buf, S_OR(vmu->language, ast_channel_language(chan)), len);
  11311. } else if (!strncasecmp(arg.attribute, "locale", 6)) {
  11312. ast_copy_string(buf, vmu->locale, len);
  11313. } else if (!strncasecmp(arg.attribute, "tz", 2)) {
  11314. ast_copy_string(buf, vmu->zonetag, len);
  11315. } else if (!strncasecmp(arg.attribute, "count", 5)) {
  11316. char *mailbox_id;
  11317. mailbox_id = ast_alloca(strlen(mailbox) + strlen(context) + 2);
  11318. sprintf(mailbox_id, "%s@%s", mailbox, context);/* Safe */
  11319. /* If mbxfolder is empty messagecount will default to INBOX */
  11320. res = messagecount(mailbox_id, arg.folder);
  11321. if (res < 0) {
  11322. ast_log(LOG_ERROR, "Unable to retrieve message count for mailbox %s\n", arg.mailbox_context);
  11323. return -1;
  11324. }
  11325. snprintf(buf, len, "%d", res);
  11326. } else {
  11327. ast_log(LOG_ERROR, "Unknown attribute '%s' for VM_INFO\n", arg.attribute);
  11328. return -1;
  11329. }
  11330. }
  11331. return 0;
  11332. }
  11333. static struct ast_custom_function mailbox_exists_acf = {
  11334. .name = "MAILBOX_EXISTS",
  11335. .read = acf_mailbox_exists,
  11336. };
  11337. static struct ast_custom_function vm_info_acf = {
  11338. .name = "VM_INFO",
  11339. .read = acf_vm_info,
  11340. };
  11341. static int vmauthenticate(struct ast_channel *chan, const char *data)
  11342. {
  11343. char *s, *user = NULL, *context = NULL, mailbox[AST_MAX_EXTENSION] = "";
  11344. struct ast_vm_user vmus;
  11345. char *options = NULL;
  11346. int silent = 0, skipuser = 0;
  11347. int res = -1;
  11348. if (data) {
  11349. s = ast_strdupa(data);
  11350. user = strsep(&s, ",");
  11351. options = strsep(&s, ",");
  11352. if (user) {
  11353. s = user;
  11354. user = strsep(&s, "@");
  11355. context = strsep(&s, "");
  11356. if (!ast_strlen_zero(user))
  11357. skipuser++;
  11358. ast_copy_string(mailbox, user, sizeof(mailbox));
  11359. }
  11360. }
  11361. if (options) {
  11362. silent = (strchr(options, 's')) != NULL;
  11363. }
  11364. if (!vm_authenticate(chan, mailbox, sizeof(mailbox), &vmus, context, NULL, skipuser, 3, silent)) {
  11365. pbx_builtin_setvar_helper(chan, "AUTH_MAILBOX", mailbox);
  11366. pbx_builtin_setvar_helper(chan, "AUTH_CONTEXT", vmus.context);
  11367. ast_play_and_wait(chan, "auth-thankyou");
  11368. res = 0;
  11369. } else if (mailbox[0] == '*') {
  11370. /* user entered '*' */
  11371. if (!ast_goto_if_exists(chan, ast_channel_context(chan), "a", 1)) {
  11372. res = 0; /* prevent hangup */
  11373. }
  11374. }
  11375. return res;
  11376. }
  11377. static char *show_users_realtime(int fd, const char *context)
  11378. {
  11379. struct ast_config *cfg;
  11380. const char *cat = NULL;
  11381. if (!(cfg = ast_load_realtime_multientry("voicemail",
  11382. "context", context, SENTINEL))) {
  11383. return CLI_FAILURE;
  11384. }
  11385. ast_cli(fd,
  11386. "\n"
  11387. "=============================================================\n"
  11388. "=== Configured Voicemail Users ==============================\n"
  11389. "=============================================================\n"
  11390. "===\n");
  11391. while ((cat = ast_category_browse(cfg, cat))) {
  11392. struct ast_variable *var = NULL;
  11393. ast_cli(fd,
  11394. "=== Mailbox ...\n"
  11395. "===\n");
  11396. for (var = ast_variable_browse(cfg, cat); var; var = var->next)
  11397. ast_cli(fd, "=== ==> %s: %s\n", var->name, var->value);
  11398. ast_cli(fd,
  11399. "===\n"
  11400. "=== ---------------------------------------------------------\n"
  11401. "===\n");
  11402. }
  11403. ast_cli(fd,
  11404. "=============================================================\n"
  11405. "\n");
  11406. ast_config_destroy(cfg);
  11407. return CLI_SUCCESS;
  11408. }
  11409. static char *complete_voicemail_show_users(const char *line, const char *word, int pos, int state)
  11410. {
  11411. int which = 0;
  11412. int wordlen;
  11413. struct ast_vm_user *vmu;
  11414. const char *context = "";
  11415. /* 0 - show; 1 - voicemail; 2 - users; 3 - for; 4 - <context> */
  11416. if (pos > 4)
  11417. return NULL;
  11418. if (pos == 3)
  11419. return (state == 0) ? ast_strdup("for") : NULL;
  11420. wordlen = strlen(word);
  11421. AST_LIST_TRAVERSE(&users, vmu, list) {
  11422. if (!strncasecmp(word, vmu->context, wordlen)) {
  11423. if (context && strcmp(context, vmu->context) && ++which > state)
  11424. return ast_strdup(vmu->context);
  11425. /* ignore repeated contexts ? */
  11426. context = vmu->context;
  11427. }
  11428. }
  11429. return NULL;
  11430. }
  11431. /*! \brief Show a list of voicemail users in the CLI */
  11432. static char *handle_voicemail_show_users(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
  11433. {
  11434. struct ast_vm_user *vmu;
  11435. #define HVSU_OUTPUT_FORMAT "%-10s %-5s %-25s %-10s %6s\n"
  11436. const char *context = NULL;
  11437. int users_counter = 0;
  11438. switch (cmd) {
  11439. case CLI_INIT:
  11440. e->command = "voicemail show users";
  11441. e->usage =
  11442. "Usage: voicemail show users [for <context>]\n"
  11443. " Lists all mailboxes currently set up\n";
  11444. return NULL;
  11445. case CLI_GENERATE:
  11446. return complete_voicemail_show_users(a->line, a->word, a->pos, a->n);
  11447. }
  11448. if ((a->argc < 3) || (a->argc > 5) || (a->argc == 4))
  11449. return CLI_SHOWUSAGE;
  11450. if (a->argc == 5) {
  11451. if (strcmp(a->argv[3],"for"))
  11452. return CLI_SHOWUSAGE;
  11453. context = a->argv[4];
  11454. }
  11455. if (ast_check_realtime("voicemail")) {
  11456. if (!context) {
  11457. ast_cli(a->fd, "You must specify a specific context to show users from realtime!\n");
  11458. return CLI_SHOWUSAGE;
  11459. }
  11460. return show_users_realtime(a->fd, context);
  11461. }
  11462. AST_LIST_LOCK(&users);
  11463. if (AST_LIST_EMPTY(&users)) {
  11464. ast_cli(a->fd, "There are no voicemail users currently defined\n");
  11465. AST_LIST_UNLOCK(&users);
  11466. return CLI_FAILURE;
  11467. }
  11468. if (!context) {
  11469. ast_cli(a->fd, HVSU_OUTPUT_FORMAT, "Context", "Mbox", "User", "Zone", "NewMsg");
  11470. } else {
  11471. int count = 0;
  11472. AST_LIST_TRAVERSE(&users, vmu, list) {
  11473. if (!strcmp(context, vmu->context)) {
  11474. count++;
  11475. break;
  11476. }
  11477. }
  11478. if (count) {
  11479. ast_cli(a->fd, HVSU_OUTPUT_FORMAT, "Context", "Mbox", "User", "Zone", "NewMsg");
  11480. } else {
  11481. ast_cli(a->fd, "No such voicemail context \"%s\"\n", context);
  11482. AST_LIST_UNLOCK(&users);
  11483. return CLI_FAILURE;
  11484. }
  11485. }
  11486. AST_LIST_TRAVERSE(&users, vmu, list) {
  11487. int newmsgs = 0, oldmsgs = 0;
  11488. char count[12], tmp[256] = "";
  11489. if (!context || !strcmp(context, vmu->context)) {
  11490. snprintf(tmp, sizeof(tmp), "%s@%s", vmu->mailbox, ast_strlen_zero(vmu->context) ? "default" : vmu->context);
  11491. inboxcount(tmp, &newmsgs, &oldmsgs);
  11492. snprintf(count, sizeof(count), "%d", newmsgs);
  11493. ast_cli(a->fd, HVSU_OUTPUT_FORMAT, vmu->context, vmu->mailbox, vmu->fullname, vmu->zonetag, count);
  11494. users_counter++;
  11495. }
  11496. }
  11497. AST_LIST_UNLOCK(&users);
  11498. ast_cli(a->fd, "%d voicemail users configured.\n", users_counter);
  11499. return CLI_SUCCESS;
  11500. }
  11501. /*! \brief Show a list of voicemail zones in the CLI */
  11502. static char *handle_voicemail_show_zones(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
  11503. {
  11504. struct vm_zone *zone;
  11505. #define HVSZ_OUTPUT_FORMAT "%-15s %-20s %-45s\n"
  11506. char *res = CLI_SUCCESS;
  11507. switch (cmd) {
  11508. case CLI_INIT:
  11509. e->command = "voicemail show zones";
  11510. e->usage =
  11511. "Usage: voicemail show zones\n"
  11512. " Lists zone message formats\n";
  11513. return NULL;
  11514. case CLI_GENERATE:
  11515. return NULL;
  11516. }
  11517. if (a->argc != 3)
  11518. return CLI_SHOWUSAGE;
  11519. AST_LIST_LOCK(&zones);
  11520. if (!AST_LIST_EMPTY(&zones)) {
  11521. ast_cli(a->fd, HVSZ_OUTPUT_FORMAT, "Zone", "Timezone", "Message Format");
  11522. AST_LIST_TRAVERSE(&zones, zone, list) {
  11523. ast_cli(a->fd, HVSZ_OUTPUT_FORMAT, zone->name, zone->timezone, zone->msg_format);
  11524. }
  11525. } else {
  11526. ast_cli(a->fd, "There are no voicemail zones currently defined\n");
  11527. res = CLI_FAILURE;
  11528. }
  11529. AST_LIST_UNLOCK(&zones);
  11530. return res;
  11531. }
  11532. /*! \brief Reload voicemail configuration from the CLI */
  11533. static char *handle_voicemail_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
  11534. {
  11535. switch (cmd) {
  11536. case CLI_INIT:
  11537. e->command = "voicemail reload";
  11538. e->usage =
  11539. "Usage: voicemail reload\n"
  11540. " Reload voicemail configuration\n";
  11541. return NULL;
  11542. case CLI_GENERATE:
  11543. return NULL;
  11544. }
  11545. if (a->argc != 2)
  11546. return CLI_SHOWUSAGE;
  11547. ast_cli(a->fd, "Reloading voicemail configuration...\n");
  11548. load_config(1);
  11549. return CLI_SUCCESS;
  11550. }
  11551. static struct ast_cli_entry cli_voicemail[] = {
  11552. AST_CLI_DEFINE(handle_voicemail_show_users, "List defined voicemail boxes"),
  11553. AST_CLI_DEFINE(handle_voicemail_show_zones, "List zone message formats"),
  11554. AST_CLI_DEFINE(handle_voicemail_reload, "Reload voicemail configuration"),
  11555. };
  11556. #ifdef IMAP_STORAGE
  11557. #define DATA_EXPORT_VM_USERS(USER) \
  11558. USER(ast_vm_user, context, AST_DATA_STRING) \
  11559. USER(ast_vm_user, mailbox, AST_DATA_STRING) \
  11560. USER(ast_vm_user, password, AST_DATA_PASSWORD) \
  11561. USER(ast_vm_user, fullname, AST_DATA_STRING) \
  11562. USER(ast_vm_user, email, AST_DATA_STRING) \
  11563. USER(ast_vm_user, emailsubject, AST_DATA_STRING) \
  11564. USER(ast_vm_user, emailbody, AST_DATA_STRING) \
  11565. USER(ast_vm_user, pager, AST_DATA_STRING) \
  11566. USER(ast_vm_user, serveremail, AST_DATA_STRING) \
  11567. USER(ast_vm_user, language, AST_DATA_STRING) \
  11568. USER(ast_vm_user, zonetag, AST_DATA_STRING) \
  11569. USER(ast_vm_user, callback, AST_DATA_STRING) \
  11570. USER(ast_vm_user, dialout, AST_DATA_STRING) \
  11571. USER(ast_vm_user, uniqueid, AST_DATA_STRING) \
  11572. USER(ast_vm_user, exit, AST_DATA_STRING) \
  11573. USER(ast_vm_user, attachfmt, AST_DATA_STRING) \
  11574. USER(ast_vm_user, flags, AST_DATA_UNSIGNED_INTEGER) \
  11575. USER(ast_vm_user, saydurationm, AST_DATA_INTEGER) \
  11576. USER(ast_vm_user, maxmsg, AST_DATA_INTEGER) \
  11577. USER(ast_vm_user, maxdeletedmsg, AST_DATA_INTEGER) \
  11578. USER(ast_vm_user, maxsecs, AST_DATA_INTEGER) \
  11579. USER(ast_vm_user, imapuser, AST_DATA_STRING) \
  11580. USER(ast_vm_user, imappassword, AST_DATA_STRING) \
  11581. USER(ast_vm_user, imapvmshareid, AST_DATA_STRING) \
  11582. USER(ast_vm_user, volgain, AST_DATA_DOUBLE)
  11583. #else
  11584. #define DATA_EXPORT_VM_USERS(USER) \
  11585. USER(ast_vm_user, context, AST_DATA_STRING) \
  11586. USER(ast_vm_user, mailbox, AST_DATA_STRING) \
  11587. USER(ast_vm_user, password, AST_DATA_PASSWORD) \
  11588. USER(ast_vm_user, fullname, AST_DATA_STRING) \
  11589. USER(ast_vm_user, email, AST_DATA_STRING) \
  11590. USER(ast_vm_user, emailsubject, AST_DATA_STRING) \
  11591. USER(ast_vm_user, emailbody, AST_DATA_STRING) \
  11592. USER(ast_vm_user, pager, AST_DATA_STRING) \
  11593. USER(ast_vm_user, serveremail, AST_DATA_STRING) \
  11594. USER(ast_vm_user, language, AST_DATA_STRING) \
  11595. USER(ast_vm_user, zonetag, AST_DATA_STRING) \
  11596. USER(ast_vm_user, callback, AST_DATA_STRING) \
  11597. USER(ast_vm_user, dialout, AST_DATA_STRING) \
  11598. USER(ast_vm_user, uniqueid, AST_DATA_STRING) \
  11599. USER(ast_vm_user, exit, AST_DATA_STRING) \
  11600. USER(ast_vm_user, attachfmt, AST_DATA_STRING) \
  11601. USER(ast_vm_user, flags, AST_DATA_UNSIGNED_INTEGER) \
  11602. USER(ast_vm_user, saydurationm, AST_DATA_INTEGER) \
  11603. USER(ast_vm_user, maxmsg, AST_DATA_INTEGER) \
  11604. USER(ast_vm_user, maxdeletedmsg, AST_DATA_INTEGER) \
  11605. USER(ast_vm_user, maxsecs, AST_DATA_INTEGER) \
  11606. USER(ast_vm_user, volgain, AST_DATA_DOUBLE)
  11607. #endif
  11608. AST_DATA_STRUCTURE(ast_vm_user, DATA_EXPORT_VM_USERS);
  11609. #define DATA_EXPORT_VM_ZONES(ZONE) \
  11610. ZONE(vm_zone, name, AST_DATA_STRING) \
  11611. ZONE(vm_zone, timezone, AST_DATA_STRING) \
  11612. ZONE(vm_zone, msg_format, AST_DATA_STRING)
  11613. AST_DATA_STRUCTURE(vm_zone, DATA_EXPORT_VM_ZONES);
  11614. /*!
  11615. * \internal
  11616. * \brief Add voicemail user to the data_root.
  11617. * \param[in] search The search tree.
  11618. * \param[in] data_root The main result node.
  11619. * \param[in] user The voicemail user.
  11620. */
  11621. static int vm_users_data_provider_get_helper(const struct ast_data_search *search,
  11622. struct ast_data *data_root, struct ast_vm_user *user)
  11623. {
  11624. struct ast_data *data_user, *data_zone;
  11625. struct ast_data *data_state;
  11626. struct vm_zone *zone = NULL;
  11627. int urgentmsg = 0, newmsg = 0, oldmsg = 0;
  11628. char ext_context[256] = "";
  11629. data_user = ast_data_add_node(data_root, "user");
  11630. if (!data_user) {
  11631. return -1;
  11632. }
  11633. ast_data_add_structure(ast_vm_user, data_user, user);
  11634. AST_LIST_LOCK(&zones);
  11635. AST_LIST_TRAVERSE(&zones, zone, list) {
  11636. if (!strcmp(zone->name, user->zonetag)) {
  11637. break;
  11638. }
  11639. }
  11640. AST_LIST_UNLOCK(&zones);
  11641. /* state */
  11642. data_state = ast_data_add_node(data_user, "state");
  11643. if (!data_state) {
  11644. return -1;
  11645. }
  11646. snprintf(ext_context, sizeof(ext_context), "%s@%s", user->mailbox, user->context);
  11647. inboxcount2(ext_context, &urgentmsg, &newmsg, &oldmsg);
  11648. ast_data_add_int(data_state, "urgentmsg", urgentmsg);
  11649. ast_data_add_int(data_state, "newmsg", newmsg);
  11650. ast_data_add_int(data_state, "oldmsg", oldmsg);
  11651. if (zone) {
  11652. data_zone = ast_data_add_node(data_user, "zone");
  11653. ast_data_add_structure(vm_zone, data_zone, zone);
  11654. }
  11655. if (!ast_data_search_match(search, data_user)) {
  11656. ast_data_remove_node(data_root, data_user);
  11657. }
  11658. return 0;
  11659. }
  11660. static int vm_users_data_provider_get(const struct ast_data_search *search,
  11661. struct ast_data *data_root)
  11662. {
  11663. struct ast_vm_user *user;
  11664. AST_LIST_LOCK(&users);
  11665. AST_LIST_TRAVERSE(&users, user, list) {
  11666. vm_users_data_provider_get_helper(search, data_root, user);
  11667. }
  11668. AST_LIST_UNLOCK(&users);
  11669. return 0;
  11670. }
  11671. static const struct ast_data_handler vm_users_data_provider = {
  11672. .version = AST_DATA_HANDLER_VERSION,
  11673. .get = vm_users_data_provider_get
  11674. };
  11675. static const struct ast_data_entry vm_data_providers[] = {
  11676. AST_DATA_ENTRY("asterisk/application/voicemail/list", &vm_users_data_provider)
  11677. };
  11678. static void poll_subscribed_mailbox(struct mwi_sub *mwi_sub)
  11679. {
  11680. int new = 0, old = 0, urgent = 0;
  11681. inboxcount2(mwi_sub->mailbox, &urgent, &new, &old);
  11682. if (urgent != mwi_sub->old_urgent || new != mwi_sub->old_new || old != mwi_sub->old_old) {
  11683. mwi_sub->old_urgent = urgent;
  11684. mwi_sub->old_new = new;
  11685. mwi_sub->old_old = old;
  11686. queue_mwi_event(NULL, mwi_sub->mailbox, urgent, new, old);
  11687. run_externnotify(NULL, mwi_sub->mailbox, NULL);
  11688. }
  11689. }
  11690. static void poll_subscribed_mailboxes(void)
  11691. {
  11692. struct mwi_sub *mwi_sub;
  11693. AST_RWLIST_RDLOCK(&mwi_subs);
  11694. AST_RWLIST_TRAVERSE(&mwi_subs, mwi_sub, entry) {
  11695. if (!ast_strlen_zero(mwi_sub->mailbox)) {
  11696. poll_subscribed_mailbox(mwi_sub);
  11697. }
  11698. }
  11699. AST_RWLIST_UNLOCK(&mwi_subs);
  11700. }
  11701. static void *mb_poll_thread(void *data)
  11702. {
  11703. while (poll_thread_run) {
  11704. struct timespec ts = { 0, };
  11705. struct timeval wait;
  11706. wait = ast_tvadd(ast_tvnow(), ast_samp2tv(poll_freq, 1));
  11707. ts.tv_sec = wait.tv_sec;
  11708. ts.tv_nsec = wait.tv_usec * 1000;
  11709. ast_mutex_lock(&poll_lock);
  11710. ast_cond_timedwait(&poll_cond, &poll_lock, &ts);
  11711. ast_mutex_unlock(&poll_lock);
  11712. if (!poll_thread_run)
  11713. break;
  11714. poll_subscribed_mailboxes();
  11715. }
  11716. return NULL;
  11717. }
  11718. static void mwi_sub_destroy(struct mwi_sub *mwi_sub)
  11719. {
  11720. ast_free(mwi_sub->uniqueid);
  11721. ast_free(mwi_sub);
  11722. }
  11723. static int handle_unsubscribe(void *datap)
  11724. {
  11725. struct mwi_sub *mwi_sub;
  11726. char *uniqueid = datap;
  11727. AST_RWLIST_WRLOCK(&mwi_subs);
  11728. AST_RWLIST_TRAVERSE_SAFE_BEGIN(&mwi_subs, mwi_sub, entry) {
  11729. if (!strcmp(mwi_sub->uniqueid, uniqueid)) {
  11730. AST_LIST_REMOVE_CURRENT(entry);
  11731. /* Don't break here since a duplicate uniqueid
  11732. * may have been added as a result of a cache dump. */
  11733. mwi_sub_destroy(mwi_sub);
  11734. }
  11735. }
  11736. AST_RWLIST_TRAVERSE_SAFE_END
  11737. AST_RWLIST_UNLOCK(&mwi_subs);
  11738. ast_free(uniqueid);
  11739. return 0;
  11740. }
  11741. static int handle_subscribe(void *datap)
  11742. {
  11743. unsigned int len;
  11744. struct mwi_sub *mwi_sub;
  11745. struct mwi_sub_task *p = datap;
  11746. len = sizeof(*mwi_sub);
  11747. if (!ast_strlen_zero(p->mailbox))
  11748. len += strlen(p->mailbox);
  11749. if (!ast_strlen_zero(p->context))
  11750. len += strlen(p->context) + 1; /* Allow for seperator */
  11751. if (!(mwi_sub = ast_calloc(1, len)))
  11752. return -1;
  11753. mwi_sub->uniqueid = ast_strdup(p->uniqueid);
  11754. if (!ast_strlen_zero(p->mailbox))
  11755. strcpy(mwi_sub->mailbox, p->mailbox);
  11756. if (!ast_strlen_zero(p->context)) {
  11757. strcat(mwi_sub->mailbox, "@");
  11758. strcat(mwi_sub->mailbox, p->context);
  11759. }
  11760. AST_RWLIST_WRLOCK(&mwi_subs);
  11761. AST_RWLIST_INSERT_TAIL(&mwi_subs, mwi_sub, entry);
  11762. AST_RWLIST_UNLOCK(&mwi_subs);
  11763. mwi_sub_task_dtor(p);
  11764. poll_subscribed_mailbox(mwi_sub);
  11765. return 0;
  11766. }
  11767. static void mwi_unsub_event_cb(struct stasis_subscription_change *change)
  11768. {
  11769. char *uniqueid = ast_strdup(change->uniqueid);
  11770. if (!uniqueid) {
  11771. ast_log(LOG_ERROR, "Unable to allocate memory for uniqueid\n");
  11772. return;
  11773. }
  11774. if (ast_taskprocessor_push(mwi_subscription_tps, handle_unsubscribe, uniqueid) < 0) {
  11775. ast_free(uniqueid);
  11776. }
  11777. }
  11778. static void mwi_sub_event_cb(struct stasis_subscription_change *change)
  11779. {
  11780. struct mwi_sub_task *mwist;
  11781. char *context;
  11782. char *mailbox;
  11783. mwist = ast_calloc(1, (sizeof(*mwist)));
  11784. if (!mwist) {
  11785. return;
  11786. }
  11787. if (separate_mailbox(ast_strdupa(stasis_topic_name(change->topic)), &mailbox, &context)) {
  11788. return;
  11789. }
  11790. mwist->mailbox = ast_strdup(mailbox);
  11791. mwist->context = ast_strdup(context);
  11792. mwist->uniqueid = ast_strdup(change->uniqueid);
  11793. if (ast_taskprocessor_push(mwi_subscription_tps, handle_subscribe, mwist) < 0) {
  11794. mwi_sub_task_dtor(mwist);
  11795. }
  11796. }
  11797. static void mwi_event_cb(void *userdata, struct stasis_subscription *sub, struct stasis_message *msg)
  11798. {
  11799. struct stasis_subscription_change *change;
  11800. /* Only looking for subscription change notices here */
  11801. if (stasis_message_type(msg) != stasis_subscription_change_type()) {
  11802. return;
  11803. }
  11804. change = stasis_message_data(msg);
  11805. if (change->topic == ast_mwi_topic_all()) {
  11806. return;
  11807. }
  11808. if (!strcmp(change->description, "Subscribe")) {
  11809. mwi_sub_event_cb(change);
  11810. } else if (!strcmp(change->description, "Unsubscribe")) {
  11811. mwi_unsub_event_cb(change);
  11812. }
  11813. }
  11814. static int dump_cache(void *obj, void *arg, int flags)
  11815. {
  11816. struct stasis_message *msg = obj;
  11817. mwi_event_cb(NULL, NULL, msg);
  11818. return 0;
  11819. }
  11820. static void start_poll_thread(void)
  11821. {
  11822. int errcode;
  11823. mwi_sub_sub = stasis_subscribe(ast_mwi_topic_all(), mwi_event_cb, NULL);
  11824. if (mwi_sub_sub) {
  11825. struct ao2_container *cached = stasis_cache_dump(ast_mwi_state_cache(), stasis_subscription_change_type());
  11826. if (cached) {
  11827. ao2_callback(cached, OBJ_MULTIPLE | OBJ_NODATA, dump_cache, NULL);
  11828. }
  11829. ao2_cleanup(cached);
  11830. }
  11831. poll_thread_run = 1;
  11832. if ((errcode = ast_pthread_create(&poll_thread, NULL, mb_poll_thread, NULL))) {
  11833. ast_log(LOG_ERROR, "Could not create thread: %s\n", strerror(errcode));
  11834. }
  11835. }
  11836. static void stop_poll_thread(void)
  11837. {
  11838. poll_thread_run = 0;
  11839. mwi_sub_sub = stasis_unsubscribe_and_join(mwi_sub_sub);
  11840. ast_mutex_lock(&poll_lock);
  11841. ast_cond_signal(&poll_cond);
  11842. ast_mutex_unlock(&poll_lock);
  11843. pthread_join(poll_thread, NULL);
  11844. poll_thread = AST_PTHREADT_NULL;
  11845. }
  11846. static int manager_voicemail_refresh(struct mansession *s, const struct message *m)
  11847. {
  11848. const char *context = astman_get_header(m, "Context");
  11849. const char *mailbox = astman_get_header(m, "Mailbox");
  11850. struct mwi_sub *mwi_sub;
  11851. const char *at;
  11852. AST_RWLIST_RDLOCK(&mwi_subs);
  11853. AST_RWLIST_TRAVERSE(&mwi_subs, mwi_sub, entry) {
  11854. if (!ast_strlen_zero(mwi_sub->mailbox)) {
  11855. if (
  11856. /* First case: everything matches */
  11857. (ast_strlen_zero(context) && ast_strlen_zero(mailbox)) ||
  11858. /* Second case: match the mailbox only */
  11859. (ast_strlen_zero(context) && !ast_strlen_zero(mailbox) &&
  11860. (at = strchr(mwi_sub->mailbox, '@')) &&
  11861. strncmp(mailbox, mwi_sub->mailbox, at - mwi_sub->mailbox) == 0) ||
  11862. /* Third case: match the context only */
  11863. (!ast_strlen_zero(context) && ast_strlen_zero(mailbox) &&
  11864. (at = strchr(mwi_sub->mailbox, '@')) &&
  11865. strcmp(context, at + 1) == 0) ||
  11866. /* Final case: match an exact specified mailbox */
  11867. (!ast_strlen_zero(context) && !ast_strlen_zero(mailbox) &&
  11868. (at = strchr(mwi_sub->mailbox, '@')) &&
  11869. strncmp(mailbox, mwi_sub->mailbox, at - mwi_sub->mailbox) == 0 &&
  11870. strcmp(context, at + 1) == 0)
  11871. ) {
  11872. poll_subscribed_mailbox(mwi_sub);
  11873. }
  11874. }
  11875. }
  11876. AST_RWLIST_UNLOCK(&mwi_subs);
  11877. astman_send_ack(s, m, "Refresh sent");
  11878. return RESULT_SUCCESS;
  11879. }
  11880. /*! \brief Manager list voicemail users command */
  11881. static int manager_list_voicemail_users(struct mansession *s, const struct message *m)
  11882. {
  11883. struct ast_vm_user *vmu = NULL;
  11884. const char *id = astman_get_header(m, "ActionID");
  11885. char actionid[128];
  11886. int num_users = 0;
  11887. actionid[0] = '\0';
  11888. if (!ast_strlen_zero(id)) {
  11889. snprintf(actionid, sizeof(actionid), "ActionID: %s\r\n", id);
  11890. }
  11891. AST_LIST_LOCK(&users);
  11892. if (AST_LIST_EMPTY(&users)) {
  11893. astman_send_ack(s, m, "There are no voicemail users currently defined.");
  11894. AST_LIST_UNLOCK(&users);
  11895. return RESULT_SUCCESS;
  11896. }
  11897. astman_send_listack(s, m, "Voicemail user list will follow", "start");
  11898. AST_LIST_TRAVERSE(&users, vmu, list) {
  11899. char dirname[256];
  11900. #ifdef IMAP_STORAGE
  11901. int new, old;
  11902. inboxcount(vmu->mailbox, &new, &old);
  11903. #endif
  11904. make_dir(dirname, sizeof(dirname), vmu->context, vmu->mailbox, "INBOX");
  11905. astman_append(s,
  11906. "Event: VoicemailUserEntry\r\n"
  11907. "%s"
  11908. "VMContext: %s\r\n"
  11909. "VoiceMailbox: %s\r\n"
  11910. "Fullname: %s\r\n"
  11911. "Email: %s\r\n"
  11912. "Pager: %s\r\n"
  11913. "ServerEmail: %s\r\n"
  11914. "MailCommand: %s\r\n"
  11915. "Language: %s\r\n"
  11916. "TimeZone: %s\r\n"
  11917. "Callback: %s\r\n"
  11918. "Dialout: %s\r\n"
  11919. "UniqueID: %s\r\n"
  11920. "ExitContext: %s\r\n"
  11921. "SayDurationMinimum: %d\r\n"
  11922. "SayEnvelope: %s\r\n"
  11923. "SayCID: %s\r\n"
  11924. "AttachMessage: %s\r\n"
  11925. "AttachmentFormat: %s\r\n"
  11926. "DeleteMessage: %s\r\n"
  11927. "VolumeGain: %.2f\r\n"
  11928. "CanReview: %s\r\n"
  11929. "CallOperator: %s\r\n"
  11930. "MaxMessageCount: %d\r\n"
  11931. "MaxMessageLength: %d\r\n"
  11932. "NewMessageCount: %d\r\n"
  11933. #ifdef IMAP_STORAGE
  11934. "OldMessageCount: %d\r\n"
  11935. "IMAPUser: %s\r\n"
  11936. "IMAPServer: %s\r\n"
  11937. "IMAPPort: %s\r\n"
  11938. "IMAPFlags: %s\r\n"
  11939. #endif
  11940. "\r\n",
  11941. actionid,
  11942. vmu->context,
  11943. vmu->mailbox,
  11944. vmu->fullname,
  11945. vmu->email,
  11946. vmu->pager,
  11947. ast_strlen_zero(vmu->serveremail) ? serveremail : vmu->serveremail,
  11948. mailcmd,
  11949. vmu->language,
  11950. vmu->zonetag,
  11951. vmu->callback,
  11952. vmu->dialout,
  11953. vmu->uniqueid,
  11954. vmu->exit,
  11955. vmu->saydurationm,
  11956. ast_test_flag(vmu, VM_ENVELOPE) ? "Yes" : "No",
  11957. ast_test_flag(vmu, VM_SAYCID) ? "Yes" : "No",
  11958. ast_test_flag(vmu, VM_ATTACH) ? "Yes" : "No",
  11959. vmu->attachfmt,
  11960. ast_test_flag(vmu, VM_DELETE) ? "Yes" : "No",
  11961. vmu->volgain,
  11962. ast_test_flag(vmu, VM_REVIEW) ? "Yes" : "No",
  11963. ast_test_flag(vmu, VM_OPERATOR) ? "Yes" : "No",
  11964. vmu->maxmsg,
  11965. vmu->maxsecs,
  11966. #ifdef IMAP_STORAGE
  11967. new, old,
  11968. vmu->imapuser,
  11969. vmu->imapserver,
  11970. vmu->imapport,
  11971. vmu->imapflags
  11972. #else
  11973. count_messages(vmu, dirname)
  11974. #endif
  11975. );
  11976. ++num_users;
  11977. }
  11978. astman_send_list_complete_start(s, m, "VoicemailUserEntryComplete", num_users);
  11979. astman_send_list_complete_end(s);
  11980. AST_LIST_UNLOCK(&users);
  11981. return RESULT_SUCCESS;
  11982. }
  11983. /*! \brief Free the users structure. */
  11984. static void free_vm_users(void)
  11985. {
  11986. struct ast_vm_user *current;
  11987. AST_LIST_LOCK(&users);
  11988. while ((current = AST_LIST_REMOVE_HEAD(&users, list))) {
  11989. ast_set_flag(current, VM_ALLOCED);
  11990. free_user(current);
  11991. }
  11992. AST_LIST_UNLOCK(&users);
  11993. }
  11994. /*! \brief Free the zones structure. */
  11995. static void free_vm_zones(void)
  11996. {
  11997. struct vm_zone *zcur;
  11998. AST_LIST_LOCK(&zones);
  11999. while ((zcur = AST_LIST_REMOVE_HEAD(&zones, list)))
  12000. free_zone(zcur);
  12001. AST_LIST_UNLOCK(&zones);
  12002. }
  12003. static const char *substitute_escapes(const char *value)
  12004. {
  12005. char *current;
  12006. /* Add 16 for fudge factor */
  12007. struct ast_str *str = ast_str_thread_get(&ast_str_thread_global_buf, strlen(value) + 16);
  12008. ast_str_reset(str);
  12009. /* Substitute strings \r, \n, and \t into the appropriate characters */
  12010. for (current = (char *) value; *current; current++) {
  12011. if (*current == '\\') {
  12012. current++;
  12013. if (!*current) {
  12014. ast_log(AST_LOG_NOTICE, "Incomplete escape at end of value.\n");
  12015. break;
  12016. }
  12017. switch (*current) {
  12018. case '\\':
  12019. ast_str_append(&str, 0, "\\");
  12020. break;
  12021. case 'r':
  12022. ast_str_append(&str, 0, "\r");
  12023. break;
  12024. case 'n':
  12025. #ifdef IMAP_STORAGE
  12026. if (!str->used || str->str[str->used - 1] != '\r') {
  12027. ast_str_append(&str, 0, "\r");
  12028. }
  12029. #endif
  12030. ast_str_append(&str, 0, "\n");
  12031. break;
  12032. case 't':
  12033. ast_str_append(&str, 0, "\t");
  12034. break;
  12035. default:
  12036. ast_log(AST_LOG_NOTICE, "Substitution routine does not support this character: \\%c\n", *current);
  12037. break;
  12038. }
  12039. } else {
  12040. ast_str_append(&str, 0, "%c", *current);
  12041. }
  12042. }
  12043. return ast_str_buffer(str);
  12044. }
  12045. static int load_config(int reload)
  12046. {
  12047. struct ast_config *cfg, *ucfg;
  12048. struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
  12049. int res;
  12050. ast_unload_realtime("voicemail");
  12051. ast_unload_realtime("voicemail_data");
  12052. if ((cfg = ast_config_load(VOICEMAIL_CONFIG, config_flags)) == CONFIG_STATUS_FILEUNCHANGED) {
  12053. if ((ucfg = ast_config_load("users.conf", config_flags)) == CONFIG_STATUS_FILEUNCHANGED) {
  12054. return 0;
  12055. } else if (ucfg == CONFIG_STATUS_FILEINVALID) {
  12056. ast_log(LOG_ERROR, "Config file users.conf is in an invalid format. Avoiding.\n");
  12057. ucfg = NULL;
  12058. }
  12059. ast_clear_flag(&config_flags, CONFIG_FLAG_FILEUNCHANGED);
  12060. if ((cfg = ast_config_load(VOICEMAIL_CONFIG, config_flags)) == CONFIG_STATUS_FILEINVALID) {
  12061. ast_config_destroy(ucfg);
  12062. ast_log(LOG_ERROR, "Config file " VOICEMAIL_CONFIG " is in an invalid format. Aborting.\n");
  12063. return 0;
  12064. }
  12065. } else if (cfg == CONFIG_STATUS_FILEINVALID) {
  12066. ast_log(LOG_ERROR, "Config file " VOICEMAIL_CONFIG " is in an invalid format. Aborting.\n");
  12067. return 0;
  12068. } else {
  12069. ast_clear_flag(&config_flags, CONFIG_FLAG_FILEUNCHANGED);
  12070. if ((ucfg = ast_config_load("users.conf", config_flags)) == CONFIG_STATUS_FILEINVALID) {
  12071. ast_log(LOG_ERROR, "Config file users.conf is in an invalid format. Avoiding.\n");
  12072. ucfg = NULL;
  12073. }
  12074. }
  12075. res = actual_load_config(reload, cfg, ucfg);
  12076. ast_config_destroy(cfg);
  12077. ast_config_destroy(ucfg);
  12078. return res;
  12079. }
  12080. #ifdef TEST_FRAMEWORK
  12081. static int load_config_from_memory(int reload, struct ast_config *cfg, struct ast_config *ucfg)
  12082. {
  12083. ast_unload_realtime("voicemail");
  12084. ast_unload_realtime("voicemail_data");
  12085. return actual_load_config(reload, cfg, ucfg);
  12086. }
  12087. #endif
  12088. static int actual_load_config(int reload, struct ast_config *cfg, struct ast_config *ucfg)
  12089. {
  12090. struct ast_vm_user *current;
  12091. char *cat;
  12092. struct ast_variable *var;
  12093. const char *val;
  12094. char *q, *stringp, *tmp;
  12095. int x;
  12096. unsigned int tmpadsi[4];
  12097. char secretfn[PATH_MAX] = "";
  12098. #ifdef IMAP_STORAGE
  12099. ast_copy_string(imapparentfolder, "\0", sizeof(imapparentfolder));
  12100. #endif
  12101. /* set audio control prompts */
  12102. strcpy(listen_control_forward_key, DEFAULT_LISTEN_CONTROL_FORWARD_KEY);
  12103. strcpy(listen_control_reverse_key, DEFAULT_LISTEN_CONTROL_REVERSE_KEY);
  12104. strcpy(listen_control_pause_key, DEFAULT_LISTEN_CONTROL_PAUSE_KEY);
  12105. strcpy(listen_control_restart_key, DEFAULT_LISTEN_CONTROL_RESTART_KEY);
  12106. strcpy(listen_control_stop_key, DEFAULT_LISTEN_CONTROL_STOP_KEY);
  12107. /* Free all the users structure */
  12108. free_vm_users();
  12109. /* Free all the zones structure */
  12110. free_vm_zones();
  12111. AST_LIST_LOCK(&users);
  12112. memset(ext_pass_cmd, 0, sizeof(ext_pass_cmd));
  12113. memset(ext_pass_check_cmd, 0, sizeof(ext_pass_check_cmd));
  12114. if (cfg) {
  12115. /* General settings */
  12116. if (!(val = ast_variable_retrieve(cfg, "general", "userscontext")))
  12117. val = "default";
  12118. ast_copy_string(userscontext, val, sizeof(userscontext));
  12119. /* Attach voice message to mail message ? */
  12120. if (!(val = ast_variable_retrieve(cfg, "general", "attach")))
  12121. val = "yes";
  12122. ast_set2_flag((&globalflags), ast_true(val), VM_ATTACH);
  12123. if (!(val = ast_variable_retrieve(cfg, "general", "searchcontexts")))
  12124. val = "no";
  12125. ast_set2_flag((&globalflags), ast_true(val), VM_SEARCH);
  12126. volgain = 0.0;
  12127. if ((val = ast_variable_retrieve(cfg, "general", "volgain")))
  12128. sscanf(val, "%30lf", &volgain);
  12129. #ifdef ODBC_STORAGE
  12130. strcpy(odbc_database, "asterisk");
  12131. if ((val = ast_variable_retrieve(cfg, "general", "odbcstorage"))) {
  12132. ast_copy_string(odbc_database, val, sizeof(odbc_database));
  12133. }
  12134. strcpy(odbc_table, "voicemessages");
  12135. if ((val = ast_variable_retrieve(cfg, "general", "odbctable"))) {
  12136. ast_copy_string(odbc_table, val, sizeof(odbc_table));
  12137. }
  12138. #endif
  12139. /* Mail command */
  12140. strcpy(mailcmd, SENDMAIL);
  12141. if ((val = ast_variable_retrieve(cfg, "general", "mailcmd")))
  12142. ast_copy_string(mailcmd, val, sizeof(mailcmd)); /* User setting */
  12143. maxsilence = 0;
  12144. if ((val = ast_variable_retrieve(cfg, "general", "maxsilence"))) {
  12145. maxsilence = atoi(val);
  12146. if (maxsilence > 0)
  12147. maxsilence *= 1000;
  12148. }
  12149. if (!(val = ast_variable_retrieve(cfg, "general", "maxmsg"))) {
  12150. maxmsg = MAXMSG;
  12151. } else {
  12152. maxmsg = atoi(val);
  12153. if (maxmsg < 0) {
  12154. ast_log(AST_LOG_WARNING, "Invalid number of messages per folder '%s'. Using default value %i\n", val, MAXMSG);
  12155. maxmsg = MAXMSG;
  12156. } else if (maxmsg > MAXMSGLIMIT) {
  12157. ast_log(AST_LOG_WARNING, "Maximum number of messages per folder is %i. Cannot accept value '%s'\n", MAXMSGLIMIT, val);
  12158. maxmsg = MAXMSGLIMIT;
  12159. }
  12160. }
  12161. if (!(val = ast_variable_retrieve(cfg, "general", "backupdeleted"))) {
  12162. maxdeletedmsg = 0;
  12163. } else {
  12164. if (sscanf(val, "%30d", &x) == 1)
  12165. maxdeletedmsg = x;
  12166. else if (ast_true(val))
  12167. maxdeletedmsg = MAXMSG;
  12168. else
  12169. maxdeletedmsg = 0;
  12170. if (maxdeletedmsg < 0) {
  12171. ast_log(AST_LOG_WARNING, "Invalid number of deleted messages saved per mailbox '%s'. Using default value %i\n", val, MAXMSG);
  12172. maxdeletedmsg = MAXMSG;
  12173. } else if (maxdeletedmsg > MAXMSGLIMIT) {
  12174. ast_log(AST_LOG_WARNING, "Maximum number of deleted messages saved per mailbox is %i. Cannot accept value '%s'\n", MAXMSGLIMIT, val);
  12175. maxdeletedmsg = MAXMSGLIMIT;
  12176. }
  12177. }
  12178. /* Load date format config for voicemail mail */
  12179. if ((val = ast_variable_retrieve(cfg, "general", "emaildateformat"))) {
  12180. ast_copy_string(emaildateformat, val, sizeof(emaildateformat));
  12181. }
  12182. /* Load date format config for voicemail pager mail */
  12183. if ((val = ast_variable_retrieve(cfg, "general", "pagerdateformat"))) {
  12184. ast_copy_string(pagerdateformat, val, sizeof(pagerdateformat));
  12185. }
  12186. /* External password changing command */
  12187. if ((val = ast_variable_retrieve(cfg, "general", "externpass"))) {
  12188. ast_copy_string(ext_pass_cmd, val, sizeof(ext_pass_cmd));
  12189. pwdchange = PWDCHANGE_EXTERNAL;
  12190. } else if ((val = ast_variable_retrieve(cfg, "general", "externpassnotify"))) {
  12191. ast_copy_string(ext_pass_cmd, val, sizeof(ext_pass_cmd));
  12192. pwdchange = PWDCHANGE_EXTERNAL | PWDCHANGE_INTERNAL;
  12193. }
  12194. /* External password validation command */
  12195. if ((val = ast_variable_retrieve(cfg, "general", "externpasscheck"))) {
  12196. ast_copy_string(ext_pass_check_cmd, val, sizeof(ext_pass_check_cmd));
  12197. ast_debug(1, "found externpasscheck: %s\n", ext_pass_check_cmd);
  12198. }
  12199. #ifdef IMAP_STORAGE
  12200. /* IMAP server address */
  12201. if ((val = ast_variable_retrieve(cfg, "general", "imapserver"))) {
  12202. ast_copy_string(imapserver, val, sizeof(imapserver));
  12203. } else {
  12204. ast_copy_string(imapserver, "localhost", sizeof(imapserver));
  12205. }
  12206. /* IMAP server port */
  12207. if ((val = ast_variable_retrieve(cfg, "general", "imapport"))) {
  12208. ast_copy_string(imapport, val, sizeof(imapport));
  12209. } else {
  12210. ast_copy_string(imapport, "143", sizeof(imapport));
  12211. }
  12212. /* IMAP server flags */
  12213. if ((val = ast_variable_retrieve(cfg, "general", "imapflags"))) {
  12214. ast_copy_string(imapflags, val, sizeof(imapflags));
  12215. }
  12216. /* IMAP server master username */
  12217. if ((val = ast_variable_retrieve(cfg, "general", "authuser"))) {
  12218. ast_copy_string(authuser, val, sizeof(authuser));
  12219. }
  12220. /* IMAP server master password */
  12221. if ((val = ast_variable_retrieve(cfg, "general", "authpassword"))) {
  12222. ast_copy_string(authpassword, val, sizeof(authpassword));
  12223. }
  12224. /* Expunge on exit */
  12225. if ((val = ast_variable_retrieve(cfg, "general", "expungeonhangup"))) {
  12226. if (ast_false(val))
  12227. expungeonhangup = 0;
  12228. else
  12229. expungeonhangup = 1;
  12230. } else {
  12231. expungeonhangup = 1;
  12232. }
  12233. /* IMAP voicemail folder */
  12234. if ((val = ast_variable_retrieve(cfg, "general", "imapfolder"))) {
  12235. ast_copy_string(imapfolder, val, sizeof(imapfolder));
  12236. } else {
  12237. ast_copy_string(imapfolder, "INBOX", sizeof(imapfolder));
  12238. }
  12239. if ((val = ast_variable_retrieve(cfg, "general", "imapparentfolder"))) {
  12240. ast_copy_string(imapparentfolder, val, sizeof(imapparentfolder));
  12241. }
  12242. if ((val = ast_variable_retrieve(cfg, "general", "imapgreetings"))) {
  12243. imapgreetings = ast_true(val);
  12244. } else {
  12245. imapgreetings = 0;
  12246. }
  12247. if ((val = ast_variable_retrieve(cfg, "general", "greetingfolder"))) {
  12248. ast_copy_string(greetingfolder, val, sizeof(greetingfolder));
  12249. } else if ((val = ast_variable_retrieve(cfg, "general", "greetingsfolder"))) {
  12250. /* Also support greetingsfolder as documented in voicemail.conf.sample */
  12251. ast_copy_string(greetingfolder, val, sizeof(greetingfolder));
  12252. } else {
  12253. ast_copy_string(greetingfolder, imapfolder, sizeof(greetingfolder));
  12254. }
  12255. /* There is some very unorthodox casting done here. This is due
  12256. * to the way c-client handles the argument passed in. It expects a
  12257. * void pointer and casts the pointer directly to a long without
  12258. * first dereferencing it. */
  12259. if ((val = ast_variable_retrieve(cfg, "general", "imapreadtimeout"))) {
  12260. mail_parameters(NIL, SET_READTIMEOUT, (void *) (atol(val)));
  12261. } else {
  12262. mail_parameters(NIL, SET_READTIMEOUT, (void *) 60L);
  12263. }
  12264. if ((val = ast_variable_retrieve(cfg, "general", "imapwritetimeout"))) {
  12265. mail_parameters(NIL, SET_WRITETIMEOUT, (void *) (atol(val)));
  12266. } else {
  12267. mail_parameters(NIL, SET_WRITETIMEOUT, (void *) 60L);
  12268. }
  12269. if ((val = ast_variable_retrieve(cfg, "general", "imapopentimeout"))) {
  12270. mail_parameters(NIL, SET_OPENTIMEOUT, (void *) (atol(val)));
  12271. } else {
  12272. mail_parameters(NIL, SET_OPENTIMEOUT, (void *) 60L);
  12273. }
  12274. if ((val = ast_variable_retrieve(cfg, "general", "imapclosetimeout"))) {
  12275. mail_parameters(NIL, SET_CLOSETIMEOUT, (void *) (atol(val)));
  12276. } else {
  12277. mail_parameters(NIL, SET_CLOSETIMEOUT, (void *) 60L);
  12278. }
  12279. /* Increment configuration version */
  12280. imapversion++;
  12281. #endif
  12282. /* External voicemail notify application */
  12283. if ((val = ast_variable_retrieve(cfg, "general", "externnotify"))) {
  12284. ast_copy_string(externnotify, val, sizeof(externnotify));
  12285. ast_debug(1, "found externnotify: %s\n", externnotify);
  12286. } else {
  12287. externnotify[0] = '\0';
  12288. }
  12289. /* SMDI voicemail notification */
  12290. if ((val = ast_variable_retrieve(cfg, "general", "smdienable")) && ast_true(val)) {
  12291. ast_debug(1, "Enabled SMDI voicemail notification\n");
  12292. if ((val = ast_variable_retrieve(cfg, "general", "smdiport"))) {
  12293. smdi_iface = ast_smdi_interface_find(val);
  12294. } else {
  12295. ast_debug(1, "No SMDI interface set, trying default (/dev/ttyS0)\n");
  12296. smdi_iface = ast_smdi_interface_find("/dev/ttyS0");
  12297. }
  12298. if (!smdi_iface) {
  12299. ast_log(AST_LOG_ERROR, "No valid SMDI interface specfied, disabling SMDI voicemail notification\n");
  12300. }
  12301. }
  12302. /* Silence treshold */
  12303. silencethreshold = ast_dsp_get_threshold_from_settings(THRESHOLD_SILENCE);
  12304. if ((val = ast_variable_retrieve(cfg, "general", "silencethreshold")))
  12305. silencethreshold = atoi(val);
  12306. if (!(val = ast_variable_retrieve(cfg, "general", "serveremail")))
  12307. val = ASTERISK_USERNAME;
  12308. ast_copy_string(serveremail, val, sizeof(serveremail));
  12309. vmmaxsecs = 0;
  12310. if ((val = ast_variable_retrieve(cfg, "general", "maxsecs"))) {
  12311. if (sscanf(val, "%30d", &x) == 1) {
  12312. vmmaxsecs = x;
  12313. } else {
  12314. ast_log(AST_LOG_WARNING, "Invalid max message time length\n");
  12315. }
  12316. } else if ((val = ast_variable_retrieve(cfg, "general", "maxmessage"))) {
  12317. static int maxmessage_deprecate = 0;
  12318. if (maxmessage_deprecate == 0) {
  12319. maxmessage_deprecate = 1;
  12320. ast_log(AST_LOG_WARNING, "Setting 'maxmessage' has been deprecated in favor of 'maxsecs'.\n");
  12321. }
  12322. if (sscanf(val, "%30d", &x) == 1) {
  12323. vmmaxsecs = x;
  12324. } else {
  12325. ast_log(AST_LOG_WARNING, "Invalid max message time length\n");
  12326. }
  12327. }
  12328. vmminsecs = 0;
  12329. if ((val = ast_variable_retrieve(cfg, "general", "minsecs"))) {
  12330. if (sscanf(val, "%30d", &x) == 1) {
  12331. vmminsecs = x;
  12332. if (maxsilence / 1000 >= vmminsecs) {
  12333. ast_log(AST_LOG_WARNING, "maxsilence should be less than minsecs or you may get empty messages\n");
  12334. }
  12335. } else {
  12336. ast_log(AST_LOG_WARNING, "Invalid min message time length\n");
  12337. }
  12338. } else if ((val = ast_variable_retrieve(cfg, "general", "minmessage"))) {
  12339. static int maxmessage_deprecate = 0;
  12340. if (maxmessage_deprecate == 0) {
  12341. maxmessage_deprecate = 1;
  12342. ast_log(AST_LOG_WARNING, "Setting 'minmessage' has been deprecated in favor of 'minsecs'.\n");
  12343. }
  12344. if (sscanf(val, "%30d", &x) == 1) {
  12345. vmminsecs = x;
  12346. if (maxsilence / 1000 >= vmminsecs) {
  12347. ast_log(AST_LOG_WARNING, "maxsilence should be less than minmessage or you may get empty messages\n");
  12348. }
  12349. } else {
  12350. ast_log(AST_LOG_WARNING, "Invalid min message time length\n");
  12351. }
  12352. }
  12353. val = ast_variable_retrieve(cfg, "general", "format");
  12354. if (!val) {
  12355. val = "wav";
  12356. } else {
  12357. tmp = ast_strdupa(val);
  12358. val = ast_format_str_reduce(tmp);
  12359. if (!val) {
  12360. ast_log(LOG_ERROR, "Error processing format string, defaulting to format 'wav'\n");
  12361. val = "wav";
  12362. }
  12363. }
  12364. ast_copy_string(vmfmts, val, sizeof(vmfmts));
  12365. skipms = 3000;
  12366. if ((val = ast_variable_retrieve(cfg, "general", "maxgreet"))) {
  12367. if (sscanf(val, "%30d", &x) == 1) {
  12368. maxgreet = x;
  12369. } else {
  12370. ast_log(AST_LOG_WARNING, "Invalid max message greeting length\n");
  12371. }
  12372. }
  12373. if ((val = ast_variable_retrieve(cfg, "general", "skipms"))) {
  12374. if (sscanf(val, "%30d", &x) == 1) {
  12375. skipms = x;
  12376. } else {
  12377. ast_log(AST_LOG_WARNING, "Invalid skipms value\n");
  12378. }
  12379. }
  12380. maxlogins = 3;
  12381. if ((val = ast_variable_retrieve(cfg, "general", "maxlogins"))) {
  12382. if (sscanf(val, "%30d", &x) == 1) {
  12383. maxlogins = x;
  12384. } else {
  12385. ast_log(AST_LOG_WARNING, "Invalid max failed login attempts\n");
  12386. }
  12387. }
  12388. minpassword = MINPASSWORD;
  12389. if ((val = ast_variable_retrieve(cfg, "general", "minpassword"))) {
  12390. if (sscanf(val, "%30d", &x) == 1) {
  12391. minpassword = x;
  12392. } else {
  12393. ast_log(AST_LOG_WARNING, "Invalid minimum password length. Default to %d\n", minpassword);
  12394. }
  12395. }
  12396. /* Force new user to record name ? */
  12397. if (!(val = ast_variable_retrieve(cfg, "general", "forcename")))
  12398. val = "no";
  12399. ast_set2_flag((&globalflags), ast_true(val), VM_FORCENAME);
  12400. /* Force new user to record greetings ? */
  12401. if (!(val = ast_variable_retrieve(cfg, "general", "forcegreetings")))
  12402. val = "no";
  12403. ast_set2_flag((&globalflags), ast_true(val), VM_FORCEGREET);
  12404. if ((val = ast_variable_retrieve(cfg, "general", "cidinternalcontexts"))) {
  12405. ast_debug(1, "VM_CID Internal context string: %s\n", val);
  12406. stringp = ast_strdupa(val);
  12407. for (x = 0 ; x < MAX_NUM_CID_CONTEXTS ; x++){
  12408. if (!ast_strlen_zero(stringp)) {
  12409. q = strsep(&stringp, ",");
  12410. while ((*q == ' ')||(*q == '\t')) /* Eat white space between contexts */
  12411. q++;
  12412. ast_copy_string(cidinternalcontexts[x], q, sizeof(cidinternalcontexts[x]));
  12413. ast_debug(1, "VM_CID Internal context %d: %s\n", x, cidinternalcontexts[x]);
  12414. } else {
  12415. cidinternalcontexts[x][0] = '\0';
  12416. }
  12417. }
  12418. }
  12419. if (!(val = ast_variable_retrieve(cfg, "general", "review"))){
  12420. ast_debug(1, "VM Review Option disabled globally\n");
  12421. val = "no";
  12422. }
  12423. ast_set2_flag((&globalflags), ast_true(val), VM_REVIEW);
  12424. /* Temporary greeting reminder */
  12425. if (!(val = ast_variable_retrieve(cfg, "general", "tempgreetwarn"))) {
  12426. ast_debug(1, "VM Temporary Greeting Reminder Option disabled globally\n");
  12427. val = "no";
  12428. } else {
  12429. ast_debug(1, "VM Temporary Greeting Reminder Option enabled globally\n");
  12430. }
  12431. ast_set2_flag((&globalflags), ast_true(val), VM_TEMPGREETWARN);
  12432. if (!(val = ast_variable_retrieve(cfg, "general", "messagewrap"))){
  12433. ast_debug(1, "VM next message wrap disabled globally\n");
  12434. val = "no";
  12435. }
  12436. ast_set2_flag((&globalflags), ast_true(val), VM_MESSAGEWRAP);
  12437. if (!(val = ast_variable_retrieve(cfg, "general", "operator"))){
  12438. ast_debug(1, "VM Operator break disabled globally\n");
  12439. val = "no";
  12440. }
  12441. ast_set2_flag((&globalflags), ast_true(val), VM_OPERATOR);
  12442. if (!(val = ast_variable_retrieve(cfg, "general", "saycid"))) {
  12443. ast_debug(1, "VM CID Info before msg disabled globally\n");
  12444. val = "no";
  12445. }
  12446. ast_set2_flag((&globalflags), ast_true(val), VM_SAYCID);
  12447. if (!(val = ast_variable_retrieve(cfg, "general", "sendvoicemail"))){
  12448. ast_debug(1, "Send Voicemail msg disabled globally\n");
  12449. val = "no";
  12450. }
  12451. ast_set2_flag((&globalflags), ast_true(val), VM_SVMAIL);
  12452. if (!(val = ast_variable_retrieve(cfg, "general", "envelope"))) {
  12453. ast_debug(1, "ENVELOPE before msg enabled globally\n");
  12454. val = "yes";
  12455. }
  12456. ast_set2_flag((&globalflags), ast_true(val), VM_ENVELOPE);
  12457. if (!(val = ast_variable_retrieve(cfg, "general", "moveheard"))) {
  12458. ast_debug(1, "Move Heard enabled globally\n");
  12459. val = "yes";
  12460. }
  12461. ast_set2_flag((&globalflags), ast_true(val), VM_MOVEHEARD);
  12462. if (!(val = ast_variable_retrieve(cfg, "general", "forward_urgent_auto"))) {
  12463. ast_debug(1, "Autoset of Urgent flag on forwarded Urgent messages disabled globally\n");
  12464. val = "no";
  12465. }
  12466. ast_set2_flag((&globalflags), ast_true(val), VM_FWDURGAUTO);
  12467. if (!(val = ast_variable_retrieve(cfg, "general", "sayduration"))) {
  12468. ast_debug(1, "Duration info before msg enabled globally\n");
  12469. val = "yes";
  12470. }
  12471. ast_set2_flag((&globalflags), ast_true(val), VM_SAYDURATION);
  12472. saydurationminfo = 2;
  12473. if ((val = ast_variable_retrieve(cfg, "general", "saydurationm"))) {
  12474. if (sscanf(val, "%30d", &x) == 1) {
  12475. saydurationminfo = x;
  12476. } else {
  12477. ast_log(AST_LOG_WARNING, "Invalid min duration for say duration\n");
  12478. }
  12479. }
  12480. if (!(val = ast_variable_retrieve(cfg, "general", "nextaftercmd"))) {
  12481. ast_debug(1, "We are not going to skip to the next msg after save/delete\n");
  12482. val = "no";
  12483. }
  12484. ast_set2_flag((&globalflags), ast_true(val), VM_SKIPAFTERCMD);
  12485. if ((val = ast_variable_retrieve(cfg, "general", "dialout"))) {
  12486. ast_copy_string(dialcontext, val, sizeof(dialcontext));
  12487. ast_debug(1, "found dialout context: %s\n", dialcontext);
  12488. } else {
  12489. dialcontext[0] = '\0';
  12490. }
  12491. if ((val = ast_variable_retrieve(cfg, "general", "callback"))) {
  12492. ast_copy_string(callcontext, val, sizeof(callcontext));
  12493. ast_debug(1, "found callback context: %s\n", callcontext);
  12494. } else {
  12495. callcontext[0] = '\0';
  12496. }
  12497. if ((val = ast_variable_retrieve(cfg, "general", "exitcontext"))) {
  12498. ast_copy_string(exitcontext, val, sizeof(exitcontext));
  12499. ast_debug(1, "found operator context: %s\n", exitcontext);
  12500. } else {
  12501. exitcontext[0] = '\0';
  12502. }
  12503. /* load password sounds configuration */
  12504. if ((val = ast_variable_retrieve(cfg, "general", "vm-password")))
  12505. ast_copy_string(vm_password, val, sizeof(vm_password));
  12506. if ((val = ast_variable_retrieve(cfg, "general", "vm-newpassword")))
  12507. ast_copy_string(vm_newpassword, val, sizeof(vm_newpassword));
  12508. if ((val = ast_variable_retrieve(cfg, "general", "vm-invalid-password")))
  12509. ast_copy_string(vm_invalid_password, val, sizeof(vm_invalid_password));
  12510. if ((val = ast_variable_retrieve(cfg, "general", "vm-passchanged")))
  12511. ast_copy_string(vm_passchanged, val, sizeof(vm_passchanged));
  12512. if ((val = ast_variable_retrieve(cfg, "general", "vm-reenterpassword")))
  12513. ast_copy_string(vm_reenterpassword, val, sizeof(vm_reenterpassword));
  12514. if ((val = ast_variable_retrieve(cfg, "general", "vm-mismatch")))
  12515. ast_copy_string(vm_mismatch, val, sizeof(vm_mismatch));
  12516. if ((val = ast_variable_retrieve(cfg, "general", "vm-pls-try-again"))) {
  12517. ast_copy_string(vm_pls_try_again, val, sizeof(vm_pls_try_again));
  12518. }
  12519. if ((val = ast_variable_retrieve(cfg, "general", "vm-prepend-timeout"))) {
  12520. ast_copy_string(vm_prepend_timeout, val, sizeof(vm_prepend_timeout));
  12521. }
  12522. /* load configurable audio prompts */
  12523. if ((val = ast_variable_retrieve(cfg, "general", "listen-control-forward-key")) && is_valid_dtmf(val))
  12524. ast_copy_string(listen_control_forward_key, val, sizeof(listen_control_forward_key));
  12525. if ((val = ast_variable_retrieve(cfg, "general", "listen-control-reverse-key")) && is_valid_dtmf(val))
  12526. ast_copy_string(listen_control_reverse_key, val, sizeof(listen_control_reverse_key));
  12527. if ((val = ast_variable_retrieve(cfg, "general", "listen-control-pause-key")) && is_valid_dtmf(val))
  12528. ast_copy_string(listen_control_pause_key, val, sizeof(listen_control_pause_key));
  12529. if ((val = ast_variable_retrieve(cfg, "general", "listen-control-restart-key")) && is_valid_dtmf(val))
  12530. ast_copy_string(listen_control_restart_key, val, sizeof(listen_control_restart_key));
  12531. if ((val = ast_variable_retrieve(cfg, "general", "listen-control-stop-key")) && is_valid_dtmf(val))
  12532. ast_copy_string(listen_control_stop_key, val, sizeof(listen_control_stop_key));
  12533. if (!(val = ast_variable_retrieve(cfg, "general", "usedirectory")))
  12534. val = "no";
  12535. ast_set2_flag((&globalflags), ast_true(val), VM_DIRECFORWARD);
  12536. if (!(val = ast_variable_retrieve(cfg, "general", "passwordlocation"))) {
  12537. val = "voicemail.conf";
  12538. }
  12539. if (!(strcmp(val, "spooldir"))) {
  12540. passwordlocation = OPT_PWLOC_SPOOLDIR;
  12541. } else {
  12542. passwordlocation = OPT_PWLOC_VOICEMAILCONF;
  12543. }
  12544. poll_freq = DEFAULT_POLL_FREQ;
  12545. if ((val = ast_variable_retrieve(cfg, "general", "pollfreq"))) {
  12546. if (sscanf(val, "%30u", &poll_freq) != 1) {
  12547. poll_freq = DEFAULT_POLL_FREQ;
  12548. ast_log(AST_LOG_ERROR, "'%s' is not a valid value for the pollfreq option!\n", val);
  12549. }
  12550. }
  12551. poll_mailboxes = 0;
  12552. if ((val = ast_variable_retrieve(cfg, "general", "pollmailboxes")))
  12553. poll_mailboxes = ast_true(val);
  12554. memset(fromstring, 0, sizeof(fromstring));
  12555. memset(pagerfromstring, 0, sizeof(pagerfromstring));
  12556. strcpy(charset, "ISO-8859-1");
  12557. if (emailbody) {
  12558. ast_free(emailbody);
  12559. emailbody = NULL;
  12560. }
  12561. if (emailsubject) {
  12562. ast_free(emailsubject);
  12563. emailsubject = NULL;
  12564. }
  12565. if (pagerbody) {
  12566. ast_free(pagerbody);
  12567. pagerbody = NULL;
  12568. }
  12569. if (pagersubject) {
  12570. ast_free(pagersubject);
  12571. pagersubject = NULL;
  12572. }
  12573. if ((val = ast_variable_retrieve(cfg, "general", "pbxskip")))
  12574. ast_set2_flag((&globalflags), ast_true(val), VM_PBXSKIP);
  12575. if ((val = ast_variable_retrieve(cfg, "general", "fromstring")))
  12576. ast_copy_string(fromstring, val, sizeof(fromstring));
  12577. if ((val = ast_variable_retrieve(cfg, "general", "pagerfromstring")))
  12578. ast_copy_string(pagerfromstring, val, sizeof(pagerfromstring));
  12579. if ((val = ast_variable_retrieve(cfg, "general", "charset")))
  12580. ast_copy_string(charset, val, sizeof(charset));
  12581. if ((val = ast_variable_retrieve(cfg, "general", "adsifdn"))) {
  12582. sscanf(val, "%2x%2x%2x%2x", &tmpadsi[0], &tmpadsi[1], &tmpadsi[2], &tmpadsi[3]);
  12583. for (x = 0; x < 4; x++) {
  12584. memcpy(&adsifdn[x], &tmpadsi[x], 1);
  12585. }
  12586. }
  12587. if ((val = ast_variable_retrieve(cfg, "general", "adsisec"))) {
  12588. sscanf(val, "%2x%2x%2x%2x", &tmpadsi[0], &tmpadsi[1], &tmpadsi[2], &tmpadsi[3]);
  12589. for (x = 0; x < 4; x++) {
  12590. memcpy(&adsisec[x], &tmpadsi[x], 1);
  12591. }
  12592. }
  12593. if ((val = ast_variable_retrieve(cfg, "general", "adsiver"))) {
  12594. if (atoi(val)) {
  12595. adsiver = atoi(val);
  12596. }
  12597. }
  12598. if ((val = ast_variable_retrieve(cfg, "general", "tz"))) {
  12599. ast_copy_string(zonetag, val, sizeof(zonetag));
  12600. }
  12601. if ((val = ast_variable_retrieve(cfg, "general", "locale"))) {
  12602. ast_copy_string(locale, val, sizeof(locale));
  12603. }
  12604. if ((val = ast_variable_retrieve(cfg, "general", "emailsubject"))) {
  12605. emailsubject = ast_strdup(substitute_escapes(val));
  12606. }
  12607. if ((val = ast_variable_retrieve(cfg, "general", "emailbody"))) {
  12608. emailbody = ast_strdup(substitute_escapes(val));
  12609. }
  12610. if ((val = ast_variable_retrieve(cfg, "general", "pagersubject"))) {
  12611. pagersubject = ast_strdup(substitute_escapes(val));
  12612. }
  12613. if ((val = ast_variable_retrieve(cfg, "general", "pagerbody"))) {
  12614. pagerbody = ast_strdup(substitute_escapes(val));
  12615. }
  12616. /* load mailboxes from users.conf */
  12617. if (ucfg) {
  12618. for (cat = ast_category_browse(ucfg, NULL); cat ; cat = ast_category_browse(ucfg, cat)) {
  12619. if (!strcasecmp(cat, "general")) {
  12620. continue;
  12621. }
  12622. if (!ast_true(ast_config_option(ucfg, cat, "hasvoicemail")))
  12623. continue;
  12624. if ((current = find_or_create(userscontext, cat))) {
  12625. populate_defaults(current);
  12626. apply_options_full(current, ast_variable_browse(ucfg, cat));
  12627. ast_copy_string(current->context, userscontext, sizeof(current->context));
  12628. if (!ast_strlen_zero(current->password) && current->passwordlocation == OPT_PWLOC_VOICEMAILCONF) {
  12629. current->passwordlocation = OPT_PWLOC_USERSCONF;
  12630. }
  12631. switch (current->passwordlocation) {
  12632. case OPT_PWLOC_SPOOLDIR:
  12633. snprintf(secretfn, sizeof(secretfn), "%s%s/%s/secret.conf", VM_SPOOL_DIR, current->context, current->mailbox);
  12634. read_password_from_file(secretfn, current->password, sizeof(current->password));
  12635. }
  12636. }
  12637. }
  12638. }
  12639. /* load mailboxes from voicemail.conf */
  12640. cat = ast_category_browse(cfg, NULL);
  12641. while (cat) {
  12642. if (strcasecmp(cat, "general")) {
  12643. var = ast_variable_browse(cfg, cat);
  12644. if (strcasecmp(cat, "zonemessages")) {
  12645. /* Process mailboxes in this context */
  12646. while (var) {
  12647. append_mailbox(cat, var->name, var->value);
  12648. var = var->next;
  12649. }
  12650. } else {
  12651. /* Timezones in this context */
  12652. while (var) {
  12653. struct vm_zone *z;
  12654. if ((z = ast_malloc(sizeof(*z)))) {
  12655. char *msg_format, *tzone;
  12656. msg_format = ast_strdupa(var->value);
  12657. tzone = strsep(&msg_format, "|,");
  12658. if (msg_format) {
  12659. ast_copy_string(z->name, var->name, sizeof(z->name));
  12660. ast_copy_string(z->timezone, tzone, sizeof(z->timezone));
  12661. ast_copy_string(z->msg_format, msg_format, sizeof(z->msg_format));
  12662. AST_LIST_LOCK(&zones);
  12663. AST_LIST_INSERT_HEAD(&zones, z, list);
  12664. AST_LIST_UNLOCK(&zones);
  12665. } else {
  12666. ast_log(AST_LOG_WARNING, "Invalid timezone definition at line %d\n", var->lineno);
  12667. ast_free(z);
  12668. }
  12669. } else {
  12670. AST_LIST_UNLOCK(&users);
  12671. return -1;
  12672. }
  12673. var = var->next;
  12674. }
  12675. }
  12676. }
  12677. cat = ast_category_browse(cfg, cat);
  12678. }
  12679. AST_LIST_UNLOCK(&users);
  12680. if (poll_mailboxes && poll_thread == AST_PTHREADT_NULL)
  12681. start_poll_thread();
  12682. if (!poll_mailboxes && poll_thread != AST_PTHREADT_NULL)
  12683. stop_poll_thread();;
  12684. return 0;
  12685. } else {
  12686. AST_LIST_UNLOCK(&users);
  12687. ast_log(AST_LOG_WARNING, "Failed to load configuration file.\n");
  12688. return 0;
  12689. }
  12690. }
  12691. static int sayname(struct ast_channel *chan, const char *mailbox, const char *context)
  12692. {
  12693. int res = -1;
  12694. char dir[PATH_MAX];
  12695. snprintf(dir, sizeof(dir), "%s%s/%s/greet", VM_SPOOL_DIR, context, mailbox);
  12696. ast_debug(2, "About to try retrieving name file %s\n", dir);
  12697. RETRIEVE(dir, -1, mailbox, context);
  12698. if (ast_fileexists(dir, NULL, NULL)) {
  12699. res = ast_stream_and_wait(chan, dir, AST_DIGIT_ANY);
  12700. }
  12701. DISPOSE(dir, -1);
  12702. return res;
  12703. }
  12704. /*!
  12705. * \internal
  12706. * \brief Play a recorded user name for the mailbox to the specified channel.
  12707. *
  12708. * \param chan Where to play the recorded name file.
  12709. * \param mailbox_id The mailbox name.
  12710. *
  12711. * \retval 0 Name played without interruption
  12712. * \retval dtmf ASCII value of the DTMF which interrupted playback.
  12713. * \retval -1 Unable to locate mailbox or hangup occurred.
  12714. */
  12715. static int vm_sayname(struct ast_channel *chan, const char *mailbox_id)
  12716. {
  12717. char *context;
  12718. char *mailbox;
  12719. if (ast_strlen_zero(mailbox_id)
  12720. || separate_mailbox(ast_strdupa(mailbox_id), &mailbox, &context)) {
  12721. return -1;
  12722. }
  12723. return sayname(chan, mailbox, context);
  12724. }
  12725. static void read_password_from_file(const char *secretfn, char *password, int passwordlen) {
  12726. struct ast_config *pwconf;
  12727. struct ast_flags config_flags = { 0 };
  12728. pwconf = ast_config_load(secretfn, config_flags);
  12729. if (valid_config(pwconf)) {
  12730. const char *val = ast_variable_retrieve(pwconf, "general", "password");
  12731. if (val) {
  12732. ast_copy_string(password, val, passwordlen);
  12733. ast_config_destroy(pwconf);
  12734. return;
  12735. }
  12736. ast_config_destroy(pwconf);
  12737. }
  12738. ast_log(LOG_NOTICE, "Failed reading voicemail password from %s, using secret from config file\n", secretfn);
  12739. }
  12740. static int write_password_to_file(const char *secretfn, const char *password) {
  12741. struct ast_config *conf;
  12742. struct ast_category *cat;
  12743. struct ast_variable *var;
  12744. int res = -1;
  12745. if (!(conf = ast_config_new())) {
  12746. ast_log(LOG_ERROR, "Error creating new config structure\n");
  12747. return res;
  12748. }
  12749. if (!(cat = ast_category_new("general", "", 1))) {
  12750. ast_log(LOG_ERROR, "Error creating new category structure\n");
  12751. ast_config_destroy(conf);
  12752. return res;
  12753. }
  12754. if (!(var = ast_variable_new("password", password, ""))) {
  12755. ast_log(LOG_ERROR, "Error creating new variable structure\n");
  12756. ast_config_destroy(conf);
  12757. ast_category_destroy(cat);
  12758. return res;
  12759. }
  12760. ast_category_append(conf, cat);
  12761. ast_variable_append(cat, var);
  12762. if (!ast_config_text_file_save(secretfn, conf, "app_voicemail")) {
  12763. res = 0;
  12764. } else {
  12765. ast_log(LOG_ERROR, "Error writing voicemail password to %s\n", secretfn);
  12766. }
  12767. ast_config_destroy(conf);
  12768. return res;
  12769. }
  12770. static int vmsayname_exec(struct ast_channel *chan, const char *data)
  12771. {
  12772. char *context;
  12773. char *mailbox;
  12774. int res;
  12775. if (ast_strlen_zero(data)
  12776. || separate_mailbox(ast_strdupa(data), &mailbox, &context)) {
  12777. ast_log(LOG_WARNING, "VMSayName requires argument mailbox@context\n");
  12778. return -1;
  12779. }
  12780. if ((res = sayname(chan, mailbox, context)) < 0) {
  12781. ast_debug(3, "Greeting not found for '%s@%s', falling back to mailbox number.\n", mailbox, context);
  12782. res = ast_stream_and_wait(chan, "vm-extension", AST_DIGIT_ANY);
  12783. if (!res) {
  12784. res = ast_say_character_str(chan, mailbox, AST_DIGIT_ANY, ast_channel_language(chan), AST_SAY_CASE_NONE);
  12785. }
  12786. }
  12787. return res;
  12788. }
  12789. #ifdef TEST_FRAMEWORK
  12790. static int fake_write(struct ast_channel *ast, struct ast_frame *frame)
  12791. {
  12792. return 0;
  12793. }
  12794. static struct ast_frame *fake_read(struct ast_channel *ast)
  12795. {
  12796. return &ast_null_frame;
  12797. }
  12798. AST_TEST_DEFINE(test_voicemail_vmsayname)
  12799. {
  12800. char dir[PATH_MAX];
  12801. char dir2[PATH_MAX];
  12802. static const char TEST_CONTEXT[] = "very_long_unique_context_so_that_nobody_will_ever_have_the_same_one_configured_3141592653";
  12803. static const char TEST_EXTENSION[] = "1234";
  12804. struct ast_channel *test_channel1 = NULL;
  12805. int res = -1;
  12806. struct ast_format_cap *capabilities;
  12807. static const struct ast_channel_tech fake_tech = {
  12808. .write = fake_write,
  12809. .read = fake_read,
  12810. };
  12811. switch (cmd) {
  12812. case TEST_INIT:
  12813. info->name = "vmsayname_exec";
  12814. info->category = "/apps/app_voicemail/";
  12815. info->summary = "Vmsayname unit test";
  12816. info->description =
  12817. "This tests passing various parameters to vmsayname";
  12818. return AST_TEST_NOT_RUN;
  12819. case TEST_EXECUTE:
  12820. break;
  12821. }
  12822. if (!(test_channel1 = ast_channel_alloc(0, AST_STATE_DOWN, NULL, NULL, NULL, NULL,
  12823. NULL, NULL, 0, 0, "TestChannel1"))) {
  12824. goto exit_vmsayname_test;
  12825. }
  12826. /* normally this is done in the channel driver */
  12827. capabilities = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT);
  12828. if (!capabilities) {
  12829. goto exit_vmsayname_test;
  12830. }
  12831. ast_format_cap_append(capabilities, ast_format_gsm, 0);
  12832. ast_channel_nativeformats_set(test_channel1, capabilities);
  12833. ao2_ref(capabilities, -1);
  12834. ast_channel_set_writeformat(test_channel1, ast_format_gsm);
  12835. ast_channel_set_rawwriteformat(test_channel1, ast_format_gsm);
  12836. ast_channel_set_readformat(test_channel1, ast_format_gsm);
  12837. ast_channel_set_rawreadformat(test_channel1, ast_format_gsm);
  12838. ast_channel_tech_set(test_channel1, &fake_tech);
  12839. ast_channel_unlock(test_channel1);
  12840. ast_test_status_update(test, "Test playing of extension when greeting is not available...\n");
  12841. snprintf(dir, sizeof(dir), "%s@%s", TEST_EXTENSION, TEST_CONTEXT); /* not a dir, don't get confused */
  12842. if (!(res = vmsayname_exec(test_channel1, dir))) {
  12843. snprintf(dir, sizeof(dir), "%s%s/%s/greet", VM_SPOOL_DIR, TEST_CONTEXT, TEST_EXTENSION);
  12844. if (ast_fileexists(dir, NULL, NULL)) {
  12845. ast_test_status_update(test, "This should not happen, most likely means clean up from previous test failed\n");
  12846. res = -1;
  12847. goto exit_vmsayname_test;
  12848. } else {
  12849. /* no greeting already exists as expected, let's create one to fully test sayname */
  12850. if ((res = create_dirpath(dir, sizeof(dir), TEST_CONTEXT, TEST_EXTENSION, ""))) {
  12851. ast_log(AST_LOG_WARNING, "Failed to make test directory\n");
  12852. goto exit_vmsayname_test;
  12853. }
  12854. snprintf(dir, sizeof(dir), "%s/sounds/beep.gsm", ast_config_AST_VAR_DIR);
  12855. snprintf(dir2, sizeof(dir2), "%s%s/%s/greet.gsm", VM_SPOOL_DIR, TEST_CONTEXT, TEST_EXTENSION);
  12856. /* we're not going to hear the sound anyway, just use a valid gsm audio file */
  12857. if ((res = symlink(dir, dir2))) {
  12858. ast_log(LOG_WARNING, "Symlink reported %s\n", strerror(errno));
  12859. goto exit_vmsayname_test;
  12860. }
  12861. ast_test_status_update(test, "Test playing created mailbox greeting...\n");
  12862. snprintf(dir, sizeof(dir), "%s@%s", TEST_EXTENSION, TEST_CONTEXT); /* not a dir, don't get confused */
  12863. res = vmsayname_exec(test_channel1, dir);
  12864. /* TODO: there may be a better way to do this */
  12865. unlink(dir2);
  12866. snprintf(dir2, sizeof(dir2), "%s%s/%s", VM_SPOOL_DIR, TEST_CONTEXT, TEST_EXTENSION);
  12867. rmdir(dir2);
  12868. snprintf(dir2, sizeof(dir2), "%s%s", VM_SPOOL_DIR, TEST_CONTEXT);
  12869. rmdir(dir2);
  12870. }
  12871. }
  12872. exit_vmsayname_test:
  12873. ast_hangup(test_channel1);
  12874. return res ? AST_TEST_FAIL : AST_TEST_PASS;
  12875. }
  12876. AST_TEST_DEFINE(test_voicemail_msgcount)
  12877. {
  12878. int i, j, res = AST_TEST_PASS, syserr;
  12879. struct ast_vm_user *vmu;
  12880. struct ast_vm_user svm;
  12881. struct vm_state vms;
  12882. #ifdef IMAP_STORAGE
  12883. struct ast_channel *chan = NULL;
  12884. #endif
  12885. struct {
  12886. char dir[256];
  12887. char file[256];
  12888. char txtfile[256];
  12889. } tmp[3];
  12890. char syscmd[256];
  12891. const char origweasels[] = "tt-weasels";
  12892. const char testcontext[] = "test";
  12893. const char testmailbox[] = "00000000";
  12894. const char testspec[] = "00000000@test";
  12895. FILE *txt;
  12896. int new, old, urgent;
  12897. const char *folders[3] = { "Old", "Urgent", "INBOX" };
  12898. const int folder2mbox[3] = { 1, 11, 0 };
  12899. const int expected_results[3][12] = {
  12900. /* hasvm-old, hasvm-urgent, hasvm-new, ic-old, ic-urgent, ic-new, ic2-old, ic2-urgent, ic2-new, mc-old, mc-urgent, mc-new */
  12901. { 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0 },
  12902. { 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1 },
  12903. { 1, 1, 1, 1, 0, 2, 1, 1, 1, 1, 1, 2 },
  12904. };
  12905. switch (cmd) {
  12906. case TEST_INIT:
  12907. info->name = "test_voicemail_msgcount";
  12908. info->category = "/apps/app_voicemail/";
  12909. info->summary = "Test Voicemail status checks";
  12910. info->description =
  12911. "Verify that message counts are correct when retrieved through the public API";
  12912. return AST_TEST_NOT_RUN;
  12913. case TEST_EXECUTE:
  12914. break;
  12915. }
  12916. /* Make sure the original path was completely empty */
  12917. snprintf(syscmd, sizeof(syscmd), "rm -rf \"%s%s/%s\"", VM_SPOOL_DIR, testcontext, testmailbox);
  12918. if ((syserr = ast_safe_system(syscmd))) {
  12919. ast_test_status_update(test, "Unable to clear test directory: %s\n",
  12920. syserr > 0 ? strerror(syserr) : "unable to fork()");
  12921. return AST_TEST_FAIL;
  12922. }
  12923. #ifdef IMAP_STORAGE
  12924. if (!(chan = ast_dummy_channel_alloc())) {
  12925. ast_test_status_update(test, "Unable to create dummy channel\n");
  12926. return AST_TEST_FAIL;
  12927. }
  12928. #endif
  12929. if (!(vmu = find_user(&svm, testcontext, testmailbox)) &&
  12930. !(vmu = find_or_create(testcontext, testmailbox))) {
  12931. ast_test_status_update(test, "Cannot create vmu structure\n");
  12932. ast_unreplace_sigchld();
  12933. #ifdef IMAP_STORAGE
  12934. chan = ast_channel_unref(chan);
  12935. #endif
  12936. return AST_TEST_FAIL;
  12937. }
  12938. populate_defaults(vmu);
  12939. memset(&vms, 0, sizeof(vms));
  12940. /* Create temporary voicemail */
  12941. for (i = 0; i < 3; i++) {
  12942. create_dirpath(tmp[i].dir, sizeof(tmp[i].dir), testcontext, testmailbox, folders[i]);
  12943. make_file(tmp[i].file, sizeof(tmp[i].file), tmp[i].dir, 0);
  12944. snprintf(tmp[i].txtfile, sizeof(tmp[i].txtfile), "%s.txt", tmp[i].file);
  12945. if (ast_fileexists(origweasels, "gsm", "en") > 0) {
  12946. snprintf(syscmd, sizeof(syscmd), "cp \"%s/sounds/en/%s.gsm\" \"%s/%s/%s/%s/msg0000.gsm\"", ast_config_AST_DATA_DIR, origweasels,
  12947. VM_SPOOL_DIR, testcontext, testmailbox, folders[i]);
  12948. if ((syserr = ast_safe_system(syscmd))) {
  12949. ast_test_status_update(test, "Unable to create test voicemail: %s\n",
  12950. syserr > 0 ? strerror(syserr) : "unable to fork()");
  12951. ast_unreplace_sigchld();
  12952. #ifdef IMAP_STORAGE
  12953. chan = ast_channel_unref(chan);
  12954. #endif
  12955. return AST_TEST_FAIL;
  12956. }
  12957. }
  12958. if ((txt = fopen(tmp[i].txtfile, "w+"))) {
  12959. fprintf(txt, "; just a stub\n[message]\nflag=%s\n", strcmp(folders[i], "Urgent") ? "" : "Urgent");
  12960. fclose(txt);
  12961. } else {
  12962. ast_test_status_update(test, "Unable to write message file '%s'\n", tmp[i].txtfile);
  12963. res = AST_TEST_FAIL;
  12964. break;
  12965. }
  12966. open_mailbox(&vms, vmu, folder2mbox[i]);
  12967. STORE(tmp[i].dir, testmailbox, testcontext, 0, chan, vmu, "gsm", 600, &vms, strcmp(folders[i], "Urgent") ? "" : "Urgent", NULL);
  12968. /* hasvm-old, hasvm-urgent, hasvm-new, ic-old, ic-urgent, ic-new, ic2-old, ic2-urgent, ic2-new, mc-old, mc-urgent, mc-new */
  12969. for (j = 0; j < 3; j++) {
  12970. /* folder[2] is INBOX, __has_voicemail will default back to INBOX */
  12971. if (ast_app_has_voicemail(testspec, (j==2 ? NULL : folders[j])) != expected_results[i][0 + j]) {
  12972. ast_test_status_update(test, "has_voicemail(%s, %s) returned %d and we expected %d\n",
  12973. testspec, folders[j], ast_app_has_voicemail(testspec, folders[j]), expected_results[i][0 + j]);
  12974. res = AST_TEST_FAIL;
  12975. }
  12976. }
  12977. new = old = urgent = 0;
  12978. if (ast_app_inboxcount(testspec, &new, &old)) {
  12979. ast_test_status_update(test, "inboxcount returned failure\n");
  12980. res = AST_TEST_FAIL;
  12981. } else if (old != expected_results[i][3 + 0] || new != expected_results[i][3 + 2]) {
  12982. ast_test_status_update(test, "inboxcount(%s) returned old=%d (expected %d) and new=%d (expected %d)\n",
  12983. testspec, old, expected_results[i][3 + 0], new, expected_results[i][3 + 2]);
  12984. res = AST_TEST_FAIL;
  12985. }
  12986. new = old = urgent = 0;
  12987. if (ast_app_inboxcount2(testspec, &urgent, &new, &old)) {
  12988. ast_test_status_update(test, "inboxcount2 returned failure\n");
  12989. res = AST_TEST_FAIL;
  12990. } else if (old != expected_results[i][6 + 0] ||
  12991. urgent != expected_results[i][6 + 1] ||
  12992. new != expected_results[i][6 + 2] ) {
  12993. ast_test_status_update(test, "inboxcount2(%s) returned old=%d (expected %d), urgent=%d (expected %d), and new=%d (expected %d)\n",
  12994. testspec, old, expected_results[i][6 + 0], urgent, expected_results[i][6 + 1], new, expected_results[i][6 + 2]);
  12995. res = AST_TEST_FAIL;
  12996. }
  12997. new = old = urgent = 0;
  12998. for (j = 0; j < 3; j++) {
  12999. if (ast_app_messagecount(testspec, folders[j]) != expected_results[i][9 + j]) {
  13000. ast_test_status_update(test, "messagecount(%s, %s) returned %d and we expected %d\n",
  13001. testspec, folders[j], ast_app_messagecount(testspec, folders[j]), expected_results[i][9 + j]);
  13002. res = AST_TEST_FAIL;
  13003. }
  13004. }
  13005. }
  13006. for (i = 0; i < 3; i++) {
  13007. /* This is necessary if the voicemails are stored on an ODBC/IMAP
  13008. * server, in which case, the rm below will not affect the
  13009. * voicemails. */
  13010. DELETE(tmp[i].dir, 0, tmp[i].file, vmu);
  13011. DISPOSE(tmp[i].dir, 0);
  13012. }
  13013. if (vms.deleted) {
  13014. ast_free(vms.deleted);
  13015. }
  13016. if (vms.heard) {
  13017. ast_free(vms.heard);
  13018. }
  13019. #ifdef IMAP_STORAGE
  13020. chan = ast_channel_unref(chan);
  13021. #endif
  13022. /* And remove test directory */
  13023. snprintf(syscmd, sizeof(syscmd), "rm -rf \"%s%s/%s\"", VM_SPOOL_DIR, testcontext, testmailbox);
  13024. if ((syserr = ast_safe_system(syscmd))) {
  13025. ast_test_status_update(test, "Unable to clear test directory: %s\n",
  13026. syserr > 0 ? strerror(syserr) : "unable to fork()");
  13027. }
  13028. return res;
  13029. }
  13030. AST_TEST_DEFINE(test_voicemail_notify_endl)
  13031. {
  13032. int res = AST_TEST_PASS;
  13033. char testcontext[] = "test";
  13034. char testmailbox[] = "00000000";
  13035. char from[] = "test@example.net", cidnum[] = "1234", cidname[] = "Mark Spencer", format[] = "gsm";
  13036. char attach[256], attach2[256];
  13037. char buf[256] = ""; /* No line should actually be longer than 80 */
  13038. struct ast_channel *chan = NULL;
  13039. struct ast_vm_user *vmu, vmus = {
  13040. .flags = 0,
  13041. };
  13042. FILE *file;
  13043. struct {
  13044. char *name;
  13045. enum { INT, FLAGVAL, STATIC, STRPTR } type;
  13046. void *location;
  13047. union {
  13048. int intval;
  13049. char *strval;
  13050. } u;
  13051. } test_items[] = {
  13052. { "plain jane config", STATIC, vmus.password, .u.strval = "1234" }, /* No, this doesn't change this test any. */
  13053. { "emailsubject", STRPTR, vmus.emailsubject, .u.strval = "Oogly boogly\xf8koogly with what appears to be UTF-8" },
  13054. { "emailbody", STRPTR, vmus.emailbody, .u.strval = "This is a test\n\twith multiple\nlines\nwithin\n" },
  13055. { "serveremail", STATIC, vmus.serveremail, .u.strval = "\"\xf8Something\xe8that\xd8seems to have UTF-8 chars\" <test@example.net>" },
  13056. { "attachment flag", FLAGVAL, &vmus.flags, .u.intval = VM_ATTACH },
  13057. { "attach2", STRPTR, attach2, .u.strval = "" },
  13058. { "attach", STRPTR, attach, .u.strval = "" },
  13059. };
  13060. int which;
  13061. switch (cmd) {
  13062. case TEST_INIT:
  13063. info->name = "test_voicemail_notify_endl";
  13064. info->category = "/apps/app_voicemail/";
  13065. info->summary = "Test Voicemail notification end-of-line";
  13066. info->description =
  13067. "Verify that notification emails use a consistent end-of-line character";
  13068. return AST_TEST_NOT_RUN;
  13069. case TEST_EXECUTE:
  13070. break;
  13071. }
  13072. snprintf(attach, sizeof(attach), "%s/sounds/en/tt-weasels", ast_config_AST_VAR_DIR);
  13073. snprintf(attach2, sizeof(attach2), "%s/sounds/en/tt-somethingwrong", ast_config_AST_VAR_DIR);
  13074. if (!(vmu = find_user(&vmus, testcontext, testmailbox)) &&
  13075. !(vmu = find_or_create(testcontext, testmailbox))) {
  13076. ast_test_status_update(test, "Cannot create vmu structure\n");
  13077. return AST_TEST_NOT_RUN;
  13078. }
  13079. if (vmu != &vmus && !(vmu = find_user(&vmus, testcontext, testmailbox))) {
  13080. ast_test_status_update(test, "Cannot find vmu structure?!!\n");
  13081. return AST_TEST_NOT_RUN;
  13082. }
  13083. populate_defaults(vmu);
  13084. vmu->email = ast_strdup("test2@example.net");
  13085. #ifdef IMAP_STORAGE
  13086. /* TODO When we set up the IMAP server test, we'll need to have credentials for the VMU structure added here */
  13087. #endif
  13088. file = tmpfile();
  13089. for (which = 0; which < ARRAY_LEN(test_items); which++) {
  13090. /* Kill previous test, if any */
  13091. rewind(file);
  13092. if (ftruncate(fileno(file), 0)) {
  13093. ast_test_status_update(test, "Cannot truncate test output file: %s\n", strerror(errno));
  13094. res = AST_TEST_FAIL;
  13095. break;
  13096. }
  13097. /* Make each change, in order, to the test mailbox */
  13098. if (test_items[which].type == INT) {
  13099. *((int *) test_items[which].location) = test_items[which].u.intval;
  13100. } else if (test_items[which].type == FLAGVAL) {
  13101. if (ast_test_flag(vmu, test_items[which].u.intval)) {
  13102. ast_clear_flag(vmu, test_items[which].u.intval);
  13103. } else {
  13104. ast_set_flag(vmu, test_items[which].u.intval);
  13105. }
  13106. } else if (test_items[which].type == STATIC) {
  13107. strcpy(test_items[which].location, test_items[which].u.strval);
  13108. } else if (test_items[which].type == STRPTR) {
  13109. test_items[which].location = test_items[which].u.strval;
  13110. }
  13111. make_email_file(file, from, vmu, 0, testcontext, testmailbox, "INBOX", cidnum, cidname, attach, attach2, format, 999, 1, chan, NULL, 0, NULL, NULL);
  13112. rewind(file);
  13113. while (fgets(buf, sizeof(buf), file)) {
  13114. if (
  13115. #ifdef IMAP_STORAGE
  13116. buf[strlen(buf) - 2] != '\r'
  13117. #else
  13118. buf[strlen(buf) - 2] == '\r'
  13119. #endif
  13120. || buf[strlen(buf) - 1] != '\n') {
  13121. res = AST_TEST_FAIL;
  13122. }
  13123. }
  13124. }
  13125. fclose(file);
  13126. return res;
  13127. }
  13128. AST_TEST_DEFINE(test_voicemail_load_config)
  13129. {
  13130. int res = AST_TEST_PASS;
  13131. struct ast_vm_user *vmu;
  13132. struct ast_config *cfg;
  13133. char config_filename[32] = "/tmp/voicemail.conf.XXXXXX";
  13134. int fd;
  13135. FILE *file;
  13136. struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
  13137. switch (cmd) {
  13138. case TEST_INIT:
  13139. info->name = "test_voicemail_load_config";
  13140. info->category = "/apps/app_voicemail/";
  13141. info->summary = "Test loading Voicemail config";
  13142. info->description =
  13143. "Verify that configuration is loaded consistently. "
  13144. "This is to test regressions of ASTERISK-18838 where it was noticed that "
  13145. "some options were loaded after the mailboxes were instantiated, causing "
  13146. "those options not to be set correctly.";
  13147. return AST_TEST_NOT_RUN;
  13148. case TEST_EXECUTE:
  13149. break;
  13150. }
  13151. /* build a config file by hand... */
  13152. if ((fd = mkstemp(config_filename)) < 0) {
  13153. return AST_TEST_FAIL;
  13154. }
  13155. if (!(file = fdopen(fd, "w"))) {
  13156. close(fd);
  13157. unlink(config_filename);
  13158. return AST_TEST_FAIL;
  13159. }
  13160. fputs("[general]\ncallback=somecontext\nlocale=de_DE.UTF-8\ntz=european\n[test]", file);
  13161. fputs("00000001 => 9999,Mr. Test,,,callback=othercontext|locale=nl_NL.UTF-8|tz=central\n", file);
  13162. fputs("00000002 => 9999,Mrs. Test\n", file);
  13163. fclose(file);
  13164. if (!(cfg = ast_config_load(config_filename, config_flags)) || !valid_config(cfg)) {
  13165. res = AST_TEST_FAIL;
  13166. goto cleanup;
  13167. }
  13168. load_config_from_memory(1, cfg, NULL);
  13169. ast_config_destroy(cfg);
  13170. #define CHECK(u, attr, value) else if (strcmp(u->attr, value)) { \
  13171. ast_test_status_update(test, "mailbox %s should have %s '%s', but has '%s'\n", \
  13172. u->mailbox, #attr, value, u->attr); res = AST_TEST_FAIL; break; }
  13173. AST_LIST_LOCK(&users);
  13174. AST_LIST_TRAVERSE(&users, vmu, list) {
  13175. if (!strcmp(vmu->mailbox, "00000001")) {
  13176. if (0); /* trick to get CHECK to work */
  13177. CHECK(vmu, callback, "othercontext")
  13178. CHECK(vmu, locale, "nl_NL.UTF-8")
  13179. CHECK(vmu, zonetag, "central")
  13180. } else if (!strcmp(vmu->mailbox, "00000002")) {
  13181. if (0); /* trick to get CHECK to work */
  13182. CHECK(vmu, callback, "somecontext")
  13183. CHECK(vmu, locale, "de_DE.UTF-8")
  13184. CHECK(vmu, zonetag, "european")
  13185. }
  13186. }
  13187. AST_LIST_UNLOCK(&users);
  13188. #undef CHECK
  13189. /* restore config */
  13190. load_config(1); /* this might say "Failed to load configuration file." */
  13191. cleanup:
  13192. unlink(config_filename);
  13193. return res;
  13194. }
  13195. AST_TEST_DEFINE(test_voicemail_vm_info)
  13196. {
  13197. struct ast_vm_user *vmu;
  13198. struct ast_channel *chan = NULL;
  13199. const char testcontext[] = "test";
  13200. const char testmailbox[] = "00000000";
  13201. const char vminfo_cmd[] = "VM_INFO";
  13202. char vminfo_buf[256], vminfo_args[256];
  13203. int res = AST_TEST_PASS;
  13204. int test_ret = 0;
  13205. int test_counter = 0;
  13206. struct {
  13207. char *vminfo_test_args;
  13208. char *vminfo_expected;
  13209. int vminfo_ret;
  13210. } test_items[] = {
  13211. { "", "", -1 }, /* Missing argument */
  13212. { "00000000@test,badparam", "", -1 }, /* Wrong argument */
  13213. { "00000000@test", "", -1 }, /* Missing argument */
  13214. { "00000000@test,exists", "1", 0 },
  13215. { "11111111@test,exists", "0", 0 }, /* Invalid mailbox */
  13216. { "00000000@test,email", "vm-info-test@example.net", 0 },
  13217. { "11111111@test,email", "", 0 }, /* Invalid mailbox */
  13218. { "00000000@test,fullname", "Test Framework Mailbox", 0 },
  13219. { "00000000@test,pager", "vm-info-pager-test@example.net", 0 },
  13220. { "00000000@test,locale", "en_US", 0 },
  13221. { "00000000@test,tz", "central", 0 },
  13222. { "00000000@test,language", "en", 0 },
  13223. { "00000000@test,password", "9876", 0 },
  13224. };
  13225. switch (cmd) {
  13226. case TEST_INIT:
  13227. info->name = "test_voicemail_vm_info";
  13228. info->category = "/apps/app_voicemail/";
  13229. info->summary = "VM_INFO unit test";
  13230. info->description =
  13231. "This tests passing various parameters to VM_INFO";
  13232. return AST_TEST_NOT_RUN;
  13233. case TEST_EXECUTE:
  13234. break;
  13235. }
  13236. if (!(chan = ast_dummy_channel_alloc())) {
  13237. ast_test_status_update(test, "Unable to create dummy channel\n");
  13238. return AST_TEST_FAIL;
  13239. }
  13240. if (!(vmu = find_user(NULL, testcontext, testmailbox)) &&
  13241. !(vmu = find_or_create(testcontext, testmailbox))) {
  13242. ast_test_status_update(test, "Cannot create vmu structure\n");
  13243. chan = ast_channel_unref(chan);
  13244. return AST_TEST_FAIL;
  13245. }
  13246. populate_defaults(vmu);
  13247. vmu->email = ast_strdup("vm-info-test@example.net");
  13248. ast_copy_string(vmu->fullname, "Test Framework Mailbox", sizeof(vmu->fullname));
  13249. ast_copy_string(vmu->pager, "vm-info-pager-test@example.net", sizeof(vmu->pager));
  13250. ast_copy_string(vmu->language, "en", sizeof(vmu->language));
  13251. ast_copy_string(vmu->zonetag, "central", sizeof(vmu->zonetag));
  13252. ast_copy_string(vmu->locale, "en_US", sizeof(vmu->zonetag));
  13253. ast_copy_string(vmu->password, "9876", sizeof(vmu->password));
  13254. for (test_counter = 0; test_counter < ARRAY_LEN(test_items); test_counter++) {
  13255. ast_copy_string(vminfo_args, test_items[test_counter].vminfo_test_args, sizeof(vminfo_args));
  13256. test_ret = acf_vm_info(chan, vminfo_cmd, vminfo_args, vminfo_buf, sizeof(vminfo_buf));
  13257. if (strcmp(vminfo_buf, test_items[test_counter].vminfo_expected)) {
  13258. ast_test_status_update(test, "VM_INFO respose was: '%s', but expected: '%s'\n", vminfo_buf, test_items[test_counter].vminfo_expected);
  13259. res = AST_TEST_FAIL;
  13260. }
  13261. if (!(test_ret == test_items[test_counter].vminfo_ret)) {
  13262. ast_test_status_update(test, "VM_INFO return code was: '%i', but expected '%i'\n", test_ret, test_items[test_counter].vminfo_ret);
  13263. res = AST_TEST_FAIL;
  13264. }
  13265. }
  13266. chan = ast_channel_unref(chan);
  13267. return res;
  13268. }
  13269. #endif /* defined(TEST_FRAMEWORK) */
  13270. static const struct ast_vm_functions vm_table = {
  13271. .module_version = VM_MODULE_VERSION,
  13272. .module_name = AST_MODULE,
  13273. .has_voicemail = has_voicemail,
  13274. .inboxcount = inboxcount,
  13275. .inboxcount2 = inboxcount2,
  13276. .messagecount = messagecount,
  13277. .copy_recording_to_vm = msg_create_from_file,
  13278. .index_to_foldername = vm_index_to_foldername,
  13279. .mailbox_snapshot_create = vm_mailbox_snapshot_create,
  13280. .mailbox_snapshot_destroy = vm_mailbox_snapshot_destroy,
  13281. .msg_move = vm_msg_move,
  13282. .msg_remove = vm_msg_remove,
  13283. .msg_forward = vm_msg_forward,
  13284. .msg_play = vm_msg_play,
  13285. };
  13286. static const struct ast_vm_greeter_functions vm_greeter_table = {
  13287. .module_version = VM_GREETER_MODULE_VERSION,
  13288. .module_name = AST_MODULE,
  13289. .sayname = vm_sayname,
  13290. };
  13291. static int reload(void)
  13292. {
  13293. return load_config(1);
  13294. }
  13295. static int unload_module(void)
  13296. {
  13297. int res;
  13298. res = ast_unregister_application(app);
  13299. res |= ast_unregister_application(app2);
  13300. res |= ast_unregister_application(app3);
  13301. res |= ast_unregister_application(app4);
  13302. res |= ast_unregister_application(playmsg_app);
  13303. res |= ast_unregister_application(sayname_app);
  13304. res |= ast_custom_function_unregister(&mailbox_exists_acf);
  13305. res |= ast_custom_function_unregister(&vm_info_acf);
  13306. res |= ast_manager_unregister("VoicemailUsersList");
  13307. res |= ast_manager_unregister("VoicemailRefresh");
  13308. res |= ast_data_unregister(NULL);
  13309. #ifdef TEST_FRAMEWORK
  13310. res |= AST_TEST_UNREGISTER(test_voicemail_vmsayname);
  13311. res |= AST_TEST_UNREGISTER(test_voicemail_msgcount);
  13312. res |= AST_TEST_UNREGISTER(test_voicemail_vmuser);
  13313. res |= AST_TEST_UNREGISTER(test_voicemail_notify_endl);
  13314. res |= AST_TEST_UNREGISTER(test_voicemail_load_config);
  13315. res |= AST_TEST_UNREGISTER(test_voicemail_vm_info);
  13316. #endif
  13317. ast_cli_unregister_multiple(cli_voicemail, ARRAY_LEN(cli_voicemail));
  13318. ast_vm_unregister(vm_table.module_name);
  13319. ast_vm_greeter_unregister(vm_greeter_table.module_name);
  13320. #ifdef TEST_FRAMEWORK
  13321. ast_uninstall_vm_test_functions();
  13322. #endif
  13323. ao2_ref(inprocess_container, -1);
  13324. if (poll_thread != AST_PTHREADT_NULL)
  13325. stop_poll_thread();
  13326. mwi_subscription_tps = ast_taskprocessor_unreference(mwi_subscription_tps);
  13327. ast_unload_realtime("voicemail");
  13328. ast_unload_realtime("voicemail_data");
  13329. free_vm_users();
  13330. free_vm_zones();
  13331. return res;
  13332. }
  13333. /*!
  13334. * \brief Load the module
  13335. *
  13336. * Module loading including tests for configuration or dependencies.
  13337. * This function can return AST_MODULE_LOAD_FAILURE, AST_MODULE_LOAD_DECLINE,
  13338. * or AST_MODULE_LOAD_SUCCESS. If a dependency or environment variable fails
  13339. * tests return AST_MODULE_LOAD_FAILURE. If the module can not load the
  13340. * configuration file or other non-critical problem return
  13341. * AST_MODULE_LOAD_DECLINE. On success return AST_MODULE_LOAD_SUCCESS.
  13342. */
  13343. static int load_module(void)
  13344. {
  13345. int res;
  13346. my_umask = umask(0);
  13347. umask(my_umask);
  13348. if (!(inprocess_container = ao2_container_alloc(573, inprocess_hash_fn, inprocess_cmp_fn))) {
  13349. return AST_MODULE_LOAD_DECLINE;
  13350. }
  13351. /* compute the location of the voicemail spool directory */
  13352. snprintf(VM_SPOOL_DIR, sizeof(VM_SPOOL_DIR), "%s/voicemail/", ast_config_AST_SPOOL_DIR);
  13353. if (!(mwi_subscription_tps = ast_taskprocessor_get("app_voicemail", 0))) {
  13354. ast_log(AST_LOG_WARNING, "failed to reference mwi subscription taskprocessor. MWI will not work\n");
  13355. }
  13356. if ((res = load_config(0)))
  13357. return res;
  13358. res = ast_register_application_xml(app, vm_exec);
  13359. res |= ast_register_application_xml(app2, vm_execmain);
  13360. res |= ast_register_application_xml(app3, vm_box_exists);
  13361. res |= ast_register_application_xml(app4, vmauthenticate);
  13362. res |= ast_register_application_xml(playmsg_app, vm_playmsgexec);
  13363. res |= ast_register_application_xml(sayname_app, vmsayname_exec);
  13364. res |= ast_custom_function_register(&mailbox_exists_acf);
  13365. res |= ast_custom_function_register(&vm_info_acf);
  13366. res |= ast_manager_register_xml("VoicemailUsersList", EVENT_FLAG_CALL | EVENT_FLAG_REPORTING, manager_list_voicemail_users);
  13367. res |= ast_manager_register_xml("VoicemailRefresh", EVENT_FLAG_USER, manager_voicemail_refresh);
  13368. #ifdef TEST_FRAMEWORK
  13369. res |= AST_TEST_REGISTER(test_voicemail_vmsayname);
  13370. res |= AST_TEST_REGISTER(test_voicemail_msgcount);
  13371. res |= AST_TEST_REGISTER(test_voicemail_vmuser);
  13372. res |= AST_TEST_REGISTER(test_voicemail_notify_endl);
  13373. res |= AST_TEST_REGISTER(test_voicemail_load_config);
  13374. res |= AST_TEST_REGISTER(test_voicemail_vm_info);
  13375. #endif
  13376. res |= ast_vm_register(&vm_table);
  13377. res |= ast_vm_greeter_register(&vm_greeter_table);
  13378. if (res) {
  13379. return res;
  13380. }
  13381. ast_cli_register_multiple(cli_voicemail, ARRAY_LEN(cli_voicemail));
  13382. ast_data_register_multiple(vm_data_providers, ARRAY_LEN(vm_data_providers));
  13383. #ifdef TEST_FRAMEWORK
  13384. ast_install_vm_test_functions(vm_test_create_user, vm_test_destroy_user);
  13385. #endif
  13386. ast_realtime_require_field("voicemail", "uniqueid", RQ_UINTEGER3, 11, "password", RQ_CHAR, 10, SENTINEL);
  13387. ast_realtime_require_field("voicemail_data", "filename", RQ_CHAR, 30, "duration", RQ_UINTEGER3, 5, SENTINEL);
  13388. return res;
  13389. }
  13390. static int dialout(struct ast_channel *chan, struct ast_vm_user *vmu, char *num, char *outgoing_context)
  13391. {
  13392. int cmd = 0;
  13393. char destination[80] = "";
  13394. int retries = 0;
  13395. if (!num) {
  13396. ast_verb(3, "Destination number will be entered manually\n");
  13397. while (retries < 3 && cmd != 't') {
  13398. destination[1] = '\0';
  13399. destination[0] = cmd = ast_play_and_wait(chan, "vm-enter-num-to-call");
  13400. if (!cmd)
  13401. destination[0] = cmd = ast_play_and_wait(chan, "vm-then-pound");
  13402. if (!cmd)
  13403. destination[0] = cmd = ast_play_and_wait(chan, "vm-star-cancel");
  13404. if (!cmd) {
  13405. cmd = ast_waitfordigit(chan, 6000);
  13406. if (cmd)
  13407. destination[0] = cmd;
  13408. }
  13409. if (!cmd) {
  13410. retries++;
  13411. } else {
  13412. if (cmd < 0)
  13413. return 0;
  13414. if (cmd == '*') {
  13415. ast_verb(3, "User hit '*' to cancel outgoing call\n");
  13416. return 0;
  13417. }
  13418. if ((cmd = ast_readstring(chan, destination + strlen(destination), sizeof(destination) - 1, 6000, 10000, "#")) < 0)
  13419. retries++;
  13420. else
  13421. cmd = 't';
  13422. }
  13423. ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", cmd, cmd);
  13424. }
  13425. if (retries >= 3) {
  13426. return 0;
  13427. }
  13428. } else {
  13429. ast_verb(3, "Destination number is CID number '%s'\n", num);
  13430. ast_copy_string(destination, num, sizeof(destination));
  13431. }
  13432. if (!ast_strlen_zero(destination)) {
  13433. if (destination[strlen(destination) -1 ] == '*')
  13434. return 0;
  13435. ast_verb(3, "Placing outgoing call to extension '%s' in context '%s' from context '%s'\n", destination, outgoing_context, ast_channel_context(chan));
  13436. ast_channel_exten_set(chan, destination);
  13437. ast_channel_context_set(chan, outgoing_context);
  13438. ast_channel_priority_set(chan, 0);
  13439. return 9;
  13440. }
  13441. return 0;
  13442. }
  13443. /*!
  13444. * \brief The advanced options within a message.
  13445. * \param chan
  13446. * \param vmu
  13447. * \param vms
  13448. * \param msg
  13449. * \param option
  13450. * \param record_gain
  13451. *
  13452. * Provides handling for the play message envelope, call the person back, or reply to message.
  13453. *
  13454. * \return zero on success, -1 on error.
  13455. */
  13456. static int advanced_options(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int msg, int option, signed char record_gain)
  13457. {
  13458. int res = 0;
  13459. char filename[PATH_MAX];
  13460. struct ast_config *msg_cfg = NULL;
  13461. const char *origtime, *context;
  13462. char *name, *num;
  13463. int retries = 0;
  13464. char *cid;
  13465. struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE, };
  13466. vms->starting = 0;
  13467. make_file(vms->fn, sizeof(vms->fn), vms->curdir, msg);
  13468. /* Retrieve info from VM attribute file */
  13469. snprintf(filename, sizeof(filename), "%s.txt", vms->fn);
  13470. RETRIEVE(vms->curdir, vms->curmsg, vmu->mailbox, vmu->context);
  13471. msg_cfg = ast_config_load(filename, config_flags);
  13472. DISPOSE(vms->curdir, vms->curmsg);
  13473. if (!valid_config(msg_cfg)) {
  13474. ast_log(AST_LOG_WARNING, "No message attribute file?!! (%s)\n", filename);
  13475. return 0;
  13476. }
  13477. if (!(origtime = ast_variable_retrieve(msg_cfg, "message", "origtime"))) {
  13478. ast_config_destroy(msg_cfg);
  13479. return 0;
  13480. }
  13481. cid = ast_strdupa(ast_variable_retrieve(msg_cfg, "message", "callerid"));
  13482. context = ast_variable_retrieve(msg_cfg, "message", "context");
  13483. if (!strncasecmp("macro", context, 5)) /* Macro names in contexts are useless for our needs */
  13484. context = ast_variable_retrieve(msg_cfg, "message", "macrocontext");
  13485. switch (option) {
  13486. case 3: /* Play message envelope */
  13487. if (!res) {
  13488. res = play_message_datetime(chan, vmu, origtime, filename);
  13489. }
  13490. if (!res) {
  13491. res = play_message_callerid(chan, vms, cid, context, 0, 1);
  13492. }
  13493. res = 't';
  13494. break;
  13495. case 2: /* Call back */
  13496. if (ast_strlen_zero(cid))
  13497. break;
  13498. ast_callerid_parse(cid, &name, &num);
  13499. while ((res > -1) && (res != 't')) {
  13500. switch (res) {
  13501. case '1':
  13502. if (num) {
  13503. /* Dial the CID number */
  13504. res = dialout(chan, vmu, num, vmu->callback);
  13505. if (res) {
  13506. ast_config_destroy(msg_cfg);
  13507. return 9;
  13508. }
  13509. } else {
  13510. res = '2';
  13511. }
  13512. break;
  13513. case '2':
  13514. /* Want to enter a different number, can only do this if there's a dialout context for this user */
  13515. if (!ast_strlen_zero(vmu->dialout)) {
  13516. res = dialout(chan, vmu, NULL, vmu->dialout);
  13517. if (res) {
  13518. ast_config_destroy(msg_cfg);
  13519. return 9;
  13520. }
  13521. } else {
  13522. ast_verb(3, "Caller can not specify callback number - no dialout context available\n");
  13523. res = ast_play_and_wait(chan, "vm-sorry");
  13524. }
  13525. ast_config_destroy(msg_cfg);
  13526. return res;
  13527. case '*':
  13528. res = 't';
  13529. break;
  13530. case '3':
  13531. case '4':
  13532. case '5':
  13533. case '6':
  13534. case '7':
  13535. case '8':
  13536. case '9':
  13537. case '0':
  13538. res = ast_play_and_wait(chan, "vm-sorry");
  13539. retries++;
  13540. break;
  13541. default:
  13542. if (num) {
  13543. ast_verb(3, "Confirm CID number '%s' is number to use for callback\n", num);
  13544. res = ast_play_and_wait(chan, "vm-num-i-have");
  13545. if (!res)
  13546. res = play_message_callerid(chan, vms, num, vmu->context, 1, 1);
  13547. if (!res)
  13548. res = ast_play_and_wait(chan, "vm-tocallnum");
  13549. /* Only prompt for a caller-specified number if there is a dialout context specified */
  13550. if (!ast_strlen_zero(vmu->dialout)) {
  13551. if (!res)
  13552. res = ast_play_and_wait(chan, "vm-calldiffnum");
  13553. }
  13554. } else {
  13555. res = ast_play_and_wait(chan, "vm-nonumber");
  13556. if (!ast_strlen_zero(vmu->dialout)) {
  13557. if (!res)
  13558. res = ast_play_and_wait(chan, "vm-toenternumber");
  13559. }
  13560. }
  13561. if (!res) {
  13562. res = ast_play_and_wait(chan, "vm-star-cancel");
  13563. }
  13564. if (!res) {
  13565. res = ast_waitfordigit(chan, 6000);
  13566. }
  13567. if (!res) {
  13568. retries++;
  13569. if (retries > 3) {
  13570. res = 't';
  13571. }
  13572. }
  13573. ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", res, res);
  13574. break;
  13575. }
  13576. if (res == 't')
  13577. res = 0;
  13578. else if (res == '*')
  13579. res = -1;
  13580. }
  13581. break;
  13582. case 1: /* Reply */
  13583. /* Send reply directly to sender */
  13584. if (ast_strlen_zero(cid))
  13585. break;
  13586. ast_callerid_parse(cid, &name, &num);
  13587. if (!num) {
  13588. ast_verb(3, "No CID number available, no reply sent\n");
  13589. if (!res)
  13590. res = ast_play_and_wait(chan, "vm-nonumber");
  13591. ast_config_destroy(msg_cfg);
  13592. return res;
  13593. } else {
  13594. struct ast_vm_user vmu2;
  13595. if (find_user(&vmu2, vmu->context, num)) {
  13596. struct leave_vm_options leave_options;
  13597. char mailbox[AST_MAX_EXTENSION * 2 + 2];
  13598. snprintf(mailbox, sizeof(mailbox), "%s@%s", num, vmu->context);
  13599. ast_verb(3, "Leaving voicemail for '%s' in context '%s'\n", num, vmu->context);
  13600. memset(&leave_options, 0, sizeof(leave_options));
  13601. leave_options.record_gain = record_gain;
  13602. res = leave_voicemail(chan, mailbox, &leave_options);
  13603. if (!res)
  13604. res = 't';
  13605. ast_config_destroy(msg_cfg);
  13606. return res;
  13607. } else {
  13608. /* Sender has no mailbox, can't reply */
  13609. ast_verb(3, "No mailbox number '%s' in context '%s', no reply sent\n", num, vmu->context);
  13610. ast_play_and_wait(chan, "vm-nobox");
  13611. res = 't';
  13612. ast_config_destroy(msg_cfg);
  13613. return res;
  13614. }
  13615. }
  13616. res = 0;
  13617. break;
  13618. }
  13619. ast_config_destroy(msg_cfg);
  13620. #ifndef IMAP_STORAGE
  13621. if (!res) {
  13622. make_file(vms->fn, sizeof(vms->fn), vms->curdir, msg);
  13623. vms->heard[msg] = 1;
  13624. res = wait_file(chan, vms, vms->fn);
  13625. }
  13626. #endif
  13627. return res;
  13628. }
  13629. static int play_record_review(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime, char *fmt,
  13630. int outsidecaller, struct ast_vm_user *vmu, int *duration, int *sound_duration, const char *unlockdir,
  13631. signed char record_gain, struct vm_state *vms, char *flag, const char *msg_id)
  13632. {
  13633. /* Record message & let caller review or re-record it, or set options if applicable */
  13634. int res = 0;
  13635. int cmd = 0;
  13636. int max_attempts = 3;
  13637. int attempts = 0;
  13638. int recorded = 0;
  13639. int msg_exists = 0;
  13640. signed char zero_gain = 0;
  13641. char tempfile[PATH_MAX];
  13642. char *acceptdtmf = "#";
  13643. char *canceldtmf = "";
  13644. int canceleddtmf = 0;
  13645. /* Note that urgent and private are for flagging messages as such in the future */
  13646. /* barf if no pointer passed to store duration in */
  13647. if (duration == NULL) {
  13648. ast_log(AST_LOG_WARNING, "Error play_record_review called without duration pointer\n");
  13649. return -1;
  13650. }
  13651. if (!outsidecaller)
  13652. snprintf(tempfile, sizeof(tempfile), "%s.tmp", recordfile);
  13653. else
  13654. ast_copy_string(tempfile, recordfile, sizeof(tempfile));
  13655. cmd = '3'; /* Want to start by recording */
  13656. while ((cmd >= 0) && (cmd != 't')) {
  13657. switch (cmd) {
  13658. case '1':
  13659. if (!msg_exists) {
  13660. /* In this case, 1 is to record a message */
  13661. cmd = '3';
  13662. break;
  13663. } else {
  13664. /* Otherwise 1 is to save the existing message */
  13665. ast_verb(3, "Saving message as is\n");
  13666. if (!outsidecaller)
  13667. ast_filerename(tempfile, recordfile, NULL);
  13668. ast_stream_and_wait(chan, "vm-msgsaved", "");
  13669. if (!outsidecaller) {
  13670. /* Saves to IMAP server only if imapgreeting=yes */
  13671. STORE(recordfile, vmu->mailbox, vmu->context, -1, chan, vmu, fmt, *duration, vms, flag, msg_id);
  13672. DISPOSE(recordfile, -1);
  13673. }
  13674. cmd = 't';
  13675. return res;
  13676. }
  13677. case '2':
  13678. /* Review */
  13679. ast_verb(3, "Reviewing the message\n");
  13680. cmd = ast_stream_and_wait(chan, tempfile, AST_DIGIT_ANY);
  13681. break;
  13682. case '3':
  13683. msg_exists = 0;
  13684. /* Record */
  13685. if (recorded == 1)
  13686. ast_verb(3, "Re-recording the message\n");
  13687. else
  13688. ast_verb(3, "Recording the message\n");
  13689. if (recorded && outsidecaller) {
  13690. cmd = ast_play_and_wait(chan, INTRO);
  13691. cmd = ast_play_and_wait(chan, "beep");
  13692. }
  13693. recorded = 1;
  13694. /* After an attempt has been made to record message, we have to take care of INTRO and beep for incoming messages, but not for greetings */
  13695. if (record_gain)
  13696. ast_channel_setoption(chan, AST_OPTION_RXGAIN, &record_gain, sizeof(record_gain), 0);
  13697. if (ast_test_flag(vmu, VM_OPERATOR))
  13698. canceldtmf = "0";
  13699. cmd = ast_play_and_record_full(chan, playfile, tempfile, maxtime, fmt, duration, sound_duration, 0, silencethreshold, maxsilence, unlockdir, acceptdtmf, canceldtmf, 0, AST_RECORD_IF_EXISTS_OVERWRITE);
  13700. if (strchr(canceldtmf, cmd)) {
  13701. /* need this flag here to distinguish between pressing '0' during message recording or after */
  13702. canceleddtmf = 1;
  13703. }
  13704. if (record_gain)
  13705. ast_channel_setoption(chan, AST_OPTION_RXGAIN, &zero_gain, sizeof(zero_gain), 0);
  13706. if (cmd == -1) {
  13707. /* User has hung up, no options to give */
  13708. if (!outsidecaller) {
  13709. /* user was recording a greeting and they hung up, so let's delete the recording. */
  13710. ast_filedelete(tempfile, NULL);
  13711. }
  13712. return cmd;
  13713. }
  13714. if (cmd == '0') {
  13715. break;
  13716. } else if (cmd == '*') {
  13717. break;
  13718. #if 0
  13719. } else if (vmu->review && sound_duration && (*sound_duration < 5)) {
  13720. /* Message is too short */
  13721. ast_verb(3, "Message too short\n");
  13722. cmd = ast_play_and_wait(chan, "vm-tooshort");
  13723. cmd = ast_filedelete(tempfile, NULL);
  13724. break;
  13725. } else if (vmu->review && (cmd == 2 && sound_duration && *sound_duration < (maxsilence + 3))) {
  13726. /* Message is all silence */
  13727. ast_verb(3, "Nothing recorded\n");
  13728. cmd = ast_filedelete(tempfile, NULL);
  13729. cmd = ast_play_and_wait(chan, "vm-nothingrecorded");
  13730. if (!cmd)
  13731. cmd = ast_play_and_wait(chan, "vm-speakup");
  13732. break;
  13733. #endif
  13734. } else {
  13735. /* If all is well, a message exists */
  13736. msg_exists = 1;
  13737. cmd = 0;
  13738. }
  13739. break;
  13740. case '4':
  13741. if (outsidecaller) { /* only mark vm messages */
  13742. /* Mark Urgent */
  13743. if ((flag && ast_strlen_zero(flag)) || (!ast_strlen_zero(flag) && strcmp(flag, "Urgent"))) {
  13744. ast_verb(3, "marking message as Urgent\n");
  13745. res = ast_play_and_wait(chan, "vm-marked-urgent");
  13746. strcpy(flag, "Urgent");
  13747. } else if (flag) {
  13748. ast_verb(3, "UNmarking message as Urgent\n");
  13749. res = ast_play_and_wait(chan, "vm-marked-nonurgent");
  13750. strcpy(flag, "");
  13751. } else {
  13752. ast_play_and_wait(chan, "vm-sorry");
  13753. }
  13754. cmd = 0;
  13755. } else {
  13756. cmd = ast_play_and_wait(chan, "vm-sorry");
  13757. }
  13758. break;
  13759. case '5':
  13760. case '6':
  13761. case '7':
  13762. case '8':
  13763. case '9':
  13764. case '*':
  13765. case '#':
  13766. cmd = ast_play_and_wait(chan, "vm-sorry");
  13767. break;
  13768. #if 0
  13769. /* XXX Commented out for the moment because of the dangers of deleting
  13770. a message while recording (can put the message numbers out of sync) */
  13771. case '*':
  13772. /* Cancel recording, delete message, offer to take another message*/
  13773. cmd = ast_play_and_wait(chan, "vm-deleted");
  13774. cmd = ast_filedelete(tempfile, NULL);
  13775. if (outsidecaller) {
  13776. res = vm_exec(chan, NULL);
  13777. return res;
  13778. }
  13779. else
  13780. return 1;
  13781. #endif
  13782. case '0':
  13783. if (!ast_test_flag(vmu, VM_OPERATOR) || (!canceleddtmf && !outsidecaller)) {
  13784. cmd = ast_play_and_wait(chan, "vm-sorry");
  13785. break;
  13786. }
  13787. if (msg_exists || recorded) {
  13788. cmd = ast_play_and_wait(chan, "vm-saveoper");
  13789. if (!cmd)
  13790. cmd = ast_waitfordigit(chan, 3000);
  13791. if (cmd == '1') {
  13792. ast_filerename(tempfile, recordfile, NULL);
  13793. ast_play_and_wait(chan, "vm-msgsaved");
  13794. cmd = '0';
  13795. } else if (cmd == '4') {
  13796. if (flag) {
  13797. ast_play_and_wait(chan, "vm-marked-urgent");
  13798. strcpy(flag, "Urgent");
  13799. }
  13800. ast_play_and_wait(chan, "vm-msgsaved");
  13801. cmd = '0';
  13802. } else {
  13803. ast_play_and_wait(chan, "vm-deleted");
  13804. DELETE(tempfile, -1, tempfile, vmu);
  13805. DISPOSE(tempfile, -1);
  13806. cmd = '0';
  13807. }
  13808. }
  13809. return cmd;
  13810. default:
  13811. /* If the caller is an ouside caller, and the review option is enabled,
  13812. allow them to review the message, but let the owner of the box review
  13813. their OGM's */
  13814. if (outsidecaller && !ast_test_flag(vmu, VM_REVIEW))
  13815. return cmd;
  13816. if (msg_exists) {
  13817. cmd = ast_play_and_wait(chan, "vm-review");
  13818. if (!cmd && outsidecaller) {
  13819. if ((flag && ast_strlen_zero(flag)) || (!ast_strlen_zero(flag) && strcmp(flag, "Urgent"))) {
  13820. cmd = ast_play_and_wait(chan, "vm-review-urgent");
  13821. } else if (flag) {
  13822. cmd = ast_play_and_wait(chan, "vm-review-nonurgent");
  13823. }
  13824. }
  13825. } else {
  13826. cmd = ast_play_and_wait(chan, "vm-torerecord");
  13827. if (!cmd)
  13828. cmd = ast_waitfordigit(chan, 600);
  13829. }
  13830. if (!cmd && outsidecaller && ast_test_flag(vmu, VM_OPERATOR)) {
  13831. cmd = ast_play_and_wait(chan, "vm-reachoper");
  13832. if (!cmd)
  13833. cmd = ast_waitfordigit(chan, 600);
  13834. }
  13835. #if 0
  13836. if (!cmd)
  13837. cmd = ast_play_and_wait(chan, "vm-tocancelmsg");
  13838. #endif
  13839. if (!cmd)
  13840. cmd = ast_waitfordigit(chan, 6000);
  13841. if (!cmd) {
  13842. attempts++;
  13843. }
  13844. if (attempts > max_attempts) {
  13845. cmd = 't';
  13846. }
  13847. }
  13848. }
  13849. if (!outsidecaller && (cmd == -1 || cmd == 't')) {
  13850. /* Hang up or timeout, so delete the recording. */
  13851. ast_filedelete(tempfile, NULL);
  13852. }
  13853. if (cmd != 't' && outsidecaller)
  13854. ast_play_and_wait(chan, "vm-goodbye");
  13855. return cmd;
  13856. }
  13857. static struct ast_vm_msg_snapshot *vm_msg_snapshot_alloc(void)
  13858. {
  13859. struct ast_vm_msg_snapshot *msg_snapshot;
  13860. if (!(msg_snapshot = ast_calloc(1, sizeof(*msg_snapshot)))) {
  13861. return NULL;
  13862. }
  13863. if (ast_string_field_init(msg_snapshot, 512)) {
  13864. ast_free(msg_snapshot);
  13865. return NULL;
  13866. }
  13867. return msg_snapshot;
  13868. }
  13869. static struct ast_vm_msg_snapshot *vm_msg_snapshot_destroy(struct ast_vm_msg_snapshot *msg_snapshot)
  13870. {
  13871. ast_string_field_free_memory(msg_snapshot);
  13872. ast_free(msg_snapshot);
  13873. return NULL;
  13874. }
  13875. #ifdef TEST_FRAMEWORK
  13876. static int vm_test_destroy_user(const char *context, const char *mailbox)
  13877. {
  13878. struct ast_vm_user *vmu;
  13879. AST_LIST_LOCK(&users);
  13880. AST_LIST_TRAVERSE_SAFE_BEGIN(&users, vmu, list) {
  13881. if (!strcmp(context, vmu->context)
  13882. && !strcmp(mailbox, vmu->mailbox)) {
  13883. AST_LIST_REMOVE_CURRENT(list);
  13884. ast_free(vmu);
  13885. break;
  13886. }
  13887. }
  13888. AST_LIST_TRAVERSE_SAFE_END
  13889. AST_LIST_UNLOCK(&users);
  13890. return 0;
  13891. }
  13892. static int vm_test_create_user(const char *context, const char *mailbox)
  13893. {
  13894. struct ast_vm_user *vmu;
  13895. if (!(vmu = find_or_create(context, mailbox))) {
  13896. return -1;
  13897. }
  13898. populate_defaults(vmu);
  13899. return 0;
  13900. }
  13901. #endif
  13902. /*!
  13903. * \brief Create and store off all the msgs in an open mailbox
  13904. *
  13905. * \note TODO XXX This function should work properly for all
  13906. * voicemail storage options, but is far more expensive for
  13907. * ODBC at the moment. This is because the RETRIEVE macro
  13908. * not only pulls out the message's meta data file from the
  13909. * database, but also the actual audio for each message, temporarily
  13910. * writing it to the file system. This is an area that needs
  13911. * to be made more efficient.
  13912. */
  13913. static int vm_msg_snapshot_create(struct ast_vm_user *vmu,
  13914. struct vm_state *vms,
  13915. struct ast_vm_mailbox_snapshot *mailbox_snapshot,
  13916. int snapshot_index,
  13917. int mailbox_index,
  13918. int descending,
  13919. enum ast_vm_snapshot_sort_val sort_val)
  13920. {
  13921. struct ast_vm_msg_snapshot *msg_snapshot;
  13922. struct ast_vm_msg_snapshot *msg_snapshot_tmp;
  13923. struct ast_config *msg_cfg;
  13924. struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
  13925. char filename[PATH_MAX];
  13926. const char *value;
  13927. for (vms->curmsg = 0; vms->curmsg <= vms->lastmsg; vms->curmsg++) {
  13928. int inserted = 0;
  13929. /* Find the msg */
  13930. make_file(vms->fn, sizeof(vms->fn), vms->curdir, vms->curmsg);
  13931. snprintf(filename, sizeof(filename), "%s.txt", vms->fn);
  13932. RETRIEVE(vms->curdir, vms->curmsg, vmu->mailbox, vmu->context);
  13933. msg_cfg = ast_config_load(filename, config_flags);
  13934. if (!msg_cfg || msg_cfg == CONFIG_STATUS_FILEINVALID) {
  13935. DISPOSE(vms->curdir, vms->curmsg);
  13936. continue;
  13937. }
  13938. /* Create the snapshot object */
  13939. if (!(msg_snapshot = vm_msg_snapshot_alloc())) {
  13940. ast_config_destroy(msg_cfg);
  13941. return -1;
  13942. }
  13943. /* Fill in the snapshot object */
  13944. if ((value = ast_variable_retrieve(msg_cfg, "message", "msg_id"))) {
  13945. ast_string_field_set(msg_snapshot, msg_id, value);
  13946. } else {
  13947. /* Message snapshots *really* should have a
  13948. * message ID. Add one to the message config
  13949. * if it does not already exist
  13950. */
  13951. char id[MSG_ID_LEN];
  13952. if (!(add_message_id(msg_cfg, vms->curdir, vms->curmsg,
  13953. filename, id, sizeof(id), vmu, mailbox_index))) {
  13954. ast_string_field_set(msg_snapshot, msg_id, id);
  13955. } else {
  13956. ast_log(LOG_WARNING, "Unable to create a message ID for message %s/%d\n", vms->curdir, vms->curmsg);
  13957. }
  13958. }
  13959. if ((value = ast_variable_retrieve(msg_cfg, "message", "callerid"))) {
  13960. ast_string_field_set(msg_snapshot, callerid, value);
  13961. }
  13962. if ((value = ast_variable_retrieve(msg_cfg, "message", "callerchan"))) {
  13963. ast_string_field_set(msg_snapshot, callerchan, value);
  13964. }
  13965. if ((value = ast_variable_retrieve(msg_cfg, "message", "exten"))) {
  13966. ast_string_field_set(msg_snapshot, exten, value);
  13967. }
  13968. if ((value = ast_variable_retrieve(msg_cfg, "message", "origdate"))) {
  13969. ast_string_field_set(msg_snapshot, origdate, value);
  13970. }
  13971. if ((value = ast_variable_retrieve(msg_cfg, "message", "origtime"))) {
  13972. ast_string_field_set(msg_snapshot, origtime, value);
  13973. }
  13974. if ((value = ast_variable_retrieve(msg_cfg, "message", "duration"))) {
  13975. ast_string_field_set(msg_snapshot, duration, value);
  13976. }
  13977. if ((value = ast_variable_retrieve(msg_cfg, "message", "flag"))) {
  13978. ast_string_field_set(msg_snapshot, flag, value);
  13979. }
  13980. msg_snapshot->msg_number = vms->curmsg;
  13981. ast_string_field_set(msg_snapshot, folder_name, mailbox_folders[mailbox_index]);
  13982. /* store msg snapshot in mailbox snapshot */
  13983. switch (sort_val) {
  13984. default:
  13985. case AST_VM_SNAPSHOT_SORT_BY_ID:
  13986. if (descending) {
  13987. AST_LIST_INSERT_HEAD(&mailbox_snapshot->snapshots[snapshot_index], msg_snapshot, msg);
  13988. } else {
  13989. AST_LIST_INSERT_TAIL(&mailbox_snapshot->snapshots[snapshot_index], msg_snapshot, msg);
  13990. }
  13991. inserted = 1;
  13992. break;
  13993. case AST_VM_SNAPSHOT_SORT_BY_TIME:
  13994. AST_LIST_TRAVERSE_SAFE_BEGIN(&mailbox_snapshot->snapshots[snapshot_index], msg_snapshot_tmp, msg) {
  13995. int val = strcmp(msg_snapshot->origtime, msg_snapshot_tmp->origtime);
  13996. if (descending && val >= 0) {
  13997. AST_LIST_INSERT_BEFORE_CURRENT(msg_snapshot, msg);
  13998. inserted = 1;
  13999. break;
  14000. } else if (!descending && val <= 0) {
  14001. AST_LIST_INSERT_BEFORE_CURRENT(msg_snapshot, msg);
  14002. inserted = 1;
  14003. break;
  14004. }
  14005. }
  14006. AST_LIST_TRAVERSE_SAFE_END;
  14007. break;
  14008. }
  14009. if (!inserted) {
  14010. AST_LIST_INSERT_TAIL(&mailbox_snapshot->snapshots[snapshot_index], msg_snapshot, msg);
  14011. }
  14012. mailbox_snapshot->total_msg_num++;
  14013. /* cleanup configs and msg */
  14014. ast_config_destroy(msg_cfg);
  14015. DISPOSE(vms->curdir, vms->curmsg);
  14016. }
  14017. return 0;
  14018. }
  14019. static struct ast_vm_mailbox_snapshot *vm_mailbox_snapshot_create(const char *mailbox,
  14020. const char *context,
  14021. const char *folder,
  14022. int descending,
  14023. enum ast_vm_snapshot_sort_val sort_val,
  14024. int combine_INBOX_and_OLD)
  14025. {
  14026. struct ast_vm_mailbox_snapshot *mailbox_snapshot;
  14027. struct vm_state vms;
  14028. struct ast_vm_user *vmu = NULL, vmus;
  14029. int res;
  14030. int i;
  14031. int this_index_only = -1;
  14032. int open = 0;
  14033. int inbox_index = get_folder_by_name("INBOX");
  14034. int old_index = get_folder_by_name("Old");
  14035. int urgent_index = get_folder_by_name("Urgent");
  14036. if (ast_strlen_zero(mailbox)) {
  14037. ast_log(LOG_WARNING, "Cannot create a mailbox snapshot since no mailbox was specified\n");
  14038. return NULL;
  14039. }
  14040. memset(&vmus, 0, sizeof(vmus));
  14041. if (!(ast_strlen_zero(folder))) {
  14042. /* find the folder index */
  14043. for (i = 0; i < ARRAY_LEN(mailbox_folders); i++) {
  14044. if (!strcasecmp(mailbox_folders[i], folder)) {
  14045. this_index_only = i;
  14046. break;
  14047. }
  14048. }
  14049. if (this_index_only == -1) {
  14050. /* Folder was specified and it did not match any folder in our list */
  14051. return NULL;
  14052. }
  14053. }
  14054. if (!(vmu = find_user(&vmus, context, mailbox))) {
  14055. ast_log(AST_LOG_WARNING, "Failed to create mailbox snapshot for unknown voicemail user %s@%s\n", mailbox, context);
  14056. return NULL;
  14057. }
  14058. if (!(mailbox_snapshot = ast_calloc(1, sizeof(*mailbox_snapshot)))) {
  14059. ast_log(AST_LOG_ERROR, "Failed to allocate memory for mailbox snapshot\n");
  14060. return NULL;
  14061. }
  14062. if (!(mailbox_snapshot->snapshots = ast_calloc(ARRAY_LEN(mailbox_folders), sizeof(*mailbox_snapshot->snapshots)))) {
  14063. ast_free(mailbox_snapshot);
  14064. return NULL;
  14065. }
  14066. mailbox_snapshot->folders = ARRAY_LEN(mailbox_folders);
  14067. for (i = 0; i < mailbox_snapshot->folders; i++) {
  14068. int msg_folder_index = i;
  14069. /* We want this message in the snapshot if any of the following:
  14070. * No folder was specified.
  14071. * The specified folder matches the current folder.
  14072. * The specified folder is INBOX AND we were asked to combine messages AND the current folder is either Old or Urgent.
  14073. */
  14074. if (!(this_index_only == -1 || this_index_only == i || (this_index_only == inbox_index && combine_INBOX_and_OLD && (i == old_index || i == urgent_index)))) {
  14075. continue;
  14076. }
  14077. /* Make sure that Old or Urgent messages are marked as being in INBOX. */
  14078. if (combine_INBOX_and_OLD && (i == old_index || i == urgent_index)) {
  14079. msg_folder_index = inbox_index;
  14080. }
  14081. memset(&vms, 0, sizeof(vms));
  14082. ast_copy_string(vms.username, mailbox, sizeof(vms.username));
  14083. vms.lastmsg = -1;
  14084. open = 0;
  14085. /* open the mailbox state */
  14086. if ((res = open_mailbox(&vms, vmu, i)) < 0) {
  14087. ast_log(LOG_WARNING, "Could not open mailbox %s\n", mailbox);
  14088. goto snapshot_cleanup;
  14089. }
  14090. open = 1;
  14091. /* Iterate through each msg, storing off info */
  14092. if (vms.lastmsg != -1) {
  14093. if ((vm_msg_snapshot_create(vmu, &vms, mailbox_snapshot, msg_folder_index, i, descending, sort_val))) {
  14094. ast_log(LOG_WARNING, "Failed to create msg snapshots for %s@%s\n", mailbox, context);
  14095. goto snapshot_cleanup;
  14096. }
  14097. }
  14098. /* close mailbox */
  14099. if ((res = close_mailbox(&vms, vmu) == ERROR_LOCK_PATH)) {
  14100. goto snapshot_cleanup;
  14101. }
  14102. open = 0;
  14103. }
  14104. snapshot_cleanup:
  14105. if (vmu && open) {
  14106. close_mailbox(&vms, vmu);
  14107. }
  14108. #ifdef IMAP_STORAGE
  14109. if (vmu) {
  14110. vmstate_delete(&vms);
  14111. }
  14112. #endif
  14113. return mailbox_snapshot;
  14114. }
  14115. static struct ast_vm_mailbox_snapshot *vm_mailbox_snapshot_destroy(struct ast_vm_mailbox_snapshot *mailbox_snapshot)
  14116. {
  14117. int i;
  14118. struct ast_vm_msg_snapshot *msg_snapshot;
  14119. for (i = 0; i < mailbox_snapshot->folders; i++) {
  14120. while ((msg_snapshot = AST_LIST_REMOVE_HEAD(&mailbox_snapshot->snapshots[i], msg))) {
  14121. msg_snapshot = vm_msg_snapshot_destroy(msg_snapshot);
  14122. }
  14123. }
  14124. ast_free(mailbox_snapshot->snapshots);
  14125. ast_free(mailbox_snapshot);
  14126. return NULL;
  14127. }
  14128. /*!
  14129. * \brief common bounds checking and existence check for Voicemail API functions.
  14130. *
  14131. * \details
  14132. * This is called by vm_msg_move, vm_msg_remove, and vm_msg_forward to
  14133. * ensure that data passed in are valid. This ensures that given the
  14134. * desired message IDs, they can be found.
  14135. *
  14136. * \param vms The voicemail state corresponding to an open mailbox
  14137. * \param msg_ids An array of message identifiers
  14138. * \param num_msgs The number of identifiers in msg_ids
  14139. * \param msg_nums [out] The message indexes corresponding to the given
  14140. * \param vmu
  14141. * message IDs
  14142. * \pre vms must have open_mailbox() called on it prior to this function.
  14143. *
  14144. * \retval -1 Failure
  14145. * \retval 0 Success
  14146. */
  14147. static int message_range_and_existence_check(struct vm_state *vms, const char *msg_ids [], size_t num_msgs, int *msg_nums, struct ast_vm_user *vmu)
  14148. {
  14149. int i;
  14150. int res = 0;
  14151. for (i = 0; i < num_msgs; ++i) {
  14152. const char *msg_id = msg_ids[i];
  14153. int found = 0;
  14154. for (vms->curmsg = 0; vms->curmsg <= vms->lastmsg; vms->curmsg++) {
  14155. const char *other_msg_id;
  14156. char filename[PATH_MAX];
  14157. struct ast_config *msg_cfg;
  14158. struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
  14159. make_file(vms->fn, sizeof(vms->fn), vms->curdir, vms->curmsg);
  14160. snprintf(filename, sizeof(filename), "%s.txt", vms->fn);
  14161. RETRIEVE(vms->curdir, vms->curmsg, vmu->mailbox, vmu->context);
  14162. msg_cfg = ast_config_load(filename, config_flags);
  14163. if (!msg_cfg || msg_cfg == CONFIG_STATUS_FILEINVALID) {
  14164. DISPOSE(vms->curdir, vms->curmsg);
  14165. res = -1;
  14166. goto done;
  14167. }
  14168. other_msg_id = ast_variable_retrieve(msg_cfg, "message", "msg_id");
  14169. if (!ast_strlen_zero(other_msg_id) && !strcmp(other_msg_id, msg_id)) {
  14170. /* Message found. We can get out of this inner loop
  14171. * and move on to the next message to find
  14172. */
  14173. found = 1;
  14174. msg_nums[i] = vms->curmsg;
  14175. ast_config_destroy(msg_cfg);
  14176. DISPOSE(vms->curdir, vms->curmsg);
  14177. break;
  14178. }
  14179. ast_config_destroy(msg_cfg);
  14180. DISPOSE(vms->curdir, vms->curmsg);
  14181. }
  14182. if (!found) {
  14183. /* If we can't find one of the message IDs requested, then OH NO! */
  14184. res = -1;
  14185. goto done;
  14186. }
  14187. }
  14188. done:
  14189. return res;
  14190. }
  14191. static void notify_new_state(struct ast_vm_user *vmu)
  14192. {
  14193. int new = 0, old = 0, urgent = 0;
  14194. char ext_context[1024];
  14195. snprintf(ext_context, sizeof(ext_context), "%s@%s", vmu->mailbox, vmu->context);
  14196. run_externnotify(vmu->context, vmu->mailbox, NULL);
  14197. ast_app_inboxcount2(ext_context, &urgent, &new, &old);
  14198. queue_mwi_event(NULL, ext_context, urgent, new, old);
  14199. }
  14200. static int vm_msg_forward(const char *from_mailbox,
  14201. const char *from_context,
  14202. const char *from_folder,
  14203. const char *to_mailbox,
  14204. const char *to_context,
  14205. const char *to_folder,
  14206. size_t num_msgs,
  14207. const char *msg_ids [],
  14208. int delete_old)
  14209. {
  14210. struct vm_state from_vms;
  14211. struct ast_vm_user *vmu = NULL, vmus;
  14212. struct ast_vm_user *to_vmu = NULL, to_vmus;
  14213. struct ast_config *msg_cfg;
  14214. struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
  14215. char filename[PATH_MAX];
  14216. int from_folder_index;
  14217. int open = 0;
  14218. int res = 0;
  14219. int i;
  14220. int *msg_nums;
  14221. if (ast_strlen_zero(from_mailbox) || ast_strlen_zero(to_mailbox)) {
  14222. ast_log(LOG_WARNING, "Cannot forward message because either the from or to mailbox was not specified\n");
  14223. return -1;
  14224. }
  14225. if (!num_msgs) {
  14226. ast_log(LOG_WARNING, "Invalid number of messages specified to forward: %zu\n", num_msgs);
  14227. return -1;
  14228. }
  14229. if (ast_strlen_zero(from_folder) || ast_strlen_zero(to_folder)) {
  14230. ast_log(LOG_WARNING, "Cannot forward message because the from_folder or to_folder was not specified\n");
  14231. return -1;
  14232. }
  14233. memset(&vmus, 0, sizeof(vmus));
  14234. memset(&to_vmus, 0, sizeof(to_vmus));
  14235. memset(&from_vms, 0, sizeof(from_vms));
  14236. from_folder_index = get_folder_by_name(from_folder);
  14237. if (from_folder_index == -1) {
  14238. return -1;
  14239. }
  14240. if (get_folder_by_name(to_folder) == -1) {
  14241. return -1;
  14242. }
  14243. if (!(vmu = find_user(&vmus, from_context, from_mailbox))) {
  14244. ast_log(LOG_WARNING, "Can't find voicemail user to forward from (%s@%s)\n", from_mailbox, from_context);
  14245. return -1;
  14246. }
  14247. if (!(to_vmu = find_user(&to_vmus, to_context, to_mailbox))) {
  14248. ast_log(LOG_WARNING, "Can't find voicemail user to forward to (%s@%s)\n", to_mailbox, to_context);
  14249. return -1;
  14250. }
  14251. ast_copy_string(from_vms.username, from_mailbox, sizeof(from_vms.username));
  14252. from_vms.lastmsg = -1;
  14253. open = 0;
  14254. /* open the mailbox state */
  14255. if ((res = open_mailbox(&from_vms, vmu, from_folder_index)) < 0) {
  14256. ast_log(LOG_WARNING, "Could not open mailbox %s\n", from_mailbox);
  14257. res = -1;
  14258. goto vm_forward_cleanup;
  14259. }
  14260. open = 1;
  14261. if ((from_vms.lastmsg + 1) < num_msgs) {
  14262. ast_log(LOG_WARNING, "Folder %s has less than %zu messages\n", from_folder, num_msgs);
  14263. res = -1;
  14264. goto vm_forward_cleanup;
  14265. }
  14266. msg_nums = ast_alloca(sizeof(int) * num_msgs);
  14267. if ((res = message_range_and_existence_check(&from_vms, msg_ids, num_msgs, msg_nums, vmu) < 0)) {
  14268. goto vm_forward_cleanup;
  14269. }
  14270. /* Now we actually forward the messages */
  14271. for (i = 0; i < num_msgs; i++) {
  14272. int cur_msg = msg_nums[i];
  14273. int duration = 0;
  14274. const char *value;
  14275. make_file(from_vms.fn, sizeof(from_vms.fn), from_vms.curdir, cur_msg);
  14276. snprintf(filename, sizeof(filename), "%s.txt", from_vms.fn);
  14277. RETRIEVE(from_vms.curdir, cur_msg, vmu->mailbox, vmu->context);
  14278. msg_cfg = ast_config_load(filename, config_flags);
  14279. /* XXX This likely will not fail since we previously ensured that the
  14280. * message we are looking for exists. However, there still could be some
  14281. * circumstance where this fails, so atomicity is not guaranteed.
  14282. */
  14283. if (!msg_cfg || msg_cfg == CONFIG_STATUS_FILEINVALID) {
  14284. DISPOSE(from_vms.curdir, cur_msg);
  14285. continue;
  14286. }
  14287. if ((value = ast_variable_retrieve(msg_cfg, "message", "duration"))) {
  14288. duration = atoi(value);
  14289. }
  14290. copy_message(NULL, vmu, from_folder_index, cur_msg, duration, to_vmu, vmfmts, from_vms.curdir, "", to_folder);
  14291. if (delete_old) {
  14292. from_vms.deleted[cur_msg] = 1;
  14293. }
  14294. ast_config_destroy(msg_cfg);
  14295. DISPOSE(from_vms.curdir, cur_msg);
  14296. }
  14297. /* close mailbox */
  14298. if ((res = close_mailbox(&from_vms, vmu) == ERROR_LOCK_PATH)) {
  14299. res = -1;
  14300. goto vm_forward_cleanup;
  14301. }
  14302. open = 0;
  14303. vm_forward_cleanup:
  14304. if (vmu && open) {
  14305. close_mailbox(&from_vms, vmu);
  14306. }
  14307. #ifdef IMAP_STORAGE
  14308. if (vmu) {
  14309. vmstate_delete(&from_vms);
  14310. }
  14311. #endif
  14312. if (!res) {
  14313. notify_new_state(to_vmu);
  14314. }
  14315. return res;
  14316. }
  14317. static int vm_msg_move(const char *mailbox,
  14318. const char *context,
  14319. size_t num_msgs,
  14320. const char *oldfolder,
  14321. const char *old_msg_ids [],
  14322. const char *newfolder)
  14323. {
  14324. struct vm_state vms;
  14325. struct ast_vm_user *vmu = NULL, vmus;
  14326. int old_folder_index;
  14327. int new_folder_index;
  14328. int open = 0;
  14329. int res = 0;
  14330. int i;
  14331. int *old_msg_nums;
  14332. if (ast_strlen_zero(mailbox)) {
  14333. ast_log(LOG_WARNING, "Cannot move message because no mailbox was specified\n");
  14334. return -1;
  14335. }
  14336. if (!num_msgs) {
  14337. ast_log(LOG_WARNING, "Invalid number of messages specified to move: %zu\n", num_msgs);
  14338. return -1;
  14339. }
  14340. if (ast_strlen_zero(oldfolder) || ast_strlen_zero(newfolder)) {
  14341. ast_log(LOG_WARNING, "Cannot move message because either oldfolder or newfolder was not specified\n");
  14342. return -1;
  14343. }
  14344. old_folder_index = get_folder_by_name(oldfolder);
  14345. new_folder_index = get_folder_by_name(newfolder);
  14346. memset(&vmus, 0, sizeof(vmus));
  14347. memset(&vms, 0, sizeof(vms));
  14348. if (old_folder_index == -1 || new_folder_index == -1) {
  14349. return -1;
  14350. }
  14351. if (!(vmu = find_user(&vmus, context, mailbox))) {
  14352. return -1;
  14353. }
  14354. ast_copy_string(vms.username, mailbox, sizeof(vms.username));
  14355. vms.lastmsg = -1;
  14356. open = 0;
  14357. /* open the mailbox state */
  14358. if ((res = open_mailbox(&vms, vmu, old_folder_index)) < 0) {
  14359. ast_log(LOG_WARNING, "Could not open mailbox %s\n", mailbox);
  14360. res = -1;
  14361. goto vm_move_cleanup;
  14362. }
  14363. open = 1;
  14364. if ((vms.lastmsg + 1) < num_msgs) {
  14365. ast_log(LOG_WARNING, "Folder %s has less than %zu messages\n", oldfolder, num_msgs);
  14366. res = -1;
  14367. goto vm_move_cleanup;
  14368. }
  14369. old_msg_nums = ast_alloca(sizeof(int) * num_msgs);
  14370. if ((res = message_range_and_existence_check(&vms, old_msg_ids, num_msgs, old_msg_nums, vmu)) < 0) {
  14371. goto vm_move_cleanup;
  14372. }
  14373. /* Now actually move the message */
  14374. for (i = 0; i < num_msgs; ++i) {
  14375. if (save_to_folder(vmu, &vms, old_msg_nums[i], new_folder_index, NULL, 0)) {
  14376. res = -1;
  14377. goto vm_move_cleanup;
  14378. }
  14379. vms.deleted[old_msg_nums[i]] = 1;
  14380. }
  14381. /* close mailbox */
  14382. if ((res = close_mailbox(&vms, vmu) == ERROR_LOCK_PATH)) {
  14383. res = -1;
  14384. goto vm_move_cleanup;
  14385. }
  14386. open = 0;
  14387. vm_move_cleanup:
  14388. if (vmu && open) {
  14389. close_mailbox(&vms, vmu);
  14390. }
  14391. #ifdef IMAP_STORAGE
  14392. if (vmu) {
  14393. vmstate_delete(&vms);
  14394. }
  14395. #endif
  14396. if (!res) {
  14397. notify_new_state(vmu);
  14398. }
  14399. return res;
  14400. }
  14401. static int vm_msg_remove(const char *mailbox,
  14402. const char *context,
  14403. size_t num_msgs,
  14404. const char *folder,
  14405. const char *msgs[])
  14406. {
  14407. struct vm_state vms;
  14408. struct ast_vm_user *vmu = NULL, vmus;
  14409. int folder_index;
  14410. int open = 0;
  14411. int res = 0;
  14412. int i;
  14413. int *msg_nums;
  14414. if (ast_strlen_zero(mailbox)) {
  14415. ast_log(LOG_WARNING, "Cannot remove message because no mailbox was specified\n");
  14416. return -1;
  14417. }
  14418. if (!num_msgs) {
  14419. ast_log(LOG_WARNING, "Invalid number of messages specified to remove: %zu\n", num_msgs);
  14420. return -1;
  14421. }
  14422. if (ast_strlen_zero(folder)) {
  14423. ast_log(LOG_WARNING, "Cannot remove message because no folder was specified\n");
  14424. return -1;
  14425. }
  14426. memset(&vmus, 0, sizeof(vmus));
  14427. memset(&vms, 0, sizeof(vms));
  14428. folder_index = get_folder_by_name(folder);
  14429. if (folder_index == -1) {
  14430. ast_log(LOG_WARNING, "Could not remove msgs from unknown folder %s\n", folder);
  14431. return -1;
  14432. }
  14433. if (!(vmu = find_user(&vmus, context, mailbox))) {
  14434. ast_log(LOG_WARNING, "Can't find voicemail user to remove msg from (%s@%s)\n", mailbox, context);
  14435. return -1;
  14436. }
  14437. ast_copy_string(vms.username, mailbox, sizeof(vms.username));
  14438. vms.lastmsg = -1;
  14439. open = 0;
  14440. /* open the mailbox state */
  14441. if ((res = open_mailbox(&vms, vmu, folder_index)) < 0) {
  14442. ast_log(LOG_WARNING, "Could not open mailbox %s\n", mailbox);
  14443. res = -1;
  14444. goto vm_remove_cleanup;
  14445. }
  14446. open = 1;
  14447. if ((vms.lastmsg + 1) < num_msgs) {
  14448. ast_log(LOG_WARNING, "Folder %s has less than %zu messages\n", folder, num_msgs);
  14449. res = -1;
  14450. goto vm_remove_cleanup;
  14451. }
  14452. msg_nums = ast_alloca(sizeof(int) * num_msgs);
  14453. if ((res = message_range_and_existence_check(&vms, msgs, num_msgs, msg_nums, vmu)) < 0) {
  14454. goto vm_remove_cleanup;
  14455. }
  14456. for (i = 0; i < num_msgs; i++) {
  14457. vms.deleted[msg_nums[i]] = 1;
  14458. }
  14459. /* close mailbox */
  14460. if ((res = close_mailbox(&vms, vmu) == ERROR_LOCK_PATH)) {
  14461. res = -1;
  14462. ast_log(AST_LOG_ERROR, "Failed to close mailbox folder %s while removing msgs\n", folder);
  14463. goto vm_remove_cleanup;
  14464. }
  14465. open = 0;
  14466. vm_remove_cleanup:
  14467. if (vmu && open) {
  14468. close_mailbox(&vms, vmu);
  14469. }
  14470. #ifdef IMAP_STORAGE
  14471. if (vmu) {
  14472. vmstate_delete(&vms);
  14473. }
  14474. #endif
  14475. if (!res) {
  14476. notify_new_state(vmu);
  14477. }
  14478. return res;
  14479. }
  14480. static int vm_msg_play(struct ast_channel *chan,
  14481. const char *mailbox,
  14482. const char *context,
  14483. const char *folder,
  14484. const char *msg_id,
  14485. ast_vm_msg_play_cb cb)
  14486. {
  14487. struct vm_state vms;
  14488. struct ast_vm_user *vmu = NULL, vmus;
  14489. int res = 0;
  14490. int open = 0;
  14491. int i;
  14492. char filename[PATH_MAX];
  14493. struct ast_config *msg_cfg;
  14494. struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
  14495. int duration = 0;
  14496. const char *value;
  14497. if (ast_strlen_zero(mailbox)) {
  14498. ast_log(LOG_WARNING, "Cannot play message because no mailbox was specified\n");
  14499. return -1;
  14500. }
  14501. if (ast_strlen_zero(folder)) {
  14502. ast_log(LOG_WARNING, "Cannot play message because no folder was specified\n");
  14503. return -1;
  14504. }
  14505. if (ast_strlen_zero(msg_id)) {
  14506. ast_log(LOG_WARNING, "Cannot play message because no message number was specified\n");
  14507. return -1;
  14508. }
  14509. memset(&vmus, 0, sizeof(vmus));
  14510. memset(&vms, 0, sizeof(vms));
  14511. if (ast_strlen_zero(context)) {
  14512. context = "default";
  14513. }
  14514. if (!(vmu = find_user(&vmus, context, mailbox))) {
  14515. return -1;
  14516. }
  14517. i = get_folder_by_name(folder);
  14518. ast_copy_string(vms.username, mailbox, sizeof(vms.username));
  14519. vms.lastmsg = -1;
  14520. if ((res = open_mailbox(&vms, vmu, i)) < 0) {
  14521. ast_log(LOG_WARNING, "Could not open mailbox %s\n", mailbox);
  14522. goto play2_msg_cleanup;
  14523. }
  14524. open = 1;
  14525. if (message_range_and_existence_check(&vms, &msg_id, 1, &vms.curmsg, vmu)) {
  14526. res = -1;
  14527. goto play2_msg_cleanup;
  14528. }
  14529. /* Find the msg */
  14530. make_file(vms.fn, sizeof(vms.fn), vms.curdir, vms.curmsg);
  14531. snprintf(filename, sizeof(filename), "%s.txt", vms.fn);
  14532. RETRIEVE(vms.curdir, vms.curmsg, vmu->mailbox, vmu->context);
  14533. msg_cfg = ast_config_load(filename, config_flags);
  14534. if (!msg_cfg || msg_cfg == CONFIG_STATUS_FILEINVALID) {
  14535. DISPOSE(vms.curdir, vms.curmsg);
  14536. res = -1;
  14537. goto play2_msg_cleanup;
  14538. }
  14539. if ((value = ast_variable_retrieve(msg_cfg, "message", "duration"))) {
  14540. duration = atoi(value);
  14541. }
  14542. ast_config_destroy(msg_cfg);
  14543. #ifdef IMAP_STORAGE
  14544. /*IMAP storage stores any prepended message from a forward
  14545. * as a separate file from the rest of the message
  14546. */
  14547. if (!ast_strlen_zero(vms.introfn) && ast_fileexists(vms.introfn, NULL, NULL) > 0) {
  14548. wait_file(chan, &vms, vms.introfn);
  14549. }
  14550. #endif
  14551. if (cb) {
  14552. cb(chan, vms.fn, duration);
  14553. } else if ((wait_file(chan, &vms, vms.fn)) < 0) {
  14554. ast_log(AST_LOG_WARNING, "Playback of message %s failed\n", vms.fn);
  14555. } else {
  14556. res = 0;
  14557. }
  14558. vms.heard[vms.curmsg] = 1;
  14559. /* cleanup configs and msg */
  14560. DISPOSE(vms.curdir, vms.curmsg);
  14561. play2_msg_cleanup:
  14562. if (vmu && open) {
  14563. close_mailbox(&vms, vmu);
  14564. }
  14565. #ifdef IMAP_STORAGE
  14566. if (vmu) {
  14567. vmstate_delete(&vms);
  14568. }
  14569. #endif
  14570. if (!res) {
  14571. notify_new_state(vmu);
  14572. }
  14573. return res;
  14574. }
  14575. /* This is a workaround so that menuselect displays a proper description
  14576. * AST_MODULE_INFO(, , "Comedian Mail (Voicemail System)"
  14577. */
  14578. AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, tdesc,
  14579. .support_level = AST_MODULE_SUPPORT_CORE,
  14580. .load = load_module,
  14581. .unload = unload_module,
  14582. .reload = reload,
  14583. .nonoptreq = "res_adsi,res_smdi",
  14584. );