pax_global_header00006660000000000000000000000064141522677520014525gustar00rootroot0000000000000052 comment=5a855ee863f778ebcb7a1ce0d247ee2e97bba5c9 vvmd-0.7/000077500000000000000000000000001415226775200123475ustar00rootroot00000000000000vvmd-0.7/.gitignore000066400000000000000000000000161415226775200143340ustar00rootroot00000000000000_build/ *.swp vvmd-0.7/.gitlab-ci.yml000066400000000000000000000021231415226775200150010ustar00rootroot00000000000000--- image: alpine:edge .only-default: &only-default only: - master - merge_requests - tags build-alpine: <<: *only-default before_script: - apk -q add build-base curl-dev dbus-dev libphonenumber-dev glib-dev meson modemmanager-dev pkgconf mobile-broadband-provider-info script: # check for uninitialized g_autoptr, which cause segfaults in musl - grep -n -e 'g_autoptr(\w*)\s*\w*;' -R . && exit 1 - meson -Dwerror=true _build - meson compile -C _build - meson test -C _build --print-errorlogs artifacts: paths: - _build/ build-debian: image: debian:bookworm <<: *only-default before_script: - apt update - apt install -y debhelper-compat meson libglib2.0-dev libphonenumber-dev libmm-glib-dev libcurl4-openssl-dev mobile-broadband-provider-info script: - meson -Dwerror=true _build - meson compile -C _build - meson test -C _build --print-errorlogs artifacts: paths: - _build/ vvmd-0.7/AUTHORS000066400000000000000000000006511415226775200134210ustar00rootroot00000000000000vvmd Authors Chris Talbot Core based on: https://gitlab.com/kop316/mmsd/ Parts adapted from: https://source.puri.sm/Librem5/purple-mm-sms https://gitlab.gnome.org/GNOME/evolution-data-server/-/blob/master/src/addressbook/libebook-contacts/ https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/blob/main/src/core/devices/wwan/nm-service-providers.c Contributors: ThePosichronic Matthew Via vvmd-0.7/COPYING000066400000000000000000000431261415226775200134100ustar00rootroot00000000000000 GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License. vvmd-0.7/ChangeLog000066400000000000000000000021531415226775200141220ustar00rootroot00000000000000vvmd (0.7) [ Chris Talbot ] * Fix various small bugs vvmd (0.6.1) [ Chris Talbot ] * Sync Settings after IMAP activation/deactivation vvmd (0.6) [ Chris Talbot ] * Add support for VZW USA * Various small bug fixes * Reset Settings and disable service if IMSI changes * Add unit test for headers parser vvmd (0.5) [ Chris Talbot ] * Add support for mobile-broadband-provider-info * Sync settings to settings file as soon as they are changed vvmd (0.4) [ Chris Talbot ] * Depend on libphonenumber instead of libebook-contacts vvmd (0.3.1) [ Chris Talbot ] * Remove potentially sensitive log messages * Ensure that there is not a dbus error sending an SMS * Remove unneeded option detach * Move dbus registerations to before name is acquired vvmd (0.3) [ Chris Talbot ] * Normalize dbus date to conform to ISO8601 * Add lifetime status to dbus message vvmd (0.2) [ Chris Talbot ] * Add Unit Tests * Refine stability issues vvmd (0.1~alpha2) [ Chris Talbot ] * Add support for AT&T USA * Refine parsers vvmd (0.1~alpha1) [ Chris Talbot ] * Initial Release vvmd-0.7/README000066400000000000000000000074071415226775200132370ustar00rootroot00000000000000Visual Voicemail Daemon (vvmd) **************************** Copyright (C) 2021, Chris Talbot About =========================== vvmd is a lower level daemon that retrieves Visual Voicemail Notes =========================== As of now, only the "cvvm" and AT&T USA (a proprietary protocol) configuration works. References: Andoird Notes on Visual Voicemail: https://source.android.com/devices/tech/config/voicemail Misc Notes on Voicemail: https://gitlab.freedesktop.org/mobile-broadband/ModemManager/-/issues/253 Visual Voicemail OTMP Specification: https://www.gsma.com/newsroom/wp-content/uploads/2012/07/OMTP_VVM_Specification_1_3.pdf Carrier Configuration settings: https://cs.android.com/android/platform/superproject/+/master:packages/apps/CarrierConfig/assets/ Android Dialer Voicemail Source code: https://cs.android.com/android/platform/superproject/+/master:packages/apps/Dialer/java/com/android/voicemail/ Modem Manager Settings Notes =========================== The Modem Manager settings file is in "$HOME/.vvm/modemmanager" There are several settings in there that are managed by vvmd. Do not attempt to change them! I am listing below the settings you can change: Please look here for a databse of known good settings: https://gitlab.com/kop316/vvmd/-/wikis/Carrier-Database IMPORTANT NOTE: If you change settings in this file, vvmd MUST NOT BE RUNNING in order for your settings to take effect! This is because vvmd will overwrite the file when it exits, destroying your changes. VVMEnabled Whether you want to enable Visual Voicemail. Set to "true" or "false" Changing this setting via dbus will automatically subscribe/unsubscribe vvmd to/from the carrier's visual voicemail. VVMType What type of Visual Voicemail this is. In CarrierConfig, this is found under "vvm_type_string". AT&T USA is "AT&TUSAProprietary". If "vvm_type_string" is not present, use "otmp". VVMDestinationNumber The destination number to send control SMS messages. In CarrierConfig, this is found under "vvm_destination_number_string" I am unsure of how to get the destination number if "vvm_destination_number_string" is not present. CarrierPrefix The string prefix attached to SMS control messages. I believe Verizon Wireless USA changes this to "//VZWVVM", but I have not tested this (nor do I remember where I saw this). If you use AT&T USA, put your phonenumber without space here (e.g. if your phone number is "(432)555-0110", use "4325550110" (without quotes). If unsure, leave this to the default "//VVM". DefaultModemNumber If you have multiple modems, this will allow you to only choose which modem to connect to by reading its number. The number MUST be set to E.164 Setting this to NULL means vvmd will connect to the first modem it finds. Unless you have multiple modems (not common), you can leave this set to "NULL" Compiling vvmd ============================ In order to compile proxy daemon you need following software packages: - GCC compiler - D-Bus library - GLib library - Modem Manager Library - Curl Library Installing vvmd ============================ Build tools requirements When building and testing directly from the repository it is important to have at least meson version 0.56 or later installed. In addition, some supported Meson backend is required (e.g. ninja). To prepare/configure the project for compilation: # meson _build To build the project: # meson compile -C _build Run tests: # meson test -C _build Install: # meson install -C _build Running from within the source code repository ============================================== Meson will build the binary in builddir (_build, using the examples above), and can be invoked directly. Run with debugging # ./_build/vvmd -d vvmd-0.7/config.h.in000066400000000000000000000032271415226775200143760ustar00rootroot00000000000000/* Define to 1 if you have the header file. */ #mesondefine HAVE_DLFCN_H /* Define to 1 if you have the header file. */ #mesondefine HAVE_INTTYPES_H /* Define to 1 if you have the header file. */ #mesondefine HAVE_RESOLV_H /* Define to 1 if you have the header file. */ #mesondefine HAVE_STDINT_H /* Define to 1 if you have the header file. */ #mesondefine HAVE_STDIO_H /* Define to 1 if you have the header file. */ #mesondefine HAVE_STDLIB_H /* Define to 1 if you have the header file. */ #mesondefine HAVE_STRINGS_H /* Define to 1 if you have the header file. */ #mesondefine HAVE_STRING_H /* Define to 1 if you have the header file. */ #mesondefine HAVE_SYS_STAT_H /* Define to 1 if you have the header file. */ #mesondefine HAVE_SYS_TYPES_H /* Define to 1 if you have the header file. */ #mesondefine HAVE_UNISTD_H /* Name of package */ #define PACKAGE "@NAME@" /* Define to the full name and version of this package. */ #define PACKAGE_STRING "@NAME@ @VERSION@" /* Define to the version of this package. */ #define PACKAGE_VERSION "@VERSION@" /* Define to 1 if all of the C90 standard headers exist (not just the ones required in a freestanding environment). This macro is provided for backward compatibility; new code need not use it. */ #mesondefine STDC_HEADERS /* Version number of package */ #define VERSION "@VERSION@" /* Location of the Mobile Broadband provider info */ #define MOBILE_BROADBAND_PROVIDER_INFO_DATABASE "@MOBILE_BROADBAND_PROVIDER_INFO_DATABASE@" /* Build source root */ #define SOURCE_ROOT "@SOURCE_ROOT@" vvmd-0.7/docs/000077500000000000000000000000001415226775200132775ustar00rootroot00000000000000vvmd-0.7/docs/architecture.txt000066400000000000000000000025121415226775200165220ustar00rootroot00000000000000Architecture overview ===================== The VVM daemon implements the Visual Voicemail Service with integration into the Modem Manager telephony stack. +-----------------------+ | Message application | +-----------------------+ ^ | Session D-Bus | +------------------------------------------------+ | | | VVM daemon | | | +------------------------------------------------+ | | | Modem Manager / oFono plugin | | | +------------------------------------------------+ ^ | System D-Bus | +------------------------------------------------+ | | | Modem Manager telephony stack | | | +------------------------------------------------+ vvmd-0.7/docs/manager-api.txt000066400000000000000000000015551415226775200162270ustar00rootroot00000000000000Manager hierarchy ================= Service org.kop316.vvm Interface org.kop316.vvm.Manager Object path /org/kop316/vvm Methods array{object,dict} GetServices() Get an array of service objects and properties that represents the currently available services. This method call should only be used once when an application starts up. Further service additions and removal shall be monitored via ServiceAdded and ServiceRemoved signals. Possible Errors: [service].Error.InvalidArguments Signals ServiceAdded(object path, dict properties) Signal that is sent when a new service is added. It contains the object path of new service and also its properties. ServiceRemoved(object path) Signal that is sent when a service has been removed. The object path is no longer accessible after this signal and only emitted for reference. vvmd-0.7/docs/message-api.txt000066400000000000000000000040221415226775200162310ustar00rootroot00000000000000Message hierarchy ================= Service org.kop316.vvm Interface org.kop316.vvm.Message Object path [variable prefix]/{message0,message1,...} Methods void MarkRead() Change the status of an incoming message to read. This is only supported for incoming messages and is meant as user interface driven action. Possible Errors: [service].Error.InvalidArguments void Delete() Delete this messages from storage. When deleting a message a MessageRemove will be triggered. Possible Errors: [service].Error.InvalidArguments Signals PropertyChanged(string name, variant value) This signal indicates a changed value of the given property. The only expected property change is for the message status. Properties string Status [readonly] The status of the message. Possible values are "received", "read", "sent" and "draft". string Date [readonly] The date of the message. string Sender [readonly] Number of sender. string To [readonly] Number of who the VVM is to. string MessageContext [readonly] Type of VVM this is. string ContentType [readonly] Type of VVM this is. array{string filename} Attachments [readonly] Location of attachment gboolean Delivery Report [readonly] Whether the message is requesting a delivery report string Delivery Status [readonly, optional] A CSV of each phone number in the format of "+12345550200=none" where "+12345550200" is the phone number and "none" is the delivery status. This will only be present if "Delivery Report" is true. NOTE: If this is a "PropertyChanged" signal, it will be in the format of "delivery_update,+12345550200=none" string Modem Number [readonly] The modem number associated with the VVM service. While it will be with every VVM, this is meant to make it easier to seperate out one's own number from the recipients in a group VVM. array{string} Recipients [readonly] Numbers of recipients. string Smil [readonly, optional] SMIL body in UTF-8 format. vvmd-0.7/docs/modem-manager-api.txt000066400000000000000000000015471415226775200173270ustar00rootroot00000000000000Message hierarchy ================= Service org.kop316.vvm Interface org.kop316.vvm.ModemManager Object path /org/kop316/vvm Methods ChangeSettings Change a setting in the Modem Manager Plugin Gvariant Input Format String ((sv)) ViewSettings Change all Modem Manager related settings Gvariant Output Format String (a{sv}) SyncVVM This forces a sync of all of the emails from the IMAP server. Signals ProvisionStatusChanged The provisioning status from the carrier has changed. Gvariant Input Format String ((sv)) SettingsChanged() mmsd-tng changed the MMS settings based on an auto configureation Gvariant Output Format String (sss) standard: The VVM standard destination_number: The destination number carrier_prefix: The carrier prefix Properties None vvmd-0.7/docs/service-api.txt000066400000000000000000000016151415226775200162520ustar00rootroot00000000000000Service hierarchy ================= Service org.kop316.vvm Interface org.kop316.vvm.Service Object path [variable prefix]/{service0,service1,...} Methods array{object,dict} GetMessages() Get an array of message objects and properties that represents the currently received and sent messages. This method call should only be used once when an service becomes available. Further message additions and removal shall be monitored via MessageAdded and MessageRemoved signals. Possible Errors: [service].Error.InvalidArguments Signals MessageAdded(object path, dict properties) Signal that is sent when a new message is added. It contains the object path of new message, its properties. MessageRemoved(object path) Signal that is sent when a message has been removed. The object path is no longer accessible after this signal and only emitted for reference. vvmd-0.7/docs/standards.txt000066400000000000000000000022601415226775200160230ustar00rootroot00000000000000Referencing standards in the source =================================== When referencing standard documents use raw numbers xx.xxx for 3GPP documents or xxx.xxx for ETSI document (eg: 23.040). If needing to point to an specific section/subsection, explicitly say "Section foo" 3GPP specs can be found in http://3gpp.org/ftp/Specs. For documents from OMA or WAP Forum use the raw numbers with its prefix WAP-xxx (eg. WAP-230). If needed use also the abbreviation within the reference (eg. WAP-230-WSP). WAP Forum specs can be found in http://www.wapforum.org/what/technical.htm or in http://www.openmobilealliance.org/Technical/wapindex.aspx for the new Open Mobile Alliance (OMA) site. WAP Forum Specification ======================= - WAP-230-WSP: Wireless Session Protocol Specification Describes the WSP encoding that is used for MMS and WAP Push messages. - WAP-209-MMSEncapsulation: MMS Encapsulation Protocol Describes the encapsulation of MMS messages using WSP. It also describes the usage of HTTP as transport and MIME multipart for message content. WAP Assigned Names or Numbers ============================= - WSP Content Type http://www.wapforum.org/wina/wsp-content-type.htm vvmd-0.7/docs/storage.txt000066400000000000000000000011721415226775200155050ustar00rootroot00000000000000vvmd storage design ******************* The vvmd stores the vvm emails on the file system in a directory named ".vvm/" under the user home directory. (e.g: /home//.vvm/modemmanager/ -> "modemmanager" is the service identifier) A vvm email is stored in a .mbox in a file named with a generated (e.g: 126588fe14db814b781803a17e99b45b0d48.mbox). Another file with the same prefix, named .status (e.g: 126588fe14db814b781803a17e99b45b0d48.status) contains meta information related to the email, and any attachments will be stored in a format "126588fe14db814b781803a17e99b45b0d48$ATTACHMENT" vvmd-0.7/meson.build000066400000000000000000000034431415226775200145150ustar00rootroot00000000000000project( 'vvmd', 'c', 'cpp', version : '0.7', meson_version : '>= 0.56.0', ) check_headers = [ ['HAVE_DLFCN_H', 'dlfcn.h'], ['HAVE_INTTYPES_H', 'inttypes.h'], ['HAVE_STDINT_H', 'stdint.h'], ['HAVE_STDIO_H', 'stdio.h'], ['HAVE_STDLIB_H', 'stdlib.h'], ['HAVE_STRINGS_H', 'strings.h'], ['HAVE_STRING_H', 'string.h'], ['HAVE_SYS_STAT_H', 'sys/stat.h'], ['HAVE_SYS_TYPES_H', 'sys/types.h'], ['HAVE_UNISTD_H', 'unistd.h'] ] cc = meson.get_compiler('c') conf_data = configuration_data() foreach h : check_headers if cc.has_header(h.get(1)) conf_data.set(h.get(0), 1) endif endforeach conf_data.set('VERSION', meson.project_version()) conf_data.set('NAME', meson.project_name()) mobile_broadband_provider_info_database = dependency('mobile-broadband-provider-info').get_variable(pkgconfig: 'database') conf_data.set('MOBILE_BROADBAND_PROVIDER_INFO_DATABASE', mobile_broadband_provider_info_database) conf_data.set('SOURCE_ROOT', meson.project_source_root()) conf = configure_file( input : 'config.h.in', output : 'config.h', configuration : conf_data ) add_project_arguments('-DVVM_PLUGIN_BUILTIN', language : 'c') add_project_arguments('-DHAVE_CONFIG_H', language : 'c') add_project_arguments('-DPLUGINDIR="@0@/vvm/plugins"'.format(get_option('libdir')), language : 'c') includes = [ include_directories('.'), include_directories('src') ] dependencies = [ dependency('glib-2.0', version : '>=2.16'), dependency('mm-glib', version : '>=1.14'), dependency('libcurl', version : '>7.70'), cc.find_library('dl'), cc.find_library('phonenumber', required: true) ] subdir('src') subdir('plugins') vvmd = executable('vvmd', 'src/main.c', dependencies : dependencies, include_directories : includes, install : true, link_with : [vvm_lib, plugins_lib] ) subdir('unit') vvmd-0.7/plugins/000077500000000000000000000000001415226775200140305ustar00rootroot00000000000000vvmd-0.7/plugins/meson.build000066400000000000000000000002501415226775200161670ustar00rootroot00000000000000plugins_sources = [ 'modemmanager.c' ] plugins_lib = static_library('plugins', plugins_sources, include_directories : includes, dependencies : dependencies, ) vvmd-0.7/plugins/modemmanager.c000066400000000000000000001435271415226775200166440ustar00rootroot00000000000000/* * * Visual Voicemail Daemon * * Copyright (C) 2018 Purism SPC * 2020-2021 Chris Talbot * * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include "vvm.h" #include "dbus.h" #include "vvmutil.h" #include "phone-utils.h" #include "service-providers.h" // SETTINGS_GROUP_MODEMMANAGER is where we store our settings for this plugin #define SETTINGS_GROUP_MODEMMANAGER "Modem Manager" //Identifier of the plugin #define IDENTIFIER "modemmanager" //dbus default timeout for Modem #define VVMD_MM_MODEM_TIMEOUT 20000 #define VVM_MODEMMANAGER_INTERFACE VVM_SERVICE ".ModemManager" enum { VVMD_MM_STATE_NO_MANAGER, VVMD_MM_STATE_MANAGER_FOUND, VVMD_MM_STATE_NO_MODEM, VVMD_MM_STATE_MODEM_FOUND, VVMD_MM_STATE_NO_MESSAGING_MODEM, VVMD_MM_STATE_MODEM_DISABLED, VVMD_MM_STATE_READY } e_vvmd_connection; struct modem_data { struct vvm_service *service; //Do not mess with the guts of this in plugin.c! GKeyFile *modemsettings; //These are pulled from the settings file, and can be set via the Dbus int provision_status; int enable_vvm; char *vvm_destination_number; char *vvm_type; char *default_number; char *carrier_prefix; // These are for settings the context (i.e. APN settings and if the bearer is active) char *context_interface; // Bearer interface here (e.g. "wwan0") char *context_path; // Dbus path of the bearer gboolean context_active; // Whether the context is active //The Bus org.kop316.vvm.ModemManager guint owner_id; guint registration_id; gulong modem_state_watch_id; gulong sms_wap_signal_id; //These are modem manager related settings MMManager *mm; guint mm_watch_id; MMObject *object; MMModem *modem; char *path; MMSim *sim; gchar *imsi; gchar *mccmnc; char *registered_imsi; MMModemMessaging *modem_messaging; MMModemState state; GPtrArray *device_arr; gboolean modem_available; gboolean modem_ready; gboolean manager_available; gboolean plugin_registered; gboolean get_all_sms_timeout; }; typedef struct { MMObject *object; MMModem *modem; MMSim *sim; } VvmdDevice; /* Introspection data for the service we are exporting */ static const gchar introspection_xml_modemmanager[] = "" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " ""; static GDBusNodeInfo *introspection_data = NULL; struct modem_data *modem; static void vvmd_mm_state(int state); static void vvmd_modem_available(void); static void vvmd_modem_unavailable(void); static void free_device(VvmdDevice *device); static void cb_mm_manager_new(GDBusConnection *connection, GAsyncResult *res, gpointer user_data); static void mm_appeared_cb(GDBusConnection *connection, const gchar *name, const gchar *name_owner, gpointer user_data); static void mm_vanished_cb(GDBusConnection *connection, const gchar *name, gpointer user_data); static int modemmanager_init(void); static void modemmanager_exit(void); static void mm_sync_vvm_imap_server(void); static void vvmd_connect_to_sms_wap(void); static void vvmd_disconnect_from_sms_wap(void); static void vvmd_get_all_sms(void); static void vvmd_unsubscribe_service(void); static void vvmd_check_subscription_status(void); static void vvmd_subscribe_service(void); static void handle_method_call(GDBusConnection *connection, const gchar *sender, const gchar *object_path, const gchar *interface_name, const gchar *method_name, GVariant *parameters, GDBusMethodInvocation *invocation, gpointer user_data) { if(g_strcmp0(method_name, "ViewSettings") == 0) { GVariantBuilder settings_builder; GVariant *settings, *all_settings; g_variant_builder_init(&settings_builder, G_VARIANT_TYPE ("a{sv}")); g_variant_builder_add_parsed (&settings_builder, "{'VVMEnabled', <%b>}", modem->enable_vvm); g_variant_builder_add_parsed (&settings_builder, "{'VVMType', <%s>}", modem->vvm_type); g_variant_builder_add_parsed (&settings_builder, "{'VVMDestinationNumber', <%s>}", modem->vvm_destination_number); g_variant_builder_add_parsed (&settings_builder, "{'CarrierPrefix', <%s>}", modem->carrier_prefix); if (modem->default_number) { g_variant_builder_add_parsed (&settings_builder, "{'DefaultModemNumber', <%s>}", modem->default_number); } else { g_variant_builder_add_parsed (&settings_builder, "{'DefaultModemNumber', <%s>}", "NULL"); } switch(modem->provision_status) { case VVM_PROVISION_STATUS_NOT_SET: g_variant_builder_add_parsed (&settings_builder, "{'ProvisionStatus', <%s>}", "Not Set"); break; case VVM_PROVISION_STATUS_NEW: g_variant_builder_add_parsed (&settings_builder, "{'ProvisionStatus', <%s>}", "New"); break; case VVM_PROVISION_STATUS_READY: g_variant_builder_add_parsed (&settings_builder, "{'ProvisionStatus', <%s>}", "Ready"); break; case VVM_PROVISION_STATUS_PROVISIONED: g_variant_builder_add_parsed (&settings_builder, "{'ProvisionStatus', <%s>}", "Provisioned"); break; case VVM_PROVISION_STATUS_UNKNOWN: g_variant_builder_add_parsed (&settings_builder, "{'ProvisionStatus', <%s>}", "Unknown"); break; case VVM_PROVISION_STATUS_BLOCKED: g_variant_builder_add_parsed (&settings_builder, "{'ProvisionStatus', <%s>}", "Blocked"); break; default: g_variant_builder_add_parsed (&settings_builder, "{'ProvisionStatus', <%s>}", "Cannot Determine"); } settings = g_variant_builder_end (&settings_builder); all_settings = g_variant_new("(*)", settings); DBG("All Settings: %s", g_variant_print(all_settings, TRUE)); g_dbus_method_invocation_return_value (invocation, all_settings); } else if(g_strcmp0(method_name, "ChangeSettings") == 0) { GVariant *variantstatus; gchar *dict; gboolean close_settings = FALSE; g_variant_get (parameters, "(sv)", &dict, &variantstatus); DBG("Dict: %s", dict); if (modem->modemsettings == NULL) { close_settings = TRUE; modem->modemsettings = vvm_settings_open(IDENTIFIER, SETTINGS_STORE); } if(g_strcmp0(dict, "VVMEnabled") == 0) { int VVMStatus; g_variant_get (variantstatus, "b", &VVMStatus); if (modem->enable_vvm == FALSE && VVMStatus == TRUE) { DBG("Subscribing from VVM"); vvmd_subscribe_service(); } else if (modem->enable_vvm == TRUE && VVMStatus == FALSE) { DBG("Unsubscribing from VVM"); vvmd_unsubscribe_service(); } else { DBG("VVM status Unchanged!"); } modem->enable_vvm = VVMStatus; g_key_file_set_boolean(modem->modemsettings, SETTINGS_GROUP_MODEMMANAGER, "VVMEnabled", modem->enable_vvm); DBG("VVM status now: %d", modem->enable_vvm); } else if(g_strcmp0(dict, "VVMType") == 0) { g_autofree char *vvm_type = NULL; g_variant_get (variantstatus, "s", &vvm_type); g_free(modem->vvm_type); modem->vvm_type = g_strdup(vvm_type); g_key_file_set_string(modem->modemsettings, SETTINGS_GROUP_MODEMMANAGER, "VVMType", modem->vvm_type); DBG("VVM type now: %s", modem->vvm_type); } else if(g_strcmp0(dict, "VVMDestinationNumber") == 0) { g_autofree char *vvm_dest_number = NULL; g_variant_get (variantstatus, "s", &vvm_dest_number); g_free(modem->vvm_destination_number); modem->vvm_destination_number = g_strdup(vvm_dest_number); g_key_file_set_string(modem->modemsettings, SETTINGS_GROUP_MODEMMANAGER, "VVMDestinationNumber", modem->vvm_destination_number); DBG("VVM destination number now: %s", modem->vvm_destination_number); } else if(g_strcmp0(dict, "CarrierPrefix") == 0) { g_autofree char *carrier_prefix = NULL; g_variant_get (variantstatus, "s", &carrier_prefix); g_free(modem->carrier_prefix); modem->carrier_prefix = g_strdup(carrier_prefix); g_key_file_set_string(modem->modemsettings, SETTINGS_GROUP_MODEMMANAGER, "CarrierPrefix", modem->carrier_prefix); DBG("VVM carrier prefix now: %s", modem->carrier_prefix); } else if(g_strcmp0(dict, "DefaultModemNumber") == 0) { g_autofree char *DefaultNumber = NULL; g_variant_get (variantstatus, "s", &DefaultNumber); g_free(modem->default_number); modem->default_number = g_strdup(DefaultNumber); g_key_file_set_string(modem->modemsettings, SETTINGS_GROUP_MODEMMANAGER, "DefaultModemNumber", modem->default_number); if(g_strcmp0(modem->default_number, "NULL") == 0) { g_free(modem->default_number); modem->default_number = NULL; } DBG("Default Modem Number set to %s", modem->default_number); } else { if (close_settings == TRUE) { vvm_settings_close(IDENTIFIER, SETTINGS_STORE, modem->modemsettings, FALSE); modem->modemsettings = NULL; } g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS, "Cannot find the Property requested!"); return; } if (close_settings == TRUE) { vvm_settings_close(IDENTIFIER, SETTINGS_STORE, modem->modemsettings, TRUE); modem->modemsettings = NULL; } else { vvm_settings_sync(IDENTIFIER, SETTINGS_STORE, modem->modemsettings); } g_dbus_method_invocation_return_value (invocation, NULL); } else if(g_strcmp0(method_name, "SyncVVM") == 0) { if(modem->state == MM_MODEM_STATE_CONNECTED) { if (modem->provision_status == VVM_PROVISION_STATUS_NEW || modem->provision_status == VVM_PROVISION_STATUS_READY) { if (modem->enable_vvm) { DBG("Processing any unreceived VVM messages."); mm_sync_vvm_imap_server(); g_dbus_method_invocation_return_value(invocation, NULL); } else { DBG("You have Visual Voicemail disabled"); g_dbus_method_invocation_return_dbus_error(invocation, VVM_MODEMMANAGER_INTERFACE, "You have Visual Voicemail disabled"); } } else { vvm_error("You are not subscribed to the Visual Voicemail."); g_dbus_method_invocation_return_dbus_error(invocation, VVM_MODEMMANAGER_INTERFACE, "You are not subscribed to the Visual Voicemail."); } } else { vvm_error("Modem is not ready to process any unsent/unreceived VVM messages."); g_dbus_method_invocation_return_dbus_error(invocation, VVM_MODEMMANAGER_INTERFACE, "Modem is not ready to process any unsent/unreceived VVM messages."); } } else if(g_strcmp0(method_name, "CheckSubscriptonStatus") == 0) { DBG("Checking Subscription Status."); vvmd_check_subscription_status(); g_dbus_method_invocation_return_value(invocation, NULL); } } static const GDBusInterfaceVTable interface_vtable = { handle_method_call }; static void cb_sms_delete_finish(MMModemMessaging *modemmessaging, GAsyncResult *result, MMSms *sms) { g_autoptr(GError) error = NULL; if(mm_modem_messaging_delete_finish(modemmessaging, result, &error)) { g_debug("Message delete finish"); } else { g_debug("Couldn't delete SMS - error: %s", error ? error->message : "unknown"); } } static void mm_sync_vvm_imap_server(void) { if(modem->state == MM_MODEM_STATE_CONNECTED) { if (modem->provision_status == VVM_PROVISION_STATUS_NEW || modem->provision_status == VVM_PROVISION_STATUS_READY) { if (modem->enable_vvm) { DBG("Processing any unreceived VVM messages."); /* * Prevent a race condition from the connection turning active to usable * for vvmd */ sleep(1); vvm_service_sync_vvm_imap_server(modem->service); } else { DBG("You have Visual Voicemail disabled"); } } else { vvm_error("You are not subscribed to the Visual Voicemail."); } } else { vvm_error("Modem is not ready to process any unsent/unreceived VVM messages."); } } static void emit_provision_status_changed(const char *new_status) { GDBusConnection *connection = vvm_dbus_get_connection (); GVariant *changedproperty; g_autoptr(GError) error = NULL; DBG("Provision Status changed to %s", new_status); changedproperty = g_variant_new_parsed("('ProvisionStatus', <%s>)", new_status); g_dbus_connection_emit_signal(connection, NULL, VVM_PATH, VVM_MODEMMANAGER_INTERFACE, "ProvisionStatusChanged", changedproperty, &error); if (error != NULL) { g_warning ("Error in Proxy call: %s\n", error->message); } } static void vvm_process_status_message(struct sms_control_message *sms_msg) { int old_provision_status = modem->provision_status; g_autofree char *provisionstatus = NULL; if(g_strcmp0(sms_msg->type, "status") != 0) { vvm_error("This is not a status sms!"); return; } DBG("Previous Provisioning Status to %d", old_provision_status); modem->provision_status = sms_msg->provision_status; DBG("Setting Provisioning Status to %d", modem->provision_status); g_key_file_set_integer(modem->modemsettings, SETTINGS_GROUP_MODEMMANAGER, "ProvisionStatus", modem->provision_status); if(sms_msg->mailbox_hostname) vvm_service_set_hostname(modem->service, sms_msg->mailbox_hostname); if(sms_msg->mailbox_port) vvm_service_set_port(modem->service, sms_msg->mailbox_port); if(sms_msg->mailbox_username) vvm_service_set_username(modem->service, sms_msg->mailbox_username); if(sms_msg->mailbox_password) vvm_service_set_password(modem->service, sms_msg->mailbox_password); if(sms_msg->language) vvm_service_set_language(modem->service, sms_msg->language); if(sms_msg->greeting_length) vvm_service_set_greeting_length(modem->service, sms_msg->greeting_length); if(sms_msg->voice_signature_length) vvm_service_set_voice_signature_length(modem->service, sms_msg->voice_signature_length); if(sms_msg->TUI_password_length) vvm_service_set_tui_password_length(modem->service, sms_msg->TUI_password_length); vvm_service_set_subscription_configuration(modem->service, modem->vvm_type); switch(modem->provision_status) { case VVM_PROVISION_STATUS_NOT_SET: provisionstatus = g_strdup("Not Set"); break; case VVM_PROVISION_STATUS_NEW: provisionstatus = g_strdup("New"); break; case VVM_PROVISION_STATUS_READY: provisionstatus = g_strdup("Ready"); break; case VVM_PROVISION_STATUS_PROVISIONED: provisionstatus = g_strdup("Provisioned"); break; case VVM_PROVISION_STATUS_UNKNOWN: provisionstatus = g_strdup("Unknown"); break; case VVM_PROVISION_STATUS_BLOCKED: provisionstatus = g_strdup("Blocked"); break; default: provisionstatus = g_strdup("Cannot Determine"); } if (old_provision_status != modem->provision_status) { emit_provision_status_changed(provisionstatus); } if (modem->provision_status == VVM_PROVISION_STATUS_PROVISIONED) { if (old_provision_status == VVM_PROVISION_STATUS_NEW || old_provision_status == VVM_PROVISION_STATUS_READY) { vvm_service_deactivate_vvm_imap_server(modem->service); } if (modem->enable_vvm) { vvmd_subscribe_service(); DBG("Unsubscribing from your carrier's VVM service"); } } else if (modem->provision_status == VVM_PROVISION_STATUS_NEW || modem->provision_status == VVM_PROVISION_STATUS_READY) { if (!modem->enable_vvm) { DBG("Unsubscribing from your carrier's VVM service"); vvmd_unsubscribe_service(); } else { mm_sync_vvm_imap_server(); } } else if (modem->provision_status == VVM_PROVISION_STATUS_UNKNOWN) { vvm_error("Provisioning Status is unknown! Contact your Carrier"); } else if (modem->provision_status == VVM_PROVISION_STATUS_BLOCKED) { vvm_error("Provisioning Status is blocked! Contact your Carrier"); } } static gboolean retry_sync_vvm_imap_server(gpointer user_data) { DBG("Retrying sync...."); mm_sync_vvm_imap_server(); return FALSE; } static gboolean new_sync_vvm_timeout(gpointer user_data) { char *dup_msg = user_data; DBG("Processing VVM sync message."); if(vvm_service_new_vvm(modem->service, dup_msg, NULL, modem->vvm_type) == FALSE) { vvm_error("Error making VVM!."); g_timeout_add_seconds(60, retry_sync_vvm_imap_server, NULL); } g_free(dup_msg); return FALSE; } static void vvmd_process_sms(MMSms *sms) { char *message; const char *path; struct sms_control_message *sms_msg; message = mm_sms_dup_text(sms); if(message) { int sms_message_type; sms_message_type = vvm_util_parse_sms_message_type(message, modem->carrier_prefix, modem->vvm_type); if(sms_message_type == SMS_MESSAGE_STATUS) { sms_msg = g_try_new0(struct sms_control_message, 1); if(sms_msg == NULL) { vvm_error("Could not allocate space for modem data!"); } DBG("Processing VVM status message."); vvm_util_parse_status_sms_message(message, sms_msg, modem->vvm_type); vvm_process_status_message(sms_msg); vvm_util_delete_status_message(sms_msg); } else if(sms_message_type == SMS_MESSAGE_SYNC) { DBG("Timeout waiting for sync message."); char *dup_msg = g_strdup (message); g_timeout_add_seconds(1,new_sync_vvm_timeout, dup_msg); } else { DBG("This is not a VVM related Message"); } if(sms_message_type == SMS_MESSAGE_STATUS || sms_message_type == SMS_MESSAGE_SYNC) { path = mm_sms_get_path(sms); if(path) { vvm_error("Deleting SMS!"); mm_modem_messaging_delete(modem->modem_messaging, path, NULL, (GAsyncReadyCallback)cb_sms_delete_finish, sms); } else { vvm_error("vvmd cannot delete SMS at this time!"); } } g_free(message); } } static void cb_sms_state_change(MMSms *sms, GParamSpec *pspec, gpointer *user_data) { MMSmsState state; state = mm_sms_get_state(sms); DBG("%s: state %s", __func__, mm_sms_state_get_string(mm_sms_get_state(sms))); if(state == MM_SMS_STATE_RECEIVED) { vvmd_process_sms(sms); } } static void vvmd_check_pdu_type(MMSms *sms) { MMSmsState state; MMSmsPduType pdu_type; pdu_type = mm_sms_get_pdu_type(sms); state = mm_sms_get_state(sms); switch(pdu_type) { case MM_SMS_PDU_TYPE_CDMA_DELIVER: case MM_SMS_PDU_TYPE_DELIVER: if(state == MM_SMS_STATE_RECEIVED) { vvmd_process_sms(sms); } if(state == MM_SMS_STATE_RECEIVING) { // The first chunk of a multipart SMS has been // received -> wait for MM_SMS_STATE_RECEIVED g_signal_connect(sms, "notify::state", G_CALLBACK(cb_sms_state_change), NULL); } break; case MM_SMS_PDU_TYPE_STATUS_REPORT: case MM_SMS_PDU_TYPE_SUBMIT: DBG("This is not an SMS being received, do not care"); break; case MM_SMS_PDU_TYPE_UNKNOWN: DBG("Unknown PDU type"); break; default: DBG("PDU type not handled"); } } static void cb_sms_list_new_ready(MMModemMessaging *modemmessaging, GAsyncResult *result, gchar *path) { GList *l, *list; g_autoptr(GError) error = NULL; MMSms *sms; list = mm_modem_messaging_list_finish(modemmessaging, result, &error); if(!list) { vvm_error("Couldn't get SMS list - error: %s", error ? error->message : "unknown"); } else { for(l = list; l; l = g_list_next(l)) { //We are searching for the SMS from the list that is new if(!g_strcmp0(mm_sms_get_path(MM_SMS(l->data)), path)) { sms = g_object_ref(MM_SMS(l->data)); vvmd_check_pdu_type(sms); break; } } g_list_free_full(list, g_object_unref); g_free(path); } } static gboolean cb_dbus_signal_sms_added(MMModemMessaging *device, gchar *const_path, gpointer user_data) { gchar *path; path = g_strdup(const_path); mm_modem_messaging_list(modem->modem_messaging, NULL, (GAsyncReadyCallback)cb_sms_list_new_ready, path); return TRUE; } static void cb_sms_list_all_ready(MMModemMessaging *modemmessaging, GAsyncResult *result, gpointer user_data) { GList *l, *list; g_autoptr(GError) error = NULL; MMSms *sms; list = mm_modem_messaging_list_finish(modemmessaging, result, &error); if(!list) { g_debug("Couldn't get SMS list - error: %s", error ? error->message : "unknown"); } else { for(l = list; l; l = g_list_next(l)) { sms = g_object_ref(MM_SMS(l->data)); vvmd_check_pdu_type(sms); } } } static gboolean vvmd_get_all_sms_timeout(gpointer user_data) { DBG("Removing timeout to vvmd_get_all_sms()"); modem->get_all_sms_timeout = FALSE; return FALSE; } static void vvmd_get_all_sms(void) { g_return_if_fail(MM_IS_MODEM_MESSAGING(modem->modem_messaging)); if (modem->get_all_sms_timeout == FALSE) { DBG("Searching for any new SMS WAPs..."); // This is needed in case vvmd is // trying to come out of suspend sleep(1); mm_modem_messaging_list(modem->modem_messaging, NULL, (GAsyncReadyCallback)cb_sms_list_all_ready, NULL); modem->get_all_sms_timeout = TRUE; DBG("Adding timeout to vvmd_get_all_sms()"); //Adding five second timeout g_timeout_add_seconds(5,vvmd_get_all_sms_timeout, NULL); } else { DBG("vvmd_get_all_sms() timed out!"); } } static void vvmd_disconnect_from_sms_wap(void) { MmGdbusModemMessaging *gdbus_sms; if (modem->modem_messaging == NULL) { vvm_error("SMS WAP Disconnect: There is no modem with messaging capabilities!"); return; } DBG("Stopping watching SMS WAPs"); gdbus_sms = MM_GDBUS_MODEM_MESSAGING(modem->modem_messaging); g_signal_handler_disconnect (gdbus_sms, modem->sms_wap_signal_id); } static void vvmd_connect_to_sms_wap(void) { MmGdbusModemMessaging *gdbus_sms; if (modem->modem_messaging == NULL) { vvm_error("SMS WAP Connect: There is no modem with messaging capabilities!"); return; } gdbus_sms = MM_GDBUS_MODEM_MESSAGING(modem->modem_messaging); DBG("Watching for new SMS WAPs"); modem->sms_wap_signal_id = g_signal_connect(gdbus_sms, "added", G_CALLBACK(cb_dbus_signal_sms_added), NULL); } static gboolean vvmd_mm_init_modem(MMObject *obj) { modem->modem_messaging = mm_object_get_modem_messaging(MM_OBJECT(obj)); if (modem->modem_messaging == NULL) { return FALSE; } modem->object = obj; modem->modem = mm_object_get_modem(MM_OBJECT(obj)); modem->path = mm_modem_dup_path(modem->modem); g_dbus_proxy_set_default_timeout(G_DBUS_PROXY(modem->modem), VVMD_MM_MODEM_TIMEOUT); DBG("%s", __func__); return TRUE; } static void free_device(VvmdDevice *device) { if(!device) return; g_clear_object(&device->sim); g_clear_object(&device->modem); g_clear_object(&device->object); g_free(device); } static gboolean device_match_by_object(VvmdDevice *device, GDBusObject *object) { g_return_val_if_fail(G_IS_DBUS_OBJECT(object), FALSE); g_return_val_if_fail(MM_OBJECT(device->object), FALSE); return object == G_DBUS_OBJECT(device->object); } static void vvmd_mm_add_object(MMObject *obj) { VvmdDevice *device; const gchar *object_path; const gchar *const *modem_number_ref; MMSim *sim; const gchar *country_code; g_autoptr(GError) error = NULL; gchar *modem_number_formatted; object_path = g_dbus_object_get_object_path(G_DBUS_OBJECT(obj)); g_return_if_fail(object_path); //Begin if statement if(g_ptr_array_find_with_equal_func(modem->device_arr, obj, (GEqualFunc)device_match_by_object, NULL)) { //End if statement DBG("Device %s already added", object_path); return; } if (modem->modem_available) { DBG("There is already a modem registered!"); return; } if (modem->default_number != NULL) { DBG("Checking if this modem number matches: %s", modem->default_number); sim = mm_modem_get_sim_sync(mm_object_get_modem(MM_OBJECT(obj)), NULL, &error); if(error != NULL) { vvm_error ("Error Getting Sim: %s", error->message); return; } country_code = get_country_iso_for_mcc (mm_sim_get_imsi(sim)); modem_number_ref = mm_modem_get_own_numbers (mm_object_get_modem(MM_OBJECT(obj))); if (modem_number_ref == NULL) { vvm_error("Could not get number!"); return; } modem_number_formatted = phone_utils_format_number_e164(*modem_number_ref, country_code, FALSE); DBG("Formatted Number %s", modem_number_formatted); if(g_strcmp0(modem_number_formatted, modem->default_number) != 0) { g_free(modem_number_formatted); vvm_error("This modem does not match default number!"); return; } g_free(modem_number_formatted); } else { DBG("Not checking for a default Modem"); } DBG("Added device at: %s", object_path); if (vvmd_mm_init_modem(obj) == TRUE) { device = g_new0(VvmdDevice, 1); device->object = g_object_ref(MM_OBJECT(obj)); device->modem = mm_object_get_modem(MM_OBJECT(obj)); g_ptr_array_add(modem->device_arr, device); vvmd_mm_state(VVMD_MM_STATE_MODEM_FOUND); } else { vvmd_mm_state(VVMD_MM_STATE_NO_MESSAGING_MODEM); } } static void vvmd_mm_get_modems(void) { GList *list, *l; gboolean has_modem = FALSE; g_return_if_fail(MM_IS_MANAGER(modem->mm)); list = g_dbus_object_manager_get_objects(G_DBUS_OBJECT_MANAGER(modem->mm)); for(l = list; l != NULL; l = l->next) { if(!mm_object_peek_modem_messaging(l->data)) continue; has_modem = TRUE; vvmd_mm_add_object(MM_OBJECT(l->data)); } if(!has_modem) { vvmd_mm_state(VVMD_MM_STATE_NO_MODEM); } else if(list) { g_list_free_full(list, g_object_unref); } } static void cb_object_added(GDBusObjectManager *manager, GDBusObject *object, gpointer user_data) { DBG("%s", __func__); if(mm_object_peek_modem_messaging(MM_OBJECT(object))) { vvm_error("New Object does not have Messaging feature, ignoring...."); vvmd_mm_add_object(MM_OBJECT(object)); } } static void cb_object_removed(GDBusObjectManager *manager, GDBusObject *object, gpointer user_data) { guint index; g_return_if_fail(G_IS_DBUS_OBJECT(object)); g_return_if_fail(G_IS_DBUS_OBJECT_MANAGER(manager)); //Begin if statement if(g_ptr_array_find_with_equal_func(modem->device_arr, object, (GEqualFunc)device_match_by_object, &index)) { //End if Statement g_ptr_array_remove_index_fast(modem->device_arr, index); } if(MM_OBJECT(object) == modem->object) { vvmd_mm_state(VVMD_MM_STATE_NO_MODEM); } DBG("Modem removed: %s", g_dbus_object_get_object_path(object)); } static void cb_name_owner_changed(GDBusObjectManager *manager, GDBusObject *object, gpointer user_data) { gchar *name_owner; name_owner = g_dbus_object_manager_client_get_name_owner(G_DBUS_OBJECT_MANAGER_CLIENT(manager)); if(!name_owner) { vvmd_mm_state(VVMD_MM_STATE_NO_MANAGER); } DBG("Name owner changed"); g_free(name_owner); } static void cb_mm_manager_new(GDBusConnection *connection, GAsyncResult *res, gpointer user_data) { gchar *name_owner; g_autoptr(GError) error = NULL; modem->mm = mm_manager_new_finish(res, &error); modem->device_arr = g_ptr_array_new_with_free_func((GDestroyNotify) free_device); if(modem->mm) { vvmd_mm_state(VVMD_MM_STATE_MANAGER_FOUND); g_signal_connect(modem->mm, "interface-added", G_CALLBACK(cb_object_added), NULL); g_signal_connect(modem->mm, "object-added", G_CALLBACK(cb_object_added), NULL); g_signal_connect(modem->mm, "object-removed", G_CALLBACK(cb_object_removed), NULL); g_signal_connect(modem->mm, "notify::name-owner", G_CALLBACK(cb_name_owner_changed), NULL); name_owner = g_dbus_object_manager_client_get_name_owner(G_DBUS_OBJECT_MANAGER_CLIENT(modem->mm)); DBG("ModemManager found: %s\n", name_owner); g_free(name_owner); vvmd_mm_get_modems(); } else { vvm_error("Error connecting to ModemManager: %s\n", error->message); vvmd_mm_state(VVMD_MM_STATE_NO_MANAGER); } } static void vvmd_mm_get_modem_state(void) { g_autoptr(GError) error = NULL; if(!modem->modem) { vvmd_mm_state(VVMD_MM_STATE_NO_MODEM); return; } if(modem->state < MM_MODEM_STATE_ENABLED) { DBG("Something May be wrong with the modem, checking...."); switch(modem->state) { case MM_MODEM_STATE_FAILED: DBG("MM_MODEM_STATE_FAILED"); vvmd_mm_state(VVMD_MM_STATE_MODEM_DISABLED); return; case MM_MODEM_STATE_UNKNOWN: DBG("MM_MODEM_STATE_UNKNOWN"); vvmd_mm_state(VVMD_MM_STATE_MODEM_DISABLED); return; case MM_MODEM_STATE_LOCKED: DBG("MM_MODEM_STATE_FAILED"); vvmd_mm_state(VVMD_MM_STATE_MODEM_DISABLED); return; case MM_MODEM_STATE_INITIALIZING: DBG("MM_MODEM_STATE_INITIALIZING"); vvmd_mm_state(VVMD_MM_STATE_MODEM_DISABLED); return; case MM_MODEM_STATE_DISABLED: DBG("MM_MODEM_STATE_DISABLED"); DBG("Turning on Modem...."); mm_modem_set_power_state_sync(modem->modem, MM_MODEM_POWER_STATE_ON, NULL, &error); vvmd_mm_state(VVMD_MM_STATE_MODEM_DISABLED); return; case MM_MODEM_STATE_DISABLING: DBG("MM_MODEM_STATE_DISABLING"); vvmd_mm_state(VVMD_MM_STATE_MODEM_DISABLED); return; case MM_MODEM_STATE_ENABLING: DBG("MM_MODEM_STATE_ENABLING"); vvmd_mm_state(VVMD_MM_STATE_MODEM_DISABLED); return; default: DBG("MM_MODEM_OTHER_BAD_STATE: %d", modem->state); vvmd_mm_state(VVMD_MM_STATE_MODEM_DISABLED); return; } } DBG("MM_MODEM_GOOD_STATE: %d", modem->state); vvmd_mm_state(VVMD_MM_STATE_READY); if(modem->state == MM_MODEM_STATE_CONNECTED) { GList *bearer_list, *l; MMBearer *modem_bearer; bearer_list = mm_modem_list_bearers_sync(modem->modem, NULL, NULL); /* There is only one bearer */ for(l = bearer_list; l != NULL; l = l->next) { const gchar *mailbox_interface; modem_bearer =(MMBearer *) l->data; mailbox_interface = mm_bearer_get_interface(modem_bearer); vvm_service_set_interface(modem->service, mailbox_interface); } } // Automatically retrieve VVM messages when the modem is connected mm_sync_vvm_imap_server(); return; } static void modem_state_changed_cb(MMModem *cb_modem, MMModemState old, MMModemState new, MMModemStateChangeReason reason) { DBG("State Change: Old State: %d New State: %d, Reason: %d", old, new, reason); modem->state = new; vvmd_mm_get_modem_state(); } static void vvmd_mm_state(int state) { switch(state) { case VVMD_MM_STATE_MODEM_FOUND: DBG("VVMD_MM_STATE_MODEM_FOUND"); if(!modem->modem_available) { vvmd_modem_available(); } break; case VVMD_MM_STATE_NO_MODEM: if(modem->modem_available) { vvmd_modem_unavailable(); DBG("Modem vanished, Disabling plugin"); } else { vvm_error("Could not connect to modem"); } modem->modem_available = FALSE; modem->modem_ready = FALSE; DBG("VVMD_MM_STATE_NO_MODEM"); break; case VVMD_MM_STATE_NO_MESSAGING_MODEM: DBG("Modem has no messaging capabilities"); DBG("VVMD_MM_STATE_NO_MESSAGING_MODEM"); modem->modem_available = FALSE; modem->modem_ready = FALSE; break; case VVMD_MM_STATE_MODEM_DISABLED: DBG("VVMD_MM_STATE_MODEM_DISABLED"); modem->modem_ready = FALSE; break; case VVMD_MM_STATE_MANAGER_FOUND: if(!modem->manager_available) { modem->manager_available = TRUE; } DBG("VVMD_MM_STATE_MANAGER_FOUND"); break; case VVMD_MM_STATE_NO_MANAGER: if(modem->manager_available) { g_clear_object(&modem->mm); modem->modem_available = FALSE; modem->modem_ready = FALSE; } else { vvm_error("Could not connect to ModemManager"); } modem->manager_available = FALSE; DBG("VVMD_MM_STATE_NO_MANAGER"); break; case VVMD_MM_STATE_READY: DBG("VVMD_MM_STATE_READY"); modem->modem_ready = TRUE; DBG("Setting Bearer Handler"); vvmd_get_all_sms(); break; default: g_return_if_reached(); } } static void mm_appeared_cb(GDBusConnection *connection, const gchar *name, const gchar *name_owner, gpointer user_data) { g_assert(G_IS_DBUS_CONNECTION(connection)); DBG("Modem Manager appeared"); mm_manager_new(connection, G_DBUS_OBJECT_MANAGER_CLIENT_FLAGS_NONE, NULL, (GAsyncReadyCallback) cb_mm_manager_new, NULL); } static void mm_vanished_cb(GDBusConnection *connection, const gchar *name, gpointer user_data) { g_assert(G_IS_DBUS_CONNECTION(connection)); DBG("Modem Manager vanished"); vvmd_mm_state(VVMD_MM_STATE_NO_MANAGER); } static void cb_sms_send_finish (MMSms *sms, GAsyncResult *result, gpointer user_data) { g_autoptr(GError) error = NULL; gboolean fin; fin = mm_sms_send_finish (sms, result, &error); if (!fin) { vvm_error ("Couldn't send SMS - error: %s", error ? error->message : "unknown"); } else { DBG ("Successfully sent SMS: %s", mm_sms_get_path (sms)); const char *path; path = mm_sms_get_path(sms); if(path) { mm_modem_messaging_delete(modem->modem_messaging, path, NULL, (GAsyncReadyCallback)cb_sms_delete_finish, sms); } else { vvm_error("vvmd cannot process SMS at this time!"); } } } static gboolean vvmd_mm_create_sms (const gchar *message) { MMSmsProperties *properties; gboolean delivery_report = 0; MMSms *sms; g_autoptr(GError) error = NULL; properties = mm_sms_properties_new (); mm_sms_properties_set_text (properties, message); mm_sms_properties_set_number (properties, modem->vvm_destination_number); mm_sms_properties_set_delivery_report_request (properties, delivery_report); DBG("Creating new SMS"); sms = mm_modem_messaging_create_sync (modem->modem_messaging, properties, NULL, &error); g_object_unref (properties); if (!sms) { DBG ("Couldn't create new SMS - error: %s", error ? error->message : "unknown"); } else { DBG ("Successfully created new SMS: %s", mm_sms_get_path (sms)); mm_sms_send (sms, NULL, (GAsyncReadyCallback)cb_sms_send_finish, NULL); g_object_unref (sms); } return TRUE; } static void vvmd_subscribe_service(void) { char *message = NULL; if(modem->provision_status == VVM_PROVISION_STATUS_NOT_SET) { /* * I have experiementally seen that I can unsubscribe to see * The status of VVM, then take action based on that */ DBG("Your provisioning status is unknown"); DBG("Checking Subscription Status"); vvmd_check_subscription_status(); return; } else { DBG("You are not be subscribed to your carrier's VVM service"); } message = vvm_util_create_activate_sms(modem->carrier_prefix, modem->vvm_type); if (message) { DBG("Message: %s", message); vvmd_mm_create_sms (message); } g_free(message); } static void vvmd_unsubscribe_service(void) { char *message = NULL; DBG("Unsubscribing from your carrier's VVM service"); if (modem->provision_status == VVM_PROVISION_STATUS_NEW || modem->provision_status == VVM_PROVISION_STATUS_READY) { if(modem->plugin_registered == TRUE) { vvm_service_deactivate_vvm_imap_server(modem->service); } } message = vvm_util_create_deactivate_sms(modem->carrier_prefix, modem->vvm_type); if (message) { DBG("Message: %s", message); vvmd_mm_create_sms (message); } g_free(message); } static void vvmd_check_subscription_status(void) { char *message = NULL; DBG("Checking your carrier's VVM service"); message = vvm_util_create_status_sms(modem->carrier_prefix, modem->vvm_type); if (message) { DBG("Message: %s", message); vvmd_mm_create_sms (message); } g_free(message); } static void find_settings_cb(const char *carrier, const char *vvm_std_to_copy, const char *dest_num, const char *carrier_prefix_to_copy, GError *error, gpointer user_data) { g_autofree char *vvm_std = NULL; g_autofree char *carrier_prefix = NULL; GDBusConnection *connection = vvm_dbus_get_connection (); if (error) { vvm_warn("Could not find settings: %s", error->message); vvm_warn("Your VVM settings for are not in the database! Please file a merge request at https://gitlab.gnome.org/GNOME/mobile-broadband-provider-info so they can be added"); return; } DBG("Autopopulating VVM Carrier Settings"); if (g_strcmp0("AT&T", carrier) == 0 && g_strcmp0("proprietary", vvm_std_to_copy) == 0) { const gchar *const *modem_number_ref; const char *country_code; modem_number_ref = mm_modem_get_own_numbers (modem->modem); country_code = get_country_iso_for_mcc (modem->imsi); if (g_strcmp0 (country_code, "US") != 0) { vvm_warn("you are parsing AT&T USA but the country code is not US"); return; } else { vvm_std = g_strdup("AT&TUSAProprietary"); } if (modem_number_ref != NULL) { g_autofree char *formatted_number = NULL; formatted_number = phone_utils_format_number_e164(*modem_number_ref, country_code, FALSE); if (formatted_number) { char *stripped; if (g_str_has_prefix (formatted_number, "+1")) stripped = formatted_number + strlen ("+1"); carrier_prefix = g_strdup(stripped); } else { vvm_error("Could not format modem number!"); } } else { vvm_error("Could not get modem number!"); } } else { carrier_prefix = g_strdup(carrier_prefix_to_copy); vvm_std = g_strdup(vvm_std_to_copy); } g_free(modem->vvm_type); modem->vvm_type = g_strdup(vvm_std); g_key_file_set_string(modem->modemsettings, SETTINGS_GROUP_MODEMMANAGER, "VVMType", modem->vvm_type); g_free(modem->vvm_destination_number); modem->vvm_destination_number = g_strdup(dest_num); g_key_file_set_string(modem->modemsettings, SETTINGS_GROUP_MODEMMANAGER, "VVMDestinationNumber", modem->vvm_destination_number); g_free(modem->carrier_prefix); modem->carrier_prefix = g_strdup(carrier_prefix); g_key_file_set_string(modem->modemsettings, SETTINGS_GROUP_MODEMMANAGER, "CarrierPrefix", modem->carrier_prefix); vvm_settings_sync(IDENTIFIER, SETTINGS_STORE, modem->modemsettings); g_dbus_connection_emit_signal(connection, NULL, VVM_PATH, VVM_MODEMMANAGER_INTERFACE, "SettingsChanged", g_variant_new("(sss)", modem->vvm_type, modem->vvm_destination_number, modem->carrier_prefix), NULL); } static void vvmd_modem_available(void) { g_autoptr(GError) error = NULL; modem->modem_available = TRUE; if(modem->modem) { const gchar *const *modem_number_ref; if(modem->plugin_registered == FALSE) { DBG("Registering Modem Manager VVM Service"); vvm_service_register(modem->service); modem->modemsettings = vvm_service_get_keyfile(modem->service); modem->plugin_registered = TRUE; } MmGdbusModem *gdbus_modem; modem->sim = mm_modem_get_sim_sync(modem->modem, NULL, &error); if(error == NULL) { modem->imsi = mm_sim_dup_imsi(modem->sim); if (g_strcmp0 (modem->imsi, modem->registered_imsi) != 0) { if (g_strcmp0 (modem->registered_imsi, "invalid") != 0) { GDBusConnection *connection = vvm_dbus_get_connection (); DBG("IMSI different from registered, Disabling VVM and resetting settings"); modem->enable_vvm = FALSE; g_key_file_set_boolean(modem->modemsettings, SETTINGS_GROUP_MODEMMANAGER, "VVMEnabled", modem->enable_vvm); vvm_service_deactivate_vvm_imap_server(modem->service); g_free(modem->vvm_type); modem->vvm_type = g_strdup("type_invalid"); g_key_file_set_string(modem->modemsettings, SETTINGS_GROUP_MODEMMANAGER, "VVMType", modem->vvm_type); g_free(modem->carrier_prefix); modem->carrier_prefix = g_strdup("//VVM"); g_key_file_set_string(modem->modemsettings, SETTINGS_GROUP_MODEMMANAGER, "CarrierPrefix", modem->carrier_prefix); g_free(modem->vvm_destination_number); modem->vvm_destination_number = g_strdup("number_invalid"); g_key_file_set_string(modem->modemsettings, SETTINGS_GROUP_MODEMMANAGER, "VVMDestinationNumber", modem->vvm_destination_number); modem->provision_status = VVM_PROVISION_STATUS_NOT_SET; emit_provision_status_changed("Not Set"); g_key_file_set_integer(modem->modemsettings, SETTINGS_GROUP_MODEMMANAGER, "ProvisionStatus", modem->provision_status); g_dbus_connection_emit_signal(connection, NULL, VVM_PATH, VVM_MODEMMANAGER_INTERFACE, "SettingsChanged", g_variant_new("(sss)", modem->vvm_type, modem->vvm_destination_number, modem->carrier_prefix), NULL); } g_free(modem->registered_imsi); modem->registered_imsi = g_strdup(modem->imsi); g_key_file_set_string(modem->modemsettings, SETTINGS_GROUP_MODEMMANAGER, "IMSI", modem->registered_imsi); vvm_settings_sync(IDENTIFIER, SETTINGS_STORE, modem->modemsettings); } vvm_service_set_country_code(modem->service, mm_sim_get_imsi(modem->sim)); if (strstr (modem->vvm_type, "invalid")) { g_free (modem->mccmnc); modem->mccmnc = mm_sim_dup_operator_identifier (modem->sim); if (!modem->mccmnc) modem->mccmnc = g_strndup(modem->imsi, 6); DBG("Attempting to autopopulate VVM carrier settings"); vvmd_service_providers_find_settings(MOBILE_BROADBAND_PROVIDER_INFO_DATABASE, modem->mccmnc, find_settings_cb, NULL); } else { DBG("VVM carrier settings already set"); } } else { vvm_error ("Error Getting Sim: %s", error->message); } modem_number_ref = mm_modem_get_own_numbers (modem->modem); if (modem_number_ref != NULL) { vvm_service_set_own_number(modem->service, *modem_number_ref); } else { vvm_error("Could not get modem number!"); } vvmd_connect_to_sms_wap(); if (modem->provision_status == VVM_PROVISION_STATUS_NOT_SET || modem->provision_status == VVM_PROVISION_STATUS_UNKNOWN || modem->provision_status == VVM_PROVISION_STATUS_BLOCKED) { DBG("Checking status of carrier subscription"); vvmd_check_subscription_status(); } else if (modem->provision_status == VVM_PROVISION_STATUS_PROVISIONED) { if (modem->enable_vvm) { DBG("Subscribing to your carrier's VVM service"); vvmd_subscribe_service(); } else { DBG("VVM is disabled, not subscribing to your carrier's VVM service"); } } else if (modem->provision_status == VVM_PROVISION_STATUS_NEW || modem->provision_status == VVM_PROVISION_STATUS_READY) { if (!modem->enable_vvm) { vvmd_unsubscribe_service(); DBG("Unsubscribing to your carrier's VVM service"); } else { DBG("VVM is enabled and subscribed!"); } } gdbus_modem = MM_GDBUS_MODEM(modem->modem); modem->modem_state_watch_id = g_signal_connect(gdbus_modem, "state-changed", G_CALLBACK(modem_state_changed_cb), NULL); modem->state = mm_modem_get_state(modem->modem); vvmd_mm_get_modem_state(); } else { vvm_error("Something very bad happened at vvmd_modem_available()!"); } } static void vvmd_modem_unavailable(void) { MmGdbusModem *gdbus_modem; gdbus_modem = MM_GDBUS_MODEM(modem->modem); vvmd_disconnect_from_sms_wap(); g_signal_handler_disconnect(gdbus_modem, modem->modem_state_watch_id); g_object_unref(modem->sim); g_clear_pointer (&modem->mccmnc, g_free); g_clear_pointer (&modem->imsi, g_free); g_clear_pointer (&modem->path, g_free); g_clear_object(&modem->modem); g_clear_object(&modem->modem_messaging); modem->object = NULL; if(modem->device_arr && modem->device_arr->len) { g_ptr_array_set_size(modem->device_arr, 0); g_ptr_array_unref(modem->device_arr); } modem->modem_available = FALSE; modem->modem_ready = FALSE; } static int modemmanager_init(void) { GDBusConnection *connection = vvm_dbus_get_connection (); g_autoptr(GError) error = NULL; DBG("Starting Modem Manager Plugin!"); // Set up modem Structure to be used here modem = g_try_new0(struct modem_data, 1); if(modem == NULL) { vvm_error("Could not allocate space for modem data!"); return -ENOMEM; } modem->service = vvm_service_create(); vvm_service_set_identity(modem->service, IDENTIFIER); modem->modemsettings = vvm_settings_open(IDENTIFIER, SETTINGS_STORE); modem->enable_vvm = g_key_file_get_boolean(modem->modemsettings, SETTINGS_GROUP_MODEMMANAGER, "VVMEnabled", &error); if(error) { vvm_error("Enabling Visual Voicemail was not configured! Setting to FALSE."); modem->enable_vvm = FALSE; g_key_file_set_boolean(modem->modemsettings, SETTINGS_GROUP_MODEMMANAGER, "VVMEnabled", modem->enable_vvm); g_clear_error(&error); } modem->vvm_type = g_key_file_get_string(modem->modemsettings, SETTINGS_GROUP_MODEMMANAGER, "VVMType", &error); if(error) { modem->vvm_type = g_strdup("type_invalid"); g_key_file_set_string(modem->modemsettings, SETTINGS_GROUP_MODEMMANAGER, "VVMType", modem->vvm_type); g_clear_error(&error); } modem->vvm_destination_number = g_key_file_get_string(modem->modemsettings, SETTINGS_GROUP_MODEMMANAGER, "VVMDestinationNumber", &error); if(error) { modem->vvm_destination_number = g_strdup("number_invalid"); g_key_file_set_string(modem->modemsettings, SETTINGS_GROUP_MODEMMANAGER, "VVMDestinationNumber", modem->vvm_destination_number); g_clear_error(&error); } modem->carrier_prefix = g_key_file_get_string(modem->modemsettings, SETTINGS_GROUP_MODEMMANAGER, "CarrierPrefix", &error); if(error) { modem->carrier_prefix = g_strdup("//VVM"); g_key_file_set_string(modem->modemsettings, SETTINGS_GROUP_MODEMMANAGER, "CarrierPrefix", modem->carrier_prefix); g_clear_error(&error); } modem->default_number = g_key_file_get_string(modem->modemsettings, SETTINGS_GROUP_MODEMMANAGER, "DefaultModemNumber", &error); if(error) { vvm_error("No Default Modem Number was configured! Setting to NULL."); modem->default_number = g_strdup("NULL"); g_key_file_set_string(modem->modemsettings, SETTINGS_GROUP_MODEMMANAGER, "DefaultModemNumber", modem->default_number); g_clear_error(&error); } if(g_strcmp0(modem->default_number, "NULL") == 0) { g_free(modem->default_number); modem->default_number = NULL; } modem->provision_status = g_key_file_get_integer(modem->modemsettings, SETTINGS_GROUP_MODEMMANAGER, "ProvisionStatus", &error); //If the user did not enable VVM, set provision status to "Not Set" if(error) { modem->provision_status = VVM_PROVISION_STATUS_NOT_SET; g_key_file_set_integer(modem->modemsettings, SETTINGS_GROUP_MODEMMANAGER, "ProvisionStatus", modem->provision_status); g_clear_error(&error); } modem->registered_imsi = g_key_file_get_string(modem->modemsettings, SETTINGS_GROUP_MODEMMANAGER, "IMSI", &error); if(error) { modem->registered_imsi = g_strdup("invalid"); g_key_file_set_string(modem->modemsettings, SETTINGS_GROUP_MODEMMANAGER, "IMSI", modem->registered_imsi); g_clear_error(&error); } vvm_settings_close(IDENTIFIER, SETTINGS_STORE, modem->modemsettings, TRUE); modem->modemsettings = NULL; introspection_data = g_dbus_node_info_new_for_xml(introspection_xml_modemmanager, NULL); g_assert(introspection_data != NULL); modem->modem_available = FALSE; modem->modem_ready = FALSE; modem->manager_available = FALSE; modem->context_active = FALSE; modem->plugin_registered = FALSE; modem->get_all_sms_timeout = FALSE; modem->registration_id = g_dbus_connection_register_object(connection, VVM_PATH, introspection_data->interfaces[0], &interface_vtable, NULL, NULL, &error); if(error) { vvm_error("Error registering VVMD ModemManager interface: %s\n", error->message); g_clear_error(&error); } modem->mm_watch_id = g_bus_watch_name(G_BUS_TYPE_SYSTEM, MM_DBUS_SERVICE, G_BUS_NAME_WATCHER_FLAGS_AUTO_START, (GBusNameAppearedCallback) mm_appeared_cb, (GBusNameVanishedCallback) mm_vanished_cb, NULL, NULL); return 0; } static void modemmanager_exit(void) { GDBusConnection *connection = vvm_dbus_get_connection (); if(modem->plugin_registered == TRUE) { vvm_service_unregister(modem->service); modem->modemsettings = NULL; } if(modem->modem_available) { vvmd_mm_state(VVMD_MM_STATE_NO_MODEM); } if(modem->manager_available) { vvmd_mm_state(VVMD_MM_STATE_NO_MANAGER); } g_dbus_connection_unregister_object(connection, modem->registration_id); g_free(modem->carrier_prefix); g_free(modem->default_number); g_free(modem->vvm_type); g_free(modem->vvm_destination_number); g_free(modem); g_dbus_node_info_unref(introspection_data); } VVM_PLUGIN_DEFINE(modemmanager, modemmanager_init, modemmanager_exit) vvmd-0.7/python examples/000077500000000000000000000000001415226775200154675ustar00rootroot00000000000000vvmd-0.7/python examples/test.py000066400000000000000000000004451415226775200170230ustar00rootroot00000000000000import pydbus import gi.repository from pydbus import SessionBus from gi.repository import GLib bus = SessionBus() TestServer = bus.get("org.kop316.vvm", "/org/kop316/vvm/modemmanager") #TestServer.ChangeSettings("DefaultModemNumber", GLib.Variant('s', "+379879")) #TestServer.GetMessages() vvmd-0.7/src/000077500000000000000000000000001415226775200131365ustar00rootroot00000000000000vvmd-0.7/src/dbus.c000066400000000000000000000026661415226775200142510ustar00rootroot00000000000000/* * * Visual Voicemail Daemon * * Copyright (C) 2021, Chris Talbot * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * */ #ifdef HAVE_CONFIG_H #include #endif #include #include "dbus.h" #include "vvm.h" static GDBusConnection *connection; static GDBusNodeInfo *introspection_data = NULL; GDBusConnection *vvm_dbus_get_connection(void) { return connection; } void __vvm_dbus_set_connection(GDBusConnection *conn) { connection = conn; } GDBusNodeInfo *vvm_dbus_get_introspection_data(void) { return introspection_data; } void __vvm_dbus_set_introspection_data(void) { introspection_data = g_dbus_node_info_new_for_xml (introspection_xml, NULL); g_assert (introspection_data != NULL); } void __vvm_dbus_unref_introspection_data(void) { g_dbus_node_info_unref (introspection_data); } vvmd-0.7/src/dbus.h000066400000000000000000000057701415226775200142550ustar00rootroot00000000000000/* * * Visual Voicemail Daemon * * Copyright (C) 2021, Chris Talbot * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * */ #include #define VVM_SERVICE "org.kop316.vvm" #define VVM_PATH "/org/kop316/vvm" #define VVM_MANAGER_INTERFACE VVM_SERVICE ".Manager" #define VVM_SERVICE_INTERFACE VVM_SERVICE ".Service" #define VVM_MESSAGE_INTERFACE VVM_SERVICE ".Message" #define VVM_ERROR_INTERFACE VVM_SERVICE ".Error" GDBusConnection *vvm_dbus_get_connection(void); GDBusNodeInfo *vvm_dbus_get_introspection_data(void); void __vvm_dbus_set_connection(GDBusConnection *conn); void __vvm_dbus_set_introspection_data(void); void __vvm_dbus_unref_introspection_data(void); /* ---------------------------------------------------------------------------------------------------- */ /* * GIO Dbus Server modified from here: * https://gitlab.gnome.org/GNOME/glib/-/blob/master/gio/tests/gdbus-example-server.c */ /* Introspection data for the service we are exporting */ static const gchar introspection_xml[] = "" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " ""; /* ---------------------------------------------------------------------------------------------------- */ vvmd-0.7/src/genbuiltin000077500000000000000000000003421415226775200152230ustar00rootroot00000000000000#!/bin/sh for i in $* do echo "extern struct vvm_plugin_desc __vvm_builtin_$i;" done echo echo "static struct vvm_plugin_desc *__vvm_builtin[] = {" for i in $* do echo " &__vvm_builtin_$i," done echo " NULL" echo "};" vvmd-0.7/src/itu-e212-iso.c000066400000000000000000000117051415226775200153460ustar00rootroot00000000000000/* * * Visual Voicemail Daemon * * Copyright (C) 2020, Purism SPC * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 or later as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * */ #ifdef HAVE_CONFIG_H #include #endif #include struct mcc_list { guint mcc; char code[3]; }; /* * Extracted from: * https://www.itu.int/dms_pub/itu-t/opb/sp/T-SP-E.212B-2018-PDF-E.pdf */ struct mcc_list mcc_list[] = { {202, "GR"}, {204, "NL"}, {206, "BE"}, {208, "FR"}, {212, "MC"}, {213, "AD"}, {214, "ES"}, {216, "HU"}, {218, "BA"}, {219, "HR"}, {220, "RS"}, {221, "XK"}, {222, "IT"}, {225, "VA"}, {226, "RO"}, {228, "CH"}, {230, "CZ"}, {231, "SK"}, {232, "AT"}, {234, "GB"}, {235, "GB"}, {238, "DK"}, {240, "SE"}, {242, "NO"}, {244, "FI"}, {246, "LT"}, {247, "LV"}, {248, "EE"}, {250, "RU"}, {255, "UA"}, {257, "BY"}, {259, "MD"}, {260, "PL"}, {262, "DE"}, {266, "GI"}, {268, "PT"}, {270, "LU"}, {272, "IE"}, {274, "IS"}, {276, "AL"}, {278, "MT"}, {280, "CY"}, {282, "GE"}, {283, "AM"}, {284, "BG"}, {286, "TR"}, {288, "FO"}, {290, "GL"}, {292, "SM"}, {293, "SI"}, {294, "MK"}, {295, "LI"}, {297, "ME"}, {302, "CA"}, {308, "PM"}, {310, "US"}, {311, "US"}, {312, "US"}, {313, "US"}, {314, "US"}, {315, "US"}, {316, "US"}, {330, "PR"}, {332, "VI"}, {334, "MX"}, {338, "JM"}, /* Guadeloupe and Martinique are part of France */ {340, "GP"}, {340, "MQ"}, {342, "BB"}, {344, "AG"}, {346, "KY"}, {348, "VG"}, {350, "BM"}, {352, "GD"}, {354, "MS"}, {356, "KN"}, {358, "LC"}, {360, "VC"}, {362, "CW"}, {363, "AW"}, {364, "BS"}, {365, "AI"}, {366, "DM"}, {368, "CU"}, {370, "DO"}, {372, "HT"}, {374, "TT"}, {376, "TC"}, {400, "AZ"}, {401, "KZ"}, {402, "BT"}, {404, "IN"}, {405, "IN"}, {406, "IN"}, {410, "PK"}, {412, "AF"}, {413, "LK"}, {414, "MM"}, {415, "LB"}, {416, "JO"}, {417, "SY"}, {418, "IQ"}, {419, "KW"}, {420, "SA"}, {421, "YE"}, {422, "OM"}, {424, "AE"}, {425, "IL"}, {426, "BH"}, {427, "QA"}, {428, "MN"}, {429, "NP"}, {430, "AE"}, {431, "AE"}, {432, "IR"}, {434, "UZ"}, {436, "TJ"}, {437, "KG"}, {438, "TM"}, {440, "JP"}, {441, "JP"}, {450, "KP"}, {452, "VN"}, {454, "HK"}, {455, "MO"}, {456, "KH"}, {457, "LA"}, {460, "CN"}, {461, "CN"}, {466, "TW"}, {467, "KR"}, {470, "BD"}, {472, "MV"}, {502, "MY"}, {505, "AU"}, {510, "ID"}, {514, "TL"}, {515, "PH"}, {520, "TH"}, {525, "SG"}, {528, "BN"}, {530, "NZ"}, {536, "NR"}, {537, "PG"}, {539, "TO"}, {540, "SB"}, {541, "VU"}, {542, "FJ"}, {543, "WF"}, {544, "AS"}, {545, "KI"}, {546, "NC"}, {547, "PF"}, {548, "CK"}, {549, "AS"}, {550, "FM"}, {551, "MH"}, {552, "PW"}, {553, "TV"}, {554, "TK"}, {555, "NU"}, {602, "EG"}, {603, "DZ"}, {604, "MA"}, {605, "TN"}, {606, "LY"}, {607, "GM"}, {608, "SN"}, {609, "MR"}, {610, "ML"}, {611, "GN"}, {612, "CI"}, {613, "BF"}, {614, "NE"}, {615, "TG"}, {616, "BJ"}, {617, "MU"}, {618, "LR"}, {619, "SL"}, {620, "GH"}, {621, "NG"}, {622, "TD"}, {623, "CF"}, {624, "CM"}, {625, "CV"}, {626, "ST"}, {627, "GQ"}, {628, "GA"}, {629, "CG"}, {630, "CD"}, {631, "AO"}, {632, "GW"}, {633, "SC"}, {634, "SD"}, {635, "RW"}, {636, "ET"}, {637, "SO"}, {638, "DJ"}, {639, "KE"}, {640, "TZ"}, {641, "UG"}, {642, "BI"}, {643, "MZ"}, {645, "ZM"}, {646, "MG"}, {647, "RE"}, {648, "ZW"}, {649, "NA"}, {650, "MW"}, {651, "LS"}, {652, "BW"}, {653, "SZ"}, {654, "KM"}, {655, "ZA"}, {657, "ER"}, {658, "SH"}, {659, "SS"}, {702, "BZ"}, {704, "GT"}, {706, "SV"}, {708, "HN"}, {710, "NI"}, {712, "CR"}, {714, "PA"}, {716, "PE"}, {722, "AR"}, {724, "BR"}, {730, "CL"}, {732, "CO"}, {734, "VE"}, {736, "BO"}, {738, "GY"}, {740, "EC"}, {742, "GF"}, {744, "PY"}, {746, "SR"}, {748, "UY"}, {750, "FK"}, }; const char * get_country_iso_for_mcc (const char *mcc_str) { g_autofree char *str = NULL; guint64 mcc; if (!mcc_str || strlen (mcc_str) < 3) return NULL; str = g_strndup (mcc_str, 3); mcc = g_ascii_strtoull (str, NULL, 10); for (guint i = 0; i < G_N_ELEMENTS (mcc_list); i++) if (mcc_list[i].mcc == mcc) return mcc_list[i].code; g_warning ("invalid MCC code: %" G_GUINT64_FORMAT, mcc); return NULL; } vvmd-0.7/src/itu-e212-iso.h000066400000000000000000000014761415226775200153570ustar00rootroot00000000000000/* * * Visual Voicemail Daemon * * Copyright (C) 2020, Purism SPC * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 or later as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * */ #include const char * get_country_iso_for_mcc (const char *mcc_str); vvmd-0.7/src/log.c000066400000000000000000000052001415226775200140600ustar00rootroot00000000000000/* * * Visual Voicemail Daemon * * Copyright (C) 2010-2011, Intel Corporation * 2021, Chris Talbot * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include "vvm.h" void vvm_info(const char *format, ...) { va_list ap; va_start(ap, format); vsyslog(LOG_INFO, format, ap); va_end(ap); } void vvm_warn(const char *format, ...) { va_list ap; va_start(ap, format); vsyslog(LOG_WARNING, format, ap); va_end(ap); } void vvm_error(const char *format, ...) { va_list ap; va_start(ap, format); vsyslog(LOG_ERR, format, ap); va_end(ap); } void vvm_debug(const char *format, ...) { va_list ap; va_start(ap, format); vsyslog(LOG_DEBUG, format, ap); va_end(ap); } extern struct vvm_debug_desc __start___debug[]; extern struct vvm_debug_desc __stop___debug[]; static gchar **enabled = NULL; static gboolean is_enabled(struct vvm_debug_desc *desc) { int i; if (enabled == NULL) return FALSE; for (i = 0; enabled[i] != NULL; i++) { if (desc->name != NULL && g_pattern_match_simple(enabled[i], desc->name) == TRUE) return TRUE; if (desc->file != NULL && g_pattern_match_simple(enabled[i], desc->file) == TRUE) return TRUE; } return FALSE; } int __vvm_log_init(const char *debug) { int option = LOG_NDELAY | LOG_PID; struct vvm_debug_desc *desc; const char *name = NULL, *file = NULL; if (debug != NULL) enabled = g_strsplit_set(debug, ":, ", 0); for (desc = __start___debug; desc < __stop___debug; desc++) { if (file != NULL || name != NULL) { if (g_strcmp0(desc->file, file) == 0) { if (desc->name == NULL) desc->name = name; } else file = NULL; } if (is_enabled(desc) == TRUE) desc->flags |= VVM_DEBUG_FLAG_PRINT; } option |= LOG_PERROR; openlog("vvmd", option, LOG_DAEMON); syslog(LOG_INFO, "vvmd version %s", VERSION); return 0; } void __vvm_log_cleanup(void) { syslog(LOG_INFO, "Exit"); closelog(); g_strfreev(enabled); } vvmd-0.7/src/log.h000066400000000000000000000032131415226775200140670ustar00rootroot00000000000000/* * * Visual Voicemail Daemon * * Copyright (C) 2010-2011, Intel Corporation * 2021, Chris Talbot * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * */ void vvm_info(const char *format, ...) __attribute__((format(printf, 1, 2))); void vvm_warn(const char *format, ...) __attribute__((format(printf, 1, 2))); void vvm_error(const char *format, ...) __attribute__((format(printf, 1, 2))); void vvm_debug(const char *format, ...) __attribute__((format(printf, 1, 2))); struct vvm_debug_desc { const char *name; const char *file; #define VVM_DEBUG_FLAG_DEFAULT (0) #define VVM_DEBUG_FLAG_PRINT (1 << 0) unsigned int flags; } __attribute__((aligned(8))); #define DBG(fmt, arg...) do { \ static struct vvm_debug_desc __vvm_debug_desc \ __attribute__((used, section("__debug"), aligned(8))) = { \ .file = __FILE__, .flags = VVM_DEBUG_FLAG_DEFAULT, \ }; \ if (__vvm_debug_desc.flags & VVM_DEBUG_FLAG_PRINT) \ vvm_debug("%s:%s() " fmt, \ __FILE__, __FUNCTION__ , ## arg); \ } while (0) vvmd-0.7/src/main.c000066400000000000000000000070761415226775200142400ustar00rootroot00000000000000/* * * Visual Voicemail Daemon * * Copyright (C) 2010-2011, Intel Corporation * 2021, Chris Talbot * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include #include "vvm.h" #include "dbus.h" static GMainLoop *main_loop = NULL; static volatile sig_atomic_t __terminated = 0; static void sig_term(int sig) { if (__terminated > 0) return; __terminated = 1; vvm_info("Terminating"); g_main_loop_quit(main_loop); } static gchar *option_debug = NULL; static gboolean option_version = FALSE; static gboolean global_debug = FALSE; static gboolean parse_debug(const char *key, const char *value, gpointer user_data, GError **error) { if (value) option_debug = g_strdup(value); else option_debug = g_strdup("*"); global_debug = TRUE; return TRUE; } static GOptionEntry options[] = { { "debug", 'd', G_OPTION_FLAG_OPTIONAL_ARG, G_OPTION_ARG_CALLBACK, parse_debug, "Specify debug options to enable", "DEBUG" }, { "version", 'v', 0, G_OPTION_ARG_NONE, &option_version, "Show version information and exit" }, { NULL }, }; static void on_bus_acquired (GDBusConnection *connection, const gchar *name, gpointer user_data) { DBG("Dbus Bus acquired!"); __vvm_dbus_set_connection(connection); __vvm_service_init(global_debug); __vvm_plugin_init(); } static void on_name_acquired (GDBusConnection *connection, const gchar *name, gpointer user_data) { DBG("Dbus name acquired!"); } static void on_name_lost (GDBusConnection *connection, const gchar *name, gpointer user_data) { vvm_error("Lost Dbus Connection! Exiting...."); exit (1); } int main(int argc, char *argv[]) { GOptionContext *context; GError *error = NULL; struct sigaction sa; guint owner_id; context = g_option_context_new(NULL); g_option_context_add_main_entries(context, options, NULL); if (g_option_context_parse(context, &argc, &argv, &error) == FALSE) { if (error != NULL) { g_printerr("%s\n", error->message); g_error_free(error); } else g_printerr("An unknown error occurred\n"); exit(1); } g_option_context_free(context); if (option_version == TRUE) { printf("%s\n", VERSION); exit(0); } main_loop = g_main_loop_new(NULL, FALSE); __vvm_dbus_set_introspection_data(); owner_id = g_bus_own_name (G_BUS_TYPE_SESSION, VVM_SERVICE, G_BUS_NAME_OWNER_FLAGS_NONE, on_bus_acquired, on_name_acquired, on_name_lost, NULL, NULL); __vvm_log_init(option_debug); memset(&sa, 0, sizeof(sa)); sa.sa_handler = sig_term; sigaction(SIGINT, &sa, NULL); sigaction(SIGTERM, &sa, NULL); g_main_loop_run(main_loop); __vvm_plugin_cleanup(); __vvm_service_cleanup(); __vvm_log_cleanup(); g_bus_unown_name (owner_id); __vvm_dbus_unref_introspection_data (); g_main_loop_unref(main_loop); return 0; } vvmd-0.7/src/meson.build000066400000000000000000000010721415226775200153000ustar00rootroot00000000000000vvm_sources = [ 'dbus.c', 'service.c', 'store.c', 'log.c', 'plugin.c', 'itu-e212-iso.c', 'service-providers.c', 'vvmutil.c', 'phone-utils.cpp' ] builtin_plugins = [ 'modemmanager' ] genbuiltin = find_program('genbuiltin') if genbuiltin.found() builtin = custom_target('generate builtin.h', output : 'builtin.h', capture : true, command : [genbuiltin] + builtin_plugins ) vvm_sources = vvm_sources + [builtin] endif vvm_lib = static_library('vvm', vvm_sources, include_directories : includes, dependencies : dependencies, ) vvmd-0.7/src/phone-utils.cpp000066400000000000000000000105121415226775200161100ustar00rootroot00000000000000/* * * Visual Voicemail Daemon * * Copyright (C) 2012, 2013 Intel Corporation * 2021, Chris Talbot * * This library is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library. If not, see . * * Authors: Mathias Hasselmann */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #include #include #include "phone-utils.h" using i18n::phonenumbers::PhoneNumber; using i18n::phonenumbers::PhoneNumberUtil; struct EPhoneNumber { PhoneNumber priv; }; typedef enum { E_PHONE_NUMBER_FORMAT_E164, E_PHONE_NUMBER_FORMAT_INTERNATIONAL, E_PHONE_NUMBER_FORMAT_NATIONAL, E_PHONE_NUMBER_FORMAT_RFC3966 } EPhoneNumberFormat; static PhoneNumberUtil * e_phone_number_util_get_instance (void) { static PhoneNumberUtil *instance = NULL; if (g_once_init_enter (&instance)) { /* FIXME: Ideally PhoneNumberUtil would not be a singleton, * so that we could safely tweak its attributes without * influencing other users of the library. */ PhoneNumberUtil *new_instance = PhoneNumberUtil::GetInstance (); /* Disable all logging: libphonenumber is pretty verbose. */ new_instance->SetLogger (new i18n::phonenumbers::NullLogger); g_once_init_leave (&instance, new_instance); } return instance; } static bool _phone_utils_cxx_parse (const std::string &phone_number, const std::string ®ion, PhoneNumber *parsed_number) { const PhoneNumberUtil::ErrorType err = e_phone_number_util_get_instance ()->Parse ( phone_number, region, parsed_number); if (err != PhoneNumberUtil::NO_PARSING_ERROR) { return false; } return true; } static char * _phone_utils_cxx_to_string (const EPhoneNumber *phone_number) { g_return_val_if_fail (NULL != phone_number, NULL); std::string formatted_number; e_phone_number_util_get_instance ()->Format (phone_number->priv, static_cast (E_PHONE_NUMBER_FORMAT_E164), &formatted_number); if (!formatted_number.empty ()) return g_strdup (formatted_number.c_str ()); return NULL; } static char * phone_utils_format_e164 (const char *phone_number, const char *region_code) { char *returned_string; EPhoneNumber *intermediate; if (!phone_number || !*phone_number || !region_code || strlen (region_code) != 2) return NULL; std::unique_ptr parsed_number(new EPhoneNumber); if (!_phone_utils_cxx_parse ( phone_number, region_code, &parsed_number->priv)) return NULL; intermediate = parsed_number.release (); returned_string = _phone_utils_cxx_to_string (intermediate); delete intermediate; return returned_string; } /* * phone_utils_format_number_e164() does two things: * * - To see if a number is valid (this does this with return_original_number set to FALSE), and * - To make sure that the valid number is in the E.164 format. * * If you see that return_original_number set to FALSE, the caller is checking * if the number is valid. If the number is valid, the number is returned in * the e.164 format. * * If return_original_number is TRUE, then the caller does not care if the * number is a real number (there are cases like one can send an email to * an MMS, so the "number" could be "foo@bar.com", and I don't want this * function to mess that up) and just wants to make sure that if there * is a number, it is properly formatted. * * No matter what, the string is newly allocated. * */ char *phone_utils_format_number_e164(const char *number, const char *country_code, gboolean return_original_number) { char *formatted_number; formatted_number = phone_utils_format_e164 (number, country_code); if (formatted_number == NULL) { if (return_original_number == FALSE) { return NULL; } else { return g_strdup(number); } } return formatted_number; } vvmd-0.7/src/phone-utils.h000066400000000000000000000020021415226775200155500ustar00rootroot00000000000000/* * * Visual Voicemail Daemon * * Copyright (C) 2012, 2013 Intel Corporation * 2021, Chris Talbot * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 or later as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * */ #pragma once #include G_BEGIN_DECLS gchar *phone_utils_format_number_e164 (const char *number, const char *country_code, gboolean return_original_number); G_END_DECLS vvmd-0.7/src/plugin.c000066400000000000000000000055031415226775200146030ustar00rootroot00000000000000/* * * Visual Voicemail Daemon * * Copyright (C) 2010-2011, Intel Corporation * 2021, Chris Talbot * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include "vvm.h" static GSList *plugins = NULL; struct vvm_plugin { void *handle; struct vvm_plugin_desc *desc; }; static gboolean add_plugin(void *handle, struct vvm_plugin_desc *desc) { struct vvm_plugin *plugin; if (desc->init == NULL) return FALSE; plugin = g_try_new0(struct vvm_plugin, 1); if (plugin == NULL) return FALSE; plugin->handle = handle; plugin->desc = desc; if (desc->init() < 0) { g_free(plugin); return FALSE; } plugins = g_slist_append(plugins, plugin); DBG("Plugin %s loaded", desc->name); return TRUE; } #include "builtin.h" int __vvm_plugin_init(void) { GDir *dir; const char *file; unsigned int i; if (strlen(PLUGINDIR) == 0) return -EINVAL; DBG(""); for (i = 0; __vvm_builtin[i]; i++) add_plugin(NULL, __vvm_builtin[i]); dir = g_dir_open(PLUGINDIR, 0, NULL); if (dir == NULL) return -EIO; while ((file = g_dir_read_name(dir)) != NULL) { struct vvm_plugin_desc *desc; void *handle; char *filename; if (g_str_has_prefix(file, "lib") == TRUE || g_str_has_suffix(file, ".so") == FALSE) continue; filename = g_build_filename(PLUGINDIR, file, NULL); handle = dlopen(filename, RTLD_NOW); if (handle == NULL) { vvm_error("Can't load plugin %s: %s", filename, dlerror()); g_free(filename); continue; } g_free(filename); desc = dlsym(handle, "vvm_plugin_desc"); if (desc == NULL) { vvm_error("Can't load plugin description: %s", dlerror()); dlclose(handle); continue; } if (add_plugin(handle, desc) == FALSE) dlclose(handle); } g_dir_close(dir); return 0; } void __vvm_plugin_cleanup(void) { GSList *list; DBG(""); for (list = plugins; list; list = list->next) { struct vvm_plugin *plugin = list->data; if (plugin->desc->exit) plugin->desc->exit(); if (plugin->handle != NULL) dlclose(plugin->handle); g_free(plugin); } g_slist_free(plugins); } vvmd-0.7/src/plugin.h000066400000000000000000000024121415226775200146040ustar00rootroot00000000000000/* * * Visual Voicemail Daemon * * Copyright (C) 2010-2011, Intel Corporation * 2021, Chris Talbot * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * */ struct vvm_plugin_desc { const char *name; int (*init) (void); void (*exit) (void); }; #ifdef VVM_PLUGIN_BUILTIN #define VVM_PLUGIN_DEFINE(name, init, exit) \ struct vvm_plugin_desc __vvm_builtin_ ## name = { \ #name, init, exit \ }; #else #define VVM_PLUGIN_DEFINE(name, init, exit) \ extern struct vvm_plugin_desc vvm_plugin_desc \ __attribute__ ((visibility("default"))); \ struct vvm_plugin_desc vvm_plugin_desc = { \ #name, init, exit \ }; #endif vvmd-0.7/src/service-providers.c000066400000000000000000000276151415226775200167700ustar00rootroot00000000000000/* * * Multimedia Messaging Service Daemon - The Next Generation * * Copyright (C) 2009, Novell, Inc. * Author: Tambet Ingo (tambet@gmail.com). * 2009-2019, Red Hat, Inc. * 2012, Lanedo GmbH * 2021, Chris Talbot * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include "service-providers.h" #include "vvm.h" typedef enum { PARSER_TOPLEVEL = 0, PARSER_COUNTRY, PARSER_PROVIDER, PARSER_METHOD_GSM, PARSER_METHOD_GSM_VVM, PARSER_METHOD_CDMA, PARSER_DONE, PARSER_ERROR } ParseContextState; typedef struct { char *mccmnc; GMarkupParseContext *ctx; char buffer[4096]; VvmdServiceProvidersCallback callback; gpointer user_data; char *text_buffer; ParseContextState state; gboolean mccmnc_matched; char *carrier; char *vvm_std; char *dest_num; char *carrier_prefix; } ParseContext; /*****************************************************************************/ static void parser_toplevel_start(ParseContext *parse_context, const char *name, const char **attribute_names, const char **attribute_values) { int i; if (strcmp(name, "serviceproviders") == 0) { for (i = 0; attribute_names && attribute_names[i]; i++) { if (strcmp(attribute_names[i], "format") == 0) { if (strcmp(attribute_values[i], "2.0")) { g_warning("%s: mobile broadband provider database format '%s'" " not supported.", __func__, attribute_values[i]); parse_context->state = PARSER_ERROR; break; } } } } else if (strcmp(name, "country") == 0) { parse_context->state = PARSER_COUNTRY; } } static void parser_country_start(ParseContext *parse_context, const char *name, const char **attribute_names, const char **attribute_values) { if (strcmp(name, "provider") == 0) { parse_context->state = PARSER_PROVIDER; } } static void parser_provider_start(ParseContext *parse_context, const char *name, const char **attribute_names, const char **attribute_values) { parse_context->mccmnc_matched = FALSE; if (strcmp(name, "gsm") == 0) parse_context->state = PARSER_METHOD_GSM; else if (strcmp(name, "cdma") == 0) parse_context->state = PARSER_METHOD_CDMA; } static void parser_gsm_start(ParseContext *parse_context, const char *name, const char **attribute_names, const char **attribute_values) { int i; if (strcmp(name, "network-id") == 0) { const char *mcc = NULL, *mnc = NULL; for (i = 0; attribute_names && attribute_names[i]; i++) { if (strcmp(attribute_names[i], "mcc") == 0) mcc = attribute_values[i]; else if (strcmp(attribute_names[i], "mnc") == 0) mnc = attribute_values[i]; if (mcc && strlen(mcc) && mnc && strlen(mnc)) { char *mccmnc = g_strdup_printf("%s%s", mcc, mnc); if (strcmp(mccmnc, parse_context->mccmnc) == 0) parse_context->mccmnc_matched = TRUE; g_free(mccmnc); break; } } } else if (strcmp(name, "visual-voicemail") == 0) { parse_context->state = PARSER_METHOD_GSM_VVM; } } static void parser_gsm_vvm_start(ParseContext *parse_context, const char *name, const char **attribute_names, const char **attribute_values) { int i; if (strcmp(name, "standard") == 0) { g_clear_pointer(&parse_context->vvm_std, g_free); for (i = 0; attribute_names && attribute_names[i]; i++) { if (strcmp(attribute_names[i], "type") == 0) { parse_context->vvm_std = g_strstrip(g_strdup(attribute_values[i])); break; } } } } static void parser_start_element(GMarkupParseContext *context, const char *element_name, const char **attribute_names, const char **attribute_values, gpointer user_data, GError **error) { ParseContext *parse_context = user_data; g_clear_pointer(&parse_context->text_buffer, g_free); switch (parse_context->state) { case PARSER_TOPLEVEL: parser_toplevel_start(parse_context, element_name, attribute_names, attribute_values); break; case PARSER_COUNTRY: parser_country_start(parse_context, element_name, attribute_names, attribute_values); break; case PARSER_PROVIDER: parser_provider_start(parse_context, element_name, attribute_names, attribute_values); break; case PARSER_METHOD_GSM: parser_gsm_start(parse_context, element_name, attribute_names, attribute_values); break; case PARSER_METHOD_GSM_VVM: parser_gsm_vvm_start(parse_context, element_name, attribute_names, attribute_values); break; case PARSER_METHOD_CDMA: break; case PARSER_ERROR: break; case PARSER_DONE: break; } } static void parser_country_end(ParseContext *parse_context, const char *name) { if (strcmp(name, "country") == 0) { g_clear_pointer(&parse_context->text_buffer, g_free); parse_context->state = PARSER_TOPLEVEL; } } static void parser_provider_end(ParseContext *parse_context, const char *name) { if (strcmp(name, "name") == 0) { g_clear_pointer(&parse_context->carrier, g_free); parse_context->carrier = g_strstrip(g_strdup(parse_context->text_buffer)); g_clear_pointer(&parse_context->text_buffer, g_free); } else if (strcmp(name, "provider") == 0) { g_clear_pointer(&parse_context->text_buffer, g_free); parse_context->state = PARSER_COUNTRY; } } static void parser_gsm_end(ParseContext *parse_context, const char *name) { if (strcmp(name, "gsm") == 0) { g_clear_pointer(&parse_context->text_buffer, g_free); parse_context->state = PARSER_PROVIDER; } } static void parser_gsm_vvm_end(ParseContext *parse_context, const char *name) { if (strcmp(name, "destination-number") == 0) { g_clear_pointer(&parse_context->dest_num, g_free); parse_context->dest_num = g_steal_pointer(&parse_context->text_buffer); } else if (strcmp(name, "carrier-prefix") == 0) { g_clear_pointer(&parse_context->carrier_prefix, g_free); parse_context->carrier_prefix = g_steal_pointer(&parse_context->text_buffer); } else if (strcmp(name, "visual-voicemail") == 0) { g_clear_pointer(&parse_context->text_buffer, g_free); if (parse_context->mccmnc_matched) parse_context->state = PARSER_DONE; else parse_context->state = PARSER_METHOD_GSM; } } static void parser_cdma_end(ParseContext *parse_context, const char *name) { if (strcmp(name, "cdma") == 0) { g_clear_pointer(&parse_context->text_buffer, g_free); parse_context->state = PARSER_PROVIDER; } } static void parser_end_element(GMarkupParseContext *context, const char *element_name, gpointer user_data, GError **error) { ParseContext *parse_context = user_data; switch (parse_context->state) { case PARSER_TOPLEVEL: break; case PARSER_COUNTRY: parser_country_end(parse_context, element_name); break; case PARSER_PROVIDER: parser_provider_end(parse_context, element_name); break; case PARSER_METHOD_GSM: parser_gsm_end(parse_context, element_name); break; case PARSER_METHOD_GSM_VVM: parser_gsm_vvm_end(parse_context, element_name); break; case PARSER_METHOD_CDMA: parser_cdma_end(parse_context, element_name); break; case PARSER_ERROR: break; case PARSER_DONE: break; } } static void parser_text(GMarkupParseContext *context, const char *text, gsize text_len, gpointer user_data, GError **error) { ParseContext *parse_context = user_data; g_free(parse_context->text_buffer); parse_context->text_buffer = g_strdup(text); } static const GMarkupParser parser = { .start_element = parser_start_element, .end_element = parser_end_element, .text = parser_text, .passthrough = NULL, .error = NULL, }; /*****************************************************************************/ static void finish_parse_context(ParseContext *parse_context, GError *error) { if (parse_context->callback) { if (error) { parse_context ->callback(NULL, NULL, NULL, NULL, error, parse_context->user_data); } else { parse_context->callback(parse_context->carrier, parse_context->vvm_std, parse_context->dest_num, parse_context->carrier_prefix, error, parse_context->user_data); } } g_free(parse_context->mccmnc); g_markup_parse_context_free(parse_context->ctx); g_free(parse_context->text_buffer); g_free(parse_context->carrier); g_free(parse_context->vvm_std); g_free(parse_context->dest_num); g_free(parse_context->carrier_prefix); g_slice_free(ParseContext, parse_context); } static void read_next_chunk(GInputStream *stream, ParseContext *parse_context); static void stream_read_cb(GObject *source_object, GAsyncResult *res, gpointer user_data) { GInputStream *stream = G_INPUT_STREAM(source_object); ParseContext *parse_context = user_data; gssize len; GError *error = NULL; len = g_input_stream_read_finish(stream, res, &error); if (len == -1) { g_prefix_error(&error, "Error reading service provider database: "); finish_parse_context(parse_context, error); g_clear_error(&error); return; } if (len == 0) { g_set_error(&error, 1, 1, "Operator ID '%s' not found in service provider database", parse_context->mccmnc); finish_parse_context(parse_context, error); g_clear_error(&error); return; } if (!g_markup_parse_context_parse(parse_context->ctx, parse_context->buffer, len, &error)) { g_prefix_error(&error, "Error parsing service provider database: "); finish_parse_context(parse_context, error); g_clear_error(&error); return; } if (parse_context->state == PARSER_DONE) { finish_parse_context(parse_context, NULL); return; } read_next_chunk(stream, parse_context); } static void read_next_chunk(GInputStream *stream, ParseContext *parse_context) { g_input_stream_read_async(stream, parse_context->buffer, sizeof(parse_context->buffer), G_PRIORITY_DEFAULT, NULL, stream_read_cb, parse_context); } static void file_read_cb(GObject *source_object, GAsyncResult *res, gpointer user_data) { GFile *file = G_FILE(source_object); ParseContext *parse_context = user_data; GFileInputStream *stream; g_autoptr(GError) error = NULL; stream = g_file_read_finish(file, res, &error); if (!stream) { g_prefix_error(&error, "Error opening service provider database: "); finish_parse_context(parse_context, error); return; } read_next_chunk(G_INPUT_STREAM(stream), parse_context); g_object_unref(stream); } /*****************************************************************************/ void vvmd_service_providers_find_settings(const char *service_providers, const char *mccmnc, VvmdServiceProvidersCallback callback, gpointer user_data) { GFile *file; ParseContext *parse_context; parse_context = g_slice_new0(ParseContext); parse_context->callback = callback; parse_context->mccmnc = g_strdup(mccmnc); parse_context->user_data = user_data; parse_context->ctx = g_markup_parse_context_new(&parser, 0, parse_context, NULL); file = g_file_new_for_path(service_providers); DBG("mccmnc: %s", parse_context->mccmnc); g_file_read_async(file, G_PRIORITY_DEFAULT, NULL, file_read_cb, parse_context); g_object_unref(file); } vvmd-0.7/src/service-providers.h000066400000000000000000000032561415226775200167700ustar00rootroot00000000000000/* * * Multimedia Messaging Service Daemon - The Next Generation * * Copyright (C) 2009, Novell, Inc. * Author: Tambet Ingo (tambet@gmail.com). * 2009-2019, Red Hat, Inc. * 2012, Lanedo GmbH * 2021, Chris Talbot * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ #ifdef HAVE_CONFIG_H #include #endif typedef void (*VvmdServiceProvidersCallback)(const char *carrier, const char *vvm_std, const char *dest_num, const char *carrier_prefix, GError *error, gpointer user_data); void vvmd_service_providers_find_settings (const char *service_providers, const char *mccmnc, VvmdServiceProvidersCallback callback, gpointer user_data); vvmd-0.7/src/service.c000066400000000000000000001702251415226775200147510ustar00rootroot00000000000000/* * * Visual Voicemail Daemon * * Copyright (C) 2010-2011, Intel Corporation * 2021, Chris Talbot * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * */ #define _XOPEN_SOURCE 700 /* See https://gitlab.gnome.org/GNOME/evolution-data-server/-/issues/332#note_1107764 */ #define EDS_DISABLE_DEPRECATED #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "vvm.h" #include "dbus.h" #include "vvmutil.h" #include "phone-utils.h" #define BEARER_SETUP_TIMEOUT 20 /* 20 seconds */ #define BEARER_IDLE_TIMEOUT 10 /* 10 seconds */ #define CHUNK_SIZE 2048 /* 2 Kib */ #define DEFAULT_MAX_ATTACHMENTS_NUMBER 25 #define MAX_ATTEMPTS 3 #define DEFAULT_MAX_ATTACHMENT_TOTAL_SIZE 1100000 struct vvm_service { char *identity; char *path; char *interface; char *country_code; char *own_number; char *mailbox_interface; char *mailbox_hostname; char *mailbox_port; char *mailbox_username; char *mailbox_password; char *mailbox_auth; char *mailbox_URI; char *mailbox_language; char *mailbox_greeting_length; char *mailbox_voice_signature_length; char *mailbox_TUI_password_length; char *mailbox_vvm_type; int mailbox_starttls; vvm_service_bearer_handler_func_t bearer_handler; void *bearer_data; guint bearer_timeout; int mailbox_active; gboolean bearer_setup; gboolean bearer_active; gboolean use_mailbox_interface; GQueue *request_queue; GHashTable *messages; GKeyFile *settings; CURL *curl; }; static GList *service_list; gboolean global_debug = FALSE; guint service_registration_id; guint manager_registration_id; static void append_properties(GVariantBuilder *service_builder, struct vvm_service *service); static void emit_msg_status_changed(const char *path, const char *new_status); static void append_message_entry(char *path, const struct vvm_service *service, struct voicemail *vvm_msg, GVariantBuilder *message_builder); static int vvm_message_unregister(const struct vvm_service *service, struct voicemail *vvm_msg, guint message_registration_id); static void handle_method_call_service (GDBusConnection *connection, const gchar *sender, const gchar *object_path, const gchar *interface_name, const gchar *method_name, GVariant *parameters, GDBusMethodInvocation *invocation, gpointer user_data) { if (g_strcmp0 (method_name, "GetMessages") == 0) { struct vvm_service *service = user_data; GVariantBuilder messages_builder; GVariant *messages, *all_messages; GHashTableIter table_iter; gpointer key, value; guint i = 0; DBG("Retrieving all Messages..."); if (g_hash_table_size (service->messages) == 0) { all_messages = g_variant_new("(a(oa{sv}))", NULL); DBG("No Messages!"); } else { g_variant_builder_init(&messages_builder, G_VARIANT_TYPE ("a(oa{sv})")); g_hash_table_iter_init(&table_iter, service->messages); while (g_hash_table_iter_next(&table_iter, &key, &value)) { i = i+1; DBG("On message %d!", i); g_variant_builder_open (&messages_builder, G_VARIANT_TYPE ("(oa{sv})")); append_message_entry(key, service, value, &messages_builder); g_variant_builder_close (&messages_builder); } DBG("Messages total: %d", i); messages = g_variant_builder_end (&messages_builder); all_messages = g_variant_new("(*)", messages); } g_dbus_method_invocation_return_value (invocation, all_messages); } else if (g_strcmp0 (method_name, "GetProperties") == 0) { struct vvm_service *service = user_data; GVariantBuilder properties_builder; GVariant *properties, *all_properties; g_variant_builder_init(&properties_builder, G_VARIANT_TYPE ("a{sv}")); g_variant_builder_add_parsed (&properties_builder, "{'ModemCountryCode', <%s>}", service->country_code); g_variant_builder_add_parsed (&properties_builder, "{'MailBoxActive', <%b>}", service->mailbox_active); properties = g_variant_builder_end (&properties_builder); all_properties = g_variant_new("(*)", properties); DBG("vvmd properties: %s", g_variant_print(properties, TRUE)); g_dbus_method_invocation_return_value (invocation, all_properties); return; } else { g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS, "Cannot find the method requested!"); return; } } static const GDBusInterfaceVTable interface_vtable_service = { handle_method_call_service }; static void handle_method_call_manager (GDBusConnection *connection, const gchar *sender, const gchar *object_path, const gchar *interface_name, const gchar *method_name, GVariant *parameters, GDBusMethodInvocation *invocation, gpointer user_data) { if (g_strcmp0 (method_name, "GetServices") == 0) { struct vvm_service *service; GVariant *all_services; GList *l; DBG("At Get Services Method Call"); if (service_list) { GVariantBuilder service_builder; GVariant *get_services; g_variant_builder_init(&service_builder, G_VARIANT_TYPE ("a(oa{sv})")); for (l = service_list; l != NULL; l = l->next) { service = l -> data; g_variant_builder_open(&service_builder, G_VARIANT_TYPE ("(oa{sv})")); append_properties(&service_builder, service); g_variant_builder_close(&service_builder); } get_services = g_variant_builder_end (&service_builder); all_services = g_variant_new("(*)", get_services); } else { all_services = g_variant_new("(a(oa{sv}))", NULL); } g_dbus_method_invocation_return_value (invocation, all_services); } else { g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS, "Cannot find the method requested!"); return; } } static const GDBusInterfaceVTable interface_vtable_manager = { handle_method_call_manager }; static void handle_method_call_message (GDBusConnection *connection, const gchar *sender, const gchar *object_path, const gchar *interface_name, const gchar *method_name, GVariant *parameters, GDBusMethodInvocation *invocation, gpointer user_data) { if (g_strcmp0 (method_name, "Delete") == 0) { struct vvm_service *service = user_data; struct voicemail *vvm_msg; const char *path = object_path; g_autofree char *uuid = NULL; g_autofree char *attachments = NULL; DBG("message path %s", path); vvm_msg = g_hash_table_lookup(service->messages, path); if (vvm_msg == NULL) { g_dbus_method_invocation_return_dbus_error(invocation, VVM_MESSAGE_INTERFACE, "Cannot find this VVM!"); } uuid = g_strdup(vvm_msg->file_uuid); attachments = g_strdup(vvm_msg->attachments); if (vvm_message_unregister(service, vvm_msg, vvm_msg->message_registration_id) < 0) { g_dbus_method_invocation_return_dbus_error(invocation, VVM_MESSAGE_INTERFACE, "There was an error deleting the VVM!"); } vvm_store_remove_attachments(service->identity, uuid, attachments); vvm_store_remove(service->identity, uuid); g_dbus_method_invocation_return_value (invocation, NULL); DBG("Successfully Deleted Message!"); return; } else if (g_strcmp0 (method_name, "MarkRead") == 0) { struct vvm_service *service = user_data; struct voicemail *vvm_msg; const char *path = object_path; GKeyFile *meta; DBG("message path %s", path); vvm_msg = g_hash_table_lookup(service->messages, path); if (vvm_msg == NULL) { g_dbus_method_invocation_return_dbus_error(invocation, VVM_MESSAGE_INTERFACE, "Cannot find this VVM!"); } meta = vvm_store_meta_open(service->identity, vvm_msg->file_uuid); vvm_msg->lifetime_status = VVM_LIFETIME_STATUS_READ; g_key_file_set_integer(meta, "info", "LifetimeStatus", vvm_msg->lifetime_status); vvm_store_meta_close(service->identity, vvm_msg->file_uuid, meta, TRUE); emit_msg_status_changed(path, "read"); g_dbus_method_invocation_return_value (invocation, NULL); return; } else { g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS, "Cannot find the method requested!"); return; } } static const GDBusInterfaceVTable interface_vtable_message = { handle_method_call_message }; void vvm_service_set_hostname(struct vvm_service *service, const char *mailbox_hostname) { g_free(service->mailbox_hostname); service->mailbox_hostname = g_strdup(mailbox_hostname); g_key_file_set_string(service->settings, SETTINGS_GROUP_SERVICE, "MailboxHostname", service->mailbox_hostname); } void vvm_service_set_interface(struct vvm_service *service, const char *mailbox_interface) { g_free(service->mailbox_interface); service->mailbox_interface = g_strdup(mailbox_interface); DBG("Modem interface set to %s", service->mailbox_interface); } void vvm_service_set_port(struct vvm_service *service, const char *mailbox_port) { g_free(service->mailbox_port); service->mailbox_port = g_strdup(mailbox_port); g_key_file_set_string(service->settings, SETTINGS_GROUP_SERVICE, "MailboxPort", service->mailbox_port); } void vvm_service_set_username(struct vvm_service *service, const char *mailbox_username) { if (mailbox_username == NULL || service == NULL) { vvm_error("There is a NULL argument! Not setting."); return; } g_free(service->mailbox_username); service->mailbox_username = g_strdup(mailbox_username); g_key_file_set_string(service->settings, SETTINGS_GROUP_SERVICE, "MailboxUsername", service->mailbox_username); } void vvm_service_set_password(struct vvm_service *service, const char *mailbox_password) { if (mailbox_password == NULL || service == NULL) { vvm_error("There is a NULL argument! Not setting."); return; } g_free(service->mailbox_password); service->mailbox_password = g_strdup(mailbox_password); g_key_file_set_string(service->settings, SETTINGS_GROUP_SERVICE, "MailboxPassword", service->mailbox_password); } void vvm_service_set_language(struct vvm_service *service, const char *language) { if (language == NULL || service == NULL) { vvm_error("There is a NULL argument! Not setting."); return; } g_free(service->mailbox_language); service->mailbox_language = g_strdup(language); g_key_file_set_string(service->settings, SETTINGS_GROUP_SERVICE, "MailboxLanguage", service->mailbox_language); } void vvm_service_set_greeting_length(struct vvm_service *service, const char *greeting_length) { if (greeting_length == NULL || service == NULL) { vvm_error("There is a NULL argument! Not setting."); return; } g_free(service->mailbox_greeting_length); service->mailbox_greeting_length = g_strdup(greeting_length); g_key_file_set_string(service->settings, SETTINGS_GROUP_SERVICE, "GreetingLength", service->mailbox_greeting_length); } void vvm_service_set_voice_signature_length(struct vvm_service *service, const char *voice_signature_length) { if (voice_signature_length == NULL || service == NULL) { vvm_error("There is a NULL argument! Not setting."); return; } g_free(service->mailbox_voice_signature_length); service->mailbox_voice_signature_length = g_strdup(voice_signature_length); g_key_file_set_string(service->settings, SETTINGS_GROUP_SERVICE, "VSLength", service->mailbox_voice_signature_length); } void vvm_service_set_tui_password_length(struct vvm_service *service, const char *TUI_password_length) { if (TUI_password_length == NULL || service == NULL) { vvm_error("There is a NULL argument! Not setting."); return; } g_free(service->mailbox_TUI_password_length); service->mailbox_TUI_password_length = g_strdup(TUI_password_length); g_key_file_set_string(service->settings, SETTINGS_GROUP_SERVICE, "TUIPasswordLength", service->mailbox_TUI_password_length); } void vvm_service_set_subscription_configuration(struct vvm_service *service, const char *configuration) { if (configuration == NULL || service == NULL) { vvm_error("There is a NULL argument! Not setting."); return; } g_free(service->mailbox_vvm_type); service->mailbox_vvm_type = g_strdup(configuration); g_key_file_set_string(service->settings, SETTINGS_GROUP_SERVICE, "MailboxVVMType", service->mailbox_vvm_type); } static void activate_curl_structure(struct vvm_service *service) { DBG("Setting up CURL..."); service->curl = curl_easy_init(); if (global_debug) { curl_easy_setopt(service->curl, CURLOPT_VERBOSE, 1L); } curl_easy_setopt(service->curl, CURLOPT_USERNAME, service->mailbox_username); curl_easy_setopt(service->curl, CURLOPT_PASSWORD, service->mailbox_password); if(service->mailbox_starttls) { curl_easy_setopt(service->curl, CURLOPT_USE_SSL, CURLUSESSL_ALL); } if (service->use_mailbox_interface) curl_easy_setopt(service->curl, CURLOPT_INTERFACE, service->mailbox_interface); curl_easy_setopt(service->curl, CURLOPT_LOGIN_OPTIONS, service->mailbox_auth); } static void deactivate_curl_structure(struct vvm_service *service) { DBG("Tearing down CURL..."); curl_easy_cleanup(service->curl); } void vvm_service_deactivate_vvm_imap_server(struct vvm_service *service) { DBG("Deactivating IMAP Mailbox"); if (service->mailbox_active == FALSE) { DBG("IMAP Mailbox already deactivated!"); return; } service->mailbox_active = FALSE; g_key_file_set_boolean(service->settings, SETTINGS_GROUP_SERVICE, "MailboxActive", service->mailbox_active); deactivate_curl_structure(service); vvm_settings_sync(service->identity, SETTINGS_STORE, service->settings); } static int vvm_service_test_vvm_imap_server(struct vvm_service *service, gboolean modem_iface) { CURL *curl; CURLcode res = CURLE_OK; char *URI, *mailbox_auth; DBG("Activating IMAP Mailbox"); if (service->mailbox_active == TRUE) { DBG("IMAP Mailbox already active!"); return TRUE; } curl = curl_easy_init(); if (global_debug) curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L); // Set Username and Password curl_easy_setopt(curl, CURLOPT_USERNAME, service->mailbox_username); curl_easy_setopt(curl, CURLOPT_PASSWORD, service->mailbox_password); //Do five second timeout curl_easy_setopt(curl, CURLOPT_TIMEOUT, 5L); if (modem_iface) curl_easy_setopt(curl, CURLOPT_INTERFACE, service->mailbox_interface); /* * This is testing various IMAP settings to see that works in this order: * IMAP over SSL (AUTH=PLAIN): * curl -v --login-options AUTH=PLAIN imaps://$USERNAME:$PASSWORD@$HOSTNAME/ * IMAP over SSL (AUTH=DIGEST-MD5) (Yes, carriers really do this): * curl -v --login-options AUTH=DIGEST-MD5 imaps://$USERNAME:$PASSWORD@$HOSTNAME/ * IMAP on the port provided by the STATUS SMS: * curl -v --login-options AUTH=DIGEST-MD5 imap://$USERNAME:$PASSWORD@$HOSTNAME:$PORT/ * IMAP (STARTTLS) on the port provided by the STATUS SMS: * curl -v --login-options AUTH=PLAIN --ssl imap://$USERNAME:$PASSWORD@$HOSTNAME:$PORT/ * IMAP over the standard port (If the provided port is different) * curl -v --login-options AUTH=PLAIN --ssl imap://$USERNAME:$PASSWORD@$HOSTNAME/ * IMAP (STARTTLS) over the standard port * curl -v --login-options AUTH=PLAIN --ssl imap://$USERNAME:$PASSWORD@$HOSTNAME:$PORT/ * */ //curl -v --login-options AUTH=PLAIN imaps://$USERNAME:$PASSWORD@$HOSTNAME/ DBG("Trying IMAP with SSL: PLAIN Authentication"); URI = g_strdup_printf("imaps://%s/INBOX",service->mailbox_hostname); curl_easy_setopt(curl, CURLOPT_URL, URI); mailbox_auth = g_strdup_printf("AUTH=PLAIN"); curl_easy_setopt(curl, CURLOPT_LOGIN_OPTIONS, mailbox_auth); // Do imaps res = curl_easy_perform(curl); if(res == CURLE_OK) { DBG("Mailbox Activated!"); service->mailbox_active = TRUE; service->mailbox_URI = URI; service->mailbox_auth = mailbox_auth; g_key_file_set_boolean(service->settings, SETTINGS_GROUP_SERVICE, "MailboxActive", service->mailbox_active); g_key_file_set_string(service->settings, SETTINGS_GROUP_SERVICE, "MailboxURI", service->mailbox_URI); g_key_file_set_string(service->settings, SETTINGS_GROUP_SERVICE, "MailboxAuth", service->mailbox_auth); curl_easy_cleanup(curl); return TRUE; } else { DBG("curl_easy_perform() failed: %s\n", curl_easy_strerror(res)); } //curl -v --login-options AUTH=DIGEST-MD5 imaps://$USERNAME:$PASSWORD@$HOSTNAME/ DBG("Trying IMAP with SSL: DIGEST MD5 Authentication"); g_free(mailbox_auth); mailbox_auth = g_strdup_printf("AUTH=DIGEST-MD5"); curl_easy_setopt(curl, CURLOPT_LOGIN_OPTIONS, mailbox_auth); res = curl_easy_perform(curl); if(res == CURLE_OK) { DBG("Mailbox Activated!"); service->mailbox_active = TRUE; service->mailbox_URI = URI; service->mailbox_auth = mailbox_auth; g_key_file_set_boolean(service->settings, SETTINGS_GROUP_SERVICE, "MailboxActive", service->mailbox_active); g_key_file_set_string(service->settings, SETTINGS_GROUP_SERVICE, "MailboxURI", service->mailbox_URI); g_key_file_set_string(service->settings, SETTINGS_GROUP_SERVICE, "MailboxAuth", service->mailbox_auth); curl_easy_cleanup(curl); return TRUE; } else { DBG("curl_easy_perform() failed: %s\n", curl_easy_strerror(res)); } DBG("Trying IMAP with Specified port"); //curl -v --login-options AUTH=DIGEST-MD5 imap://$USERNAME:$PASSWORD@$HOSTNAME:$PORT/ g_free(URI); URI = g_strdup_printf("imap://%s:%s/INBOX",service->mailbox_hostname, service->mailbox_port); curl_easy_setopt(curl, CURLOPT_URL, URI); g_free(mailbox_auth); mailbox_auth = g_strdup_printf("AUTH=DIGEST-MD5"); curl_easy_setopt(curl, CURLOPT_USE_SSL, CURLUSESSL_NONE); curl_easy_setopt(curl, CURLOPT_LOGIN_OPTIONS, mailbox_auth); res = curl_easy_perform(curl); if(res == CURLE_OK) { DBG("Mailbox Activated!"); service->mailbox_active = TRUE; service->mailbox_URI = URI; service->mailbox_auth = mailbox_auth; g_key_file_set_boolean(service->settings, SETTINGS_GROUP_SERVICE, "MailboxActive", service->mailbox_active); g_key_file_set_string(service->settings, SETTINGS_GROUP_SERVICE, "MailboxURI", service->mailbox_URI); g_key_file_set_string(service->settings, SETTINGS_GROUP_SERVICE, "MailboxAuth", service->mailbox_auth); curl_easy_cleanup(curl); return TRUE; } else { DBG("curl_easy_perform() failed: %s\n", curl_easy_strerror(res)); } DBG("Retrying IMAP with STARTTLS"); //curl -v --login-options AUTH=PLAIN --ssl imap://$USERNAME:$PASSWORD@$HOSTNAME:$PORT/ g_free(mailbox_auth); mailbox_auth = g_strdup_printf("AUTH=PLAIN"); curl_easy_setopt(curl, CURLOPT_USE_SSL, CURLUSESSL_ALL); curl_easy_setopt(curl, CURLOPT_LOGIN_OPTIONS, mailbox_auth); res = curl_easy_perform(curl); if(res == CURLE_OK) { DBG("Mailbox Activated!"); service->mailbox_active = TRUE; service->mailbox_URI = URI; service->mailbox_auth = mailbox_auth; service->mailbox_starttls = TRUE; g_key_file_set_boolean(service->settings, SETTINGS_GROUP_SERVICE, "MailboxStartTLS", service->mailbox_starttls); g_key_file_set_boolean(service->settings, SETTINGS_GROUP_SERVICE, "MailboxActive", service->mailbox_active); g_key_file_set_string(service->settings, SETTINGS_GROUP_SERVICE, "MailboxURI", service->mailbox_URI); g_key_file_set_string(service->settings, SETTINGS_GROUP_SERVICE, "MailboxAuth", service->mailbox_auth); curl_easy_cleanup(curl); return TRUE; } else { DBG("curl_easy_perform() failed: %s\n", curl_easy_strerror(res)); } //Next, IMAP with default port if (g_strcmp0(service->mailbox_port, "143") != 0) { //service->mailbox_port isn't 143 (default IMAP port) DBG("Trying IMAP with Default port"); //curl -v --login-options AUTH=DIGEST-MD5 imap://$USERNAME:$PASSWORD@$HOSTNAME/ g_free(mailbox_auth); mailbox_auth = g_strdup_printf("AUTH=DIGEST-MD5"); curl_easy_setopt(curl, CURLOPT_USE_SSL, CURLUSESSL_NONE); curl_easy_setopt(curl, CURLOPT_LOGIN_OPTIONS, mailbox_auth); res = curl_easy_perform(curl); if(res == CURLE_OK) { DBG("Mailbox Activated!"); service->mailbox_active = TRUE; service->mailbox_URI = URI; service->mailbox_auth = mailbox_auth; g_key_file_set_boolean(service->settings, SETTINGS_GROUP_SERVICE, "MailboxActive", service->mailbox_active); g_key_file_set_string(service->settings, SETTINGS_GROUP_SERVICE, "MailboxURI", service->mailbox_URI); g_key_file_set_string(service->settings, SETTINGS_GROUP_SERVICE, "MailboxAuth", service->mailbox_auth); curl_easy_cleanup(curl); return TRUE; } else { DBG("curl_easy_perform() failed: %s\n", curl_easy_strerror(res)); } DBG("Retrying IMAP with STARTTLS"); //curl -v --login-options AUTH=PLAIN --ssl imap://$USERNAME:$PASSWORD@$HOSTNAME/ g_free(mailbox_auth); mailbox_auth = g_strdup_printf("AUTH=PLAIN"); curl_easy_setopt(curl, CURLOPT_USE_SSL, CURLUSESSL_ALL); curl_easy_setopt(curl, CURLOPT_LOGIN_OPTIONS, mailbox_auth); res = curl_easy_perform(curl); if(res == CURLE_OK) { DBG("Mailbox Activated!"); service->mailbox_active = TRUE; service->mailbox_URI = URI; service->mailbox_auth = mailbox_auth; service->mailbox_starttls = FALSE; g_key_file_set_boolean(service->settings, SETTINGS_GROUP_SERVICE, "MailboxStartTLS", service->mailbox_starttls); g_key_file_set_boolean(service->settings, SETTINGS_GROUP_SERVICE, "MailboxActive", service->mailbox_active); g_key_file_set_string(service->settings, SETTINGS_GROUP_SERVICE, "MailboxURI", service->mailbox_URI); g_key_file_set_string(service->settings, SETTINGS_GROUP_SERVICE, "MailboxAuth", service->mailbox_auth); curl_easy_cleanup(curl); return TRUE; } else { DBG("curl_easy_perform() failed: %s\n", curl_easy_strerror(res)); } } vvm_error("Unable to activate mailbox!"); vvm_error("You may not get any SMS SYNC Messages until the mailbox is active."); curl_easy_cleanup(curl); return FALSE; } static int vvm_service_activate_vvm_imap_server(struct vvm_service *service) { service->use_mailbox_interface = FALSE; if (vvm_service_test_vvm_imap_server(service, service->use_mailbox_interface)) { g_key_file_set_boolean(service->settings, SETTINGS_GROUP_SERVICE, "UseMailboxInterface", service->use_mailbox_interface); vvm_settings_sync(service->identity, SETTINGS_STORE, service->settings); return TRUE; } service->use_mailbox_interface = TRUE; if (vvm_service_test_vvm_imap_server(service, service->use_mailbox_interface)) { g_key_file_set_boolean(service->settings, SETTINGS_GROUP_SERVICE, "UseMailboxInterface", service->use_mailbox_interface); vvm_settings_sync(service->identity, SETTINGS_STORE, service->settings); return TRUE; } vvm_settings_sync(service->identity, SETTINGS_STORE, service->settings); return FALSE; } struct struct_char { char *response; }; //CURL really wants a struct, I don't know why..... static size_t headers_cb(void *data, size_t size, size_t nmemb, void *userdata) { size_t realsize = size * nmemb; struct struct_char *mem = (struct struct_char *)userdata; mem->response = g_strdup_printf("%s", (char *)data); return realsize; } static char *format_email(struct vvm_service *service, const char *input) { g_autofree char *parsed_number = NULL; parsed_number = parse_email_address(input); return phone_utils_format_number_e164(parsed_number, service->country_code, TRUE); } static int vvmd_service_retrieve_headers(struct vvm_service *service, const char *index, struct sms_control_message *sms_msg, struct voicemail *vvm_msg) { g_autofree char *URI = NULL; g_autofree char *mailbox = NULL; g_autofree char *mailbox_index = NULL; struct struct_char chunk = {0}; CURLcode res = CURLE_OK; if (sms_msg) { //Sync Message exists, so copy over data from here vvm_msg->uid = g_strdup(sms_msg->uid); vvm_msg->mailbox_message_type = sms_msg->mailbox_message_type; vvm_msg->message_sender = phone_utils_format_number_e164(sms_msg->message_sender, service->country_code, TRUE); //I am using the UID to find the voicemail mailbox = g_strdup("UID"); mailbox_index = g_strdup(sms_msg->uid); } else { // I am using the mailindex to find the voicemail // (since I am looking at all of them) mailbox = g_strdup("MAILINDEX"); mailbox_index = g_strdup(index); vvm_msg->mailindex = g_strdup(index); } /* * Trying to find these headers: * - From (Mandatory) * - To (Mandatory) * - Date (Mandatory) * - Subject (Optional) * - Message-Context (Mandatory) * - Content-Duration (Mandatory for Voice/Video Messages) * - Content-Type (Mandatory) * - MIME-Version (Maondatory) * - Importance (Optional) * - Sensitivity (Optional) * - X-Content-Pages (Mandatory in Faxes) * - X-Original-Msg-UID (Optional) */ URI = g_strdup_printf("%s;%s=%s;SECTION=HEADER.FIELDS%%20(FROM%%20TO%%20DATE%%20SUBJECT%%20MESSAGE-CONTEXT%%20CONTENT-DURATION%%20CONTENT-TYPE%%20MIME-VERSION%%20IMPORTANCE%%20X-CONTENT-PAGES%%20X-ORIGINAL-MSG-UID)",service->mailbox_URI, mailbox, mailbox_index); DBG("URL: %s", URI); curl_easy_setopt(service->curl, CURLOPT_URL, URI); curl_easy_setopt(service->curl, CURLOPT_WRITEFUNCTION, headers_cb); curl_easy_setopt(service->curl, CURLOPT_WRITEDATA, (void *)&chunk); res = curl_easy_perform(service->curl); if(res == CURLE_OK) { g_autofree char *sender; g_autofree char *to; char **tokens = NULL; DBG("Returned: %s", chunk.response); tokens = g_strsplit_set(chunk.response,"\r\n", -1); g_free(chunk.response); vvm_util_decode_vvm_headers (vvm_msg, tokens); g_strfreev (tokens); sender = format_email(service, vvm_msg->message_sender); g_free(vvm_msg->message_sender); vvm_msg->message_sender = g_strdup(sender); to = format_email(service, vvm_msg->to); g_free(vvm_msg->to); vvm_msg->to = g_strdup(to); } else { vvm_error("Error Downloading headers!"); return FALSE; } return TRUE; } static int vvm_service_store_headers (struct vvm_service *service, struct voicemail *vvm_msg) { GKeyFile *meta; DBG("Storing headers"); meta = vvm_store_meta_open(service->identity, vvm_msg->file_uuid); if(vvm_msg->message_date) { g_key_file_set_string(meta, "info", "Date", vvm_msg->message_date); } else { vvm_error("There is no date!"); return FALSE; } if(vvm_msg->message_sender) { g_key_file_set_string(meta, "info", "Sender", vvm_msg->message_sender); } else { vvm_error("There is no sender!"); return FALSE; } if(vvm_msg->to) { g_key_file_set_string(meta, "info", "To", vvm_msg->to); } else { vvm_error("There is no To!"); return FALSE; } if(vvm_msg->message_context) g_key_file_set_string(meta, "info", "MessageContext", vvm_msg->message_context); if(vvm_msg->mime_version) g_key_file_set_string(meta, "info", "MIMEVersion", vvm_msg->mime_version); if(vvm_msg->content_type) { g_key_file_set_string(meta, "info", "ContentType", vvm_msg->content_type); } else { vvm_error("There is no Content Type!"); return FALSE; } //Mailindex is relative, so do not store that if(vvm_msg->uid) g_key_file_set_string(meta, "info", "UID", vvm_msg->uid); if(vvm_msg->attachments) { g_key_file_set_string(meta, "info", "Attachments", vvm_msg->attachments); } else { vvm_warn("There are no attachments!"); } if(vvm_msg->email_filepath) g_key_file_set_string(meta, "info", "EmailFilepath", vvm_msg->email_filepath); g_key_file_set_integer(meta, "info", "LifetimeStatus", vvm_msg->lifetime_status); //DEBUG /* if(vvm_msg->contents)lifetime_status g_key_file_set_string(meta, "contents", "contents", vvm_msg->contents); */ vvm_store_meta_close(service->identity, vvm_msg->file_uuid, meta, TRUE); return TRUE; } struct struct_curl_email { GString *response; struct voicemail *vvm_msg; }; static size_t curl_email_cb(void *data, size_t size, size_t nmemb, void *userdata) { size_t realsize = size * nmemb; struct struct_curl_email *mem = (struct struct_curl_email *)userdata; DBG("received %lu", realsize); mem->response = g_string_append(mem->response, (char *)data); //mem->response = g_strdup_printf("%s", (char *)data); return realsize; } static int vvm_service_retrieve_vvm_email (struct vvm_service *service, struct voicemail *vvm_msg) { g_autofree char *URI = NULL; g_autofree char *mailbox = NULL; g_autofree char *mailbox_index = NULL; struct struct_curl_email chunk = {0}; CURLcode res = CURLE_OK; chunk.vvm_msg = vvm_msg; chunk.response = g_string_new(NULL); DBG("Retrieving VVM Email"); if (vvm_msg->uid) { //We have a uid, so use that mailbox = g_strdup("UID"); mailbox_index = g_strdup(vvm_msg->uid); } else { // I am using the mailindex to find the voicemail // (since I am looking at all of them) mailbox = g_strdup("MAILINDEX"); mailbox_index = g_strdup(vvm_msg->mailindex); } URI = g_strdup_printf("%s;%s=%s",service->mailbox_URI, mailbox, mailbox_index); DBG("URL: %s", URI); curl_easy_setopt(service->curl, CURLOPT_URL, URI); curl_easy_setopt(service->curl, CURLOPT_WRITEFUNCTION, curl_email_cb); curl_easy_setopt(service->curl, CURLOPT_WRITEDATA, (void *)&chunk); res = curl_easy_perform(service->curl); if(res == CURLE_OK) { vvm_error("Downloaded email Successfully"); vvm_msg->contents = g_string_free(chunk.response, FALSE); //DBG("Email Contents: %s", vvm_msg->contents); } else { vvm_error("Error Downloading email!"); return FALSE; } return TRUE; } static int vvm_message_register(struct vvm_service *service, struct voicemail *vvm_msg) { GDBusConnection *connection = vvm_dbus_get_connection (); GDBusNodeInfo *introspection_data = vvm_dbus_get_introspection_data(); g_autoptr(GError) error = NULL; //This builds the path for the message, do not disturb! vvm_msg->dbus_path = g_strdup_printf("%s/%s", service->path, vvm_msg->file_uuid); if (vvm_msg->dbus_path == NULL) return -ENOMEM; vvm_msg->message_registration_id = g_dbus_connection_register_object (connection, vvm_msg->dbus_path, introspection_data->interfaces[2], &interface_vtable_message, service, // user_data NULL, // user_data_free_func &error); // GError** if (error) { vvm_error("Error Registering Message %s: %s", vvm_msg->dbus_path, error->message); return FALSE; } g_assert (vvm_msg->message_registration_id > 0); g_hash_table_replace(service->messages, vvm_msg->dbus_path, vvm_msg); DBG("message registered %s", vvm_msg->dbus_path); return TRUE; } static char *iso8601_date(const char *date) { time_t time_tmp; GDateTime *time_utc; GTimeZone *here = g_time_zone_new_local (); char *converted_time; time_tmp = curl_getdate(date, NULL); time_utc = g_date_time_new_from_unix_utc (time_tmp); converted_time = g_date_time_format_iso8601 (time_utc); g_date_time_unref (time_utc); g_time_zone_unref (here); return converted_time; } static void append_message(const char *path, const struct vvm_service *service, struct voicemail *vvm_msg, GVariantBuilder *message_builder) { g_variant_builder_add (message_builder, "o", vvm_msg->dbus_path); g_variant_builder_open (message_builder, G_VARIANT_TYPE ("a{sv}")); if(vvm_msg->message_date) { g_autofree char *normalized_date = NULL; /* Emails dont gives dates in iso8601 */ normalized_date = iso8601_date(vvm_msg->message_date); g_variant_builder_add_parsed (message_builder, "{'Date', <%s>}", normalized_date); } else { vvm_error("There is no date!"); } if(vvm_msg->message_sender) { g_variant_builder_add_parsed (message_builder, "{'Sender', <%s>}", vvm_msg->message_sender); } else { vvm_error("There is no sender!"); } if(vvm_msg->to) { g_variant_builder_add_parsed (message_builder, "{'To', <%s>}", vvm_msg->to); } else { vvm_error("There is no To!"); } if(vvm_msg->message_context){ g_variant_builder_add_parsed (message_builder, "{'MessageContext', <%s>}", vvm_msg->message_context); } if(vvm_msg->mime_version) { g_variant_builder_add_parsed (message_builder, "{'MIMEVersion', <%s>}", vvm_msg->mime_version); } if(vvm_msg->content_type) { g_variant_builder_add_parsed (message_builder, "{'ContentType', <%s>}", vvm_msg->content_type); } else { vvm_error("There is no Content Type!"); } if(vvm_msg->attachments) { g_variant_builder_add_parsed (message_builder, "{'Attachments', <%s>}", vvm_msg->attachments); } else { vvm_warn("There are no attachments!"); } if(vvm_msg->email_filepath) { g_variant_builder_add_parsed (message_builder, "{'EmailFilepath', <%s>}", vvm_msg->email_filepath); } else { vvm_warn("There are no attachments!"); } g_variant_builder_add_parsed (message_builder, "{'LifetimeStatus', <%i>}", vvm_msg->lifetime_status); g_variant_builder_close (message_builder); } static void append_message_entry(char *path, const struct vvm_service *service, struct voicemail *vvm_msg, GVariantBuilder *message_builder) { g_autoptr(GError) error = NULL; DBG("Message Added %p", vvm_msg); append_message(vvm_msg->dbus_path, service, vvm_msg, message_builder); } static void emit_message_added(const struct vvm_service *service, struct voicemail *vvm_msg) { GDBusConnection *connection = vvm_dbus_get_connection (); GVariantBuilder message_builder; GVariant *message; g_autoptr(GError) error = NULL; g_variant_builder_init(&message_builder, G_VARIANT_TYPE ("(oa{sv})")); append_message(vvm_msg->dbus_path, service, vvm_msg, &message_builder); message = g_variant_builder_end (&message_builder); g_dbus_connection_emit_signal(connection, NULL, service->path, VVM_SERVICE_INTERFACE, "MessageAdded", message, &error); if (error != NULL) { g_warning ("Error in Proxy call: %s\n", error->message); } } static int vvm_service_delete_vvm_email_from_server (struct vvm_service *service, struct voicemail *vvm_msg) { g_autofree char *URI = NULL; g_autofree char *mailbox = NULL; g_autofree char *mailbox_index = NULL; g_autofree char *delete_command = NULL; CURLcode res = CURLE_OK; deactivate_curl_structure(service); activate_curl_structure(service); DBG("Deleting VVM Email from server"); if (vvm_msg->uid) { //We have a uid, so use that mailbox = g_strdup("UID"); mailbox_index = g_strdup(vvm_msg->uid); delete_command = g_strdup_printf("UID STORE %s +Flags \\Deleted", mailbox_index); } else { // I am using the mailindex to find the voicemail // (since I am looking at all of them) mailbox = g_strdup("MAILINDEX"); mailbox_index = g_strdup(vvm_msg->mailindex); delete_command = g_strdup_printf("STORE %s +Flags \\Deleted", mailbox_index); } URI = g_strdup_printf("%s;%s=%s",service->mailbox_URI, mailbox, mailbox_index); DBG("URL: %s", URI); curl_easy_setopt(service->curl, CURLOPT_URL, URI); DBG("Delete Command: %s", delete_command); curl_easy_setopt(service->curl, CURLOPT_CUSTOMREQUEST, delete_command); res = curl_easy_perform(service->curl); if(res == CURLE_OK) { curl_easy_setopt(service->curl, CURLOPT_CUSTOMREQUEST, "EXPUNGE"); res = curl_easy_perform(service->curl); if(res == CURLE_OK) { DBG("Deleted email Successfully"); } else { vvm_error("Error deleting email!"); return FALSE; } } else { vvm_error("Error deleting email!"); return FALSE; } deactivate_curl_structure(service); activate_curl_structure(service); return TRUE; } int vvm_service_new_vvm (struct vvm_service *service, char *sync_message, char *index, const char *mailbox_vvm_type) { struct sms_control_message *sms_msg = NULL; struct voicemail *vvm_msg = NULL; g_autofree char *mailindex = NULL; g_autofree char *email_filename = NULL; g_autofree char *email_dir = NULL; g_autofree char *folderpath = NULL; if (service->mailbox_active == FALSE) { if(vvm_service_activate_vvm_imap_server(service) == TRUE) { activate_curl_structure(service); } else { vvm_error("Could not activate mailbox!"); return FALSE; } } vvm_msg = g_try_new0(struct voicemail, 1); if(vvm_msg == NULL) { vvm_error("Could not allocate space for VVM Message!"); return FALSE; } if (sync_message) { //This is a sync message sms_msg = g_try_new0(struct sms_control_message, 1); if(sms_msg == NULL) { vvm_error("Could not allocate space for SMS SYNC Message!"); return FALSE; } vvm_util_parse_sync_sms_message(sync_message, sms_msg, mailbox_vvm_type); if (sms_msg->sync_status_reason != SYNC_SMS_NEW_MESSAGE) { vvm_util_delete_status_message(sms_msg); DBG("This is not a New Message SYNC SMS."); //TODO: Is there anything we can or want to do with // the other SYNC SMS Messages? return FALSE; } mailindex = g_strdup(sms_msg->uid); } else { //This was from vvm_service_sync_vvm_imap_server() mailindex = g_strdup(index); } if (vvmd_service_retrieve_headers(service, mailindex, sms_msg, vvm_msg) == FALSE) { if(sms_msg) { vvm_util_delete_status_message(sms_msg); } vvm_util_delete_vvm_message(vvm_msg); return FALSE; } if(sms_msg) { vvm_util_delete_status_message(sms_msg); } vvm_msg->file_uuid = vvm_store_generate_uuid_objpath(); //Retrieve voicemail email if (vvm_service_retrieve_vvm_email(service, vvm_msg) == FALSE) { vvm_util_delete_vvm_message(vvm_msg); return FALSE; } //Store voicemail email email_filename = g_strdup_printf("%s%s", vvm_msg->file_uuid, VVM_META_EMAIL_SUFFIX); if (vvm_store(service->identity, vvm_msg->contents, NULL, strlen(vvm_msg->contents), email_filename, NULL) == FALSE) { vvm_error("Error storing VVM Email!"); vvm_util_delete_vvm_message(vvm_msg); return FALSE; } email_dir = vvm_store_get_path(service->identity, " "); g_strstrip(email_dir); vvm_msg->email_filepath = g_strdup_printf("%s%s", email_dir, email_filename); folderpath = vvm_store_get_path (service->identity, " "); if (folderpath == NULL) { vvm_error("Could not make directory for attachments!"); return FALSE; } g_strstrip(folderpath); //Decode voicemail email attachments if (vvm_util_decode_vvm_all_email_attachments(vvm_msg, folderpath) == FALSE) { vvm_error("Error decoding VVM Email!"); vvm_store_remove(service->identity, vvm_msg->file_uuid); vvm_util_delete_vvm_message(vvm_msg); return FALSE; } // vvm_msg->contents contains the entire email, and you already decoded // it. Let's free up the memory. g_free(vvm_msg->contents); vvm_msg->contents = NULL; vvm_msg->lifetime_status = VVM_LIFETIME_STATUS_NOT_READ; //Save Headers if (vvm_service_store_headers(service, vvm_msg) == FALSE) { vvm_error("Error storing headers!"); vvm_store_remove_attachments(service->identity, vvm_msg->file_uuid, vvm_msg->attachments); vvm_store_remove(service->identity, vvm_msg->file_uuid); vvm_util_delete_vvm_message(vvm_msg); return FALSE; } //Delete voicemail from server if (vvm_service_delete_vvm_email_from_server(service, vvm_msg) == FALSE) { vvm_error("Error deleting email from server!"); vvm_store_remove_attachments(service->identity, vvm_msg->file_uuid, vvm_msg->attachments); vvm_store_remove(service->identity, vvm_msg->file_uuid); vvm_util_delete_vvm_message(vvm_msg); return FALSE; } vvm_message_register(service, vvm_msg); //Emit voicemail added emit_message_added(service, vvm_msg); return TRUE; } //CURL really wants a struct, I don't know why..... static size_t inbox_status_cb(void *data, size_t size, size_t nmemb, void *userdata) { size_t realsize = size * nmemb; struct struct_char *mem = (struct struct_char *)userdata; g_autofree char *response = NULL; g_autofree char **tokens = NULL; response = g_strdup_printf("%s", (char *)data); DBG("Response to write: %s", response); tokens = g_strsplit_set(response,"\r\n", -1); for (int i = 0; tokens[i] != NULL; i++) { if (g_str_match_string("SEARCH", tokens[i], TRUE) == TRUE) { mem->response = g_strdup(tokens[0]); } } return realsize; } static gboolean retry_sync_vvm_imap_server(gpointer user_data) { struct vvm_service *service = user_data; DBG("retrying sync...."); if(vvm_service_sync_vvm_imap_server(service) == FALSE) { vvm_error("Error resyncing voicemail!"); } return FALSE; } int vvm_service_sync_vvm_imap_server(struct vvm_service *service) { g_autofree char *URI = NULL; struct struct_char chunk = {0}; CURLcode res = CURLE_OK; if (service->mailbox_active == FALSE) { if(vvm_service_activate_vvm_imap_server(service) == TRUE) { activate_curl_structure(service); } else { vvm_error("Could not activate mailbox!"); return FALSE; } } DBG("Checking INBOX for new messages"); URI = g_strdup_printf("%s?ALL",service->mailbox_URI); curl_easy_setopt(service->curl, CURLOPT_URL, URI); curl_easy_setopt(service->curl, CURLOPT_WRITEFUNCTION, inbox_status_cb); curl_easy_setopt(service->curl, CURLOPT_WRITEDATA, (void *)&chunk); res = curl_easy_perform(service->curl); if(res == CURLE_OK) { g_autofree char **tokens = NULL; int found_search = FALSE; DBG("Returned: %s", chunk.response); if (chunk.response == NULL) { vvm_error("Something weird happened!"); vvm_service_deactivate_vvm_imap_server(service); return FALSE; } /* * The IMAP response I have seen is: " * SEARCH 1 2 3 4", where * "1", "2", "3", and "4" are emails coming in */ tokens = g_strsplit(chunk.response," ", -1); g_free(chunk.response); for (int i = 0; tokens[i] != NULL; i++) { if (g_strcmp0(tokens[i], "SEARCH") == 0) { found_search = TRUE; continue; } if (found_search == FALSE) { continue; } DBG("Token: %s", tokens[i]); if(vvm_service_new_vvm(service, NULL, tokens[i], service->mailbox_vvm_type) == FALSE) { vvm_error("Error creating VVM!"); g_timeout_add_seconds(60, retry_sync_vvm_imap_server, service); return FALSE; } } return TRUE; } else { DBG("curl_easy_perform() failed: %s\n", curl_easy_strerror(res)); return FALSE; } } static void vvm_load_settings(struct vvm_service *service) { g_autoptr(GError) error = NULL; service->settings = vvm_settings_open(service->identity, SETTINGS_STORE); if (service->settings == NULL) return; service->mailbox_hostname = g_key_file_get_string(service->settings, SETTINGS_GROUP_SERVICE, "MailboxHostname", &error); if(error) { service->mailbox_hostname = g_strdup("mailbox.invalid"); g_key_file_set_string(service->settings, SETTINGS_GROUP_SERVICE, "MailboxHostname", service->mailbox_hostname); error = NULL; } service->mailbox_port = g_key_file_get_string(service->settings, SETTINGS_GROUP_SERVICE, "MailboxPort", &error); if(error) { // 143 is the default non-secure IMAP port service->mailbox_port = g_strdup("143"); g_key_file_set_string(service->settings, SETTINGS_GROUP_SERVICE, "MailboxPort", service->mailbox_port); error = NULL; } service->mailbox_username = g_key_file_get_string(service->settings, SETTINGS_GROUP_SERVICE, "MailboxUsername", &error); if(error) { service->mailbox_username = g_strdup("username_invalid"); g_key_file_set_string(service->settings, SETTINGS_GROUP_SERVICE, "MailboxUsername", service->mailbox_username); error = NULL; } service->mailbox_password = g_key_file_get_string(service->settings, SETTINGS_GROUP_SERVICE, "MailboxPassword", &error); if(error) { service->mailbox_password = g_strdup("password_invalid"); g_key_file_set_string(service->settings, SETTINGS_GROUP_SERVICE, "MailboxPassword", service->mailbox_password); error = NULL; } service->mailbox_active = g_key_file_get_boolean(service->settings, SETTINGS_GROUP_SERVICE, "MailboxActive", &error); if(error) { service->mailbox_active = FALSE; g_key_file_set_boolean(service->settings, SETTINGS_GROUP_SERVICE, "MailboxActive", service->mailbox_active); error = NULL; } service->mailbox_URI = g_key_file_get_string(service->settings, SETTINGS_GROUP_SERVICE, "MailboxURI", &error); if(error) { service->mailbox_URI = g_strdup("mailboxURI.invalid"); g_key_file_set_string(service->settings, SETTINGS_GROUP_SERVICE, "MailboxURI", service->mailbox_URI); error = NULL; } service->mailbox_auth = g_key_file_get_string(service->settings, SETTINGS_GROUP_SERVICE, "MailboxAuth", &error); if(error) { service->mailbox_auth = g_strdup("AUTH=invalid"); g_key_file_set_string(service->settings, SETTINGS_GROUP_SERVICE, "MailboxAuth", service->mailbox_auth); error = NULL; } service->use_mailbox_interface = g_key_file_get_boolean(service->settings, SETTINGS_GROUP_SERVICE, "UseMailboxInterface", &error); if(error) { service->use_mailbox_interface = FALSE; g_key_file_set_boolean(service->settings, SETTINGS_GROUP_SERVICE, "UseMailboxInterface", service->use_mailbox_interface); error = NULL; } service->mailbox_starttls = g_key_file_get_boolean(service->settings, SETTINGS_GROUP_SERVICE, "MailboxStartTLS", &error); if(error) { service->mailbox_starttls = FALSE; g_key_file_set_boolean(service->settings, SETTINGS_GROUP_SERVICE, "MailboxStartTLS", service->mailbox_starttls); error = NULL; } service->mailbox_language = g_key_file_get_string(service->settings, SETTINGS_GROUP_SERVICE, "MailboxLanguage", &error); if(error) { service->mailbox_language = g_strdup("language_invalid"); g_key_file_set_string(service->settings, SETTINGS_GROUP_SERVICE, "MailboxLanguage", service->mailbox_language); error = NULL; } service->mailbox_greeting_length = g_key_file_get_string(service->settings, SETTINGS_GROUP_SERVICE, "GreetingLength", &error); if(error) { service->mailbox_greeting_length = g_strdup("greeting_invalid"); g_key_file_set_string(service->settings, SETTINGS_GROUP_SERVICE, "GreetingLength", service->mailbox_greeting_length); error = NULL; } service->mailbox_voice_signature_length = g_key_file_get_string(service->settings, SETTINGS_GROUP_SERVICE, "VSLength", &error); if(error) { service->mailbox_voice_signature_length = g_strdup("VS_invalid"); g_key_file_set_string(service->settings, SETTINGS_GROUP_SERVICE, "VSLength", service->mailbox_voice_signature_length); error = NULL; } service->mailbox_TUI_password_length = g_key_file_get_string(service->settings, SETTINGS_GROUP_SERVICE, "TUIPasswordLength", &error); if(error) { service->mailbox_TUI_password_length = g_strdup("VS_invalid"); g_key_file_set_string(service->settings, SETTINGS_GROUP_SERVICE, "TUIPasswordLength", service->mailbox_TUI_password_length); error = NULL; } service->mailbox_vvm_type = g_key_file_get_string(service->settings, SETTINGS_GROUP_SERVICE, "MailboxVVMType", &error); if(error) { service->mailbox_vvm_type = g_strdup("VVMType.invalid"); g_key_file_set_string(service->settings, SETTINGS_GROUP_SERVICE, "MailboxVVMType", service->mailbox_vvm_type); error = NULL; } } static void emit_msg_status_changed(const char *path, const char *new_status) { GDBusConnection *connection = vvm_dbus_get_connection (); GVariant *changedproperty; g_autoptr(GError) error = NULL; changedproperty = g_variant_new_parsed("('status', <%s>)", new_status); g_dbus_connection_emit_signal(connection, NULL, path, VVM_MESSAGE_INTERFACE, "PropertyChanged", changedproperty, &error); if (error != NULL) { g_warning ("Error in Proxy call: %s\n", error->message); error = NULL; } } static void destroy_message(gpointer data) { struct voicemail *vvm_msg = data; vvm_util_delete_vvm_message(vvm_msg); } struct vvm_service *vvm_service_create(void) { struct vvm_service *service; service = g_try_new0(struct vvm_service, 1); if (service == NULL) return NULL; service->request_queue = g_queue_new(); if (service->request_queue == NULL) { g_free(service); return NULL; } service->messages = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, destroy_message); if (service->messages == NULL) { g_queue_free(service->request_queue); g_free(service); return NULL; } DBG("service %p", service); return service; } static void emit_message_removed(const char *svc_path, const char *msg_path) { GDBusConnection *connection = vvm_dbus_get_connection (); GVariant *msgpathvariant; g_autoptr(GError) error = NULL; msgpathvariant = g_variant_new("(o)", msg_path); DBG("Message Removed: %s", g_variant_print(msgpathvariant, TRUE)); g_dbus_connection_emit_signal(connection, NULL, svc_path, VVM_SERVICE_INTERFACE, "MessageRemoved", msgpathvariant, &error); if (error != NULL) { g_warning ("Error in Proxy call: %s\n", error->message); error = NULL; } } static int vvm_message_unregister(const struct vvm_service *service, struct voicemail *vvm_msg, guint message_registration_id) { emit_message_removed(service->path, vvm_msg->dbus_path); //Disconnect the message dbus interface GDBusConnection *connection = vvm_dbus_get_connection (); g_dbus_connection_unregister_object(connection, message_registration_id); DBG("message unregistered %s", vvm_msg->dbus_path); g_hash_table_remove(service->messages, vvm_msg->dbus_path); return 0; } static void unregister_message(gpointer key, gpointer value, gpointer user_data) { struct vvm_service *service = user_data; struct voicemail *vvm_msg = value; vvm_message_unregister(service, vvm_msg, vvm_msg->message_registration_id); } static void destroy_message_table(struct vvm_service *service) { if (service->messages == NULL) return; /* * Each message is first unregistered from dbus, then destroyed from * the hash table. * This step is required because we need access to vvm_service when * unregistering the message object. */ g_hash_table_foreach(service->messages, unregister_message, service); g_hash_table_destroy(service->messages); service->messages = NULL; } static void append_properties(GVariantBuilder *service_builder, struct vvm_service *service) { g_variant_builder_add (service_builder, "o", service->path); g_variant_builder_open (service_builder, G_VARIANT_TYPE ("a{sv}")); g_variant_builder_add_parsed (service_builder, "{'Identity', <%s>}", service->identity); g_variant_builder_close (service_builder); } static void emit_service_added(struct vvm_service *service) { GDBusConnection *connection = vvm_dbus_get_connection (); GVariantBuilder service_builder; GVariant *service_added; g_autoptr(GError) error = NULL; g_variant_builder_init(&service_builder, G_VARIANT_TYPE ("(oa{sv})")); DBG("Service Added %p", service); append_properties(&service_builder, service); service_added = g_variant_builder_end (&service_builder); g_dbus_connection_emit_signal(connection, NULL, VVM_PATH, VVM_MANAGER_INTERFACE, "ServiceAdded", service_added, &error); if (error != NULL) { g_warning ("Error in Proxy call: %s\n", error->message); error = NULL; } } static void emit_service_removed(struct vvm_service *service) { GDBusConnection *connection = vvm_dbus_get_connection (); GVariant *servicepathvariant; g_autoptr(GError) error = NULL; servicepathvariant = g_variant_new("(o)", service->path); g_dbus_connection_emit_signal(connection, NULL, VVM_PATH, VVM_MANAGER_INTERFACE, "ServiceRemoved", servicepathvariant, &error); if (error != NULL) { g_warning ("Error in Proxy call: %s\n", error->message); error = NULL; } } static gboolean load_message_from_store(const char *service_id, const char *uuid, struct voicemail *vvm_msg) { GKeyFile *meta; meta = vvm_store_meta_open(service_id, uuid); if (meta == NULL) return FALSE; vvm_msg->message_date = g_key_file_get_string(meta, "info", "Date", NULL); if(vvm_msg->message_date == NULL) { vvm_error("There is no date!"); return FALSE; } vvm_msg->message_sender = g_key_file_get_string(meta, "info", "Sender", NULL); if(vvm_msg->message_sender == NULL) { vvm_error("There is no sender!"); return FALSE; } vvm_msg->to = g_key_file_get_string(meta, "info", "To", NULL); if(vvm_msg->to == NULL) { vvm_error("There is no To!"); return FALSE; } vvm_msg->message_context = g_key_file_get_string(meta, "info", "MessageContext", NULL); vvm_msg->mime_version = g_key_file_get_string(meta, "info", "MIMEVersion", NULL); vvm_msg->content_type = g_key_file_get_string(meta, "info", "ContentType", NULL); if(vvm_msg->content_type == NULL) { vvm_error("There is no Content Type!"); return FALSE; } vvm_msg->uid = g_key_file_get_string(meta, "info", "UID", NULL); vvm_msg->attachments = g_key_file_get_string(meta, "info", "Attachments", NULL); if(vvm_msg->attachments == NULL) { vvm_warn("There are no attachments!"); } vvm_msg->email_filepath = g_key_file_get_string(meta, "info", "EmailFilepath", NULL); if(vvm_msg->email_filepath == NULL) { vvm_warn("Cannot find the Email!"); return FALSE; } vvm_msg->lifetime_status = g_key_file_get_integer(meta, "info", "LifetimeStatus", NULL); vvm_store_meta_close(service_id, uuid, meta, FALSE); return TRUE; } static void process_message_on_start(struct vvm_service *service, const char *uuid) { struct voicemail *vvm_msg = NULL; vvm_msg = g_try_new0(struct voicemail, 1); if(vvm_msg == NULL) { vvm_error("Could not allocate space for VVM Message!"); return; } vvm_msg->file_uuid = g_strdup(uuid); if (load_message_from_store(service->identity, uuid, vvm_msg) == FALSE) { DBG("Failed to load_message_from_store() from VVM with uuid %s", uuid); vvm_util_delete_vvm_message(vvm_msg); return; } vvm_message_register(service, vvm_msg); } static void load_messages(struct vvm_service *service) { GDir *dir; const char *file; const char *homedir; g_autofree char *service_path = NULL; homedir = g_get_home_dir(); if (homedir == NULL) return; service_path = g_strdup_printf("%s/%s/%s/", homedir, STORAGE_FOLDER, service->identity); dir = g_dir_open(service_path, 0, NULL); if (dir == NULL) return; while ((file = g_dir_read_name(dir)) != NULL) { const size_t suffix_len = 7; g_autofree char *uuid = NULL; if (g_str_has_suffix(file, VVM_META_UUID_SUFFIX) == FALSE) continue; if (strlen(file) - suffix_len == 0) continue; uuid = g_strndup(file, strlen(file) - suffix_len); process_message_on_start(service, uuid); } g_dir_close(dir); } GKeyFile *vvm_service_get_keyfile(struct vvm_service *service) { return service->settings; } int vvm_service_register(struct vvm_service *service) { GDBusConnection *connection = vvm_dbus_get_connection (); GDBusNodeInfo *introspection_data = vvm_dbus_get_introspection_data(); g_autoptr(GError) error = NULL; DBG("service %p", service); if (service == NULL) return -EINVAL; if (service->identity == NULL) return -EINVAL; if (service->path != NULL) return -EBUSY; service->path = g_strdup_printf("%s/%s", VVM_PATH, service->identity); if (service->path == NULL) return -ENOMEM; service_registration_id = g_dbus_connection_register_object (connection, service->path, introspection_data->interfaces[0], &interface_vtable_service, service, // user_data NULL, // user_data_free_func &error); // GError** if (error) { vvm_error("Error Registering Service: %s", error->message); } g_assert (service_registration_id > 0); service_list = g_list_append(service_list, service); emit_service_added(service); vvm_load_settings(service); load_messages(service); if (service->mailbox_active) activate_curl_structure(service); return 0; } int vvm_service_unregister(struct vvm_service *service) { GDBusConnection *connection = vvm_dbus_get_connection (); DBG("service %p", service); if (service == NULL) return -EINVAL; if (service->path == NULL) return -EINVAL; if (service->messages != NULL) destroy_message_table(service); if (service->settings != NULL) { vvm_settings_close(service->identity, SETTINGS_STORE, service->settings, TRUE); g_free(service->mailbox_interface); g_free(service->mailbox_hostname); g_free(service->mailbox_port); g_free(service->mailbox_username); g_free(service->mailbox_password); g_free(service->mailbox_auth); g_free(service->mailbox_URI); g_free(service->mailbox_language); g_free(service->mailbox_greeting_length); g_free(service->mailbox_voice_signature_length); g_free(service->mailbox_TUI_password_length); g_free(service->mailbox_vvm_type); service->settings = NULL; } //Disconnect the service dbus interface g_dbus_connection_unregister_object(connection, service_registration_id); service_list = g_list_remove(service_list, service); emit_service_removed(service); g_free(service->country_code); service->country_code = NULL; g_free(service->path); service->path = NULL; g_free(service->interface); service->interface = NULL; g_free(service->own_number); service->own_number = NULL; if (service->mailbox_active) deactivate_curl_structure(service); return 0; } int vvm_service_set_identity(struct vvm_service *service, const char *identity) { DBG("service %p identity %s", service, identity); if (service == NULL) return -EINVAL; if (service->path != NULL) return -EBUSY; g_free(service->identity); service->identity = g_strdup(identity); return 0; } int vvm_service_set_country_code(struct vvm_service *service, const char *imsi) { const char *country_code; DBG("Setting Service Country Code..."); country_code = get_country_iso_for_mcc (imsi); if (service->country_code != NULL) g_free(service->country_code); service->country_code = g_strdup(country_code); return 0; } int vvm_service_set_own_number(struct vvm_service *service, const char *own_number) { DBG("Setting own Number..."); if (service->own_number != NULL) { g_free(service->own_number); service->own_number = NULL; } if (service->country_code == NULL) { vvm_warn("Country Code is NULL! vvm_service_set_own_number()"); vvm_warn("May not work properly"); } service->own_number = phone_utils_format_number_e164(own_number, service->country_code, FALSE); if (service->own_number == NULL) { vvm_warn("phone_utils_format_number_e164() Had an error!"); vvm_warn("phone_utils_format_number_e164() is copying over unformatted number"); service->own_number = g_strdup(own_number); } return 0; } int __vvm_service_init(gboolean enable_debug) { GDBusConnection *connection = vvm_dbus_get_connection (); GDBusNodeInfo *introspection_data = vvm_dbus_get_introspection_data(); g_autoptr(GError) error = NULL; global_debug = enable_debug; DBG("Starting Up VVMD Service Manager"); manager_registration_id = g_dbus_connection_register_object (connection, VVM_PATH, introspection_data->interfaces[1], &interface_vtable_manager, NULL, // user_data NULL, // user_data_free_func &error); // GError** if (error) { vvm_error("Error Registering Manager: %s", error->message); } return 0; } void __vvm_service_cleanup(void) { GDBusConnection *connection = vvm_dbus_get_connection (); DBG("Cleaning Up VVMD Service Manager"); //Disconnect the manager dbus interface g_dbus_connection_unregister_object(connection, manager_registration_id); } vvmd-0.7/src/service.h000066400000000000000000000053331415226775200147530ustar00rootroot00000000000000/* * * Visual Voicemail Daemon * * Copyright (C) 2010-2011, Intel Corporation * 2021, Chris Talbot * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * */ struct vvm_service; struct vvm_message; typedef void (*vvm_service_bearer_handler_func_t) (gboolean active, void *user_data); struct vvm_service *vvm_service_create(void); int vvm_service_register(struct vvm_service *service); int vvm_service_unregister(struct vvm_service *service); int vvm_service_set_identity(struct vvm_service *service, const char *identity); GKeyFile *vvm_service_get_keyfile(struct vvm_service *service); int vvm_service_set_country_code(struct vvm_service *service, const char *imsi); int vvm_service_set_own_number(struct vvm_service *service, const char *own_number); int vvm_service_sync_vvm_imap_server(struct vvm_service *service); void vvm_service_set_interface(struct vvm_service *service, const char *mailbox_interface); void vvm_service_set_hostname(struct vvm_service *service, const char *mailbox_hostname); void vvm_service_set_port(struct vvm_service *service, const char *mailbox_port); void vvm_service_set_username(struct vvm_service *service, const char *mailbox_username); void vvm_service_set_password(struct vvm_service *service, const char *mailbox_password); void vvm_service_set_language(struct vvm_service *service, const char *language); void vvm_service_set_greeting_length(struct vvm_service *service, const char *greeting_length); void vvm_service_set_voice_signature_length(struct vvm_service *service, const char *voice_signature_length); void vvm_service_set_tui_password_length(struct vvm_service *service, const char *TUI_password_length); void vvm_service_set_subscription_configuration(struct vvm_service *service, const char *configuration); int vvm_service_new_vvm (struct vvm_service *service, char *sync_message, char *index, const char *mailbox_config); void vvm_service_deactivate_vvm_imap_server(struct vvm_service *service); vvmd-0.7/src/store.c000066400000000000000000000213301415226775200144350ustar00rootroot00000000000000/* * * Visual Voicemail Daemon * * Copyright (C) 2010-2011, Intel Corporation * 2021, Chris Talbot * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include #include #include #include #include #include "vvm.h" #ifdef TEMP_FAILURE_RETRY #define TFR TEMP_FAILURE_RETRY #else #define TFR #endif static int create_dirs(const char *filename, const mode_t mode) { struct stat st; char *dir; const char *prev, *next; int err; if (filename[0] != '/') return -1; err = stat(filename, &st); if (!err && S_ISREG(st.st_mode)) return 0; dir = g_try_malloc(strlen(filename) + 1); if (dir == NULL) return -1; strcpy(dir, "/"); for (prev = filename; (next = strchr(prev + 1, '/')); prev = next) { /* Skip consecutive '/' characters */ if (next - prev == 1) continue; strncat(dir, prev + 1, next - prev); if (mkdir(dir, mode) == -1 && errno != EEXIST) { g_free(dir); return -1; } } g_free(dir); return 0; } char *vvm_store_generate_uuid_objpath(void) { char *uuid, *reformatted; uuid = g_uuid_string_random(); reformatted = g_strdelimit(g_strdup(uuid), "-", '1'); g_free(uuid); return reformatted; } char *vvm_store_get_path(const char *service_id, const char *uuid) { const char *homedir; homedir = g_get_home_dir(); if (homedir == NULL) return NULL; return g_strdup_printf("%s/%s/%s/%s", homedir, STORAGE_FOLDER, service_id, uuid); } static char *generate_pdu_pathname(const char *service_id, const char *uuid) { char *pathname; pathname = vvm_store_get_path(service_id, uuid); if (pathname == NULL) return NULL; if (create_dirs(pathname, S_IRUSR | S_IWUSR | S_IXUSR) != 0) { vvm_error("Failed to create path %s", pathname); g_free(pathname); return NULL; } return pathname; } /* * Write a buffer to a file in a transactionally safe form * * Given a buffer, write it to a file named after * @filename. However, to make sure the file contents are * consistent (ie: a crash right after opening or during write() * doesn't leave a file half baked), the contents are written to a * file with a temporary name and when closed, it is renamed to the * specified name (@filename). */ static ssize_t write_file(const char *buffer, const unsigned char *unsigned_buffer, size_t len, const char *filename) { char *tmp_file; ssize_t written; int fd; tmp_file = g_strdup_printf("%s.XXXXXX.tmp", filename); written = -1; fd = TFR(g_mkstemp_full(tmp_file, O_WRONLY | O_CREAT | O_TRUNC, S_IWUSR | S_IRUSR)); if (fd < 0) goto error_mkstemp_full; if (buffer) { DBG("writing signed buffer"); written = TFR(write(fd, buffer, len)); } else { DBG("writing unsigned buffer"); written = TFR(write(fd, unsigned_buffer, len)); } TFR(fdatasync(fd)); TFR(close(fd)); if (written != (ssize_t) len) { written = -1; goto error_write; } /* * Now that the file contents are written, rename to the real * file name; this way we are uniquely sure that the whole * thing is there. */ unlink(filename); /* conserve @written's value from 'write' */ if (link(tmp_file, filename) == -1) written = -1; error_write: unlink(tmp_file); error_mkstemp_full: g_free(tmp_file); return written; } int vvm_store(const char *service_id, const char *pdu, const unsigned char *unsigned_pdu, unsigned int len, const char *filename, const char *path) { char *pathname; if (path == NULL) { if (service_id == NULL) { vvm_error("Need either service_id or path!"); return FALSE; } pathname = generate_pdu_pathname(service_id, filename); if (pathname == NULL) { return FALSE; } } else { pathname = g_strdup_printf("%s%s", path, filename); } DBG("Pathname to save at: %s", pathname); if (write_file(pdu, unsigned_pdu, len, pathname) < 0) { vvm_error("Failed to write to %s", pathname); return FALSE; } DBG("pathname %s", pathname); g_free(pathname); return TRUE; } int vvm_store_file(const char *service_id, const char *path, const char *uuid) { char *pathname; int fd; struct stat st; unsigned char *pdu; fd = open(path, O_RDONLY); if (fd < 0) { vvm_error("Failed to open %s\n", path); return FALSE; } if (fstat(fd, &st) < 0) { vvm_error("Failed to fstat %s\n", path); close(fd); return FALSE; } pdu = mmap(NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0); if (pdu == NULL || pdu == MAP_FAILED) { vvm_error("Failed to mmap %s\n", path); close(fd); return FALSE; } munmap(pdu, st.st_size); close(fd); if (uuid == NULL) return FALSE; pathname = generate_pdu_pathname(service_id, uuid); if (pathname == NULL) return FALSE; if (g_rename(path, pathname) < 0) { vvm_error("Failed to rename %s to %s\n", path, pathname); return FALSE; } DBG("pathname %s", pathname); g_free(pathname); return TRUE; } void vvm_store_remove(const char *service_id, const char *uuid) { char *pdu_path; char *email_path; char *meta_path; pdu_path = vvm_store_get_path(service_id, uuid); if (pdu_path == NULL) return; meta_path = g_strdup_printf("%s%s", pdu_path, VVM_META_UUID_SUFFIX); email_path = g_strdup_printf("%s%s", pdu_path, VVM_META_EMAIL_SUFFIX); g_free(pdu_path); unlink(meta_path); g_free(meta_path); unlink(email_path); g_free(email_path); } void vvm_store_remove_attachments(const char *service_id, const char *uuid, const char *attachstring) { g_autofree char *pdu_path = NULL; g_autofree char **attachments = NULL; if (attachstring == NULL) { DBG("No attachments to remove!"); return; } pdu_path = vvm_store_get_path(service_id, " "); if (pdu_path == NULL) return; g_strstrip(pdu_path); attachments = g_strsplit_set(attachstring,";", -1); for (int i = 0; attachments[i] != NULL; i++) { DBG("Removing Attachment %s", attachments[i]); unlink(attachments[i]); } } GKeyFile *vvm_store_meta_open(const char *service_id, const char *uuid) { GKeyFile *keyfile; char *pdu_path; char *meta_path; pdu_path = generate_pdu_pathname(service_id, uuid); if (pdu_path == NULL) return NULL; meta_path = g_strdup_printf("%s%s", pdu_path, VVM_META_UUID_SUFFIX); g_free(pdu_path); keyfile = g_key_file_new(); g_key_file_load_from_file(keyfile, meta_path, 0, NULL); g_free(meta_path); return keyfile; } static void meta_store_sync(const char *service_id, const char *uuid, GKeyFile *keyfile) { char *data; gsize length = 0; char *pdu_path; char *meta_path; pdu_path = vvm_store_get_path(service_id, uuid); if (pdu_path == NULL) return; meta_path = g_strdup_printf("%s%s", pdu_path, VVM_META_UUID_SUFFIX); g_free(pdu_path); data = g_key_file_to_data(keyfile, &length, NULL); g_file_set_contents(meta_path, data, length, NULL); g_free(data); g_free(meta_path); } void vvm_store_meta_close(const char *service_id, const char *uuid, GKeyFile *keyfile, gboolean save) { if (save == TRUE) meta_store_sync(service_id, uuid, keyfile); g_key_file_free(keyfile); } GKeyFile *vvm_settings_open(const char *service_id, const char *store) { GKeyFile *keyfile; char *path; if (store == NULL) return NULL; path = vvm_store_get_path(service_id, store); if (path == NULL) return NULL; if (create_dirs(path, S_IRUSR | S_IWUSR | S_IXUSR) != 0) { vvm_error("Failed to create path %s", path); g_free(path); return NULL; } keyfile = g_key_file_new(); g_key_file_load_from_file(keyfile, path, 0, NULL); g_free(path); return keyfile; } void vvm_settings_sync(const char *service_id, const char *store, GKeyFile *keyfile) { char *path; char *data; gsize length = 0; path = vvm_store_get_path(service_id, store); if (path == NULL) return; data = g_key_file_to_data(keyfile, &length, NULL); g_file_set_contents(path, data, length, NULL); g_free(data); g_free(path); } void vvm_settings_close(const char *service_id, const char *store, GKeyFile *keyfile, gboolean save) { if (save == TRUE) vvm_settings_sync(service_id, store, keyfile); g_key_file_free(keyfile); } vvmd-0.7/src/store.h000066400000000000000000000040471415226775200144500ustar00rootroot00000000000000/* * * Visual Voicemail Daemon * * Copyright (C) 2010-2011, Intel Corporation * 2021, Chris Talbot * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * */ #define VVM_SHA1_UUID_LEN 20 #define VVM_META_UUID_SUFFIX ".status" #define VVM_META_UUID_SUFFIX_LEN 7 #define VVM_META_EMAIL_SUFFIX ".mbox" /***** SETTINGS CONFIGURATION ****/ #define STORAGE_FOLDER ".vvm" #define SETTINGS_STORE "vvm" #define SETTINGS_GROUP_SERVICE "Settings" int vvm_store(const char *service_id, const char *pdu, const unsigned char *unsigned_pdu, unsigned int len, const char *filename, const char *path); int vvm_store_file(const char *service_id, const char *path, const char *uuid); void vvm_store_remove(const char *service_id, const char *uuid); void vvm_store_remove_attachments(const char *service_id, const char *uuid, const char *attachments); char *vvm_store_get_path(const char *service_id, const char *uuid); GKeyFile *vvm_store_meta_open(const char *service_id, const char *uuid); void vvm_store_meta_close(const char *service_id, const char *uuid, GKeyFile *keyfile, gboolean save); void vvm_settings_sync(const char *service_id, const char *store, GKeyFile *keyfile); GKeyFile *vvm_settings_open(const char *service_id, const char *store); void vvm_settings_close(const char *service_id, const char *store, GKeyFile *keyfile, gboolean save); vvmd-0.7/src/vvm.h000066400000000000000000000022021415226775200141130ustar00rootroot00000000000000/* * * Visual Voicemail Daemon * * Copyright (C) 2010-2011, Intel Corporation * 2021, Chris Talbot * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * */ #include #include "itu-e212-iso.h" #include "log.h" int __vvm_log_init(const char *debug); void __vvm_log_cleanup(void); #include "plugin.h" int __vvm_plugin_init(void); void __vvm_plugin_cleanup(void); #include "service.h" int __vvm_service_init(gboolean enable_debug); void __vvm_service_cleanup(void); #include "store.h" vvmd-0.7/src/vvmutil.c000066400000000000000000000736511415226775200150240ustar00rootroot00000000000000/* * * Visual Voicemail Daemon * * Copyright (C) 2021, Chris Talbot * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * */ #ifdef HAVE_CONFIG_H #include #endif #include "vvm.h" #include "vvmutil.h" char *parse_email_address(const char *input) { g_autofree gchar **input_parts = NULL; g_autofree gchar **full_email = NULL; g_autofree gchar **email = NULL; int full_email_length; int input_parts_length; //T-Mobile does "VOICE=$NUMBER@domain.com" input_parts = g_strsplit(input, "=", 2); input_parts_length = g_strv_length(input_parts); //The email can come in the form: //"$NUMBER" <$NUMBER@domain.com> full_email = g_strsplit(input_parts[input_parts_length-1], " ", -1); full_email_length = g_strv_length(full_email); g_strdelimit (full_email[full_email_length-1], "<>", ' '); g_strstrip (full_email[full_email_length-1]); email = g_strsplit(full_email[full_email_length-1], "@", 2); return g_strdup(email[0]); } static char *vvm_util_create_att_usa_status_sms(const char *carrier_prefix) { vvm_warn("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"); vvm_warn("!!!!!WARNING!WARNING!WARNING!WARNING!WARNING!WARNING!WARNING!WARNING!!!!!!"); vvm_warn("!!!!! AT&T USA uses a non-standard protocol for subscribing and !!!!!!"); vvm_warn("!!!!! unsubscribing. It may change at anytime and no longer work !!!!!!"); vvm_warn("!!!!!WARNING!WARNING!WARNING!WARNING!WARNING!WARNING!WARNING!WARNING!!!!!!"); vvm_warn("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"); return g_strdup_printf("GET?c=ATTV:Pixel 5/11:4.4.0.10183&v=1.0&l=%s&AD", carrier_prefix); // Attributes for AT&T USA: // Pixel 5 => Model Number // 11 => Android 11 // 4.4.0.10183 => AT&T Visual Voicemail Version // 1.0 => Version of VVM I guess? // carrier_prefix => put your number with no spaces // (i.e if your number is (999) 555-1123, use 9995551123 } char *vvm_util_create_deactivate_sms(const char *carrier_prefix, const char *vvm_type) { if (g_strcmp0(vvm_type, "cvvm") == 0) { return g_strdup_printf("%s%s", DEACTIVATE_SMS_PREFIX, CCVM_SUFFIX); } else if (g_strcmp0(vvm_type, "otmp") == 0) { //TODO: Fix this for other carriers // Refer to "OMTP_VVM_Specification v1.3, Section 2.9.2.3 // It should look similar to: "Deactivate:pv=;ct=;pt=;:" // message = g_strdup("Deactivate:pv=;ct=;pt=;:"); // message = g_strdup("%s%s%s;%s;%s;:", DEACTIVATE_SMS_PREFIX, PROTOCOL_VERSION_PREFIX, PROTOCOL_VERSION_1_3, CLIENT_TYPE_PREFIX, TERMINAL_DESTINATION_PORT_NUMBER_PREFIX); // pv should be "13", I do not know the values for "ct" and "pt" // I think if is default (//VVM), it can be "Deactivate:pv=;ct=;pt=" // message = g_strdup("Deactivate:pv=;ct=;pt="); // or with vvmutil.h // message = g_strdup("%s%s%s;%s;%s", DEACTIVATE_SMS_PREFIX, PROTOCOL_VERSION_PREFIX, PROTOCOL_VERSION_1_3, CLIENT_TYPE_PREFIX, TERMINAL_DESTINATION_PORT_NUMBER_PREFIX); DBG("I do not know how to unsubscribe from an OMTP VVM service!"); } else if (g_strcmp0(vvm_type, "AT&TUSAProprietary") == 0) { vvm_error("I do not know how to unsubscribe from AT&T USA!"); } else if (g_strcmp0(vvm_type, "vvm3") == 0) { vvm_error("There is no way to unsubscribe from vvm3!"); } else { vvm_error("Unknown type of VVM service."); } return NULL; } char *vvm_util_create_activate_sms(const char *carrier_prefix, const char *vvm_type) { if (g_strcmp0(vvm_type, "cvvm") == 0) { return g_strdup_printf("%s%s", ACTIVATE_SMS_PREFIX, CCVM_SUFFIX); } else if (g_strcmp0(vvm_type, "otmp") == 0) { //TODO: Fix this for other carriers // Refer to "OMTP_VVM_Specification v1.3, Section 2.9.2.2 // It should look similar to: "Activate:pv=;ct=;pt=;:" // message = g_strdup("Activate:pv=;ct=;pt=;:"); // or with vvmutil.h // message = g_strdup("%s%s%s;%s;%s;:", ACTIVATE_SMS_PREFIX, PROTOCOL_VERSION_PREFIX, PROTOCOL_VERSION_1_3, CLIENT_TYPE_PREFIX, TERMINAL_DESTINATION_PORT_NUMBER_PREFIX); // pv should be "13", I do not know the values for "ct" and "pt" // I think if is default (//VVM), it can be:"Activate:pv=;ct=;pt=" // message = g_strdup("Activate:pv=;ct=;pt="); // or with vvmutil.h // message = g_strdup("%s%s%s;%s;%s", ACTIVATE_SMS_PREFIX, PROTOCOL_VERSION_PREFIX, PROTOCOL_VERSION_1_3, CLIENT_TYPE_PREFIX, TERMINAL_DESTINATION_PORT_NUMBER_PREFIX); DBG("I do not know how to subscribe to an OMTP VVM service!"); } else if (g_strcmp0(vvm_type, "AT&TUSAProprietary") == 0) { return vvm_util_create_att_usa_status_sms(carrier_prefix); } else if (g_strcmp0(vvm_type, "vvm3") == 0) { return g_strdup_printf("STATUS"); } else { vvm_error("Unknown type of VVM service."); } return NULL; } char *vvm_util_create_status_sms(const char *carrier_prefix, const char *vvm_type) { DBG("VVM type: %s", vvm_type); if (g_strcmp0(vvm_type, "cvvm") == 0) { return g_strdup_printf("%s%s", STATUS_SMS_PREFIX, CCVM_SUFFIX); } else if (g_strcmp0(vvm_type, "otmp") == 0) { //TODO: Fix this for other carriers // Refer to "OMTP_VVM_Specification v1.3, Section 2.9.2.1 // The message should look similar to "STATUS:pv=;ct=;pt=;:" // message = g_strdup("STATUS:pv=;ct=;pt=;:"); // or with vvmutil.h // message = g_strdup("%s%s%s;%s;%s;:", STATUS_SMS_PREFIX, PROTOCOL_VERSION_PREFIX, PROTOCOL_VERSION_1_3, CLIENT_TYPE_PREFIX, TERMINAL_DESTINATION_PORT_NUMBER_PREFIX); // pv should be "13", I do not know the values for "ct" and "pt" // I think if is default (//VVM), it can be: "STATUS:pv=;ct=;pt=" // message = g_strdup("STATUS:pv=;ct=;pt="); // or with vvmutil.h // message = g_strdup("%s%s%s;%s;%s;", STATUS_SMS_PREFIX, PROTOCOL_VERSION_PREFIX, PROTOCOL_VERSION_1_3, CLIENT_TYPE_PREFIX, TERMINAL_DESTINATION_PORT_NUMBER_PREFIX); DBG("I do not know how check the status of an OMTP VVM service!"); } else if (g_strcmp0(vvm_type, "AT&TUSAProprietary") == 0) { return vvm_util_create_att_usa_status_sms(carrier_prefix); } else if (g_strcmp0(vvm_type, "vvm3") == 0) { return g_strdup_printf("STATUS"); } else { vvm_error("Unknown type of VVM service."); } return NULL; } static int vvm_util_parse_otmp_sms_message_type(const char *message, const char *carrier_prefix) { g_autofree char *status_prefix = NULL; g_autofree char *sync_prefix = NULL; status_prefix = g_strdup_printf("%s:STATUS:", carrier_prefix); sync_prefix = g_strdup_printf("%s:SYNC:", carrier_prefix); if (g_str_has_prefix(message, status_prefix)) { DBG("This is a status SMS message"); return SMS_MESSAGE_STATUS; } else if (g_str_has_prefix(message, sync_prefix)) { DBG("This is a sync SMS message"); return SMS_MESSAGE_SYNC; } else { DBG("Not a status or sync SMS message"); return SMS_MESSAGE_OTHER; } } static int vvm_util_parse_att_usa_sms_message_type(const char *message, const char *carrier_prefix) { if(g_str_match_string("vvm.mobile.att.net", message, FALSE)) { // AT&T has pretty much identical STATUS and SYNC messages, and the SYNC // Messages aren't helpful, just process all as STATUS SMS return SMS_MESSAGE_STATUS; } else { DBG("Not a status or sync SMS message"); return SMS_MESSAGE_OTHER; } } int vvm_util_parse_sms_message_type(const char *message, const char *carrier_prefix, const char *vvm_type) { int sms_message_type; if ((g_strcmp0(vvm_type, "cvvm") == 0) || (g_strcmp0(vvm_type, "otmp") == 0) || (g_strcmp0(vvm_type, "vvm3") == 0)) { sms_message_type = vvm_util_parse_otmp_sms_message_type(message, carrier_prefix); } else if (g_strcmp0(vvm_type, "AT&TUSAProprietary") == 0) { sms_message_type = vvm_util_parse_att_usa_sms_message_type(message, carrier_prefix); } else { vvm_error("Unknown VVM type!"); sms_message_type = SMS_MESSAGE_OTHER; } return sms_message_type; } static void vvm_util_parse_sync_otmp_sms_message(const char *message, struct sms_control_message *sms_msg) { gchar **status_parts, **settings, **single_setting; unsigned int status_parts_length, settings_length, adjust_factor; /* * Example SYNC Message from OTMP VVM Specification: * //VVM:SYNC:ev=NM;id=3446456;c=1;t=v;s=01234567898;dt=02/08/200812:53 +0200;l=30 */ status_parts = g_strsplit(message, ":", 3); status_parts_length = g_strv_length (status_parts); if (strlen(status_parts[status_parts_length-1]) == 0) { adjust_factor = 2; } else { adjust_factor = 1; } settings = g_strsplit(status_parts[status_parts_length-adjust_factor], ";", 0); settings_length = g_strv_length (settings); g_strfreev(status_parts); for (int i = 0; i < settings_length; i++) { if (settings[i] == NULL) continue; if (strlen(settings[i]) < 1) continue; single_setting = g_strsplit(settings[i], "=", 2); if (single_setting[1] == NULL) continue; if (strlen(single_setting[1]) < 1) continue; if (g_strcmp0(single_setting[0], "ev") == 0) { //event that triggered the SYNC SMS. if (g_strcmp0(single_setting[1], "NM") == 0) { sms_msg->sync_status_reason = SYNC_SMS_NEW_MESSAGE; } else if (g_strcmp0(single_setting[1], "MBU") == 0) { sms_msg->sync_status_reason = SYNC_SMS_MAILBOX_UPDATE; } else if (g_strcmp0(single_setting[1], "GU") == 0) { sms_msg->sync_status_reason = SYNC_SMS_GREETINGS_VOICE_SIGNATURE_UPDATE; } else { sms_msg->sync_status_reason = SYNC_SMS_UNKNOWN; } } else if (g_strcmp0(single_setting[0], "id") == 0) { //UID of the message in the Mailbox sms_msg->uid = g_strdup(single_setting[1]); } else if (g_strcmp0(single_setting[0], "c") == 0) { //Type of Message sms_msg->new_mailbox_messages = g_strdup(single_setting[1]); } else if (g_strcmp0(single_setting[0], "t") == 0) { //Type of Message if (g_strcmp0(single_setting[1], "v") == 0) { sms_msg->mailbox_message_type = MAILBOX_MESSAGE_VOICE; } else if (g_strcmp0(single_setting[1], "o") == 0) { sms_msg->mailbox_message_type = MAILBOX_MESSAGE_VIDEO; } else if (g_strcmp0(single_setting[1], "f") == 0) { sms_msg->mailbox_message_type = MAILBOX_MESSAGE_FAX; } else if (g_strcmp0(single_setting[1], "i") == 0) { sms_msg->mailbox_message_type = MAILBOX_MESSAGE_INFOTAINMENT; } else if (g_strcmp0(single_setting[1], "e") == 0) { sms_msg->mailbox_message_type = MAILBOX_MESSAGE_ECC; } else { sms_msg->mailbox_message_type = MAILBOX_MESSAGE_UNKNOWN; } } else if (g_strcmp0(single_setting[0], "s") == 0) { //Message Sender sms_msg->message_sender = g_strdup(single_setting[1]); } else if (g_strcmp0(single_setting[0], "dt") == 0) { //Date of Message // format DD/MM/YYYY HH:MM TZ sms_msg->message_date = g_strdup(single_setting[1]); } else if (g_strcmp0(single_setting[0], "l") == 0) { //Date of Message sms_msg->message_length = g_strdup(single_setting[1]); } else { DBG("Not procesing setting %s", settings[i]); } g_strfreev(single_setting); } g_strfreev(settings); } void vvm_util_parse_sync_att_usa_sms_message(const char *message, struct sms_control_message *sms_msg) { vvm_error("AT&T has no sync message!!"); } void vvm_util_parse_sync_sms_message(const char *message, struct sms_control_message *sms_msg, const char *vvm_type) { DBG("Parsing VVM sync message."); sms_msg->type = g_strdup("sync"); if ((g_strcmp0(vvm_type, "cvvm") == 0) || (g_strcmp0(vvm_type, "otmp") == 0) || (g_strcmp0(vvm_type, "vvm3") == 0)) { vvm_util_parse_sync_otmp_sms_message(message, sms_msg); } else if (g_strcmp0(vvm_type, "AT&TUSAProprietary") == 0) { vvm_util_parse_sync_att_usa_sms_message(message, sms_msg); } else { vvm_error("Unknown VVM type!"); } } void vvm_util_parse_status_otmp_sms_message(const char *message, struct sms_control_message *sms_msg) { gchar **status_parts, **settings, **single_setting; unsigned int settings_length; /* * Example STATUS Message from OTMP VVM Specification: * //VVM :STATUS:st=N;rc=0;srv=1:10.115.67.251;tui=123;dn=999;ipt=143;spt=25; u=78236487@wirelesscarrier.com;pw=32u4yguetrr34;lang=eng|fre;g_len=25;vs_len=15;pw_len=4-6;smtp_u=super_user@wirelesscarrier.com;smtp_pw=48769463wer;pm=Y;gm=N;vtc=D;vt=1 */ status_parts = g_strsplit(message, ":", 3); settings = g_strsplit(status_parts[2], ";", 0); settings_length = g_strv_length (settings); g_strfreev(status_parts); for (int i = 0; i < settings_length; i++) { if (settings[i] == NULL) continue; if (strlen(settings[i]) < 1) continue; single_setting = g_strsplit(settings[i], "=", 2); if (single_setting[1] == NULL) continue; if (strlen(single_setting[1]) < 1) continue; ; if (g_strcmp0(single_setting[0], "st") == 0) { //Provisioning Status if (g_strcmp0(single_setting[1], "N") == 0) { // N = Subscriber New sms_msg->provision_status = VVM_PROVISION_STATUS_NEW; DBG("Provisioning Status: New."); } else if (g_strcmp0(single_setting[1], "R") == 0) { // R = Subscriber Ready sms_msg->provision_status = VVM_PROVISION_STATUS_READY; DBG("Provisioning Status: Ready."); } else if (g_strcmp0(single_setting[1], "P") == 0) { // P = Subscriber Provisioned sms_msg->provision_status = VVM_PROVISION_STATUS_PROVISIONED; DBG("Provisioning Status: Provisioned."); } else if (g_strcmp0(single_setting[1], "U") == 0) { // U = Subscriber Unknown sms_msg->provision_status = VVM_PROVISION_STATUS_UNKNOWN; DBG("Provisioning Status: Unknown."); } else if (g_strcmp0(single_setting[1], "B") == 0) { // B = Subscriber Blocked sms_msg->provision_status = VVM_PROVISION_STATUS_BLOCKED; DBG("Provisioning Status: Blocked."); } else { sms_msg->provision_status = VVM_PROVISION_STATUS_NOT_SET; } } else if (g_strcmp0(single_setting[0], "rc") == 0) { //Return Code if (g_strcmp0(single_setting[1], "0") == 0) { //0 = Success DBG("Return Code: Success."); } else if (g_strcmp0(single_setting[1], "1") == 0) { //1 = System error DBG("Return Code: System error."); } else if (g_strcmp0(single_setting[1], "2") == 0) { //2 = Subscriber error DBG("Return Code: Subscriber error."); } else if (g_strcmp0(single_setting[1], "3") == 0) { //3 = Mailbox unknown DBG("Return Code: Mailbox unknown."); } else if (g_strcmp0(single_setting[1], "4") == 0) { //4 = VVM not activated DBG("Return Code: VVM not activated."); } else if (g_strcmp0(single_setting[1], "5") == 0) { //5 = VVM not provisioned DBG("Return Code: System error."); } else if (g_strcmp0(single_setting[1], "6") == 0) { //6 = VVM client unknown DBG("Return Code: VVM not provisioned."); } else if (g_strcmp0(single_setting[1], "7") == 0) { //7 = VVM mailbox not initialised DBG("Return Code: VVM mailbox not initialised."); } else { vvm_error("Unknown Return Code."); } } else if (g_strcmp0(single_setting[0], "srv") == 0) { //Mailbox Hostname sms_msg->mailbox_hostname = g_strdup(single_setting[1]); } else if (g_strcmp0(single_setting[0], "ipt") == 0) { //Mailbox Port sms_msg->mailbox_port = g_strdup(single_setting[1]); } else if (g_strcmp0(single_setting[0], "u") == 0) { //Mailbox Username sms_msg->mailbox_username = g_strdup(single_setting[1]); } else if (g_strcmp0(single_setting[0], "pw") == 0) { //Mailbox Password sms_msg->mailbox_password = g_strdup(single_setting[1]); } else if (g_strcmp0(single_setting[0], "lang") == 0) { //Language sms_msg->language = g_strdup(single_setting[1]); } else if (g_strcmp0(single_setting[0], "g_len") == 0) { //Greeting Length sms_msg->greeting_length = g_strdup(single_setting[1]); } else if (g_strcmp0(single_setting[0], "vs_len") == 0) { //Voice Signature Length sms_msg->voice_signature_length = g_strdup(single_setting[1]); } else if (g_strcmp0(single_setting[0], "pw_len") == 0) { //TUI Password Length sms_msg->TUI_password_length = g_strdup(single_setting[1]); } else if (g_strcmp0(single_setting[0], "vmg_url") == 0) { //vvm3: URL for activation sms_msg->activate_url = g_strdup(single_setting[1]); } else { DBG("Not procesing setting %s", single_setting[0]); } g_strfreev(single_setting); } g_strfreev(settings); } char *decode(const char *input) { gsize base64_out_len; g_autofree unsigned char *base_64_decoded = NULL; g_autofree char *decoded_string = NULL; GString *decoded = g_string_new (NULL); char *to_return; base_64_decoded = g_base64_decode (input, &base64_out_len); decoded_string = g_strndup ((char *)base_64_decoded, base64_out_len); for (int i = 0; i 14) { vvm_warn("This string should not be longer than 15!"); decoded = g_string_append_unichar (decoded, decoded_string[i]); } else { if(decode[j][i] == decoded_string[i]) { decoded = g_string_append_c (decoded, j + '0'); } } } } to_return = g_string_free(decoded, FALSE); return to_return; } void vvm_util_parse_status_att_usa_sms_message(const char *message, struct sms_control_message *sms_msg) { g_autofree gchar **status_parts = NULL; g_autofree gchar **hostname_parts = NULL; g_autofree gchar **other_parts = NULL; int other_parts_length; status_parts = g_strsplit(message, "?", 2); hostname_parts = g_strsplit(status_parts[0], ":", 2); sms_msg->mailbox_hostname = g_strdup(hostname_parts[0]); DBG("Hostname: %s", sms_msg->mailbox_hostname); other_parts = g_strsplit(status_parts[1], "&", -1); other_parts_length = g_strv_length (other_parts); for (int i = 0; i < other_parts_length; i++) { g_autofree gchar **single_setting = NULL; if (strlen(other_parts[i]) < 1) continue; if (other_parts[i] == NULL) continue; single_setting = g_strsplit(other_parts[i], "=", 2); if (single_setting[1] == NULL) continue; if (strlen(single_setting[1]) < 1) continue; if (g_strcmp0(single_setting[0], "m") == 0) { //Mailbox Username sms_msg->mailbox_username = g_strdup(single_setting[1]); } else if (g_strcmp0(single_setting[0], "p") == 0) { //Mailbox Password sms_msg->mailbox_password = decode(single_setting[1]); } else if (g_strcmp0(single_setting[0], "i") == 0) { //Mailbox Port if (g_str_match_string("143", single_setting[1], FALSE)) { sms_msg->mailbox_port = g_strdup("143"); } else { sms_msg->mailbox_port = g_strdup(single_setting[1]); } } else { //I have no idea what these settings are DBG("Not procesing setting %s", single_setting[0]); } } sms_msg->provision_status = VVM_PROVISION_STATUS_READY; DBG("Provisioning Status: Ready."); //This is from the AT&T VVM app sms_msg->TUI_password_length = g_strdup("7-15"); } void vvm_util_parse_status_sms_message(const char *message, struct sms_control_message *sms_msg, const char *vvm_type) { DBG("Parsing VVM status message."); sms_msg->type = g_strdup("status"); if ((g_strcmp0(vvm_type, "cvvm") == 0) || (g_strcmp0(vvm_type, "otmp") == 0) || (g_strcmp0(vvm_type, "vvm3") == 0)) { vvm_util_parse_status_otmp_sms_message(message, sms_msg); } else if (g_strcmp0(vvm_type, "AT&TUSAProprietary") == 0) { vvm_util_parse_status_att_usa_sms_message(message, sms_msg); } else { vvm_error("Unknown VVM type!"); } } void vvm_util_decode_vvm_headers (struct voicemail *vvm_msg, char **tokens) { for (int i = 0; tokens[i] != NULL; i++) { char **single_token = NULL; if (tokens[i] == NULL) continue; if (strlen(tokens[i]) < 1) continue; g_strstrip(tokens[i]); single_token = g_strsplit_set(tokens[i],":", 2); if (!single_token[1]) { g_autofree char *new_token = NULL; g_strfreev (single_token); if (i > 1) { if (!*tokens[i-1]) new_token = g_strdup_printf ("%s%s", tokens[i-2], tokens[i]); else new_token = g_strdup_printf ("%s%s", tokens[i-1], tokens[i]); } DBG("New string to parse %s", new_token); single_token = g_strsplit_set(new_token,":", 2); if (!single_token[1]) { continue; } } g_strstrip(single_token[1]); if (g_str_match_string("Date", single_token[0], TRUE)) { //DBG("Date %s", single_token[1]); if(!vvm_msg->message_date) vvm_msg->message_date = g_strdup(single_token[1]); } else if (g_strcmp0("From", single_token[0]) == 0) { if(!vvm_msg->message_sender) { vvm_msg->message_sender = g_strdup(single_token[1]); } //DBG("From: %s", vvm_msg->message_sender); } else if (g_strcmp0("To", single_token[0]) == 0) { g_clear_pointer (&vvm_msg->to, g_free); vvm_msg->to = g_strdup(single_token[1]); //DBG("To: %s", vvm_msg->to); } else if (g_str_match_string("Message-Context", single_token[0], TRUE)) { g_clear_pointer (&vvm_msg->message_context, g_free); vvm_msg->message_context = g_strdup(single_token[1]); //DBG("Message-Context: %s", single_token[1]); } else if (g_str_match_string("MIME-Version", single_token[0], TRUE)) { g_clear_pointer (&vvm_msg->mime_version, g_free); vvm_msg->mime_version = g_strdup(single_token[1]); //DBG("MIME-Version: %s", single_token[1]); } else if (g_str_match_string("Content-type", single_token[0], TRUE)) { g_clear_pointer (&vvm_msg->content_type, g_free); vvm_msg->content_type = g_strdup(single_token[1]); //DBG("Content-type: %s", single_token[1]); } else if (strlen(single_token[0]) > 0) { vvm_warn("Cannot process Header:%s:containing:%s", single_token[0], single_token[1]); } else { vvm_warn("Cannot process line: %s", tokens[i]); } g_strfreev (single_token); } } static char *vvm_util_decode_vvm_single_email_attachment (const char *attachment, const char *folderpath) { DBG("Decoding Attachment"); g_autofree char **lines = NULL; g_autofree char *contentencoding = NULL; g_autofree char *contenttype = NULL; g_autofree char *duration = NULL; g_autofree char *attributes = NULL; g_autofree char *string_to_decode = NULL; int attachment_line; int found_attachment = FALSE; int base64_encoded; g_autofree char **attribute_parse = NULL; g_autofree char *filename = NULL; lines = g_strsplit_set(attachment,"\r\n", -1); //Decode Attachment headers first for (int i = 0; lines[i] != NULL; i++) { g_autofree char **header = NULL; if (strlen(lines[i]) < 1) continue; header = g_strsplit_set(lines[i],":", 2); if (g_str_match_string("Content-Transfer-Encoding", header[0], TRUE)) { contentencoding = g_strdup(header[1]); DBG("Content-Transfer-Encoding: %s", contentencoding); } else if (g_str_match_string("Content-Type", header[0], TRUE)) { contenttype = g_strdup(header[1]); DBG("Content-Type: %s", contenttype); } else if (g_str_match_string("X-AppleVM-Duration", header[0], TRUE)) { duration = g_strdup(header[1]); DBG("X-AppleVM-Duration: %s", duration); } else if (g_str_match_string("Content-Disposition", header[0], TRUE)) { attributes = g_strdup(header[1]); DBG("Content-Disposition: %s", attributes); } else { if (g_strv_length(header) == 2) { DBG("Do not know how to debug header: %s", lines[i]); } else { if (found_attachment == FALSE) { attachment_line = i; DBG("Attachment contents on line: %d, length %lu", attachment_line, strlen(lines[i])); found_attachment = TRUE; } } } } if (contenttype == NULL) { vvm_error("Unknown content type."); return NULL; } if (attributes == NULL) { vvm_error("No attributes. This is an attachment, but not one to save"); return g_strdup("NONE"); } attribute_parse = g_strsplit_set(attributes,";", -1); for (int i = 0; attribute_parse[i] != NULL; i++) { if (g_str_match_string("filename", attribute_parse[i], TRUE)) { g_autofree char **filename_parse = NULL; filename_parse = g_strsplit_set(attribute_parse[i],"=", 2); filename = g_strdup(filename_parse[1]); g_strdelimit (filename, "\"", ' '); g_strstrip(filename); DBG("filename: %s", filename); } } if (filename == NULL) { vvm_error("Could not find a file name."); return g_strdup("NONE"); } DBG("Folder Path: %s", folderpath); if (g_str_match_string("base64", contentencoding, TRUE)) { base64_encoded = TRUE; } else { base64_encoded = FALSE; } DBG("Reconstructing the message!!"); //AT&T USA linebreaks the base64 attachment every 60 characters //Why you ask? I have no idea. GString *decoded = g_string_new (NULL); for (int i = attachment_line; lines[i] != NULL; i++) { if (lines[i+1] != NULL) { decoded = g_string_append(decoded, lines[i]); if (base64_encoded == FALSE) { //The encoding is probably "text/plain", so //add /r/n back in decoded = g_string_append(decoded, "/r/n"); } } else { //This tends to be at the end of the email if(g_strcmp0("--", lines[i]) != 0) { decoded = g_string_append(decoded, lines[i]); } } } string_to_decode = g_string_free(decoded, FALSE); if (string_to_decode == NULL) { vvm_error("Error reconstructing attachment!"); return NULL; } else { //DBG("decoded message: %s", string_to_decode); } if (base64_encoded) { gsize base64_out_len; g_autofree unsigned char *base_64_decoded = NULL; base_64_decoded = g_base64_decode (string_to_decode, &base64_out_len); vvm_store (NULL, NULL, base_64_decoded, base64_out_len, filename, folderpath); } else if (g_strrstr("text/plain", contentencoding) != NULL) { vvm_store (NULL, string_to_decode, NULL, strlen(string_to_decode), filename, folderpath); } else { vvm_error("Do not know how to decode this attachment!"); return NULL; } //Content-Transfer-Encoding: base64 //Content-Type: audio/amr //X-AppleVM-Duration: 36 //Content-Disposition: attachment; size=58826;filename="voicemail-20210624165921.amr" return g_strdup_printf("%s%s", folderpath, filename); } static int vvm_util_decode_vvm_email_multipart_mixed (struct voicemail *vvm_msg, const char *folderpath) { g_autofree char **tokens = NULL; g_autofree char **boundary = NULL; g_autofree char **email_attachments = NULL; DBG("Decoding message of content type: %s", vvm_msg->content_type); //Content type looks like: multipart/mixed; boundary="_Part_287_1624553961" // "_Part_287_1624553961" divides headers and each attachment tokens = g_strsplit_set(vvm_msg->content_type,";", 2); if (g_str_match_string("boundary=", tokens[1], FALSE) == FALSE) { vvm_error("could not find boundary!"); return FALSE; } boundary = g_strsplit_set(tokens[1],"=", 2); g_strdelimit (boundary[1], "\"", ' '); g_strstrip(boundary[1]); DBG("Boundary %s", boundary[1]); //TODO: Decode the message now that you have the boundary email_attachments = g_strsplit(vvm_msg->contents, boundary[1], -1); //The first part are the headers, skip for (int i = 2; email_attachments[i] != NULL; i++) { if (g_str_match_string("Content-Type", email_attachments[i], FALSE)) { g_autofree char *save_file_path = NULL; save_file_path = vvm_util_decode_vvm_single_email_attachment (email_attachments[i], folderpath); if (save_file_path == NULL) { vvm_error("Failed to decode attachment"); return FALSE; } else if (g_strcmp0(save_file_path, "NONE") == 0) { vvm_error("Not an attachment to save"); } else { DBG("Putting attachment in settings"); if (vvm_msg->attachments == NULL) { vvm_msg->attachments = g_strdup(save_file_path); } else { g_autofree char *tmp = NULL; tmp = g_strdup(vvm_msg->attachments); g_free(vvm_msg->attachments); vvm_msg->attachments = g_strdup_printf("%s;%s", tmp, save_file_path); } DBG("Attachments saved: %s", vvm_msg->attachments); } } else { DBG("There is no Content-Type, not an attachment: %s", email_attachments[i]); } } return TRUE; } int vvm_util_decode_vvm_all_email_attachments (struct voicemail *vvm_msg, const char *folderpath) { g_autofree char *new_dir = NULL; new_dir = g_strdup_printf("%s%s-", folderpath, vvm_msg->file_uuid); DBG("Attachment Folder Path: %s", new_dir); if(vvm_util_decode_vvm_email_multipart_mixed(vvm_msg, new_dir) == FALSE) { vvm_error("Failed to decode message of content type: %s", vvm_msg->content_type); return FALSE; } return TRUE; } void vvm_util_delete_vvm_message(struct voicemail *vvm_msg) { g_free(vvm_msg->uid); g_free(vvm_msg->mailindex); g_free(vvm_msg->message_sender); g_free(vvm_msg->message_date); g_free(vvm_msg->to); g_free(vvm_msg->mime_version); g_free(vvm_msg->message_context); g_free(vvm_msg->file_uuid); g_free(vvm_msg->contents); g_free(vvm_msg->attachments); g_free(vvm_msg->dbus_path); g_free(vvm_msg->email_filepath); g_free(vvm_msg); } void vvm_util_delete_status_message(struct sms_control_message *sms_msg) { g_free(sms_msg->type); g_free(sms_msg->mailbox_hostname); g_free(sms_msg->mailbox_port); g_free(sms_msg->vvm_destination_number); g_free(sms_msg->mailbox_username); g_free(sms_msg->mailbox_password); g_free(sms_msg->vvm_type); g_free(sms_msg->default_number); g_free(sms_msg->carrier_prefix); g_free(sms_msg->uid); g_free(sms_msg->new_mailbox_messages); g_free(sms_msg->message_sender); g_free(sms_msg->message_date); g_free(sms_msg->message_length); g_free(sms_msg->language); g_free(sms_msg->greeting_length); g_free(sms_msg->voice_signature_length); g_free(sms_msg->TUI_password_length); g_free(sms_msg->mailindex); g_free(sms_msg->activate_url); g_free(sms_msg); } vvmd-0.7/src/vvmutil.h000066400000000000000000000102221415226775200150120ustar00rootroot00000000000000/* * * Visual Voicemail Daemon * * Copyright (C) 2021, Chris Talbot * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * */ #ifdef HAVE_CONFIG_H #include #endif //Refer to Android's Dialer, OmtpCvvmMessageSender.java // dt = device type, 6 = no VTT (transcription) support #define CCVM_SUFFIX "dt=6" #define ACTIVATE_SMS_PREFIX "Activate:" #define DEACTIVATE_SMS_PREFIX "Deactivate:" #define STATUS_SMS_PREFIX "STATUS:" #define PROTOCOL_VERSION_PREFIX "pv=" #define CLIENT_TYPE_PREFIX "ct=" #define TERMINAL_DESTINATION_PORT_NUMBER_PREFIX "pt=" //Protocol version 1.1 #define PROTOCOL_VERSION_1_1 "13" //Protocol version 1.2 #define PROTOCOL_VERSION_1_2 "13" //Protocol version 1.3 #define PROTOCOL_VERSION_1_3 "13" struct sms_control_message { char *type; int provision_status; int enable_vvm; char *mailbox_hostname; char *mailbox_port; char *vvm_destination_number; char *mailbox_username; char *mailbox_password; char *vvm_type; char *default_number; char *carrier_prefix; int sync_status_reason; char *uid; char *new_mailbox_messages; int mailbox_message_type; char *message_sender; char *message_date; char *message_length; char *language; char *greeting_length; char *voice_signature_length; char *TUI_password_length; char *mailindex; char *activate_url; }; struct voicemail { int mailbox_message_type; int lifetime_status; unsigned int message_registration_id; char *file_uuid; char *email_filepath; char *uid; char *mailindex; char *message_sender; char *message_date; char *to; char *mime_version; char *message_context; char *content_type; char *contents; char *attachments; char *dbus_path; }; // Refer to "OMTP_VVM_Specification v1.3, Section 2.8 enum vvmd_provision_status { VVM_PROVISION_STATUS_NOT_SET, //vvmd only, not a VVM Specification VVM_PROVISION_STATUS_NEW, VVM_PROVISION_STATUS_READY, VVM_PROVISION_STATUS_PROVISIONED, VVM_PROVISION_STATUS_UNKNOWN, VVM_PROVISION_STATUS_BLOCKED, }; enum vvmd_lifetime_status { VVM_LIFETIME_STATUS_UNKNOWN, VVM_LIFETIME_STATUS_NOT_READ, VVM_LIFETIME_STATUS_READ }; enum sync_sms_status_reason { SYNC_SMS_UNKNOWN, SYNC_SMS_NEW_MESSAGE, SYNC_SMS_MAILBOX_UPDATE, SYNC_SMS_GREETINGS_VOICE_SIGNATURE_UPDATE }; enum mailbox_message_type { MAILBOX_MESSAGE_UNKNOWN, MAILBOX_MESSAGE_VOICE, MAILBOX_MESSAGE_VIDEO, MAILBOX_MESSAGE_FAX, MAILBOX_MESSAGE_INFOTAINMENT, MAILBOX_MESSAGE_ECC }; enum SMS_type { SMS_MESSAGE_OTHER, SMS_MESSAGE_STATUS, SMS_MESSAGE_SYNC }; char *parse_email_address(const char *input); int vvm_util_parse_sms_message_type(const char *message, const char *carrier_prefix, const char *mailbox_config); void vvm_util_parse_status_sms_message(const char *message, struct sms_control_message *sms_msg, const char *mailbox_config); void vvm_util_parse_sync_sms_message(const char *message, struct sms_control_message *sms_msg, const char *mailbox_config); void vvm_util_delete_status_message(struct sms_control_message *sms_msg); void vvm_util_delete_vvm_message(struct voicemail *vvm_msg); char *vvm_util_create_activate_sms(const char *carrier_prefix, const char *vvm_type); char *vvm_util_create_deactivate_sms(const char *carrier_prefix, const char *vvm_type); char *vvm_util_create_status_sms(const char *carrier_prefix, const char *vvm_type); char *vvm_store_generate_uuid_objpath(void); int vvm_util_decode_vvm_all_email_attachments (struct voicemail *vvm_msg, const char *folderpath); void vvm_util_decode_vvm_headers (struct voicemail *vvm_msg, char **tokens); char *decode(const char *input); vvmd-0.7/unit/000077500000000000000000000000001415226775200133265ustar00rootroot00000000000000vvmd-0.7/unit/ATT1.mbox000066400000000000000000000211361415226775200147310ustar00rootroot00000000000000Message-Context: Voice-message Content-Duration: 3 sensitivity: Personal importance: Normal Received: from ts03 (ts03 [172.19.160.52]) by ms06; Thu, 24 Jun 2021 12:14:24 -0400 Mail-From: non-mail-user@crafpaalvml001.nsdeng.att.com From: "4325550110" <4325550110@crafpaalvml001.nsdeng.att.com> x-cli_number: 4325550110 To: "2065550110" <2065550110@crafpaalvml001.nsdeng.att.com> Message-ID: ts03_1624551264_3983410_AnyPath X-Priority: 3 x-restrictions: 6 x-cdr-session-id: 172.19.161.22.00003456021624551244 X-MODE-ORIG: TUI X-DELIVERY-TYPE: CA X-INCOMING-PORT: trusted X-API_TM: 24 14 12 24 5 121 4 174 1 Date: Thu, 24 Jun 2021 12:14:24 -0400 x-mc_orig_machine_route: COMPLEX x-access_time: 1624561159 x-anypath-expires: Sat, 24 Jul 2021 14:59:19 -0400 x-callback_number: x-mc_message_type: 30 Subject: =?iso-8859-1?Q?call_from_4325550110?= x-generated_subject: Mime-Version: 1.0 (Voice 2.0) Content-Type: multipart/mixed; Boundary="============>>AnyPath 1624551264<<============" This is a multi-part message in MIME format. --============>>AnyPath 1624551264<<============ Content-Duration: 3 Content-Type: audio/amr; X-Codec="amr"; X-Duration="3" Content-Disposition: inline; filename="voice.amr" Content-Description: Voice File Content-Transfer-Encoding: base64 Content-Length: 7416 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 625KIB7tKv+5sOjgz73q2WKfMUU0dGqrl+YQ --============>>AnyPath 1624551264<<============-- vvmd-0.7/unit/ATT1Headers.txt000066400000000000000000000017251415226775200161010ustar00rootroot00000000000000Message-Context: Voice-message Content-Duration: 3 sensitivity: Personal importance: Normal Received: from ts03 (ts03 [172.19.160.52]) by ms06; Thu, 24 Jun 2021 12:14:24 -0400 Mail-From: non-mail-user@crafpaalvml001.nsdeng.att.com From: "4325550110" <4325550110@crafpaalvml001.nsdeng.att.com> x-cli_number: 4325550110 To: "2065550110" <2065550110@crafpaalvml001.nsdeng.att.com> Message-ID: ts03_1624551264_3983410_AnyPath X-Priority: 3 x-restrictions: 6 x-cdr-session-id: 172.19.161.22.00003456021624551244 X-MODE-ORIG: TUI X-DELIVERY-TYPE: CA X-INCOMING-PORT: trusted X-API_TM: 24 14 12 24 5 121 4 174 1 Date: Thu, 24 Jun 2021 12:14:24 -0400 x-mc_orig_machine_route: COMPLEX x-access_time: 1624561159 x-anypath-expires: Sat, 24 Jul 2021 14:59:19 -0400 x-callback_number: x-mc_message_type: 30 Subject: =?iso-8859-1?Q?call_from_4325550110?= x-generated_subject: Mime-Version: 1.0 (Voice 2.0) Content-Type: multipart/mixed; Boundary="============>>AnyPath 1624551264<<============" vvmd-0.7/unit/MintMobile1.mbox000066400000000000000000001110171415226775200163360ustar00rootroot00000000000000To: VOICE=4325550110@domain.com From: VOICE=2065550110@tmo.com Date: Thu, 22 Jul 2021 23:04:30 +0000 MIME-version: 1.0 Content-type: multipart/mixed; boundary="_Part_740_1626995070" Message-id: <2021-07-22T23:04:30Z-conn:d67cdcc8-312fbd0a-13c4-65014-551bf4-a3f4a03-551bf4> Reply-To: VOICE=2065550110@tmo.com X-Priority: 3 X-AppleVM-Message-Version: 1.0 Message-Context: voice-message --_Part_740_1626995070 Content-Transfer-Encoding: 7bit Content-Type: text/plain; charset=ISO-8859-1 This is a message for the Apple Voicemail system. --_Part_740_1626995070 Content-Transfer-Encoding: base64 Content-Type: audio/amr X-AppleVM-Duration: 17 Content-Disposition: attachment; size=27464;filename="voicemail-20210722230430.amr" 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 --_Part_740_1626995070-- vvmd-0.7/unit/MintMobile1Headers.txt000066400000000000000000000006001415226775200174770ustar00rootroot00000000000000To: VOICE=4325550110@domain.com From: VOICE=2065550110@tmo.com Date: Thu, 22 Jul 2021 23:04:30 +0000 MIME-version: 1.0 Content-type: multipart/mixed; boundary="_Part_740_1626995070" Message-id: <2021-07-22T23:04:30Z-conn:d67cdcc8-312fbd0a-13c4-65014-551bf4-a3f4a03-551bf4> Reply-To: VOICE=2065550110@tmo.com X-Priority: 3 X-AppleVM-Message-Version: 1.0 Message-Context: voice-message vvmd-0.7/unit/Tmobile1.mbox000066400000000000000000002317561415226775200157070ustar00rootroot00000000000000To: VOICE=+12065550110@domain.com From: VOICE=+14325550110@tmo.com Date: Mon, 21 Jun 2021 13:16:58 +0000 MIME-version: 1.0 Content-type: multipart/mixed; boundary="_Part_922_1624281418" Message-id: <2021-06-21T13:16:58Z-conn:d3403428-a816be0a-13c4-65014-252072-45aee167-252072> Reply-To: VOICE=+14325550110@tmo.com X-Priority: 3 X-AppleVM-Message-Version: 1.0 Message-Context: voice-message --_Part_922_1624281418 Content-Transfer-Encoding: 7bit Content-Type: text/plain; charset=ISO-8859-1 This is a message for the Apple Voicemail system. --_Part_922_1624281418 Content-Transfer-Encoding: base64 Content-Type: audio/amr X-AppleVM-Duration: 36 Content-Disposition: attachment; size=58538;filename="voicemail-20210621131658.amr" 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 --_Part_922_1624281418-- vvmd-0.7/unit/Tmobile1Headers.txt000066400000000000000000000006211415226775200170360ustar00rootroot00000000000000To: VOICE=+12065550110@domain.com From: VOICE=+14325550110@tmo.com Date: Mon, 21 Jun 2021 13:16:58 +0000 MIME-version: 1.0 Content-type: multipart/mixed; boundary="_Part_922_1624281418" Message-id: <2021-06-21T13:16:58Z-conn:d3403428-a816be0a-13c4-65014-252072-45aee167-252072> Reply-To: VOICE=+14325550110@tmo.com X-Priority: 3 X-AppleVM-Message-Version: 1.0 Message-Context: voice-message vvmd-0.7/unit/VZWUSA1.mbox000066400000000000000000000312341415226775200153400ustar00rootroot00000000000000Return-Path: <> Received: from vm-asu9.ChanIns43.vzwazc.com (10.216.120.154) by MIPS1.ChanIns43.vzwazc.com (Multi Media IP Store) id 6102419104DCCCC2 for 15465557654@vzwazc.com; Thu, 21 Oct 2021 09:26:23 -0400 Date: Thu, 21 Oct 2021 09:26:23 -0400 (EDT) From: +15235555432@vzwazc.com Reply-To: 15235555432 To: +15465557654@vzwazc.com Message-ID: <3677412.45040778.1634822783904.JavaMail.vxvuser@vm-asu9.ChanIns43.vzwazc.com> Subject: Voice message MIME-Version: 1.0 (Voice Version 2.0) Content-Type: multipart/voice-message; boundary="----=_Part_45040777_18760913.1634822783904" Message-Context: voice-message X-Voicemail: Content-Duration: 5 X-Priority: 3 X-CNS-UID: 7340305-101 X-Mailer: CNS-VxV(4.3.7) X-CNS-Originator-Phone-Number: 15235555432 X-CNS-Accessed-Phone-Type: MainPhone X-CNS-Accessed-Phone: 15465557654 Importance: Normal X-CNS-Message-Context: voice-message X-CNS-Media-Size: total=1msgs; voice=1msgs; d_voice=5sec; ------=_Part_45040777_18760913.1634822783904 Content-Type: audio/AMR; name=audio_0.amr Content-Transfer-Encoding: base64 Content-Disposition: attachment; size=8550; filename=audio_0.amr Content-Duration: 5 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 ------=_Part_45040777_18760913.1634822783904-- vvmd-0.7/unit/VZWUSA1Headers.txt000066400000000000000000000017111415226775200165030ustar00rootroot00000000000000Return-Path: <> Received: from vm-asu9.ChanIns43.vzwazc.com (10.216.120.154) by MIPS1.ChanIns43.vzwazc.com (Multi Media IP Store) id 6102419104DCCCC2 for 15465557654@vzwazc.com; Thu, 21 Oct 2021 09:26:23 -0400 Date: Thu, 21 Oct 2021 09:26:23 -0400 (EDT) From: +15235555432@vzwazc.com Reply-To: 15235555432 To: +15465557654@vzwazc.com Message-ID: <3677412.45040778.1634822783904.JavaMail.vxvuser@vm-asu9.ChanIns43.vzwazc.com> Subject: Voice message MIME-Version: 1.0 (Voice Version 2.0) Content-Type: multipart/voice-message; boundary="----=_Part_45040777_18760913.1634822783904" Message-Context: voice-message X-Voicemail: Content-Duration: 5 X-Priority: 3 X-CNS-UID: 7340305-101 X-Mailer: CNS-VxV(4.3.7) X-CNS-Originator-Phone-Number: 15235555432 X-CNS-Accessed-Phone-Type: MainPhone X-CNS-Accessed-Phone: 15465557654 Importance: Normal X-CNS-Message-Context: voice-message X-CNS-Media-Size: total=1msgs; voice=1msgs; d_voice=5sec; vvmd-0.7/unit/meson.build000066400000000000000000000037211415226775200154730ustar00rootroot00000000000000test_dependencies = [ dependency('glib-2.0', version : '>=2.16'), dependency('mm-glib', version : '>=1.14'), dependency('libcurl', version : '>7.70'), cc.find_library('dl'), cc.find_library('phonenumber', required: true) ] test_vvmutil = executable('test-vvmutil', ['test-vvmutil.c', '../src/vvmutil.c', '../src/log.c', '../src/store.c'], include_directories : includes, dependencies : test_dependencies ) test_decode = executable('test_decode', ['test-decode.c', '../src/vvmutil.c', '../src/log.c', '../src/store.c'], include_directories : includes, dependencies : test_dependencies ) test_number_decode = executable('test_number_decode', ['test-number-decode.c', '../src/vvmutil.c', '../src/log.c', '../src/store.c'], include_directories : includes, dependencies : test_dependencies ) test_phone_utils = executable('test_phone_utils', ['test-phone-utils.c', '../src/phone-utils.cpp'], include_directories : includes, dependencies : test_dependencies ) test_service_providers = executable('test_service_providers', ['test-service-providers.c', '../src/service-providers.c', '../src/log.c'], include_directories : includes, dependencies : test_dependencies ) test_vvmparse = executable('test-vvmparse', ['test-vvmparse.c', '../src/vvmutil.c', '../src/log.c', '../src/store.c'], include_directories : includes, dependencies : test_dependencies ) # tests expect pwd to be at the project root, not in ./unit test('test vvm attachment decode', test_vvmutil, workdir : meson.current_source_dir() + '/..') test('test vvm headers', test_vvmparse, workdir : meson.current_source_dir() + '/..') test('test decode', test_decode, workdir : meson.current_source_dir() + '/..') test('test number decode', test_number_decode, workdir : meson.current_source_dir() + '/..') test('test phone utils', test_phone_utils, workdir : meson.current_source_dir() + '/..') test('test service providers', test_service_providers, workdir : meson.current_source_dir() + '/..') vvmd-0.7/unit/test-decode.c000066400000000000000000000106061415226775200156750ustar00rootroot00000000000000/* * * Visual Voicemail Daemon * * Copyright (C) 2021, Chris Talbot * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include #include #include #include "vvmutil.h" static void test_decode(gconstpointer data) { char *output; const char decode_0[] = "WFRRXlpTVVVfWTAwMDAw"; //000000000000000 const char decode_1[] = "WVVQX1tSVFReWDExMTEx"; //111111111111111 const char decode_2[] = "WlZTXFhRV1ddWzIyMjIy"; //222222222222222 const char decode_3[] = "W1dSXVlQVlZcWjMzMzMz"; //333333333333333 const char decode_4[] = "XFBVWl5XUVFbXTQ0NDQ0"; //444444444444444 const char decode_5[] = "XVFUW19WUFBaXDU1NTU1"; //555555555555555 const char decode_6[] = "XlJXWFxVU1NZXzY2NjY2"; //666666666666666 const char decode_7[] = "X1NWWV1UUlJYXjc3Nzc3"; //777777777777777 const char decode_8[] = "UFxZVlJbXV1XUTg4ODg4"; //888888888888888 const char decode_9[] = "UV1YV1NaXFxWUDk5OTk5"; //999999999999999 const char decode_007[] = "WFRRXlpTVQ=="; //0000000 const char decode_008[] = "WFRRXlpTVVU="; //00000000 const char decode_009[] = "WFRRXlpTVVVf"; //000000000 const char decode_010[] = "WFRRXlpTVVVfWQ=="; //0000000000 const char decode_011[] = "WFRRXlpTVVVfWTA="; //00000000000 const char decode_012[] = "WFRRXlpTVVVfWTAw"; //000000000000 const char decode_013[] = "WFRRXlpTVVVfWTAwMA=="; //0000000000000 const char decode_014[] = "WFRRXlpTVVVfWTAwMDA="; //00000000000000 output = decode(decode_007); g_assert(g_strcmp0(output, "0000000") == 0); g_free(output); output = NULL; output = decode(decode_008); g_assert(g_strcmp0(output, "00000000") == 0); g_free(output); output = NULL; output = decode(decode_009); g_assert(g_strcmp0(output, "000000000") == 0); g_free(output); output = NULL; output = decode(decode_010); g_assert(g_strcmp0(output, "0000000000") == 0); g_free(output); output = NULL; output = decode(decode_011); g_assert(g_strcmp0(output, "00000000000") == 0); g_free(output); output = NULL; output = decode(decode_012); g_assert(g_strcmp0(output, "000000000000") == 0); g_free(output); output = NULL; output = decode(decode_013); g_assert(g_strcmp0(output, "0000000000000") == 0); g_free(output); output = NULL; output = decode(decode_014); g_assert(g_strcmp0(output, "00000000000000") == 0); g_free(output); output = NULL; output = decode(decode_0); g_assert(g_strcmp0(output, "000000000000000") == 0); g_free(output); output = NULL; output = decode(decode_1); g_assert(g_strcmp0(output, "111111111111111") == 0); g_free(output); output = NULL; output = decode(decode_2); g_assert(g_strcmp0(output, "222222222222222") == 0); g_free(output); output = NULL; output = decode(decode_3); g_assert(g_strcmp0(output, "333333333333333") == 0); g_free(output); output = NULL; output = decode(decode_4); g_assert(g_strcmp0(output, "444444444444444") == 0); g_free(output); output = NULL; output = decode(decode_5); g_assert(g_strcmp0(output, "555555555555555") == 0); g_free(output); output = NULL; output = decode(decode_6); g_assert(g_strcmp0(output, "666666666666666") == 0); g_free(output); output = NULL; output = decode(decode_7); g_assert(g_strcmp0(output, "777777777777777") == 0); g_free(output); output = NULL; output = decode(decode_8); g_assert(g_strcmp0(output, "888888888888888") == 0); g_free(output); output = NULL; output = decode(decode_9); g_assert(g_strcmp0(output, "999999999999999") == 0); g_free(output); output = NULL; } int main(int argc, char **argv) { g_test_init(&argc, &argv, NULL); g_test_add_data_func("/vvmutil/Decode Test", NULL, test_decode); return g_test_run(); } vvmd-0.7/unit/test-number-decode.c000066400000000000000000000055461415226775200171720ustar00rootroot00000000000000/* * * Visual Voicemail Daemon * * Copyright (C) 2021, Chris Talbot * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include #include #include #include "vvmutil.h" static void test_number_decode(gconstpointer data) { char *output; const char decode_t_mobile_to[] = "VOICE=+12065550110@domain.com"; const char decode_t_mobile_from[] = "VOICE=+14325550110@tmo.com"; const char decode_mint_mobile_to[] = "VOICE=4325550110@domain.com"; const char decode_mint_mobile_from[] = "VOICE=2065550110@tmo.com"; const char decode_att_usa_to[] = "\"4325550110\" <4325550110@crafpaalvml001.nsdeng.att.com>"; const char decode_att_usa_from[] = "\"2065550110\" <2065550110@crafpaalvml001.nsdeng.att.com>"; const char decode_e164_to[] = "+12065550110"; const char decode_e164_from[] = "+14325550110"; output = parse_email_address(decode_t_mobile_to); g_assert(g_strcmp0(output, "+12065550110") == 0); g_clear_pointer(&output, g_free); output = parse_email_address(decode_t_mobile_from); g_assert(g_strcmp0(output, "+14325550110") == 0); g_free(output); output = NULL; output = parse_email_address(decode_mint_mobile_to); g_assert(g_strcmp0(output, "4325550110") == 0); g_clear_pointer(&output, g_free); output = parse_email_address(decode_mint_mobile_from); g_assert(g_strcmp0(output, "2065550110") == 0); g_clear_pointer(&output, g_free); output = parse_email_address(decode_att_usa_to); g_assert(g_strcmp0(output, "4325550110") == 0); g_clear_pointer(&output, g_free); output = parse_email_address(decode_att_usa_from); g_assert(g_strcmp0(output, "2065550110") == 0); g_clear_pointer(&output, g_free); output = parse_email_address(decode_e164_to); g_assert(g_strcmp0(output, "+12065550110") == 0); g_clear_pointer(&output, g_free); output = parse_email_address(decode_e164_from); g_assert(g_strcmp0(output, "+14325550110") == 0); g_clear_pointer(&output, g_free); } int main(int argc, char **argv) { g_test_init(&argc, &argv, NULL); g_test_add_data_func("/vvmutil/Number Decode Test", NULL, test_number_decode); return g_test_run(); } vvmd-0.7/unit/test-phone-utils.c000066400000000000000000000060541415226775200167230ustar00rootroot00000000000000/* * * Visual Voicemail Daemon * * Copyright (C) 2021, Chris Talbot * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include #include #include #include "phone-utils.h" static void test_number_decode(gconstpointer data) { char *output; const char phone_test1[] = "12065550110"; const char phone_test2[] = "2065550110"; const char phone_test3[] = "(206) 555-0110"; const char phone_test4[] = "206-555-0110"; const char phone_output[] = "+12065550110"; const char email_test1[] = "testemail@example.com"; output = phone_utils_format_number_e164(phone_test1, "US", TRUE); if (g_strcmp0(output, phone_output) != 0) { g_debug("Output %s is wrong! Expected output: %s\n", output, phone_output); } g_assert(g_strcmp0(output, phone_output) == 0); g_free(output); output = NULL; output = phone_utils_format_number_e164(phone_test2, "US", TRUE); if (g_strcmp0(output, phone_output) != 0) { g_debug("Output %s is wrong! Expected output: %s\n", output, phone_output); } g_assert(g_strcmp0(output, phone_output) == 0); g_free(output); output = NULL; output = phone_utils_format_number_e164(phone_test3, "US", TRUE); if (g_strcmp0(output, phone_output) != 0) { g_debug("Output %s is wrong! Expected output: %s\n", output, phone_output); } g_assert(g_strcmp0(output, phone_output) == 0); g_free(output); output = NULL; output = phone_utils_format_number_e164(phone_test4, "US", TRUE); if (g_strcmp0(output, phone_output) != 0) { g_debug("Output %s is wrong! Expected output: %s\n", output, phone_output); } g_assert(g_strcmp0(output, phone_output) == 0); g_free(output); output = NULL; output = phone_utils_format_number_e164(email_test1, "US", TRUE); if (g_strcmp0(output, output) != 0) { g_debug("Output %s is wrong! Expected output: %s\n", email_test1, output); } g_assert(g_strcmp0(output, email_test1) == 0); g_free(output); output = NULL; output = phone_utils_format_number_e164(email_test1, "US", FALSE); if (output) { g_debug("Output %s is not NULL and should be!\n", output); } g_assert(output == NULL); g_free(output); output = NULL; } int main(int argc, char **argv) { g_test_init(&argc, &argv, NULL); g_test_add_data_func("/vvmutil/Number Decode Test", NULL, test_number_decode); return g_test_run(); } vvmd-0.7/unit/test-service-providers.c000066400000000000000000000060311415226775200201220ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * Copyright (C) 2019 Red Hat */ #ifdef HAVE_CONFIG_H #include #endif #include #include "service-providers.h" static void test_positive_cb(const char *carrier, const char *vvm_std, const char *dest_num, const char *carrier_prefix, GError *error, gpointer user_data) { GMainLoop *loop = user_data; g_main_loop_quit(loop); g_assert_no_error(error); g_assert_cmpstr(carrier, ==, "Personal"); g_assert_cmpstr(vvm_std, ==, "cvvm"); g_assert_cmpstr(dest_num, ==, "098765"); g_assert_cmpstr(carrier_prefix, ==, "//VVM"); } static void test_positive(void) { GMainLoop *loop = g_main_loop_new(NULL, FALSE); vvmd_service_providers_find_settings(SOURCE_ROOT "/unit/test-service-providers.xml", "345999", test_positive_cb, loop); g_main_loop_run(loop); g_main_loop_unref(loop); } static void test_negative_cb(const char *carrier, const char *vvm_std, const char *dest_num, const char *carrier_prefix, GError *error, gpointer user_data) { GMainLoop *loop = user_data; g_main_loop_quit(loop); g_assert_error(error, 1, 1); } static void test_negative(void) { GMainLoop *loop = g_main_loop_new(NULL, FALSE); vvmd_service_providers_find_settings(SOURCE_ROOT "/unit/test-service-providers.xml", "133666", test_negative_cb, loop); g_main_loop_run(loop); g_main_loop_unref(loop); } /*****************************************************************************/ static void test_nonexistent_cb(const char *carrier, const char *vvm_std, const char *dest_num, const char *carrier_prefix, GError *error, gpointer user_data) { GMainLoop *loop = user_data; g_assert_error(error, G_IO_ERROR, G_IO_ERROR_AGAIN); g_main_loop_quit(loop); } static void test_nonexistent(void) { GMainLoop *loop = g_main_loop_new(NULL, FALSE); vvmd_service_providers_find_settings("nonexistent.xml", "13337", test_nonexistent_cb, loop); g_main_loop_run(loop); g_main_loop_unref(loop); } /*****************************************************************************/ int main(int argc, char **argv) { g_test_init(&argc, &argv, NULL); g_test_add_func("/service-providers/positive", test_positive); g_test_add_func("/service-providers/negative", test_negative); g_test_add_func("/service-providers/nonexistent", test_nonexistent); return g_test_run(); } vvmd-0.7/unit/test-service-providers.xml000066400000000000000000000041161415226775200205020ustar00rootroot00000000000000 Sophia APN 192.0.2.1 192.0.2.2 Demiurge Seperate MMS APN mms mms http://mms.seperate.example.com/ 192.0.2.1:8080 Seperate Internet APN praise santa 192.0.2.1 192.0.2.2 192.0.2.3 Second worship doom Personal 1234567 098765 //VVM Other worship doom Combined MMS and Internet APN 192.0.2.1 192.0.2.2 http://mms.combined.example.com/ vvmd-0.7/unit/test-vvmparse.c000066400000000000000000000142051415226775200163140ustar00rootroot00000000000000/* * * Visual Voicemail Daemon * * Copyright (C) 2010-2011 Intel Corporation. * 2021, Chris Talbot * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include #include #include #include "vvmutil.h" #define VVM_SHA1_UUID_LEN 20 struct vvm_test { const char *pathname; const char *content_type; const char *to; const char *from; const char *date; const char *mime; const char *message_context; }; static const char vvm_mbox_1[] = "./unit/Tmobile1Headers.txt"; static const char vvm_mbox_1_content_type[] = "multipart/mixed; boundary=\"_Part_922_1624281418\""; static const char vvm_mbox_1_to[] = "VOICE=+12065550110@domain.com"; static const char vvm_mbox_1_from[] = "VOICE=+14325550110@tmo.com"; static const char vvm_mbox_1_date[] = "Mon, 21 Jun 2021 13:16:58 +0000"; static const char vvm_mbox_1_mime[] = "1.0"; static const char vvm_mbox_1_message_context[] = "voice-message"; static const struct vvm_test vvm_test_1 = { .pathname = vvm_mbox_1, .content_type = vvm_mbox_1_content_type, .to = vvm_mbox_1_to, .from = vvm_mbox_1_from, .date = vvm_mbox_1_date, .mime = vvm_mbox_1_mime, .message_context = vvm_mbox_1_message_context, }; static const char vvm_mbox_2[] = "./unit/ATT1Headers.txt"; static const char vvm_mbox_2_content_type[] = "multipart/mixed; Boundary=\"============>>AnyPath 1624551264<<============\""; static const char vvm_mbox_2_to[] = "\"2065550110\" <2065550110@crafpaalvml001.nsdeng.att.com>"; static const char vvm_mbox_2_from[] = "\"4325550110\" <4325550110@crafpaalvml001.nsdeng.att.com>"; static const char vvm_mbox_2_date[] = "Thu, 24 Jun 2021 12:14:24 -0400"; static const char vvm_mbox_2_mime[] = "1.0 (Voice 2.0)"; static const char vvm_mbox_2_message_context[] = "Voice-message"; static const struct vvm_test vvm_test_2 = { .pathname = vvm_mbox_2, .content_type = vvm_mbox_2_content_type, .to = vvm_mbox_2_to, .from = vvm_mbox_2_from, .date = vvm_mbox_2_date, .mime = vvm_mbox_2_mime, .message_context = vvm_mbox_2_message_context, }; static const char vvm_mbox_3[] = "./unit/MintMobile1Headers.txt"; static const char vvm_mbox_3_content_type[] = "multipart/mixed; boundary=\"_Part_740_1626995070\""; static const char vvm_mbox_3_to[] = "VOICE=4325550110@domain.com"; static const char vvm_mbox_3_from[] = "VOICE=2065550110@tmo.com"; static const char vvm_mbox_3_date[] = "Thu, 22 Jul 2021 23:04:30 +0000"; static const char vvm_mbox_3_mime[] = "1.0"; static const char vvm_mbox_3_message_context[] = "voice-message"; static const struct vvm_test vvm_test_3 = { .pathname = vvm_mbox_3, .content_type = vvm_mbox_3_content_type, .to = vvm_mbox_3_to, .from = vvm_mbox_3_from, .date = vvm_mbox_3_date, .mime = vvm_mbox_3_mime, .message_context = vvm_mbox_3_message_context, }; static const char vvm_mbox_4[] = "./unit/VZWUSA1Headers.txt"; static const char vvm_mbox_4_content_type[] = "multipart/voice-message;boundary=\"----=_Part_45040777_18760913.1634822783904\""; static const char vvm_mbox_4_to[] = "+15465557654@vzwazc.com"; static const char vvm_mbox_4_from[] = "+15235555432@vzwazc.com"; static const char vvm_mbox_4_date[] = "Thu, 21 Oct 2021 09:26:23 -0400 (EDT)"; static const char vvm_mbox_4_mime[] = "1.0 (Voice Version 2.0)"; static const char vvm_mbox_4_message_context[] = "voice-message"; static const struct vvm_test vvm_test_4 = { .pathname = vvm_mbox_4, .content_type = vvm_mbox_4_content_type, .to = vvm_mbox_4_to, .from = vvm_mbox_4_from, .date = vvm_mbox_4_date, .mime = vvm_mbox_4_mime, .message_context = vvm_mbox_4_message_context, }; static void test_decode_headers(gconstpointer data) { const struct vvm_test *test = data; struct voicemail *vvm_msg; unsigned int len; char **tokens = NULL; vvm_msg = g_try_new0(struct voicemail, 1); if (vvm_msg == NULL) { g_printerr("Failed allocate message"); return; } struct stat st; unsigned char *pdu; int fd; fd = open(test->pathname, O_RDONLY); if (fd < 0) { g_printerr("Failed to open path %s\n", test->pathname); goto out; } if (fstat(fd, &st) < 0) { g_printerr("Failed to stat %s\n", test->pathname); close(fd); goto out; } len = st.st_size; pdu = mmap(NULL, len, PROT_READ, MAP_SHARED, fd, 0); if (!pdu || pdu == MAP_FAILED) { g_printerr("Failed to mmap %s\n", test->pathname); close(fd); goto out; } tokens = g_strsplit_set((char *) pdu,"\r\n", -1); vvm_util_decode_vvm_headers (vvm_msg, tokens); g_strfreev (tokens); g_assert_cmpstr (test->content_type, ==, vvm_msg->content_type); g_assert_cmpstr (test->date, ==, vvm_msg->message_date); g_assert_cmpstr (test->from, ==, vvm_msg->message_sender); g_assert_cmpstr (test->to, ==, vvm_msg->to); g_assert_cmpstr (test->message_context, ==, vvm_msg->message_context); g_assert_cmpstr (test->mime, ==, vvm_msg->mime_version); munmap(pdu, len); close(fd); vvm_util_delete_vvm_message(vvm_msg); return; out: vvm_util_delete_vvm_message(vvm_msg); g_assert(FALSE); } int main(int argc, char **argv) { g_test_init(&argc, &argv, NULL); g_test_add_data_func("/vvmutil/Decode T-Mobile USA 1 headers", &vvm_test_1, test_decode_headers); g_test_add_data_func("/vvmutil/Decode AT&T USA 1 headers", &vvm_test_2, test_decode_headers); g_test_add_data_func("/vvmutil/Decode Mint Mobile 1 headers", &vvm_test_3, test_decode_headers); g_test_add_data_func("/vvmutil/Decode VZW USA 1 headers", &vvm_test_4, test_decode_headers); return g_test_run(); } vvmd-0.7/unit/test-vvmutil.c000066400000000000000000000137201415226775200161600ustar00rootroot00000000000000/* * * Visual Voicemail Daemon * * Copyright (C) 2010-2011 Intel Corporation. * 2021, Chris Talbot * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include #include #include #include "vvmutil.h" #define VVM_SHA1_UUID_LEN 20 struct vvm_test { const char *pathname; const char *content_type; const char *sha; }; static const char vvm_mbox_1[] = "./unit/Tmobile1.mbox"; static const char vvm_mbox_1_content_type[] = "multipart/mixed; boundary=\"_Part_922_1624281418\""; static const char vvm_mbox_1_sha[] = "9D25A01C4D47C3F309D4C45B0C584DFF1B5CBE11"; static const struct vvm_test vvm_test_1 = { .pathname = vvm_mbox_1, .content_type = vvm_mbox_1_content_type, .sha = vvm_mbox_1_sha }; static const char vvm_mbox_2[] = "./unit/ATT1.mbox"; static const char vvm_mbox_2_content_type[] = "multipart/mixed; Boundary=\"============>>AnyPath 1624551264<<============\""; static const char vvm_mbox_2_sha[] = "6160CA0D43739ED268FCC59757D8758238D179AD"; static const struct vvm_test vvm_test_2 = { .pathname = vvm_mbox_2, .content_type = vvm_mbox_2_content_type, .sha = vvm_mbox_2_sha }; static const char vvm_mbox_3[] = "./unit/MintMobile1.mbox"; static const char vvm_mbox_3_content_type[] = "multipart/mixed; boundary=\"_Part_740_1626995070\""; static const char vvm_mbox_3_sha[] = "8C389E984DB96CA75C9ECEF7EB4A2A01EFB24727"; static const struct vvm_test vvm_test_3 = { .pathname = vvm_mbox_3, .content_type = vvm_mbox_3_content_type, .sha = vvm_mbox_3_sha }; static const char vvm_mbox_4[] = "./unit/VZWUSA1.mbox"; static const char vvm_mbox_4_content_type[] = "multipart/voice-message;boundary=\"----=_Part_45040777_18760913.1634822783904\""; static const char vvm_mbox_4_sha[] = "0D59618C89F509FD9860EB316B2B8366FD404E88"; static const struct vvm_test vvm_test_4 = { .pathname = vvm_mbox_4, .content_type = vvm_mbox_4_content_type, .sha = vvm_mbox_4_sha }; static const char *digest_to_str(const unsigned char *digest) { static char buf[VVM_SHA1_UUID_LEN * 2 + 1]; unsigned int i; for (i = 0; i < VVM_SHA1_UUID_LEN; i++) sprintf(&buf[i * 2], "%02X", digest[i]); buf[VVM_SHA1_UUID_LEN * 2] = 0; return buf; } static const char *generate_sha_from_pdu(const unsigned char *pdu, unsigned int len) { GChecksum *checksum; guint8 digest[VVM_SHA1_UUID_LEN]; gsize digest_size = VVM_SHA1_UUID_LEN; checksum = g_checksum_new(G_CHECKSUM_SHA1); if (checksum == NULL) return NULL; g_checksum_update(checksum, pdu, len); g_checksum_get_digest(checksum, digest, &digest_size); g_checksum_free(checksum); return digest_to_str(digest); } static void test_decode_vvm(gconstpointer data) { const struct vvm_test *test = data; struct voicemail *vvm_msg; char *tmp_dir; const char *sha; unsigned int len; gboolean ret = TRUE; vvm_msg = g_try_new0(struct voicemail, 1); if (vvm_msg == NULL) { g_printerr("Failed allocate message"); return; } struct stat st; unsigned char *pdu; int fd; fd = open(test->pathname, O_RDONLY); if (fd < 0) { g_printerr("Failed to open path %s\n", test->pathname); goto out; } if (fstat(fd, &st) < 0) { g_printerr("Failed to stat %s\n", test->pathname); close(fd); goto out; } len = st.st_size; pdu = mmap(NULL, len, PROT_READ, MAP_SHARED, fd, 0); if (!pdu || pdu == MAP_FAILED) { g_printerr("Failed to mmap %s\n", test->pathname); close(fd); goto out; } vvm_msg->contents = (char *) pdu; vvm_msg->content_type = g_strdup(test->content_type); vvm_msg->file_uuid = vvm_store_generate_uuid_objpath(); tmp_dir = g_strdup_printf("%s/", g_get_home_dir ()); ret = vvm_util_decode_vvm_all_email_attachments (vvm_msg, tmp_dir); if (ret != TRUE) { g_printerr("vvm_util_decode_vvm_all_email_attachments() Failed!\n"); } g_assert(ret == TRUE); g_free(tmp_dir); munmap(pdu, len); close(fd); fd = open(vvm_msg->attachments, O_RDONLY); if (fd < 0) { g_printerr("Failed to open Attachment %s\n", vvm_msg->attachments); goto out; } if (fstat(fd, &st) < 0) { g_printerr("Failed to stat %s\n", test->pathname); close(fd); goto out; } len = st.st_size; pdu = mmap(NULL, len, PROT_READ, MAP_SHARED, fd, 0); if (!pdu || pdu == MAP_FAILED) { g_printerr("Failed to mmap %s\n", test->pathname); close(fd); goto out; } sha = generate_sha_from_pdu(pdu, len); if (g_strcmp0(sha, test->sha) != 0) { g_debug("%s sha: %s is different! compare sha: %s\n", vvm_msg->attachments, test->sha, sha); } g_assert(g_strcmp0(sha, test->sha) == 0); munmap(pdu, len); close(fd); unlink(vvm_msg->attachments); // Contents are freed already vvm_msg->contents = NULL; vvm_util_delete_vvm_message(vvm_msg); return; out: vvm_util_delete_vvm_message(vvm_msg); g_assert(FALSE); } int main(int argc, char **argv) { g_test_init(&argc, &argv, NULL); g_test_add_data_func("/vvmutil/Decode T-Mobile USA 1 (One Attachment)", &vvm_test_1, test_decode_vvm); g_test_add_data_func("/vvmutil/Decode AT&T USA 1 (One Attachment)", &vvm_test_2, test_decode_vvm); g_test_add_data_func("/vvmutil/Decode Mint Mobile USA 1 (One Attachment)", &vvm_test_3, test_decode_vvm); g_test_add_data_func("/vvmutil/Decode VZW USA 1 (One Attachment)", &vvm_test_4, test_decode_vvm); return g_test_run(); }