pax_global_header00006660000000000000000000000064145637447500014531gustar00rootroot0000000000000052 comment=8ec1946a9e80627825639ea25a8a62a725f3ff1e vvmd-0.18/000077500000000000000000000000001456374475000124355ustar00rootroot00000000000000vvmd-0.18/.gitignore000066400000000000000000000000161456374475000144220ustar00rootroot00000000000000_build/ *.swp vvmd-0.18/.gitlab-ci.yml000066400000000000000000000021741456374475000150750ustar00rootroot00000000000000--- image: alpine:edge .only-default: &only-default only: - master - merge_requests - tags build-alpine: <<: *only-default before_script: - apk -q add build-base c-ares-dev 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 libc-ares-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.18/AUTHORS000066400000000000000000000007111456374475000135040ustar00rootroot00000000000000vvmd 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 akrosi8 Xela Geo anteater Undef vvmd-0.18/COPYING000066400000000000000000000431261456374475000134760ustar00rootroot00000000000000 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.18/ChangeLog000066400000000000000000000054251456374475000142150ustar00rootroot00000000000000vvmd (0.18) [ Chris Talbot ] * Add and fix a bunch of compiler warnings. vvmd (0.17) [ Chris Talbot ] * Reorganize tests * Use modem interface by default vvmd (0.16) [ Chris Talbot ] * Fix memory leaks * Create iOS vvm standard * add +LOGIN * Account for short codes [ Undef ] * Add configurable CA bundle vvmd (0.15) [ Chris Talbot ] * Properly check vvmd status on startup vvmd (0.14.2) [ anteater ] * Fix multi-character UIDs vvmd (0.14.1) [ Arnav Singh ] * Only use CURLOPT_PROTOCOLS_STR when libcurl is v7.85 or higher vvmd (0.14) [ Chris Talbot ] * Ensure only VVM type numbers are processed * exit vvmd if the home directory is not readable/writable * Default to using STARTTLS vvmd (0.13) [ Chris Talbot ] * Fix bug that doesn't allow all VVMs to download at once vvmd (0.12) [ Chris Talbot ] * Fix bug where STATUS message doesn't update VVM settings * Automatically save/restore old MMS settings * Do not reset settings on bad modem loads * Make some functions more resilient vvmd (0.11) [ Chris Talbot ] * Fix debugging * Fix status messages for AT&T * Various other small fixes [ Xela Geo ] * Add support for FreeMobile FRA vvmd (0.10) [ Chris Talbot ] * Fix issue with curl writeback * Add test to validate SYNC and STATUS SMS [ akrosi8 ] * Enable vvm3 activation vvmd (0.9) [ Chris Talbot ] * Switch to glib logging * Fix issue with some T-Mobile servers (needs this patch https://github.com/curl/curl/pull/8986 ) * Fix issue where VVM is deleted if curl has an issue and it should not be. vvmd (0.8) [ Chris Talbot ] * Fix various memory leaks * Use correct IMAP port vvmd (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.18/README000066400000000000000000000075561456374475000133320ustar00rootroot00000000000000Visual Voicemail Daemon (vvmd) **************************** Copyright (C) 2021, Chris Talbot About =========================== vvmd is a lower level daemon that retrieves Visual Voicemail Notes =========================== The protocols "cvvm", "vvm3", "ios", 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/$PLUGIN/vvm" 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: It will then attempt to look up your carrier settings from: https://gitlab.gnome.org/GNOME/mobile-broadband-provider-info If it cannot find it, I highly suggest for you to add your carrrier settings here. 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.18/config.h.in000066400000000000000000000034041456374475000144610ustar00rootroot00000000000000/* 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@" /* Define to 1 if libcurl supports CURLOPT_PROTOCOLS_STR. */ #mesondefine HAVE_LIBCURL_CURLOPT_PROTOCOLS_STR vvmd-0.18/docs/000077500000000000000000000000001456374475000133655ustar00rootroot00000000000000vvmd-0.18/docs/architecture.txt000066400000000000000000000025121456374475000166100ustar00rootroot00000000000000Architecture 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.18/docs/manager-api.txt000066400000000000000000000015551456374475000163150ustar00rootroot00000000000000Manager 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.18/docs/message-api.txt000066400000000000000000000040221456374475000163170ustar00rootroot00000000000000Message 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.18/docs/modem-manager-api.txt000066400000000000000000000015471456374475000174150ustar00rootroot00000000000000Message 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.18/docs/service-api.txt000066400000000000000000000023201456374475000163320ustar00rootroot00000000000000Service 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 dict GetProperties() Returns properties for the manager object. See the properties section for available properties. Possible Errors: [service].Error.NotImplemented 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. Properties ModemCountryCode(String) The country code of the modem MailboxActive(Boolean) Whether vvmd can access the VVM IMAP Server vvmd-0.18/docs/standards.txt000066400000000000000000000022601456374475000161110ustar00rootroot00000000000000Referencing 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.18/docs/storage.txt000066400000000000000000000011721456374475000155730ustar00rootroot00000000000000vvmd 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.18/meson.build000066400000000000000000000071641456374475000146070ustar00rootroot00000000000000project( 'vvmd', 'c', 'cpp', version : '0.18', 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()) 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') project_c_args = [] test_c_args = [ '-Wcast-align', '-Wdeclaration-after-statement', '-Werror=address', '-Werror=array-bounds', '-Werror=empty-body', '-Werror=implicit', '-Werror=implicit-function-declaration', '-Werror=incompatible-pointer-types', '-Werror=init-self', '-Werror=int-conversion', '-Werror=int-to-pointer-cast', '-Werror=main', '-Werror=misleading-indentation', '-Werror=missing-braces', '-Werror=missing-include-dirs', '-Werror=nonnull', '-Werror=overflow', '-Werror=parenthesis', '-Werror=pointer-arith', '-Werror=pointer-to-int-cast', '-Werror=redundant-decls', '-Werror=return-type', '-Werror=sequence-point', '-Werror=shadow', '-Werror=strict-prototypes', '-Werror=trigraphs', '-Werror=undef', '-Werror=write-strings', '-Wformat-nonliteral', '-Wignored-qualifiers', '-Wimplicit-function-declaration', '-Wlogical-op', '-Wmissing-declarations', '-Wmissing-format-attribute', '-Wmissing-include-dirs', '-Wmissing-noreturn', '-Wnested-externs', '-Wno-cast-function-type', '-Wno-dangling-pointer', '-Wno-missing-field-initializers', '-Wno-sign-compare', '-Wno-unused-parameter', '-Wold-style-definition', '-Wpointer-arith', '-Wredundant-decls', '-Wstrict-prototypes', '-Wswitch-default', '-Wswitch-enum', '-Wundef', '-Wuninitialized', '-Wunused', '-fno-strict-aliasing', ['-Werror=format-security', '-Werror=format=2'], ] if get_option('buildtype') != 'plain' test_c_args += '-fstack-protector-strong' endif foreach arg: test_c_args if cc.has_multi_arguments(arg) project_c_args += arg endif endforeach add_project_arguments(project_c_args, language: 'c') includes = [ include_directories('.'), include_directories('src') ] dep_curl = dependency('libcurl', version : '>7.70') if dep_curl.version().version_compare('>=7.85.0') conf_data.set('HAVE_LIBCURL_CURLOPT_PROTOCOLS_STR', '1') endif dependencies = [ dependency('glib-2.0', version : '>=2.16'), dependency('mm-glib', version : '>=1.14'), dependency('libcares', version : '>=1.18.1'), dep_curl, cc.find_library('dl'), cc.find_library('phonenumber', required: true) ] conf = configure_file( input : 'config.h.in', output : 'config.h', configuration : conf_data ) 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.18/plugins/000077500000000000000000000000001456374475000141165ustar00rootroot00000000000000vvmd-0.18/plugins/meson.build000066400000000000000000000002501456374475000162550ustar00rootroot00000000000000plugins_sources = [ 'modemmanager.c' ] plugins_lib = static_library('plugins', plugins_sources, include_directories : includes, dependencies : dependencies, ) vvmd-0.18/plugins/modemmanager.c000066400000000000000000002221211456374475000167160ustar00rootroot00000000000000/* * * Visual Voicemail Daemon * * Copyright (C) 2018 Purism SPC * 2020-2022 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 "vvm3-activation.h" #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; gboolean strict_number_check; }; 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 gboolean vvmd_unsubscribe_service (gpointer user_data); static gboolean vvmd_check_subscription_status (gpointer user_data); static gboolean vvmd_subscribe_service (gpointer user_data); 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 to VVM"); vvmd_subscribe_service (NULL); } else if (modem->enable_vvm == TRUE && VVMStatus == FALSE) { DBG ("Unsubscribing from VVM"); vvmd_unsubscribe_service (NULL); } 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 { g_critical ("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 { g_critical ("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 (NULL); 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 g_critical ("You are not subscribed to the Visual Voicemail."); } else g_critical ("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 mm_vvm3_activation (const char *stripped_number, struct sms_control_message *sms_msg) { 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 char *mailbox_interface; MMBearerIpConfig *modem_bearer_ip6_config; const char **dns_ipv6; char *dns_ipv6_csv = NULL; MMBearerIpConfig *modem_bearer_ip4_config; const char **dns_ipv4; char *dns_ipv4_csv = NULL; modem_bearer = (MMBearer *) l->data; mailbox_interface = mm_bearer_get_interface (modem_bearer); modem_bearer_ip4_config = mm_bearer_peek_ipv4_config (modem_bearer); dns_ipv4 = mm_bearer_ip_config_get_dns (modem_bearer_ip4_config); if (dns_ipv4) dns_ipv4_csv = g_strjoinv (",", (gchar **)dns_ipv4); modem_bearer_ip6_config = mm_bearer_peek_ipv6_config (modem_bearer); dns_ipv6 = mm_bearer_ip_config_get_dns (modem_bearer_ip6_config); if (dns_ipv6) dns_ipv6_csv = g_strjoinv (",", (gchar **)dns_ipv6); DBG ("DNS from modemmanager IPv6: '%s', IPv4: '%s'", dns_ipv6_csv, dns_ipv4_csv); vvm3_activation (stripped_number, sms_msg->activate_url, mailbox_interface, dns_ipv4_csv, dns_ipv6_csv); g_clear_pointer (&dns_ipv6_csv, g_free); g_clear_pointer (&dns_ipv4_csv, g_free); } } else g_warning ("Cannot process vvvm3 activation"); } 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) { g_critical ("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 (NULL); DBG ("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) { DBG ("Unsubscribing from your carrier's VVM service"); vvmd_unsubscribe_service (NULL); } else { /* * The carrier may send a STATUS message to update something, * like the IMAP server. You need to make sure you incorporate the fix! */ vvm_service_deactivate_vvm_imap_server (modem->service); mm_sync_vvm_imap_server (); } } else if (modem->provision_status == VVM_PROVISION_STATUS_UNKNOWN) { //VZW reports that the Provision status is unknown if the VVM service is unprovisioned if (g_strcmp0 (modem->vvm_type, "vvm3") == 0) { if (modem->state == MM_MODEM_STATE_CONNECTED) { const gchar *const *modem_number_ref; const char *country_code; g_autofree char *stripped_number = NULL; g_autofree char *formatted_number = NULL; country_code = get_country_iso_for_mcc (modem->imsi); if (g_strcmp0 (country_code, "US") != 0) { g_warning ("you are using a standard for VZW USA but the country code is not US"); return; } modem_number_ref = mm_modem_get_own_numbers (modem->modem); if (modem_number_ref == NULL) { g_critical ("Could not get modem number!"); return; } formatted_number = phone_utils_format_number_e164 (*modem_number_ref, country_code, FALSE); if (formatted_number) { char *stripped = NULL; if (g_str_has_prefix (formatted_number, "+1")) stripped = formatted_number + strlen ("+1"); else stripped = formatted_number; stripped_number = g_strdup (stripped); } else { g_critical ("Could not format modem number!"); return; } mm_vvm3_activation (stripped_number, sms_msg); } else { g_warning ("Modem is disconnected, cannot attempt to provision vvm3"); return; } } else g_critical ("Provisioning Status is unknown! Contact your Carrier"); } else if (modem->provision_status == VVM_PROVISION_STATUS_BLOCKED) g_critical ("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) { g_critical ("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; if (modem->strict_number_check) { const char *number; const char *country_code; number = mm_sms_get_number (sms); country_code = vvm_service_get_country_code (modem->service); if (!number || !*number) return; if (!country_code || !*country_code) return; if (!phone_utils_simple_is_valid (number)) { DBG ("SMS number does not pass simple validation"); return; } /* VVM numbers are generally regarded as unvalid by libphonenumber */ if (phone_utils_is_valid (number, country_code)) { DBG ("SMS number is not a VVM related number"); return; } } else g_warning ("You disabled strict number checking."); 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) g_critical ("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) { char *dup_msg = g_strdup (message); DBG ("Timeout waiting for sync 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) { g_critical ("Deleting SMS!"); mm_modem_messaging_delete (modem->modem_messaging, path, NULL, (GAsyncReadyCallback)cb_sms_delete_finish, sms); } else g_critical ("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; if (!modem->enable_vvm) { DBG ("VVM is not enabled! Not Processing SMS"); return; } 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; case MM_SMS_PDU_TYPE_CDMA_SUBMIT: case MM_SMS_PDU_TYPE_CDMA_CANCELLATION: case MM_SMS_PDU_TYPE_CDMA_DELIVERY_ACKNOWLEDGEMENT: case MM_SMS_PDU_TYPE_CDMA_USER_ACKNOWLEDGEMENT: case MM_SMS_PDU_TYPE_CDMA_READ_ACKNOWLEDGEMENT: 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) g_critical ("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) { g_critical ("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) { g_critical ("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 = g_object_ref (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) { g_critical ("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) { g_critical ("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); g_critical ("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); 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))) vvmd_mm_add_object (MM_OBJECT (object)); else g_warning ("New Object does not have Messaging feature, ignoring...."); } 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 { g_critical ("Error connecting to ModemManager: %s\n", error->message); vvmd_mm_state (VVMD_MM_STATE_NO_MANAGER); } } static void vvmd_mm_get_modem_state (void) { 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"); 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; case MM_MODEM_STATE_ENABLED: case MM_MODEM_STATE_SEARCHING: case MM_MODEM_STATE_REGISTERED: case MM_MODEM_STATE_DISCONNECTING: case MM_MODEM_STATE_CONNECTING: case MM_MODEM_STATE_CONNECTED: g_warning ("This code should not be reached"); break; 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 char *mailbox_interface; MMBearerIpConfig *modem_bearer_ip6_config; const char **dns_ipv6; char *dns_ipv6_csv = NULL; MMBearerIpConfig *modem_bearer_ip4_config; const char **dns_ipv4; char *dns_ipv4_csv = NULL; modem_bearer = (MMBearer *) l->data; mailbox_interface = mm_bearer_get_interface (modem_bearer); vvm_service_set_interface (modem->service, mailbox_interface); modem_bearer_ip4_config = mm_bearer_peek_ipv4_config (modem_bearer); dns_ipv4 = mm_bearer_ip_config_get_dns (modem_bearer_ip4_config); if (dns_ipv4) dns_ipv4_csv = g_strjoinv (",", (gchar **)dns_ipv4); modem_bearer_ip6_config = mm_bearer_peek_ipv6_config (modem_bearer); dns_ipv6 = mm_bearer_ip_config_get_dns (modem_bearer_ip6_config); if (dns_ipv6) dns_ipv6_csv = g_strjoinv (",", (gchar **)dns_ipv6); vvm_service_set_resolvers (modem->service, dns_ipv4_csv, dns_ipv6_csv); DBG ("DNS from modemmanager IPv6: '%s', IPv4: '%s'", dns_ipv6_csv, dns_ipv4_csv); g_clear_pointer (&dns_ipv6_csv, g_free); g_clear_pointer (&dns_ipv4_csv, g_free); } if (g_strcmp0 (modem->vvm_type, "vvm3") == 0) if (modem->provision_status == VVM_PROVISION_STATUS_NOT_SET || modem->provision_status == VVM_PROVISION_STATUS_UNKNOWN) { if (!modem->enable_vvm) { DBG ("VVM is not enabled!"); return; } vvmd_check_subscription_status (NULL); return; } } // 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 DBG ("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->modem_available) { vvmd_modem_unavailable (); DBG ("Modem vanished, Disabling plugin"); } if (modem->manager_available) { g_clear_object (&modem->mm); if (modem->device_arr) { g_ptr_array_set_size (modem->device_arr, 0); g_ptr_array_unref (modem->device_arr); modem->device_arr = NULL; } modem->modem_available = FALSE; modem->modem_ready = FALSE; } else { g_critical ("Could not connect to ModemManager"); DBG ("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) g_critical ("Couldn't send SMS - error: %s", error ? error->message : "unknown"); else { const char *path; DBG ("Successfully sent SMS: %s", mm_sms_get_path (sms)); path = mm_sms_get_path (sms); if (path) mm_modem_messaging_delete (modem->modem_messaging, path, NULL, (GAsyncReadyCallback)cb_sms_delete_finish, sms); else g_critical ("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 gboolean vvmd_subscribe_service (gpointer user_data) { g_autofree char *message = NULL; DBG ("Subscribing to your carrier's VVM service"); if (modem->provision_status == VVM_PROVISION_STATUS_NOT_SET) { DBG ("Your provisioning status is unknown"); DBG ("Checking Subscription Status"); vvmd_check_subscription_status (NULL); return G_SOURCE_REMOVE; } else if (g_strcmp0 (modem->vvm_type, "vvm3") == 0) { DBG ("Checking vvm3 subscription status"); vvmd_check_subscription_status (NULL); return G_SOURCE_REMOVE; } else DBG ("You are not be subscribed to your carrier's VVM service"); if (modem->modem_available == FALSE) { DBG ("Modem not available"); return G_SOURCE_REMOVE; } if (modem->modem_ready == FALSE) { DBG ("Modem not ready"); return G_SOURCE_REMOVE; } message = vvm_util_create_activate_sms (modem->carrier_prefix, modem->vvm_type); if (message) { DBG ("Message: %s", message); vvmd_mm_create_sms (message); } return G_SOURCE_REMOVE; } static gboolean vvmd_unsubscribe_service (gpointer user_data) { g_autofree 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); if (modem->modem_available == FALSE) { DBG ("Modem not available"); return G_SOURCE_REMOVE; } if (modem->modem_ready == FALSE) { DBG ("Modem not ready"); return G_SOURCE_REMOVE; } message = vvm_util_create_deactivate_sms (modem->carrier_prefix, modem->vvm_type); if (message) { DBG ("Message: %s", message); vvmd_mm_create_sms (message); } return G_SOURCE_REMOVE; } static gboolean vvmd_check_subscription_status (gpointer user_data) { g_autofree char *message = NULL; DBG ("Checking your carrier's VVM service"); if (g_strcmp0 (modem->vvm_type, "vvm3") == 0) if (modem->provision_status == VVM_PROVISION_STATUS_NOT_SET || modem->provision_status == VVM_PROVISION_STATUS_UNKNOWN) { if (modem->state == MM_MODEM_STATE_CONNECTED) // Give the connection a second so it is usable sleep (1); else { DBG ("Modem is disconnected, cannot attempt to provision vvm3"); return G_SOURCE_REMOVE; } } if (modem->modem_available == FALSE) { DBG ("Modem not available"); return G_SOURCE_REMOVE; } if (modem->modem_ready == FALSE) { DBG ("Modem not ready"); return G_SOURCE_REMOVE; } message = vvm_util_create_status_sms (modem->carrier_prefix, modem->vvm_type); if (message) { DBG ("Message: %s", message); vvmd_mm_create_sms (message); } return G_SOURCE_REMOVE; } 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) { g_warning ("Could not find settings: %s", error->message); g_warning ("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) { g_warning ("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 = NULL; if (g_str_has_prefix (formatted_number, "+1")) stripped = formatted_number + strlen ("+1"); else stripped = formatted_number; carrier_prefix = g_strdup (stripped); } else { g_critical ("Could not format modem number!"); carrier_prefix = g_strdup (*modem_number_ref); } } else { g_critical ("Could not get modem number!"); carrier_prefix = g_strdup ("invalid"); } } else if (g_strcmp0 ("Free Mobile", carrier) == 0 && g_strcmp0 ("proprietary", vvm_std_to_copy) == 0) { const char *country_code; country_code = get_country_iso_for_mcc (modem->imsi); if (g_strcmp0 (country_code, "FR") != 0) { g_warning ("you are parsing French Free mobile but the country code is not FR"); return; } else vvm_std = g_strdup ("FreeMobileProprietary"); } 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 mm_save_settings (void) { g_autofree char *imsi_hash = NULL; /* Settings are default, no use in saving them */ if (g_strcmp0 (modem->vvm_type, "type_invalid") == 0) return; imsi_hash = g_strdup_printf ("Old Settings: %u", g_str_hash (modem->registered_imsi)); g_key_file_set_string (modem->modemsettings, imsi_hash, "VVMType", modem->vvm_type); g_key_file_set_string (modem->modemsettings, imsi_hash, "VVMDestinationNumber", modem->vvm_destination_number); g_key_file_set_string (modem->modemsettings, imsi_hash, "CarrierPrefix", modem->carrier_prefix); vvm_settings_sync (IDENTIFIER, SETTINGS_STORE, modem->modemsettings); } static gboolean mm_retrieve_settings (void) { g_autofree char *imsi_hash = NULL; g_autoptr(GError) error = NULL; imsi_hash = g_strdup_printf ("Old Settings: %u", g_str_hash (modem->imsi)); modem->vvm_type = g_key_file_get_string (modem->modemsettings, imsi_hash, "VVMType", &error); if (error) return FALSE; modem->vvm_destination_number = g_key_file_get_string (modem->modemsettings, imsi_hash, "VVMDestinationNumber", &error); if (error) { g_clear_pointer (&modem->vvm_type, g_free); return FALSE; } modem->carrier_prefix = g_key_file_get_string (modem->modemsettings, imsi_hash, "CarrierPrefix", &error); if (error) { g_clear_pointer (&modem->vvm_type, g_free); g_clear_pointer (&modem->vvm_destination_number, g_free); return FALSE; } return TRUE; } static void vvmd_modem_available (void) { g_autoptr(GError) error = NULL; modem->modem_available = TRUE; if (modem->modem) { const char *imsi = NULL; const gchar *const *modem_number_ref; MmGdbusModem *gdbus_modem; 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; } modem->sim = mm_modem_get_sim_sync (modem->modem, NULL, &error); if (error == NULL) imsi = mm_sim_get_imsi (modem->sim); else g_warning ("Error Getting Sim: %s", error->message); if (imsi != NULL) { modem->imsi = g_strdup (imsi); 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"); mm_save_settings (); g_free (modem->vvm_type); g_free (modem->carrier_prefix); g_free (modem->vvm_destination_number); if (mm_retrieve_settings ()) DBG ("Reloading settings"); else { DBG ("Resetting settings"); modem->vvm_type = g_strdup ("type_invalid"); modem->carrier_prefix = g_strdup ("//VVM"); modem->vvm_destination_number = g_strdup ("number_invalid"); } g_key_file_set_string (modem->modemsettings, SETTINGS_GROUP_MODEMMANAGER, "VVMType", modem->vvm_type); g_key_file_set_string (modem->modemsettings, SETTINGS_GROUP_MODEMMANAGER, "CarrierPrefix", modem->carrier_prefix); g_key_file_set_string (modem->modemsettings, SETTINGS_GROUP_MODEMMANAGER, "VVMDestinationNumber", modem->vvm_destination_number); 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); 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 g_warning ("IMSI is NULL!"); 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 g_critical ("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) { if (modem->enable_vvm) g_timeout_add_seconds (5, vvmd_check_subscription_status, NULL); } else if (modem->provision_status == VVM_PROVISION_STATUS_PROVISIONED) { if (modem->enable_vvm) g_timeout_add_seconds (5, vvmd_subscribe_service, NULL); 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) g_timeout_add_seconds (5, vvmd_unsubscribe_service, NULL); 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 g_critical ("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); g_clear_object (&modem->object); 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) { g_critical ("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) { g_critical ("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) { g_critical ("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); } modem->strict_number_check = g_key_file_get_boolean (modem->modemsettings, SETTINGS_GROUP_MODEMMANAGER, "StrictNumberCheck", &error); if (error) { modem->strict_number_check = TRUE; g_key_file_set_boolean (modem->modemsettings, SETTINGS_GROUP_MODEMMANAGER, "StrictNumberCheck", modem->strict_number_check); 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) { g_critical ("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->modem_available) vvmd_mm_state (VVMD_MM_STATE_NO_MODEM); if (modem->manager_available) vvmd_mm_state (VVMD_MM_STATE_NO_MANAGER); if (modem->plugin_registered == TRUE) { vvm_service_unregister (modem->service); modem->modemsettings = NULL; } vvm_service_unref (modem->service); g_dbus_connection_unregister_object (connection, modem->registration_id); g_free (modem->registered_imsi); g_free (modem->carrier_prefix); g_free (modem->default_number); g_free (modem->vvm_type); g_free (modem->vvm_destination_number); g_bus_unwatch_name (modem->mm_watch_id); g_free (modem); g_dbus_node_info_unref (introspection_data); } VVM_PLUGIN_DEFINE (modemmanager, modemmanager_init, modemmanager_exit) vvmd-0.18/python examples/000077500000000000000000000000001456374475000155555ustar00rootroot00000000000000vvmd-0.18/python examples/test.py000066400000000000000000000004451456374475000171110ustar00rootroot00000000000000import 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.18/src/000077500000000000000000000000001456374475000132245ustar00rootroot00000000000000vvmd-0.18/src/dbus.c000066400000000000000000000027031456374475000143270ustar00rootroot00000000000000/* * * 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.18/src/dbus.h000066400000000000000000000060221456374475000143320ustar00rootroot00000000000000/* * * 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.18/src/genbuiltin000077500000000000000000000003421456374475000153110ustar00rootroot00000000000000#!/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.18/src/itu-e212-iso.c000066400000000000000000000117401456374475000154330ustar00rootroot00000000000000/* * * 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 #include "itu-e212-iso.h" 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.18/src/itu-e212-iso.h000066400000000000000000000014761456374475000154450ustar00rootroot00000000000000/* * * 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.18/src/log.h000066400000000000000000000017741456374475000141670ustar00rootroot00000000000000/* * * 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 DBG(fmt, arg ...) do { \ g_log (G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, "%s:%s() " fmt, \ __FILE__, __FUNCTION__, ## arg); \ } while (0) vvmd-0.18/src/main.c000066400000000000000000000104351456374475000143170ustar00rootroot00000000000000/* * * 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 "version.h" #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; g_message ("Terminating"); g_main_loop_quit (main_loop); } static gboolean option_version = FALSE; static gboolean global_debug = FALSE; static GOptionEntry options[] = { { "debug", 'd', 0, G_OPTION_ARG_NONE, &global_debug, "Enable debugging output" }, { "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) { g_warning ("Lost Dbus Connection! Exiting...."); exit (1); } int main (int argc, char *argv[]) { GOptionContext *context; GError *error = NULL; struct sigaction sa; guint owner_id; g_autofree char *vvm_home_path = NULL; 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); } /* Check if the home/.vvm directory can be written to */ vvm_home_path = g_build_filename (g_get_home_dir () , ".vvm", NULL); if (g_mkdir_with_parents (vvm_home_path, 0700) != 0) g_printerr ("Cannot create directory in '%s'\n", vvm_home_path); if (access (vvm_home_path, R_OK | W_OK) != 0) { g_printerr ("Cannot read and write in '%s', exiting...\n", vvm_home_path); exit (1); } g_option_context_free (context); if (option_version == TRUE) { printf ("%s, git version: %s\n", VERSION, PACKAGE_VCS_VERSION); exit (0); } if (global_debug == TRUE) g_setenv ("G_MESSAGES_DEBUG", "all", TRUE); 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); g_message ("VVMD version %s, git version: %s", VERSION, PACKAGE_VCS_VERSION); 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 (); g_message ("EXIT"); g_bus_unown_name (owner_id); __vvm_dbus_unref_introspection_data (); g_main_loop_unref (main_loop); return 0; } vvmd-0.18/src/meson.build000066400000000000000000000013321456374475000153650ustar00rootroot00000000000000vvm_sources = [ 'dbus.c', 'service.c', 'store.c', 'plugin.c', 'itu-e212-iso.c', 'resolve.c', 'service-providers.c', 'vvmutil.c', 'vvm3-activation.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 revision_tag = vcs_tag( input: 'version.h.in', output: 'version.h', ) dependencies += declare_dependency(sources: revision_tag) vvm_lib = static_library('vvm', vvm_sources, include_directories : includes, dependencies : dependencies, ) vvmd-0.18/src/phone-utils.cpp000066400000000000000000000136421456374475000162050ustar00rootroot00000000000000/* * * 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; } /* * phone_utils_is_valid is a bit smarter on if a phone number is valid. Here * I just want to make sure that something like an email isn't coming through * like a phone number */ gboolean phone_utils_simple_is_valid (const char *number) { if (strspn (number, "+()- 0123456789") != strlen (number)) return false; return true; } gboolean phone_utils_is_valid (const char *number, const char *country_code) { PhoneNumberUtil *util = PhoneNumberUtil::GetInstance (); PhoneNumber phone_number; if (!number || !*number || !country_code || strlen (country_code) != 2) return FALSE; if (util->Parse (number, country_code, &phone_number) == PhoneNumberUtil::NO_PARSING_ERROR) return util->IsValidNumber (phone_number); return FALSE; } 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_e164 (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_cxx_to_string_national (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_NATIONAL), &formatted_number); if (!formatted_number.empty ()) return g_strdup (formatted_number.c_str ()); return NULL; } static char * phone_utils_format (const char *phone_number, const char *region_code, gboolean national) { 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 (); if (national) returned_string = _phone_utils_cxx_to_string_national (intermediate); else returned_string = _phone_utils_cxx_to_string_e164 (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; if (!phone_utils_simple_is_valid (number)) goto error; formatted_number = phone_utils_format (number, country_code, (*number != '+' && !phone_utils_is_valid (number, country_code))); if (formatted_number == NULL) goto error; return formatted_number; error: if (return_original_number == FALSE) { return NULL; } else { return g_strdup(number); } } vvmd-0.18/src/phone-utils.h000066400000000000000000000024151456374475000156460ustar00rootroot00000000000000/* * * 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); gboolean phone_utils_is_valid (const char *number, const char *country_code); gboolean phone_utils_simple_is_valid (const char *number); G_END_DECLS vvmd-0.18/src/plugin.c000066400000000000000000000061761456374475000147000ustar00rootroot00000000000000/* * * 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) { g_critical ("Can't load plugin %s: %s", filename, dlerror ()); g_free (filename); continue; } g_free (filename); desc = dlsym (handle, "vvm_plugin_desc"); if (desc == NULL) { g_critical ("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.18/src/plugin.h000066400000000000000000000025311456374475000146740ustar00rootroot00000000000000/* * * 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.18/src/resolve.c000066400000000000000000000076621456374475000150620ustar00rootroot00000000000000/* * * Visual Voicemail Daemon * * Copyright (C) 2024, 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 "resolve.h" #include #include #include #include #include static void resolve_callback (void *arg, int status, int timeouts, struct hostent *host) { char **host_ip = (char **)arg; gchar ip[64]; if (!host || status != ARES_SUCCESS) { DBG ("Failed to resolve host: %s\n", ares_strerror (status)); return; } inet_ntop (host->h_addrtype, host->h_addr_list[0], ip, sizeof(ip)); *host_ip = g_strdup (ip); DBG ("Found IP for '%s': %s\n", host->h_name, *host_ip); } static void resolve_wait (ares_channel channel) { int nfds, count; fd_set readers, writers; struct timeval tv, *tvp; while (1) { FD_ZERO (&readers); FD_ZERO (&writers); nfds = ares_fds (channel, &readers, &writers); if (nfds == 0) break; tvp = ares_timeout (channel, NULL, &tv); count = select (nfds, &readers, &writers, NULL, tvp); if (count == -1) { g_critical ("Error waiting for c-ares read/write descriptors"); break; } ares_process (channel, &readers, &writers); } } char * vvm_resolve_resolve_host (const char *host, const char *mailbox_interface, const char *resolvers_ipv4_csv, const char *resolvers_ipv6_csv) { ares_channel chan; int ares_return; gchar *host_ip = NULL; g_autofree gchar *nsall_csv = NULL; DBG ("%s", __func__); ares_return = ares_init (&chan); if (ares_return != ARES_SUCCESS) { g_warning ("Ares init failed: %s\n", ares_strerror (ares_return)); goto ares_out; } /* * ares_set_local_dev () works without root * https://github.com/c-ares/c-ares/issues/405 */ DBG ("Binding resolver queries to interface %s", mailbox_interface); ares_set_local_dev (chan, mailbox_interface); if (resolvers_ipv6_csv && *resolvers_ipv6_csv && resolvers_ipv4_csv && *resolvers_ipv4_csv) nsall_csv = g_strjoin (",", resolvers_ipv6_csv, resolvers_ipv4_csv, NULL); else if (resolvers_ipv6_csv && *resolvers_ipv6_csv) nsall_csv = g_strdup (resolvers_ipv6_csv); else if (resolvers_ipv4_csv && *resolvers_ipv4_csv) nsall_csv = g_strdup (resolvers_ipv4_csv); else { g_warning ("No active DNS Servers\n"); goto ares_out; } DBG ("All Nameservers: %s", nsall_csv); ares_return = ares_set_servers_csv (chan, nsall_csv); if (ares_return != ARES_SUCCESS) g_warning ("Ares failed to set list of nameservers ('%s'), %s\n", nsall_csv, ares_strerror (ares_return)); ares_gethostbyname (chan, host, AF_UNSPEC, resolve_callback, (void *)&host_ip); resolve_wait (chan); if (host_ip == NULL) { g_warning ("Failed to resolve '%s'\n", host); goto ares_out; } ares_out: ares_destroy (chan); return host_ip; } int vvm_resolve_ip_version(const char *src) { char buf[INET6_ADDRSTRLEN]; if (inet_pton(AF_INET, src, buf)) return 4; else if (inet_pton(AF_INET6, src, buf)) return 6; return -1; } vvmd-0.18/src/resolve.h000066400000000000000000000021171456374475000150550ustar00rootroot00000000000000/* * * Visual Voicemail Daemon * * Copyright (C) 2024, 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 char *vvm_resolve_resolve_host (const char *host, const char *mailbox_interface, const char *resolvers_ipv4_csv, const char *resolvers_ipv6_csv); int vvm_resolve_ip_version (const char *src); vvmd-0.18/src/service-providers.c000066400000000000000000000343751456374475000170570ustar00rootroot00000000000000/* * * 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; default: g_warning ("Got to undefined state"); 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; default: g_warning ("Got to undefined state"); 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.18/src/service-providers.h000066400000000000000000000035001456374475000170460ustar00rootroot00000000000000/* * * 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.18/src/service.c000066400000000000000000002245651456374475000150460ustar00rootroot00000000000000/* * * Visual Voicemail Daemon * * Copyright (C) 2010-2011, Intel Corporation * 2021-2022, 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" #include "resolve.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 *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; char *mailbox_ca_bundle; char *resolvers_ipv4_csv; char *resolvers_ipv6_csv; int mailbox_legacytls; vvm_service_bearer_handler_func_t bearer_handler; void *bearer_data; guint bearer_timeout; int mailbox_active; int allow_plaintext_connection; gboolean bearer_setup; gboolean bearer_active; GHashTable *messages; GKeyFile *settings; CURL *curl; struct curl_slist *host_slist; }; 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); 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) < 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) { g_critical ("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) { g_critical ("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) { g_critical ("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) { g_critical ("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) { g_critical ("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) { g_critical ("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) { g_critical ("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); } int vvm_service_set_resolvers (struct vvm_service *service, const gchar *ipv4_csv, const gchar *ipv6_csv) { DBG ("service %p resolvers: ipv4: %s, ipv6: %s", service, ipv4_csv, ipv6_csv); if (service == NULL) return -EINVAL; g_clear_pointer (&service->resolvers_ipv4_csv, g_free); if (ipv4_csv && *ipv4_csv) service->resolvers_ipv4_csv = g_strdup (ipv4_csv); g_clear_pointer (&service->resolvers_ipv6_csv, g_free); if (ipv6_csv && *ipv6_csv) service->resolvers_ipv6_csv = g_strdup (ipv6_csv); return 0; } static void vvm_service_set_host_resolve (struct vvm_service *service) { g_autofree char *ip = NULL; g_autofree char *host_list = NULL; int ip_type; curl_slist_free_all (service->host_slist); service->host_slist = NULL; ip = vvm_resolve_resolve_host (service->mailbox_hostname, service->mailbox_interface, service->resolvers_ipv4_csv, service->resolvers_ipv6_csv); DBG ("Host %s IP: %s", service->mailbox_hostname, ip); if (!ip) return; ip_type = vvm_resolve_ip_version (ip); if (ip_type == 6) { g_autofree char *new_ip = NULL; new_ip = g_strdup_printf ("[%s]", ip); g_free (ip); ip = g_strdup (new_ip); } else if (ip_type == 4) DBG ("IP is v4"); else { g_warning ("Cannot figure out if IP `%s` is ipv4 or ipv6", ip); return; } host_list = g_strdup_printf ("+%s:993:%s", service->mailbox_hostname, ip); service->host_slist = curl_slist_append(service->host_slist, host_list); g_clear_pointer (&host_list, g_free); host_list = g_strdup_printf ("+%s:143:%s", service->mailbox_hostname, ip); service->host_slist = curl_slist_append(service->host_slist, host_list); g_clear_pointer (&host_list, g_free); if (service->mailbox_port) { g_autoptr(GError) error = NULL; guint64 port; g_ascii_string_to_unsigned (service->mailbox_port, 10, 1, 65535, &port, &error); if (error) g_warning ("Error converting string `%s` to unsigned 64: %s", service->mailbox_port, error->message); else if (port != 993 && port != 143) { host_list = g_strdup_printf ("+%s:%s:%s", service->mailbox_hostname, service->mailbox_port, ip); service->host_slist = curl_slist_append(service->host_slist, host_list); g_clear_pointer (&host_list, g_free); } } curl_easy_setopt(service->curl, CURLOPT_RESOLVE, service->host_slist); } static CURL *create_curl_structure (struct vvm_service *service) { CURL *curl; 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); curl_easy_setopt (curl, CURLOPT_INTERFACE, service->mailbox_interface); if ((service->mailbox_ca_bundle != NULL) && (service->mailbox_ca_bundle[0] != '\0')) curl_easy_setopt (curl, CURLOPT_CAINFO, service->mailbox_ca_bundle); return curl; } static void activate_curl_structure (struct vvm_service *service) { DBG ("Setting up CURL..."); service->curl = create_curl_structure (service); if (!service->allow_plaintext_connection) curl_easy_setopt (service->curl, CURLOPT_USE_SSL, CURLUSESSL_ALL); else g_warning ("Allowing plaintext connection over IMAP"); if (service->mailbox_legacytls) curl_easy_setopt (service->curl, CURLOPT_SSL_CIPHER_LIST, "ALL:@SECLEVEL=1"); if (*service->mailbox_auth) 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); } /* * The IMAP setting succeeds, URI and mailbox_auth are transferred to a * different pointer. As such, only free them if this function returns false. * curl_easy_cleanup () is also performed if this returns TRUE. */ static gboolean vvm_service_try_imap_setting (struct vvm_service *service, CURL *curl, char *URI, char *mailbox_auth) { CURLcode res = CURLE_OK; curl_easy_setopt (curl, CURLOPT_URL, URI); if (mailbox_auth && *mailbox_auth) curl_easy_setopt (curl, CURLOPT_LOGIN_OPTIONS, mailbox_auth); else curl_easy_setopt (curl, CURLOPT_LOGIN_OPTIONS, NULL); vvm_service_set_host_resolve (service); res = curl_easy_perform (curl); // Try to guess if the server is using legacy ciphers if (res == CURLE_SSL_CONNECT_ERROR) { DBG ("curl_easy_perform() failed with SSL Error"); DBG ("Trying with legacy ciphers"); curl_easy_setopt (curl, CURLOPT_SSL_CIPHER_LIST, "ALL:@SECLEVEL=1"); res = curl_easy_perform (curl); if (res == CURLE_OK) { service->mailbox_legacytls = TRUE; g_key_file_set_boolean (service->settings, SETTINGS_GROUP_SERVICE, "MailboxLegacyTLS", service->mailbox_legacytls); } } 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); vvm_settings_sync (service->identity, SETTINGS_STORE, service->settings); return TRUE; } /* Reset authorized ciphers to the default value */ curl_easy_setopt (curl, CURLOPT_SSL_CIPHER_LIST, NULL); g_warning ("curl_easy_perform() failed: %s\n", curl_easy_strerror (res)); return FALSE; } static int vvm_service_activate_vvm_imap_server (struct vvm_service *service) { CURL *curl; char *URI, *mailbox_auth; DBG ("Activating IMAP Mailbox"); if (service->mailbox_active == TRUE) { DBG ("IMAP Mailbox already active!"); return TRUE; } curl = create_curl_structure (service); /* * This is testing various IMAP settings to see that works in this order: * IMAP over SSL: * curl -v imaps://$USERNAME:$PASSWORD@$HOSTNAME/ * IMAP with non-SASL LOGIN * curl -v --login-options AUTH=+LOGIN imaps://$USERNAME:$PASSWORD@$HOSTNAME/ * IMAP over SSL (AUTH=PLAIN): * curl -v --login-options AUTH=PLAIN imaps://$USERNAME:$PASSWORD@$HOSTNAME/ * IMAP over SSL (AUTH=DIGEST-MD5): * curl -v --login-options AUTH=DIGEST-MD5 imaps://$USERNAME:$PASSWORD@$HOSTNAME/ * IMAP over the standard port: * curl -v imap://$USERNAME:$PASSWORD@$HOSTNAME/ * IMAP on the port provided by the STATUS SMS (If the provided port is different): * curl -v imap://$USERNAME:$PASSWORD@$HOSTNAME:$PORT/ * * Note that on Android AOSP, non-SASL LOGIN and DIGEST-MD5 are the only ones * supported */ /* curl -v imaps://$USERNAME:$PASSWORD@$HOSTNAME/ */ DBG ("Trying IMAP with SSL"); mailbox_auth = g_strdup (""); URI = g_strdup_printf ("imaps://%s/INBOX", service->mailbox_hostname); if (vvm_service_try_imap_setting (service, curl, URI, mailbox_auth)) return TRUE; /* curl -v --login-options AUTH=+LOGIN imaps://$USERNAME:$PASSWORD@$HOSTNAME/ */ DBG ("Trying IMAP with SSL: Explicit +LOGIN Authentication"); g_free (mailbox_auth); mailbox_auth = g_strdup ("AUTH=+LOGIN"); if (vvm_service_try_imap_setting (service, curl, URI, mailbox_auth)) return TRUE; /* curl -v --login-options AUTH=PLAIN imaps://$USERNAME:$PASSWORD@$HOSTNAME/ */ DBG ("Trying IMAP with SSL: Explicit PLAIN Authentication"); g_free (mailbox_auth); mailbox_auth = g_strdup ("AUTH=PLAIN"); if (vvm_service_try_imap_setting (service, curl, URI, mailbox_auth)) return TRUE; /* curl -v --login-options AUTH=DIGEST-MD5 imaps://$USERNAME:$PASSWORD@$HOSTNAME/ */ DBG ("Trying IMAP with SSL: Explicit DIGEST-MD5 Authentication"); g_free (mailbox_auth); mailbox_auth = g_strdup ("AUTH=DIGEST-MD5"); if (vvm_service_try_imap_setting (service, curl, URI, mailbox_auth)) return TRUE; /* * We are trying imap now instad of imaps, so I have to reset a lot of the curl stuff. * I need to clean out the curl_easy_setopt(), so tear down and re-setup */ /* Do not allow any non-TLS connection */ if (!service->allow_plaintext_connection) curl_easy_setopt (curl, CURLOPT_USE_SSL, CURLUSESSL_ALL); else g_warning ("Allowing plaintext connection over IMAP"); g_free (mailbox_auth); mailbox_auth = g_strdup (""); DBG ("Trying IMAP on default port"); /* curl -v imap://$USERNAME:$PASSWORD@$HOSTNAME:$PORT */ g_free (URI); URI = g_strdup_printf ("imap://%s/INBOX", service->mailbox_hostname); if (vvm_service_try_imap_setting (service, curl, URI, mailbox_auth)) return TRUE; //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 on specified port"); /* curl -v imap://$USERNAME:$PASSWORD@$HOSTNAME/ */ g_free (URI); URI = g_strdup_printf ("imap://%s:%s/INBOX", service->mailbox_hostname, service->mailbox_port); if (vvm_service_try_imap_setting (service, curl, URI, mailbox_auth)) return TRUE; } g_free (URI); g_free (mailbox_auth); curl_easy_cleanup (curl); vvm_settings_sync (service->identity, SETTINGS_STORE, service->settings); return FALSE; } struct struct_string { GString *response; }; //CURL really wants a struct, I don't know why..... static size_t curl_string_cb (void *data, size_t size, size_t nmemb, void *userdata) { size_t realsize = size * nmemb; struct struct_string *mem = (struct struct_string *)userdata; DBG ("received %lu", realsize); mem->response = g_string_append_len (mem->response, (char *)data, realsize); //DBG("String so far: %s", mem->response->str); 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_string chunk = {0}; CURLcode res = CURLE_OK; chunk.response = g_string_new (NULL); 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, curl_string_cb); curl_easy_setopt (service->curl, CURLOPT_WRITEDATA, (void *)&chunk); vvm_service_set_host_resolve (service); res = curl_easy_perform (service->curl); if (res == CURLE_OK) { g_autofree char *sender; g_autofree char *to; char **tokens = NULL; g_autofree char *headers_response; headers_response = g_string_free (chunk.response, FALSE); DBG ("Returned: %s", headers_response); tokens = g_strsplit_set (headers_response, "\r\n", -1); 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 { g_warning ("Error Downloading headers: %s\n", curl_easy_strerror (res)); 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 { g_critical ("There is no date!"); return FALSE; } if (vvm_msg->message_sender) g_key_file_set_string (meta, "info", "Sender", vvm_msg->message_sender); else { g_critical ("There is no sender!"); return FALSE; } if (vvm_msg->to) g_key_file_set_string (meta, "info", "To", vvm_msg->to); else { g_critical ("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 { g_critical ("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 g_warning ("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; } 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_string chunk = {0}; CURLcode res = CURLE_OK; 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_string_cb); curl_easy_setopt (service->curl, CURLOPT_WRITEDATA, (void *)&chunk); vvm_service_set_host_resolve (service); res = curl_easy_perform (service->curl); if (res == CURLE_OK) { g_critical ("Downloaded email Successfully"); vvm_msg->contents = g_string_free (chunk.response, FALSE); //DBG("Email Contents: %s", vvm_msg->contents); } else { g_warning ("Error Downloading email: %s\n", curl_easy_strerror (res)); 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) { g_critical ("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 g_critical ("There is no date!"); if (vvm_msg->message_sender) g_variant_builder_add_parsed (message_builder, "{'Sender', <%s>}", vvm_msg->message_sender); else g_critical ("There is no sender!"); if (vvm_msg->to) g_variant_builder_add_parsed (message_builder, "{'To', <%s>}", vvm_msg->to); else g_critical ("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 g_critical ("There is no Content Type!"); if (vvm_msg->attachments) g_variant_builder_add_parsed (message_builder, "{'Attachments', <%s>}", vvm_msg->attachments); else g_warning ("There are no attachments!"); if (vvm_msg->email_filepath) g_variant_builder_add_parsed (message_builder, "{'EmailFilepath', <%s>}", vvm_msg->email_filepath); else g_warning ("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); vvm_service_set_host_resolve (service); res = curl_easy_perform (service->curl); if (res == CURLE_OK) { DBG ("Deleted email Successfully, now expunging everything"); curl_easy_setopt (service->curl, CURLOPT_CUSTOMREQUEST, "EXPUNGE"); res = curl_easy_perform (service->curl); if (res == CURLE_OK) DBG ("Deleted email Successfully"); else { g_warning ("Error expunging email: %s\n", curl_easy_strerror (res)); return FALSE; } } else { g_warning ("Error deleting email: %s\n", curl_easy_strerror (res)); 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 return FALSE; } vvm_msg = g_try_new0 (struct voicemail, 1); if (vvm_msg == NULL) { g_critical ("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) { g_critical ("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 && sms_msg->sync_status_reason != SYNC_SMS_MAILBOX_UPDATE) { vvm_util_delete_status_message (sms_msg); DBG ("This is not a New Message SYNC SMS."); return TRUE; } if (sms_msg->uid) mailindex = g_strdup (sms_msg->uid); else { // Some SYNC messages don't have a UID, so just do a normal sync DBG ("SYNC messages without UID."); vvm_util_delete_status_message (sms_msg); vvm_service_sync_vvm_imap_server (service); return TRUE; } } 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) { g_critical ("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) { g_critical ("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) { g_warning ("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) { g_warning ("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; } vvm_message_register (service, vvm_msg); //Emit voicemail added emit_message_added (service, vvm_msg); //DBG("NOT DELETING YET"); //return TRUE; //Delete voicemail from server if (vvm_service_delete_vvm_email_from_server (service, vvm_msg) == FALSE) { /* * Sometimes curl says there was an error but it works fine. * I would rather have a duplicate VVM versus accidentally * deleting one. */ g_critical ("Error deleting email from server!"); return FALSE; } return TRUE; } 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) g_critical ("Error resyncing voicemail!"); return FALSE; } int vvm_service_sync_vvm_imap_server (struct vvm_service *service) { g_autofree char *URI = NULL; struct struct_string chunk = {0}; CURLcode res = CURLE_OK; chunk.response = g_string_new (NULL); if (service->mailbox_active == FALSE) { if (vvm_service_activate_vvm_imap_server (service) == TRUE) activate_curl_structure (service); else 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, curl_string_cb); curl_easy_setopt (service->curl, CURLOPT_WRITEDATA, (void *)&chunk); vvm_service_set_host_resolve (service); res = curl_easy_perform (service->curl); if (res == CURLE_OK) { g_auto(GStrv) tokens = NULL; g_auto(GStrv) lines = NULL; g_autofree char *inbox_response; const char *inbox_response_search = NULL; g_autofree char *inbox_response_to_parse = NULL; lines = g_strsplit_set (chunk.response->str, "\r\n", 2); g_string_free (chunk.response, TRUE); inbox_response = g_strdup (lines[0]); DBG ("Returned: %s", inbox_response); if (inbox_response == NULL) { g_critical ("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 * * We need to reverse " * SEARCH 1 2 3 4" to "4 3 2 1" so we don't delete * messages then stumble over them! */ /* remove " * SEARCH" */ inbox_response_search = g_strrstr (inbox_response, "SEARCH"); if (inbox_response_search) { inbox_response_search = inbox_response_search + 6; inbox_response_to_parse = g_strdup (inbox_response_search); } else inbox_response_to_parse = g_strdup (inbox_response); /* Now we have " 1 2 3 4 10" */ g_strstrip (inbox_response_to_parse); tokens = g_strsplit (inbox_response_to_parse, " ", -1); /* Have to go in reverse since this the index changes as you delete voicemails */ for (unsigned int i = g_strv_length (tokens); i-- > 0; ) { DBG ("Token: %s", tokens[i]); if (vvm_service_new_vvm (service, NULL, tokens[i], service->mailbox_vvm_type) == FALSE) { g_critical ("Error creating VVM!"); g_timeout_add_seconds (60, retry_sync_vvm_imap_server, service); return FALSE; } } return TRUE; } else { g_warning ("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); g_clear_error (&error); } 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); g_clear_error (&error); } 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); g_clear_error (&error); } 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); g_clear_error (&error); } 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); g_clear_error (&error); } 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); g_clear_error (&error); } 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); g_clear_error (&error); } service->mailbox_legacytls = g_key_file_get_boolean (service->settings, SETTINGS_GROUP_SERVICE, "MailboxLegacyTLS", &error); if (error) { service->mailbox_legacytls = FALSE; g_key_file_set_boolean (service->settings, SETTINGS_GROUP_SERVICE, "MailboxLegacyTLS", service->mailbox_legacytls); g_clear_error (&error); } 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); g_clear_error (&error); } 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); g_clear_error (&error); } 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); g_clear_error (&error); } 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); g_clear_error (&error); } 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); g_clear_error (&error); } service->allow_plaintext_connection = g_key_file_get_boolean (service->settings, SETTINGS_GROUP_SERVICE, "AllowPlaintextConnection", &error); if (error) { service->allow_plaintext_connection = FALSE; g_key_file_set_boolean (service->settings, SETTINGS_GROUP_SERVICE, "AllowPlaintextConnection", service->allow_plaintext_connection); g_clear_error (&error); } service->mailbox_ca_bundle = g_key_file_get_string (service->settings, SETTINGS_GROUP_SERVICE, "MailboxCABundle", &error); if (error) { service->mailbox_ca_bundle = g_strdup (""); g_key_file_set_string (service->settings, SETTINGS_GROUP_SERVICE, "MailboxCABundle", service->mailbox_ca_bundle); g_clear_error (&error); } } 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); g_clear_error (&error); } } 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; /* * The key in the hash table is the dbus path, which is destroyed * in destroy_message(). Thus, no need to have a key_destroy_func */ service->messages = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, destroy_message); if (service->messages == NULL) { 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); g_clear_error (&error); } } static void vvm_message_dbus_unregister (unsigned int message_registration_id) { GDBusConnection *connection = vvm_dbus_get_connection (); g_dbus_connection_unregister_object (connection, message_registration_id); } static int vvm_message_unregister (const struct vvm_service *service, struct voicemail *vvm_msg) { emit_message_removed (service->path, vvm_msg->dbus_path); vvm_message_dbus_unregister (vvm_msg->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 dbus_unregister_message (gpointer key, gpointer value, gpointer user_data) { struct voicemail *vvm_msg = value; vvm_message_dbus_unregister (vvm_msg->message_registration_id); } static void destroy_message_table (struct vvm_service *service) { if (service->messages == NULL) return; g_hash_table_foreach (service->messages, dbus_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); g_clear_error (&error); } } 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); g_clear_error (&error); } } static gboolean load_message_from_store (const char *service_id, const char *uuid, struct voicemail *vvm_msg) { GKeyFile *meta; g_autofree char *vvm_path = NULL; int number_of_attachments; gboolean path_changed = FALSE; vvm_path = vvm_store_get_path (service_id, " "); g_strstrip (vvm_path); 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) { g_critical ("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) { g_critical ("There is no sender!"); return FALSE; } vvm_msg->to = g_key_file_get_string (meta, "info", "To", NULL); if (vvm_msg->to == NULL) { g_critical ("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) { g_critical ("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) { g_auto(GStrv) Attachment_list = NULL; Attachment_list = g_strsplit (vvm_msg->attachments, ";", -1); number_of_attachments = g_strv_length (Attachment_list); for (int i = 0; i < number_of_attachments; i++) if (!g_file_test (Attachment_list[i], G_FILE_TEST_EXISTS)) { g_autofree char *basename = NULL; path_changed = TRUE; basename = g_path_get_basename (Attachment_list[i]); DBG ("Attachment %s cannot be found!", Attachment_list[i]); g_free (Attachment_list[i]); Attachment_list[i] = g_strdup_printf ("%s%s", vvm_path, basename); if (!g_file_test (Attachment_list[i], G_FILE_TEST_EXISTS)) { g_warning ("Attachment %s does not exist!", Attachment_list[i]); return FALSE; } } g_free (vvm_msg->attachments); vvm_msg->attachments = g_strjoinv (";", Attachment_list); if (path_changed) g_key_file_set_string (meta, "info", "Attachments", vvm_msg->attachments); } else { g_warning ("There are no attachments!"); return FALSE; } vvm_msg->email_filepath = g_key_file_get_string (meta, "info", "EmailFilepath", NULL); if (vvm_msg->email_filepath) { if (!g_file_test (vvm_msg->email_filepath, G_FILE_TEST_EXISTS)) { g_autofree char *basename = NULL; path_changed = TRUE; basename = g_path_get_basename (vvm_msg->email_filepath); DBG ("Email %s cannot be found!", vvm_msg->email_filepath); g_free (vvm_msg->email_filepath); vvm_msg->email_filepath = g_strdup_printf ("%s%s", vvm_path, basename); if (!g_file_test (vvm_msg->email_filepath, G_FILE_TEST_EXISTS)) { g_warning ("Email %s does not exist!", vvm_msg->email_filepath); return FALSE; } } if (path_changed) g_key_file_set_string (meta, "info", "EmailFilepath", vvm_msg->email_filepath); } else { g_warning ("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, path_changed); 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) { g_critical ("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) g_critical ("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_unref (struct vvm_service *service) { 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); g_free (service->mailbox_ca_bundle); g_clear_pointer (&service->resolvers_ipv4_csv, g_free); g_clear_pointer (&service->resolvers_ipv6_csv, g_free); curl_slist_free_all (service->host_slist); service->host_slist = NULL; service->settings = NULL; } g_free (service->identity); g_free (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); g_free (service->mailbox_ca_bundle); g_clear_pointer (&service->resolvers_ipv4_csv, g_free); g_clear_pointer (&service->resolvers_ipv6_csv, g_free); 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_clear_pointer (&service->country_code, g_free); g_clear_pointer (&service->path, g_free); g_clear_pointer (&service->own_number, g_free); 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; if (imsi == NULL) { g_warning ("vvm_service_set_country_code(): IMSI is NULL!"); return FALSE; } country_code = get_country_iso_for_mcc (imsi); if (country_code == NULL) { g_warning ("vvm_service_set_country_code(): Country Code is NULL!"); return FALSE; } g_free (service->country_code); service->country_code = g_strdup (country_code); DBG ("Service Country Code set to %s", service->country_code); return TRUE; } const char * vvm_service_get_country_code (struct vvm_service *service) { return service->country_code; } int vvm_service_set_own_number (struct vvm_service *service, const char *own_number) { if (own_number == NULL) { g_warning ("vvm_service_set_own_number(): Number is NULL!"); return FALSE; } if (service->country_code == NULL) { g_warning ("Country Code is NULL!"); return FALSE; } g_clear_pointer (&service->own_number, g_free); service->own_number = phone_utils_format_number_e164 (own_number, service->country_code, FALSE); if (service->own_number) DBG ("Service own number set"); else { g_warning ("Error setting own number!"); return FALSE; } return TRUE; } 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) g_critical ("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.18/src/service.h000066400000000000000000000071341456374475000150420ustar00rootroot00000000000000/* * * 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_unref (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); const char *vvm_service_get_country_code (struct vvm_service *service); 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_set_resolvers (struct vvm_service *service, const gchar *ipv4_csv, const gchar *ipv6_csv); 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.18/src/store.c000066400000000000000000000234521456374475000145320ustar00rootroot00000000000000/* * * 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) { g_critical ("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) { g_critical ("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) { g_critical ("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) { g_critical ("Failed to open %s\n", path); return FALSE; } if (fstat (fd, &st) < 0) { g_critical ("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) { g_critical ("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) { g_critical ("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) { g_critical ("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.18/src/store.h000066400000000000000000000050571456374475000145400ustar00rootroot00000000000000/* * * 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); char *vvm_store_generate_uuid_objpath (void); 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.18/src/version.h.in000066400000000000000000000015721456374475000154740ustar00rootroot00000000000000/* version.h.in * * Copyright 2021-2022 Chris Talbot * * 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 3 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, see . * * Authors: * Chris Talbot * * SPDX-License-Identifier: GPL-3.0-or-later */ #pragma once #define PACKAGE_VCS_VERSION "@VCS_TAG@" vvmd-0.18/src/vvm.h000066400000000000000000000022101456374475000142000ustar00rootroot00000000000000/* * * 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.18/src/vvm3-activation.c000066400000000000000000000353751456374475000164370ustar00rootroot00000000000000/* * * Visual Voicemail Daemon * * Copyright (C) 2022, akrosi8 * 2022, 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 "resolve.h" #include "vvm3-activation.h" #include "vvm.h" // Code on AOSP that activates vvm3 // https://cs.android.com/android/platform/superproject/+/master:packages/apps/Dialer/java/com/android/voicemail/impl/protocol/Vvm3Subscriber.java static size_t vvm3_curl_writeback (void *ptr, size_t size, size_t nmemb, GString *s) { if (g_string_append_len (s, ptr, size * nmemb) == NULL) { g_warning ("Curl's writeback function wrote no data"); return 0; } return size * nmemb; } static void vvm3_gmarkup_parsetext (GMarkupParseContext *context, const char *text, gsize text_len, gpointer user_data, GError **error) { if (!strcmp (g_markup_parse_context_get_element (context), "spgurl")) { *((char **)user_data) = g_strndup (text, text_len); g_debug ("URL grabbed: %s", text); } } static void vvm3_gmarkup_parseelement (GMarkupParseContext *context, const char *element_name, const char **attribute_names, const char **attribute_values, gpointer user_data, GError **error) { const char **name_cursor = attribute_names; const char **value_cursor = attribute_values; while (*name_cursor) { if ((strcmp (*name_cursor, "href") == 0) && (!strcmp (g_markup_parse_context_get_element (context), "a"))) { *((char **)user_data) = g_strdup (*value_cursor); g_debug ("Grabbed url %s", *value_cursor); } name_cursor++; value_cursor++; } } static GMarkupParser vvm3_xml_parser = { NULL, NULL, vvm3_gmarkup_parsetext, NULL, NULL }; static GMarkupParser vvm3_htmlline_parser = { vvm3_gmarkup_parseelement, NULL, NULL, NULL, NULL }; static char * vvm3_find_button (char *input) { g_auto(GStrv) inputLines = NULL; inputLines = g_strsplit_set (input, "\r\n", -1); if (inputLines == NULL) { g_debug ("Failed to split the HTML!"); return NULL; } for (unsigned int i = 0; i < g_strv_length (inputLines); i++) { /* * https://cs.android.com/android/platform/superproject/+/master:packages/apps/Dialer/java/com/android/voicemail/impl/protocol/Vvm3Subscriber.java;drc=ba93c4fe5016341d06be1ce47410885522e09a58;l=126 * Look for `Subscribe to Basic Visual Voice Mail` or `Subscribe to Basic Visual Voicemail` */ char *cmpResult; cmpResult = strstr (inputLines[i], "Subscribe to Basic Visual Voice Mail"); if (cmpResult == NULL) cmpResult = strstr (inputLines[i], "Subscribe to Basic Visual Voicemail"); if (cmpResult != NULL) { // Return the beginning of the `str == NULL) return NULL; context = g_markup_parse_context_new (&vvm3_xml_parser, 0, &spgUrl, NULL); g_markup_parse_context_parse (context, data->str, data->len, NULL); g_markup_parse_context_free (context); g_debug ("URL to pass back: %s", spgUrl); return g_strdup (spgUrl); } char * vvm3_html_find_buttonUrl (GString *data) { g_autofree char *buttonElement = NULL; g_autofree char *buttonUrl = NULL; GMarkupParseContext *context; if (data->str == NULL) return NULL; //Third stage, "clicking" a button on a returned webpage buttonElement = vvm3_find_button (data->str); if (buttonElement == NULL) { g_warning ("Could not find the subscribe button!"); return NULL; } context = g_markup_parse_context_new (&vvm3_htmlline_parser, 0, &buttonUrl, NULL); if (g_markup_parse_context_parse (context, buttonElement, strlen (buttonElement), NULL) == FALSE) g_warning ("Parsing of the response HTML failed! You may want to try again, or file a bug if this happens repeatedly."); g_markup_parse_context_free (context); return g_strdup (buttonUrl); } /* * https://datatracker.ietf.org/doc/html/rfc1738#section-3.3 * An HTTP URL takes the form: http://:/? * */ static char * vvm3_get_host (const char *spgUrl) { g_auto(GStrv) host_and_port = NULL; g_auto(GStrv) host = NULL; const char *stripped_url = NULL; if (!spgUrl || !*spgUrl) return NULL; g_debug ("URL to Check: %s", spgUrl); if (g_str_has_prefix (spgUrl, "http://")) stripped_url = spgUrl + strlen ("http://"); else if (g_str_has_prefix (spgUrl, "https://")) stripped_url = spgUrl + strlen ("https://"); else { g_debug ("URL does not begin with http:// or https://"); return NULL; } host_and_port = g_strsplit_set (stripped_url, "/", 2); host = g_strsplit_set (host_and_port[0], ":", 2); return g_strdup (host[0]); } int validate_vvm3_host (const char *spgUrl) { g_auto(GStrv) known_hosts = NULL; g_autofree char *host = NULL; if (!spgUrl || !*spgUrl) return FALSE; host = vvm3_get_host (spgUrl); if (!host) return FALSE; g_debug ("Hostname to Check: %s", host); known_hosts = g_strsplit_set (KNOWN_VVM3_HOSTS, ",", -1); for (unsigned int i = 0; i < g_strv_length (known_hosts); i++) if (g_str_has_suffix (host, known_hosts[i])) return TRUE; g_debug ("URL: %s is not on the list of trusted hosts: %s.", spgUrl, KNOWN_VVM3_HOSTS); return FALSE; } void vvm3_activation (const char *stripped_number, const char *activate_url, const char *mailbox_interface, const char *resolvers_ipv4_csv, const char *resolvers_ipv6_csv) { g_autofree char *gatewayRequestXml = NULL; CURL *curl = curl_easy_init (); GString *data = g_string_new (NULL); struct curl_slist *host_slist = NULL; g_autofree char *ip = NULL; g_autofree char *host_list = NULL; g_autofree char *host = NULL; int ip_type; /* Make sure that the activate_url is known */ if (!validate_vvm3_host (activate_url)) { curl_easy_cleanup (curl); goto cleanup; } host = vvm3_get_host (activate_url); ip = vvm_resolve_resolve_host (host, mailbox_interface, resolvers_ipv4_csv, resolvers_ipv6_csv); DBG ("Host %s IP: %s", host, ip); if (!ip) { curl_easy_cleanup (curl); goto cleanup; } ip_type = vvm_resolve_ip_version (ip); if (ip_type == 6) { g_autofree char *new_ip = NULL; new_ip = g_strdup_printf ("[%s]", ip); g_free (ip); ip = g_strdup (new_ip); } else if (ip_type == 4) DBG ("IP is v4"); else { curl_easy_cleanup (curl); goto cleanup; } host_list = g_strdup_printf ("+%s:443:%s", host, ip); host_slist = curl_slist_append(host_slist, host_list); g_clear_pointer (&host_list, g_free); host_list = g_strdup_printf ("+%s:80:%s", host, ip); host_slist = curl_slist_append(host_slist, host_list); g_clear_pointer (&host_list, g_free); /* * Seed the random number generator with system time. This is necessary * because transaction ID in the initial request XML is randomly * generated, and without seeding, the generator will return 0. */ srand (time (NULL)); /* * This is the template for the special XML request that needs to be * sent to the server obtained via SMS. * transactionid is a random number: https://cs.android.com/android/platform/superproject/+/master:packages/apps/Dialer/java/com/android/voicemail/impl/protocol/Vvm3Subscriber.java * mdn is your number. From: https://cs.android.com/android/platform/superproject/+/master:packages/apps/Dialer/java/com/android/voicemail/impl/protocol/Vvm3Subscriber.java;drc=ba93c4fe5016341d06be1ce47410885522e09a58;l=187 * Self provisioning gateway expects 10 digit national format * Device Model is a Pixel 5 */ gatewayRequestXml = g_strdup_printf ("%ld%sretrieveSPGURLDevice%s", random (), stripped_number, VVM3_DEVICE_MODEL); g_debug ("The XML request to transmit is (newline):\n%s", gatewayRequestXml); if (curl) { CURLcode res = CURLE_OK; g_autofree char *spgUrl = NULL; char *post_data = NULL; post_data = curl_easy_escape (curl, gatewayRequestXml, strlen (gatewayRequestXml)); curl_easy_setopt (curl, CURLOPT_URL, activate_url); curl_easy_setopt (curl, CURLOPT_POSTFIELDS, post_data); curl_easy_setopt (curl, CURLOPT_WRITEFUNCTION, vvm3_curl_writeback); curl_easy_setopt (curl, CURLOPT_WRITEDATA, data); curl_easy_setopt (curl, CURLOPT_INTERFACE, mailbox_interface); curl_easy_setopt (curl, CURLOPT_RESOLVE, host_slist); /* This doesn't happen very often and can fail, so let's leave debug on */ curl_easy_setopt (curl, CURLOPT_VERBOSE, 1L); /* only allow HTTP and HTTPS */ #ifdef HAVE_LIBCURL_CURLOPT_PROTOCOLS_STR curl_easy_setopt (curl, CURLOPT_PROTOCOLS_STR, "http,https"); #else curl_easy_setopt (curl, CURLOPT_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS); #endif res = curl_easy_perform (curl); curl_free(post_data); if (res != CURLE_OK) { g_warning ("Error with the first curl request: %s\n", curl_easy_strerror (res)); curl_easy_cleanup (curl); goto cleanup; } curl_easy_cleanup (curl); curl = NULL; g_debug ("The response to the request (newline):\n%s\n", data->str); spgUrl = vvm3_xml_find_spgUrl (data); if (spgUrl == NULL) { g_warning ("Parsing of the response XML failed! You might want to try again, or file a bug if this happens repeatedly."); goto cleanup; } if (!validate_vvm3_host (spgUrl)) goto cleanup; //Prepare for second stage, sending request to SPG URL grabbed from XML g_debug ("Captured URL is %s", spgUrl); data = g_string_truncate (data, 0); curl = curl_easy_init (); if (curl) { g_autofree char *requestParams = NULL; g_autofree char *buttonUrl = NULL; curl_easy_setopt (curl, CURLOPT_URL, spgUrl); curl_easy_setopt (curl, CURLOPT_WRITEFUNCTION, vvm3_curl_writeback); curl_easy_setopt (curl, CURLOPT_WRITEDATA, data); curl_easy_setopt (curl, CURLOPT_COOKIEFILE, ""); curl_easy_setopt (curl, CURLOPT_POST, 1); /* This doesn't happen very often and can fail, so let's leave debug on */ curl_easy_setopt (curl, CURLOPT_VERBOSE, 1L); /* only allow HTTP and HTTPS */ #ifdef HAVE_LIBCURL_CURLOPT_PROTOCOLS_STR curl_easy_setopt (curl, CURLOPT_PROTOCOLS_STR, "http,https"); #else curl_easy_setopt (curl, CURLOPT_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS); #endif //Form a special request based on constants derived from AOSP requestParams = g_strdup_printf ("%s=%s&%s=%s&%s=%s&%s=%s&%s=%s", SPG_VZW_MDN_PARAM, stripped_number, SPG_VZW_SERVICE_PARAM, SPG_VZW_SERVICE_BASIC, SPG_DEVICE_MODEL_PARAM, SPG_DEVICE_MODEL_ANDROID, SPG_APP_TOKEN_PARAM, SPG_APP_TOKEN, SPG_LANGUAGE_PARAM, SPG_LANGUAGE_EN); g_debug ("The second request is being sent with the following parameters:\n%s", requestParams); curl_easy_setopt (curl, CURLOPT_POSTFIELDS, requestParams); res = curl_easy_perform (curl); if (res != CURLE_OK) { g_warning ("Error with the second curl request: %s\n", curl_easy_strerror (res)); curl_easy_cleanup (curl); goto cleanup; } g_debug ("The response to the second request was:\n%s", data->str); buttonUrl = vvm3_html_find_buttonUrl (data); if (buttonUrl == NULL) { g_warning ("No URL was found during parsing! You may want to try again, or file a bug if this happens repeatedly."); curl_easy_cleanup (curl); goto cleanup; } g_debug ("URL obtained is %s", buttonUrl); if (!validate_vvm3_host (buttonUrl)) { curl_easy_cleanup (curl); goto cleanup; } data = g_string_truncate (data, 0); curl_easy_setopt (curl, CURLOPT_URL, buttonUrl); curl_easy_setopt (curl, CURLOPT_HTTPGET, 1); res = curl_easy_perform (curl); if (res != CURLE_OK) { g_warning ("Error with the third curl request: %s\n", curl_easy_strerror (res)); curl_easy_cleanup (curl); goto cleanup; } g_debug ("The response to the third request was:\n%s", data->str); curl_easy_cleanup (curl); } else { g_critical ("Failed to set up curl!"); goto cleanup; } } else { g_critical ("Failed to set up curl!"); goto cleanup; } cleanup: g_string_free (data, TRUE); curl_slist_free_all (host_slist); return; } vvmd-0.18/src/vvm3-activation.h000066400000000000000000000036611456374475000164350ustar00rootroot00000000000000/* * * Visual Voicemail Daemon * * Copyright (C) 2022, akrosi8 * 2022, 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 * */ // Constants used for initializing Verizon's vvm3 // From: https://cs.android.com/android/platform/superproject/+/master:packages/apps/Dialer/java/com/android/voicemail/impl/protocol/Vvm3Subscriber.java;drc=ba93c4fe5016341d06be1ce47410885522e09a58;l=110 #define SPG_VZW_MDN_PARAM "VZW_MDN" #define SPG_VZW_SERVICE_PARAM "VZW_SERVICE" #define SPG_DEVICE_MODEL_PARAM "DEVICE_MODEL" #define SPG_LANGUAGE_PARAM "SPG_LANGUAGE_PARAM" #define SPG_APP_TOKEN_PARAM "APP_TOKEN" #define SPG_VZW_SERVICE_BASIC "BVVM" #define SPG_DEVICE_MODEL_ANDROID "DROID_4G" #define SPG_APP_TOKEN "q8e3t5u2o1" #define SPG_LANGUAGE_EN "ENGLISH" #define VVM3_DEVICE_MODEL "Pixel 5" //You need to have the . before the domain you are validating! #define KNOWN_VVM3_HOSTS ".vzw.com" void vvm3_activation (const char *stripped_number, const char *activate_url, const char *mailbox_interface, const char *resolvers_ipv4_csv, const char *resolvers_ipv6_csv); char *vvm3_xml_find_spgUrl (GString *data); char *vvm3_html_find_buttonUrl (GString *data); int validate_vvm3_host (const char *spgUrl); vvmd-0.18/src/vvmutil.c000066400000000000000000001212671456374475000151070ustar00rootroot00000000000000/* * * 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) { g_warning ("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"); g_warning ("!!!!!WARNING!WARNING!WARNING!WARNING!WARNING!WARNING!WARNING!WARNING!!!!!!"); g_warning ("!!!!! AT&T USA uses a non-standard protocol for subscribing and !!!!!!"); g_warning ("!!!!! unsubscribing. It may change at anytime and no longer work !!!!!!"); g_warning ("!!!!!WARNING!WARNING!WARNING!WARNING!WARNING!WARNING!WARNING!WARNING!!!!!!"); g_warning ("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"); 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 } /* * 'STATE?' works for France Free Mobile However, 'STATE' reliably works * for several carriers. As of now, just changed all of them to 'STATE' * and we can work out other issues if they come up later */ static char * vvm_util_create_ios_status_sms (const char *carrier_prefix) { return g_strdup_printf ("STATE"); } 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) g_critical ("I do not know how to unsubscribe from AT&T USA!"); else if (g_strcmp0 (vvm_type, "FreeMobileProprietary") == 0) g_critical ("I do not know how to unsubscribe from Free Mobile!"); else if (g_strcmp0 (vvm_type, "ios") == 0) g_critical ("I do not know how to unsubscribe from ios!"); else if (g_strcmp0 (vvm_type, "vvm3") == 0) g_critical ("There is no way to unsubscribe from vvm3!"); else g_critical ("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, "FreeMobileProprietary") == 0) return vvm_util_create_ios_status_sms (carrier_prefix); else if (g_strcmp0 (vvm_type, "ios") == 0) return vvm_util_create_ios_status_sms (carrier_prefix); else if (g_strcmp0 (vvm_type, "vvm3") == 0) return g_strdup_printf ("STATUS"); else g_critical ("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, "FreeMobileProprietary") == 0) return vvm_util_create_ios_status_sms (carrier_prefix); else if (g_strcmp0 (vvm_type, "ios") == 0) return vvm_util_create_ios_status_sms (carrier_prefix); else if (g_strcmp0 (vvm_type, "vvm3") == 0) return g_strdup_printf ("STATUS"); else g_critical ("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 if (g_str_has_prefix (message, "GET?AD=")) // This is from AWESim return SMS_MESSAGE_STATUS; else if (g_str_has_prefix (message, "STATE?state=")) // This is a new user message return SMS_MESSAGE_STATUS; else { DBG ("Not a status or sync SMS message"); return SMS_MESSAGE_OTHER; } } static int vvm_util_parse_ios_sms_message_type (const char *message, const char *carrier_prefix) { if (g_str_has_prefix (message, "STATE?state=")) return SMS_MESSAGE_STATUS; else if (g_str_has_prefix (message, "MBOXUPDATE?m=")) // This is a new user message return SMS_MESSAGE_SYNC; 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 if (g_strcmp0 (vvm_type, "FreeMobileProprietary") == 0) sms_message_type = vvm_util_parse_ios_sms_message_type (message, carrier_prefix); else if (g_strcmp0 (vvm_type, "ios") == 0) sms_message_type = vvm_util_parse_ios_sms_message_type (message, carrier_prefix); else { g_critical ("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) { g_critical ("AT&T has no sync message!!"); } void vvm_util_parse_sync_ios_sms_message (const char *message, struct sms_control_message *sms_msg) { DBG ("iOS Sync message contain only the number of new messages."); sms_msg->sync_status_reason = SYNC_SMS_MAILBOX_UPDATE; } 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 if (g_strcmp0 (vvm_type, "FreeMobileProprietary") == 0) vvm_util_parse_sync_ios_sms_message (message, sms_msg); else if (g_strcmp0 (vvm_type, "ios") == 0) vvm_util_parse_sync_ios_sms_message (message, sms_msg); else g_critical ("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 g_critical ("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 < strlen (decoded_string); i++) { const char *decode[] = { "XTQ^ZSUU_Y00000", "YUP_[RTT^X11111", "ZVS\\XQWW][22222", "[WR]YPVV\\Z33333", "\\PUZ^WQQ[]44444", "]QT[_VPPZ\\55555", "^RWX\\USSY_66666", "_SVY]TRRX^77777", "P\\YVR[]]WQ88888", "Q]XWSZ\\\\VP99999" }; for (int j = 0; j < 10; j++) { if (i > 14) { g_warning ("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_ios_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; if (g_str_has_prefix (message, "STATE?state=")) { message = message + 6; other_parts = g_strsplit (message, ";", -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], "state") == 0) { if (g_strcmp0 (single_setting[1], "NewAccount") == 0) { sms_msg->provision_status = VVM_PROVISION_STATUS_NEW; DBG ("Provisioning Status: New."); } else if (g_strcmp0 (single_setting[1], "Active") == 0) { sms_msg->provision_status = VVM_PROVISION_STATUS_READY; DBG ("Provisioning Status: Active."); } else if (g_strcmp0 (single_setting[1], "NotAvailable") == 0) { sms_msg->provision_status = VVM_PROVISION_STATUS_BLOCKED; DBG ("Provisioning Status: Not Available."); } else sms_msg->provision_status = VVM_PROVISION_STATUS_NOT_SET; } else if (g_strcmp0 (single_setting[0], "server") == 0) sms_msg->mailbox_hostname = g_strdup (single_setting[1]); else if (g_strcmp0 (single_setting[0], "pw") == 0) sms_msg->mailbox_password = g_strdup (single_setting[1]); else if (g_strcmp0 (single_setting[0], "port") == 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 if (g_strcmp0 (single_setting[0], "name") == 0) { sms_msg->mailbox_username = g_strdup (single_setting[1]); if (strstr (sms_msg->mailbox_username, "CMSGROUP")) { char **name_parts = NULL; name_parts = g_strsplit (sms_msg->mailbox_username, ":", -1); g_clear_pointer (&sms_msg->mailbox_username, g_free); sms_msg->mailbox_username = g_strdup (name_parts[1]); g_strfreev (name_parts); } } else //I have no idea what these settings are DBG ("Not procesing setting %s", single_setting[0]); } } } 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; if (g_str_has_prefix (message, "STATE?state=")) { vvm_util_parse_status_ios_sms_message (message, sms_msg); } else { if (g_str_has_prefix (message, "GET?AD=")) { g_autofree gchar *stripped_message = NULL; message = message + 7; stripped_message = g_strdup (message); g_strdelimit (stripped_message, "\"", ' '); g_strstrip (stripped_message); status_parts = g_strsplit (stripped_message, "?", 2); } else 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 if (g_strcmp0 (single_setting[0], "S") == 0) //Provisioning Status { if (g_strcmp0 (single_setting[1], "I") == 0) { // R = Subscriber Ready sms_msg->provision_status = VVM_PROVISION_STATUS_READY; DBG ("Provisioning Status: Ready."); } 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 sms_msg->provision_status = VVM_PROVISION_STATUS_NOT_SET; } else //I have no idea what these settings are DBG ("Not procesing setting %s", single_setting[0]); } } //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 if (g_strcmp0 (vvm_type, "FreeMobileProprietary") == 0) vvm_util_parse_status_ios_sms_message (message, sms_msg); else if (g_strcmp0 (vvm_type, "ios") == 0) vvm_util_parse_status_ios_sms_message (message, sms_msg); else g_critical ("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) g_debug ("Cannot process Header:%s:containing:%s", single_token[0], single_token[1]); else g_debug ("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) { g_auto(GStrv) 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 = 1; int found_attachment = FALSE; int base64_encoded; g_auto(GStrv) attribute_parse = NULL; g_autofree char *filename = NULL; GString *decoded = g_string_new (NULL); DBG ("Decoding Attachment"); lines = g_strsplit_set (attachment, "\r\n", -1); //Decode Attachment headers first for (int i = 0; lines[i] != NULL; i++) { g_auto(GStrv) 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) { g_critical ("Unknown content type."); return NULL; } if (attributes == NULL) { g_debug ("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_auto(GStrv) 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) { g_critical ("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. 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) { g_critical ("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 { g_critical ("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_auto(GStrv) tokens = NULL; g_auto(GStrv) boundary = NULL; g_auto(GStrv) 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) { g_critical ("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]); 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) { g_critical ("Failed to decode attachment"); return FALSE; } else if (g_strcmp0 (save_file_path, "NONE") == 0) g_debug ("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) { g_critical ("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.18/src/vvmutil.h000066400000000000000000000131541456374475000151070ustar00rootroot00000000000000/* * * 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); 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); void vvm_util_parse_sync_att_usa_sms_message (const char *message, struct sms_control_message *sms_msg); void vvm_util_parse_sync_ios_sms_message (const char *message, struct sms_control_message *sms_msg); void vvm_util_parse_status_otmp_sms_message (const char *message, struct sms_control_message *sms_msg); void vvm_util_parse_status_ios_sms_message (const char *message, struct sms_control_message *sms_msg); void vvm_util_parse_status_att_usa_sms_message (const char *message, struct sms_control_message *sms_msg); vvmd-0.18/uncrustify.cfg000066400000000000000000000077361456374475000153460ustar00rootroot00000000000000# # GNOME style (GNU variant) # input_tab_size = 8 output_tab_size = 8 # { # size = 2; // etc. # ^^ indent_columns = 2 indent_with_tabs = 0 # Don't use tabs for indentation # if ()\n{} // level of endentation before '{' # ^ indent_brace = 2 # exit: #^ // indentation before label # 2 = 1 space indent_label = 2 # size = size + 2; // +, -, *, /, &, |, >>, <<, etc # ^ ^ sp_arith = force # int width, height # ^ ^ sp_after_comma = force sp_before_comma = remove # size = 4; // +=, -=, |=, etc. # ^ ^ sp_assign = add # success = start && go; // &&, || # ^ ^ sp_bool = force # if (size > 2) // >, <, ==, >=, etc. # ^ ^ sp_compare = force # perimeter = (width + height) * 2 // remove space around '(' # ^ ^ sp_inside_paren = remove # int main (void) // remove spaces around '(' in functions # ^ ^ sp_inside_fparen = remove # int main () // remove spaces in empty parameters # ^ sp_inside_fparens = remove # int foo (void); # ^ sp_func_proto_paren = add # int foo (void){} # ^ sp_func_def_paren = force # printf (""); # ^ sp_func_call_paren = force # if ( // if/for/while/switch etc. # ^ sp_before_sparen = force set func_call_user _ N_ C_ Q_ NC_ set func_call_user g_autoptr g_auto # space after user defined functions (above) and paren sp_func_call_user_paren = remove # printf ("") ; # ^ // remove the space sp_before_semi = remove sp_inside_braces = ignore # return (3*2); # ^ sp_return_paren = force # long distance; # int name; # // ^ space b/w type and variable name sp_after_type = add # char *name; // add space before '*' (keep spacing if more than 1) # ^ sp_before_ptr_star = add # char *name; // remove space after '*' # ^ sp_after_ptr_star = remove # char **name (){} // remove space b/w 2 '*' # ^ sp_between_ptr_star = remove # maximum consecutive new lines # 3 = 2 blank lines nl_max = 3 # char *names[] = { // remove newline if any # ^ nl_assign_brace = remove # } else // insert newline # ^ nl_brace_else = force # else if // remove newline if any # ^ nl_else_if = remove # do {} // insert newline # ^ nl_do_brace = force nl_union_brace = force nl_enum_brace = ignore # don't care for enums nl_struct_brace = force # while () {} // insert newline. # ^ nl_while_brace = force nl_for_brace = force nl_if_brace = force nl_switch_brace = force # do ()\n while(); # ^ nl_brace_while = force # int main (void){} // newline after type # ^ nl_func_type_name = force # int main () {} // insert newline # ^ nl_fdef_brace = force # int main () {} # ^ # nl_after_func_body = 2 # int main (int argc, char **argv) // force newline at ',' # ^ nl_func_decl_args = force nl_func_def_args = force # remove blank lines after '{' and before '}' eat_blanks_after_open_brace = true eat_blanks_before_close_brace = true # add newline at end of file nl_end_of_file = force # min number of newlines to be added nl_end_of_file_min = 1 # main (int argc, // add enough spaces to align # char *argv[]) # ^^ align_func_params = true # func (int *square, // stick '*' to parameter # double match) align_var_def_star_style = 2 # #define ADD(x,y,x) ((x) \ # + (y) \ # + (z)) # ^ // align '/' align_nl_cont = true # Add a '*' at beginning of multiline comment lines cmt_star_cont = true # Remove braces for single line statements in # while, for, if, and switch case mod_full_brace_while = remove mod_full_brace_for = remove mod_full_brace_if = remove mod_case_brace = remove # if (is_good \n || \n is_okay) # ^ ^ // If newline present, # where to put &&, ||, | etc. pos_conditional = lead # FIXME: Not working pos_bool = trail # Hack to avoid indentation of following line # consider the following tokens as a comment set COMMENT G_BEGIN_DECLS vvmd-0.18/uncrustify.sh000077500000000000000000000031001456374475000152010ustar00rootroot00000000000000#!/bin/sh # Written in 2018 by Mohammed Sadiq # To the extent possible under law, the author(s) have dedicated all # copyright and related and neighboring rights to this software to # the public domain worldwide. This software is distributed without # any warranty. # You should have received a copy of the CC0 Public Domain Dedication # along with this software. If not, see # . if [ ! "$(which uncrustify)" ]; then echo "'uncrustify' Not found. Exiting..." exit 1 fi if [ ! "$(which diff)" ]; then echo "'diff' Not found. Exiting..." exit 1 fi # Handle only .c files. There are unfixable issues uncrustifying .h header files uncrustify -l c -c uncrustify.cfg --no-backup --mtime $(find . -path './src/*' -name '*.c') # Simple Hack to work around issues with handling .h files HEADER_FILES=$(find . -path './src/*' -name '*.h') for file in $HEADER_FILES do # Work on a copy. Replace the file only if some actual change # happened. Otherwise, every header file will have a new mtime # and that will result in compiling every file on rebuild. MTIME=$(stat -c '%Y' "$file") cp "$file" temp.h sed -i "/^G_DECLARE_/ s/)$/);/" temp.h cp temp.h temp.bak uncrustify -l c -c uncrustify.cfg --no-backup temp.h -q echo "Parsing: $file as language C" DIFF="$(diff temp.h temp.bak)" if [ "$DIFF" ]; then sed -i "/^G_DECLARE_/ s/;$//" temp.h mv temp.h "$file" # Reset modification time touch --date=@${MTIME} "$file" fi done # Remove temp files, if any. rm -rf temp.h temp.bak vvmd-0.18/unit/000077500000000000000000000000001456374475000134145ustar00rootroot00000000000000vvmd-0.18/unit/ATT1.mbox000066400000000000000000000211361456374475000150170ustar00rootroot00000000000000Message-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.18/unit/ATT1Headers.txt000066400000000000000000000017251456374475000161670ustar00rootroot00000000000000Message-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.18/unit/MintMobile1.mbox000066400000000000000000001110171456374475000164240ustar00rootroot00000000000000To: 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.18/unit/MintMobile1Headers.txt000066400000000000000000000006001456374475000175650ustar00rootroot00000000000000To: 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.18/unit/Tmobile1.mbox000066400000000000000000002317561456374475000157750ustar00rootroot00000000000000To: 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.18/unit/Tmobile1Headers.txt000066400000000000000000000006211456374475000171240ustar00rootroot00000000000000To: 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.18/unit/VZWUSA1.mbox000066400000000000000000000312341456374475000154260ustar00rootroot00000000000000Return-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.18/unit/VZWUSA1Headers.txt000066400000000000000000000017111456374475000165710ustar00rootroot00000000000000Return-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.18/unit/meson.build000066400000000000000000000047041456374475000155630ustar00rootroot00000000000000test_dependencies = [ dependency('glib-2.0', version : '>=2.16'), dependency('mm-glib', version : '>=1.14'), dependency('libcurl', version : '>7.70'), dependency('libcares', version : '>=1.18.1'), cc.find_library('dl'), cc.find_library('phonenumber', required: true) ] test_vvmutil = executable('test-vvmutil', ['test-vvmutil.c', '../src/vvmutil.c', '../src/store.c'], include_directories : includes, dependencies : test_dependencies ) test_decode = executable('test_decode', ['test-decode.c', '../src/vvmutil.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/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'], include_directories : includes, dependencies : test_dependencies ) test_sms = executable('test_sms', ['test-sms.c', '../src/vvmutil.c', '../src/store.c'], include_directories : includes, dependencies : test_dependencies ) test_vvmparse = executable('test-vvmparse', ['test-vvmparse.c', '../src/vvmutil.c', '../src/store.c'], include_directories : includes, dependencies : test_dependencies ) test_vvm3 = executable('test_vvm3', ['test-vvm3.c', '../src/vvmutil.c', '../src/store.c', '../src/vvm3-activation.c', '../src/resolve.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() + '/..') test('test SMS', test_sms, workdir : meson.current_source_dir() + '/..') test('test vvm3 parsers', test_vvm3, workdir : meson.current_source_dir() + '/..') vvmd-0.18/unit/test-decode.c000066400000000000000000000113221456374475000157570ustar00rootroot00000000000000/* * * 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.18/unit/test-number-decode.c000066400000000000000000000065561456374475000172620ustar00rootroot00000000000000/* * * 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_vzw_usa_to[] = "+15465557654@vzwazc.com"; const char decode_vzw_usa_from[] = "+15235555432@vzwazc.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_vzw_usa_to); g_assert (g_strcmp0 (output, "+15465557654") == 0); g_clear_pointer (&output, g_free); output = parse_email_address (decode_vzw_usa_from); g_assert (g_strcmp0 (output, "+15235555432") == 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.18/unit/test-phone-utils.c000066400000000000000000000120161456374475000170040ustar00rootroot00000000000000/* * * 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; gboolean valid = FALSE; 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"; //Free Mobile FR Dest Number const char carrer_vvm_test1[] = "2050"; //AT&T US const char carrer_vvm_test2[] = "94183567"; //AT&T US: This is where the texts come from const char carrer_vvm_test6[] = "1000000000"; //T-mobile US and Mint Mobile US const char carrer_vvm_test3[] = "127"; //Ting US const char carrer_vvm_test4[] = "122"; //VZW USA US const char carrer_vvm_test5[] = "900080006200"; output = phone_utils_format_number_e164 (phone_test1, "US", FALSE); g_assert_cmpstr (output, ==, phone_output); output = phone_utils_format_number_e164 (phone_test2, "US", FALSE); g_assert_cmpstr (output, ==, phone_output); output = phone_utils_format_number_e164 (phone_test3, "US", FALSE); g_assert_cmpstr (output, ==, phone_output); output = phone_utils_format_number_e164 (phone_test4, "US", FALSE); g_assert_cmpstr (output, ==, phone_output); output = phone_utils_format_number_e164 (phone_output, "US", FALSE); g_assert_cmpstr (output, ==, phone_output); output = phone_utils_format_number_e164 (email_test1, "US", TRUE); g_assert_cmpstr (output, ==, email_test1); output = phone_utils_format_number_e164 (email_test1, "US", FALSE); g_assert_null (output); output = phone_utils_format_number_e164 (carrer_vvm_test1, "US", FALSE); g_assert_cmpstr (output, ==, carrer_vvm_test1); output = phone_utils_format_number_e164 (carrer_vvm_test2, "US", FALSE); g_assert_cmpstr (output, ==, carrer_vvm_test2); output = phone_utils_format_number_e164 (carrer_vvm_test3, "US", FALSE); g_assert_cmpstr (output, ==, carrer_vvm_test3); output = phone_utils_format_number_e164 (carrer_vvm_test4, "US", FALSE); g_assert_cmpstr (output, ==, carrer_vvm_test4); output = phone_utils_format_number_e164 (carrer_vvm_test5, "US", FALSE); g_assert_cmpstr (output, ==, carrer_vvm_test5); valid = phone_utils_simple_is_valid (phone_test1); g_assert (valid); valid = phone_utils_is_valid (phone_test1, "US"); g_assert (valid); valid = phone_utils_simple_is_valid (phone_test2); g_assert (valid); valid = phone_utils_is_valid (phone_test2, "US"); g_assert (valid); valid = phone_utils_simple_is_valid (phone_test3); g_assert (valid); valid = phone_utils_is_valid (phone_test3, "US"); g_assert (valid); valid = phone_utils_simple_is_valid (phone_test4); g_assert (valid); valid = phone_utils_is_valid (phone_test4, "US"); g_assert (valid); valid = phone_utils_simple_is_valid (email_test1); g_assert (!valid); valid = phone_utils_is_valid (email_test1, "US"); g_assert (!valid); valid = phone_utils_simple_is_valid (carrer_vvm_test1); g_assert (valid); valid = phone_utils_is_valid (carrer_vvm_test1, "FR"); g_assert (!valid); valid = phone_utils_simple_is_valid (carrer_vvm_test2); g_assert (valid); valid = phone_utils_is_valid (carrer_vvm_test2, "US"); g_assert (!valid); valid = phone_utils_simple_is_valid (carrer_vvm_test3); g_assert (valid); valid = phone_utils_is_valid (carrer_vvm_test3, "US"); g_assert (!valid); valid = phone_utils_simple_is_valid (carrer_vvm_test4); g_assert (valid); valid = phone_utils_is_valid (carrer_vvm_test4, "US"); g_assert (!valid); valid = phone_utils_simple_is_valid (carrer_vvm_test5); g_assert (valid); valid = phone_utils_is_valid (carrer_vvm_test5, "US"); g_assert (!valid); valid = phone_utils_simple_is_valid (carrer_vvm_test6); g_assert (valid); valid = phone_utils_is_valid (carrer_vvm_test6, "US"); g_assert (!valid); } 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.18/unit/test-service-providers.c000066400000000000000000000125361456374475000202170ustar00rootroot00000000000000/* 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_no_carrier_prefix_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, ==, "Sophia"); g_assert_cmpstr (vvm_std, ==, "vvm3"); g_assert_cmpstr (dest_num, ==, "123456"); g_assert_null (carrier_prefix); } /* * The MCC/MNC 345999 exists and has all visual voicemail attributes except for * carrier prefix. */ static void test_no_carrier_prefix (void) { GMainLoop *loop = g_main_loop_new (NULL, FALSE); vvmd_service_providers_find_settings (SOURCE_ROOT "/unit/test-service-providers.xml", "123123", test_no_carrier_prefix_cb, loop); g_main_loop_run (loop); g_main_loop_unref (loop); } 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"); } /* * The MCC/MNC 345999 exists and has all visual voicemail attributes. */ 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_no_vvm_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); } /* * The MCC/MNC 133666 exists, but has no visual voicemail attributes. */ static void test_no_vvm (void) { GMainLoop *loop = g_main_loop_new (NULL, FALSE); vvmd_service_providers_find_settings (SOURCE_ROOT "/unit/test-service-providers.xml", "133666", test_no_vvm_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); } /* * The MCC/MNC 78130 does not exist. */ 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", "78130", 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); } /* * Test to see what happens when you point to the wrong xml file, it won't * exist */ 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/no_carrier_prefix", test_no_carrier_prefix); g_test_add_func ("/service-providers/no_vvm", test_no_vvm); g_test_add_func ("/service-providers/negative", test_negative); g_test_add_func ("/service-providers/nonexistent", test_nonexistent); return g_test_run (); } vvmd-0.18/unit/test-service-providers.xml000066400000000000000000000043131456374475000205670ustar00rootroot00000000000000 Sophia 123456 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.18/unit/test-sms.c000066400000000000000000000515401456374475000153440ustar00rootroot00000000000000/* * * Visual Voicemail Daemon * * Copyright (C) 2022, 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_sms (gconstpointer data) { struct sms_control_message *sms_msg = NULL; /****************OMTP***********************/ //This is an example SYNC Message from OTMP VVM Specification: const char otmp_sync_sms_0[] = "//VVM:SYNC:ev=NM;id=3446456;c=1;t=v;s=01234567898;dt=02/08/200812:53 +0200;l=30"; //This is an example STATUS Message from OTMP VVM Specification: const char otmp_status_sms_0[] = "//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"; /****************vvm3***********************/ const char vzw_usa_sync_sms_0[] = "//VZWVVM:SYNC:ev=NM;id=132;c=1;t=v;s=00000000000;dt=23/06/2021 22:08 -0500;l=10;dev_t=5"; // This is an unprovisioned status SMS const char vzw_usa_status_sms_0[] = "//VZWVVM:STATUS:rc=3;st=U;vmg_url=https://vmg.vzw.com/VMGIMS/VMServices"; // This is a provisioned status SMS const char vzw_usa_status_sms_1[] = "//VZWVVM:STATUS:st=R;rc=0;srv=blt2lv.imsvm.com;ipt=143;u=00000000000@vzwazc.com;pw=removed;lang=1,2,3,4,5,6,7,8;g_len=60;vs_len=10;pw_len=4-7;dev_t=5;vmg_url=https://vmg.vzw.com/VMGIMS/VMServices"; /****************cvvm***********************/ const char t_mobile_usa_sync_sms_0[] = "//VVM:SYNC:ev=NM;id=328;c=1;t=v;s=11235556754;dt=28/07/2022 17:45 -0700;l=9"; const char t_mobile_usa_status_sms_0[] = "//VVM:STATUS:st=R;rc=0;srv=vvm.mstore.msg.t-mobile.com;ipt=148;u=12125551234;pw=rh84hf77dh;lang=1|2|3|4;g_len=180;vs_len=10;pw_len=4-9"; /****************AT&T Proprietary***********/ //This is a sync SMS const char att_usa_sync_sms_0[] = "vvm.mobile.att.net:5400?f=0&v=1010&m=1235550000&p=&S=I&s=5433&i=143/993&t=4:1235550000:A:CMSGROUP0241238:ms06:client:31413"; //This is an ready status SMS const char att_usa_status_sms_0[] = "vvm.mobile.att.net:5400?f=0&v=1010&m=1235550000&p=WFRRXlpTVVVfWTAwMDAw&P=WFRRXlpTVVVfWTAwMDAw&S=I&s=5433&i=143/993&t=4:1235550000:A:CMSGROUP024121413"; //From AWESim (when it was an AT&T MVNO), they are blocked const char awesim_status_sms_0[] = "GET?AD=\"vvm.mobile.att.net:5400?v=1010&S=U&s=5433&m=1235550000\""; /****************iOS***********************/ //This is from AT&T, but is a iOS status message const char ios_status_sms_0[] = "STATE?state=NewAccount;server=vvm.mobile.att.net;port=143;name=4:1235550000:A:CMSGROUP0241238:ms06:NAT:31413;pw=5550000"; const char ios_status_sms_5[] = "STATE?state=NewAccount;server=vvm.mobile.att.net;port=143;pw=1234567;name=4:2015550123:A:CMSGROUP024230:ms08:IMAP4ACME:41115"; //FreeMobile Fr, These are really iOS messages too //This is a provisioned status SMS (New Account) const char ios_status_sms_1[] = "STATE?state=NewAccount;name=1234567;server=vvm.proxad.net;port=993;pw=PASSWORD"; //This is a provisioned status SMS (Active) const char ios_status_sms_2[] = "STATE?state=Active;server=vvm.proxad.net;port=993;pw=PASSWORD;name=1234567"; //This is an unprovisioned status SMS const char ios_status_sms_3[] = "STATE?state=NotAvailable"; //This is a sync SMS const char ios_sync_sms_0[] = "MBOXUPDATE?m=1"; //From Vodafone AU // These are really iOS messages too const char ios_status_sms_4[] = "STATE?state=NewAccount;name=username;server=prodvvm.vodafone.com.au;port=993;pw=pass"; const char ios_sync_sms_1[] = "MBOXUPDATE?m=17;server=prodvvm.vodafone.com.au;port=993;name=username;pw=pass"; /* * 'STATE?' works for France Free Mobile, but not Vodafone au. However, * 'STATE' works for both. As of now, just changed all of them to 'STATE' * and we can work out other issues if they come up later * If one sent `STATE?' to vodafone au, they got back the following. Keep it around for now * in case it is useful later */ //const char *ios_unrecognised_command_state_0 = "UNRECOGNISED?cmd=STATE?"; sms_msg = g_try_new0 (struct sms_control_message, 1); g_assert_nonnull (sms_msg); g_assert_cmpint (vvm_util_parse_sms_message_type (vzw_usa_sync_sms_0, "//VZWVVM", "vvm3"), ==, SMS_MESSAGE_SYNC); vvm_util_parse_sync_sms_message (vzw_usa_sync_sms_0, sms_msg, "vvm3"); //ev g_assert_cmpint (sms_msg->sync_status_reason, ==, SYNC_SMS_NEW_MESSAGE); //id g_assert_cmpstr (sms_msg->uid, ==, "132"); //c g_assert_cmpstr (sms_msg->new_mailbox_messages, ==, "1"); //t g_assert_cmpint (sms_msg->mailbox_message_type, ==, MAILBOX_MESSAGE_VOICE); //s g_assert_cmpstr (sms_msg->message_sender, ==, "00000000000"); //dt g_assert_cmpstr (sms_msg->message_date, ==, "23/06/2021 22:08 -0500"); //l g_assert_cmpstr (sms_msg->message_length, ==, "10"); vvm_util_delete_status_message (sms_msg); sms_msg = g_try_new0 (struct sms_control_message, 1); g_assert_nonnull (sms_msg); g_assert_cmpint (vvm_util_parse_sms_message_type (vzw_usa_status_sms_0, "//VZWVVM", "vvm3"), ==, SMS_MESSAGE_STATUS); vvm_util_parse_status_sms_message (vzw_usa_status_sms_0, sms_msg, "vvm3"); //Don't parse rc //st g_assert_cmpint (sms_msg->provision_status, ==, VVM_PROVISION_STATUS_UNKNOWN); //vmg_url g_assert_cmpstr (sms_msg->activate_url, ==, "https://vmg.vzw.com/VMGIMS/VMServices"); vvm_util_delete_status_message (sms_msg); sms_msg = g_try_new0 (struct sms_control_message, 1); g_assert_nonnull (sms_msg); g_assert_cmpint (vvm_util_parse_sms_message_type (vzw_usa_status_sms_1, "//VZWVVM", "vvm3"), ==, SMS_MESSAGE_STATUS); vvm_util_parse_status_sms_message (vzw_usa_status_sms_1, sms_msg, "vvm3"); //Don't parse rc //st g_assert_cmpint (sms_msg->provision_status, ==, VVM_PROVISION_STATUS_READY); //srv g_assert_cmpstr (sms_msg->mailbox_hostname, ==, "blt2lv.imsvm.com"); //ipt g_assert_cmpstr (sms_msg->mailbox_port, ==, "143"); //u g_assert_cmpstr (sms_msg->mailbox_username, ==, "00000000000@vzwazc.com"); //pw g_assert_cmpstr (sms_msg->mailbox_password, ==, "removed"); //lang g_assert_cmpstr (sms_msg->language, ==, "1,2,3,4,5,6,7,8"); //g_len g_assert_cmpstr (sms_msg->greeting_length, ==, "60"); //vs_len g_assert_cmpstr (sms_msg->voice_signature_length, ==, "10"); //vs_len g_assert_cmpstr (sms_msg->TUI_password_length, ==, "4-7"); //vmg_url g_assert_cmpstr (sms_msg->activate_url, ==, "https://vmg.vzw.com/VMGIMS/VMServices"); vvm_util_delete_status_message (sms_msg); sms_msg = g_try_new0 (struct sms_control_message, 1); g_assert_nonnull (sms_msg); g_assert_cmpint (vvm_util_parse_sms_message_type (otmp_sync_sms_0, "//VVM", "otmp"), ==, SMS_MESSAGE_SYNC); vvm_util_parse_sync_sms_message (otmp_sync_sms_0, sms_msg, "otmp"); //ev g_assert_cmpint (sms_msg->sync_status_reason, ==, SYNC_SMS_NEW_MESSAGE); //id g_assert_cmpstr (sms_msg->uid, ==, "3446456"); //c g_assert_cmpstr (sms_msg->new_mailbox_messages, ==, "1"); //t g_assert_cmpint (sms_msg->mailbox_message_type, ==, MAILBOX_MESSAGE_VOICE); //s g_assert_cmpstr (sms_msg->message_sender, ==, "01234567898"); //dt g_assert_cmpstr (sms_msg->message_date, ==, "02/08/200812:53 +0200"); //l g_assert_cmpstr (sms_msg->message_length, ==, "30"); vvm_util_delete_status_message (sms_msg); sms_msg = g_try_new0 (struct sms_control_message, 1); g_assert_nonnull (sms_msg); g_assert_cmpint (vvm_util_parse_sms_message_type (otmp_status_sms_0, "//VVM", "otmp"), ==, SMS_MESSAGE_STATUS); vvm_util_parse_status_sms_message (otmp_status_sms_0, sms_msg, "otmp"); //Don't parse rc //st g_assert_cmpint (sms_msg->provision_status, ==, VVM_PROVISION_STATUS_NEW); //srv g_assert_cmpstr (sms_msg->mailbox_hostname, ==, "1:10.115.67.251"); //ipt g_assert_cmpstr (sms_msg->mailbox_port, ==, "143"); //u g_assert_cmpstr (sms_msg->mailbox_username, ==, "78236487@wirelesscarrier.com"); //pw g_assert_cmpstr (sms_msg->mailbox_password, ==, "32u4yguetrr34"); //lang g_assert_cmpstr (sms_msg->language, ==, "eng|fre"); //g_len g_assert_cmpstr (sms_msg->greeting_length, ==, "25"); //vs_len g_assert_cmpstr (sms_msg->voice_signature_length, ==, "15"); //vs_len g_assert_cmpstr (sms_msg->TUI_password_length, ==, "4-6"); sms_msg = g_try_new0 (struct sms_control_message, 1); g_assert_nonnull (sms_msg); g_assert_cmpint (vvm_util_parse_sms_message_type (t_mobile_usa_sync_sms_0, "//VVM", "cvvm"), ==, SMS_MESSAGE_SYNC); vvm_util_parse_sync_sms_message (t_mobile_usa_sync_sms_0, sms_msg, "cvvm"); //ev g_assert_cmpint (sms_msg->sync_status_reason, ==, SYNC_SMS_NEW_MESSAGE); //id g_assert_cmpstr (sms_msg->uid, ==, "328"); //c g_assert_cmpstr (sms_msg->new_mailbox_messages, ==, "1"); //t g_assert_cmpint (sms_msg->mailbox_message_type, ==, MAILBOX_MESSAGE_VOICE); //s g_assert_cmpstr (sms_msg->message_sender, ==, "11235556754"); //dt g_assert_cmpstr (sms_msg->message_date, ==, "28/07/2022 17:45 -0700"); //l g_assert_cmpstr (sms_msg->message_length, ==, "9"); vvm_util_delete_status_message (sms_msg); sms_msg = g_try_new0 (struct sms_control_message, 1); g_assert_nonnull (sms_msg); g_assert_cmpint (vvm_util_parse_sms_message_type (t_mobile_usa_status_sms_0, "//VVM", "cvvm"), ==, SMS_MESSAGE_STATUS); vvm_util_parse_status_sms_message (t_mobile_usa_status_sms_0, sms_msg, "cvvm"); //Don't parse rc //st g_assert_cmpint (sms_msg->provision_status, ==, VVM_PROVISION_STATUS_READY); //srv g_assert_cmpstr (sms_msg->mailbox_hostname, ==, "vvm.mstore.msg.t-mobile.com"); //ipt g_assert_cmpstr (sms_msg->mailbox_port, ==, "148"); //u g_assert_cmpstr (sms_msg->mailbox_username, ==, "12125551234"); //pw g_assert_cmpstr (sms_msg->mailbox_password, ==, "rh84hf77dh"); //lang g_assert_cmpstr (sms_msg->language, ==, "1|2|3|4"); //g_len g_assert_cmpstr (sms_msg->greeting_length, ==, "180"); //vs_len g_assert_cmpstr (sms_msg->voice_signature_length, ==, "10"); //vs_len g_assert_cmpstr (sms_msg->TUI_password_length, ==, "4-9"); vvm_util_delete_status_message (sms_msg); sms_msg = g_try_new0 (struct sms_control_message, 1); g_assert_nonnull (sms_msg); // AT&T has pretty much identical STATUS and SYNC messages, and the SYNC // Messages aren't helpful, just process all as STATUS SMS g_assert_cmpint (vvm_util_parse_sms_message_type (att_usa_sync_sms_0, "1235550000", "AT&TUSAProprietary"), ==, SMS_MESSAGE_STATUS); vvm_util_parse_status_sms_message (att_usa_sync_sms_0, sms_msg, "AT&TUSAProprietary"); //Don't parse rc //st g_assert_cmpint (sms_msg->provision_status, ==, VVM_PROVISION_STATUS_READY); //srv g_assert_cmpstr (sms_msg->mailbox_hostname, ==, "vvm.mobile.att.net"); //ipt g_assert_cmpstr (sms_msg->mailbox_port, ==, "143"); //u g_assert_cmpstr (sms_msg->mailbox_username, ==, "1235550000"); //vs_len g_assert_cmpstr (sms_msg->TUI_password_length, ==, "7-15"); vvm_util_delete_status_message (sms_msg); sms_msg = g_try_new0 (struct sms_control_message, 1); g_assert_nonnull (sms_msg); g_assert_cmpint (vvm_util_parse_sms_message_type (att_usa_status_sms_0, "1235550000", "AT&TUSAProprietary"), ==, SMS_MESSAGE_STATUS); vvm_util_parse_status_sms_message (att_usa_status_sms_0, sms_msg, "AT&TUSAProprietary"); //Don't parse rc //st g_assert_cmpint (sms_msg->provision_status, ==, VVM_PROVISION_STATUS_READY); //srv g_assert_cmpstr (sms_msg->mailbox_hostname, ==, "vvm.mobile.att.net"); //ipt g_assert_cmpstr (sms_msg->mailbox_port, ==, "143"); //u g_assert_cmpstr (sms_msg->mailbox_username, ==, "1235550000"); //pw g_assert_cmpstr (sms_msg->mailbox_password, ==, "000000000000000"); //vs_len g_assert_cmpstr (sms_msg->TUI_password_length, ==, "7-15"); vvm_util_delete_status_message (sms_msg); sms_msg = g_try_new0 (struct sms_control_message, 1); g_assert_nonnull (sms_msg); g_assert_cmpint (vvm_util_parse_sms_message_type (awesim_status_sms_0, "1235550000", "AT&TUSAProprietary"), ==, SMS_MESSAGE_STATUS); vvm_util_parse_status_sms_message (awesim_status_sms_0, sms_msg, "AT&TUSAProprietary"); //Don't parse rc //st g_assert_cmpint (sms_msg->provision_status, ==, VVM_PROVISION_STATUS_UNKNOWN); //srv g_assert_cmpstr (sms_msg->mailbox_hostname, ==, "vvm.mobile.att.net"); //u g_assert_cmpstr (sms_msg->mailbox_username, ==, "1235550000"); //vs_len g_assert_cmpstr (sms_msg->TUI_password_length, ==, "7-15"); vvm_util_delete_status_message (sms_msg); sms_msg = g_try_new0 (struct sms_control_message, 1); g_assert_nonnull (sms_msg); g_assert_cmpint (vvm_util_parse_sms_message_type (ios_status_sms_0, "1235550000", "AT&TUSAProprietary"), ==, SMS_MESSAGE_STATUS); vvm_util_parse_status_sms_message (ios_status_sms_0, sms_msg, "AT&TUSAProprietary"); g_assert_cmpint (sms_msg->provision_status, ==, VVM_PROVISION_STATUS_NEW); g_assert_cmpstr (sms_msg->mailbox_hostname, ==, "vvm.mobile.att.net"); g_assert_cmpstr (sms_msg->mailbox_port, ==, "143"); g_assert_cmpstr (sms_msg->mailbox_username, ==, "1235550000"); g_assert_cmpstr (sms_msg->mailbox_password, ==, "5550000"); //This is only present for AT&T g_assert_cmpstr (sms_msg->TUI_password_length, ==, "7-15"); vvm_util_delete_status_message (sms_msg); //Freemobile message sms_msg = g_try_new0 (struct sms_control_message, 1); g_assert_nonnull (sms_msg); g_assert_cmpint (vvm_util_parse_sms_message_type (ios_status_sms_1, "//VVM", "FreeMobileProprietary"), ==, SMS_MESSAGE_STATUS); vvm_util_parse_status_sms_message (ios_status_sms_1, sms_msg, "FreeMobileProprietary"); //st g_assert_cmpint (sms_msg->provision_status, ==, VVM_PROVISION_STATUS_NEW); //server g_assert_cmpstr (sms_msg->mailbox_hostname, ==, "vvm.proxad.net"); //port g_assert_cmpstr (sms_msg->mailbox_port, ==, "993"); //name g_assert_cmpstr (sms_msg->mailbox_username, ==, "1234567"); //password g_assert_cmpstr (sms_msg->mailbox_password, ==, "PASSWORD"); vvm_util_delete_status_message (sms_msg); sms_msg = g_try_new0 (struct sms_control_message, 1); g_assert_nonnull (sms_msg); g_assert_cmpint (vvm_util_parse_sms_message_type (ios_status_sms_2, "//VVM", "FreeMobileProprietary"), ==, SMS_MESSAGE_STATUS); vvm_util_parse_status_sms_message (ios_status_sms_2, sms_msg, "FreeMobileProprietary"); //st g_assert_cmpint (sms_msg->provision_status, ==, VVM_PROVISION_STATUS_READY); //server g_assert_cmpstr (sms_msg->mailbox_hostname, ==, "vvm.proxad.net"); //port g_assert_cmpstr (sms_msg->mailbox_port, ==, "993"); //name g_assert_cmpstr (sms_msg->mailbox_username, ==, "1234567"); //password g_assert_cmpstr (sms_msg->mailbox_password, ==, "PASSWORD"); vvm_util_delete_status_message (sms_msg); sms_msg = g_try_new0 (struct sms_control_message, 1); g_assert_nonnull (sms_msg); g_assert_cmpint (vvm_util_parse_sms_message_type (ios_status_sms_3, "//VVM", "FreeMobileProprietary"), ==, SMS_MESSAGE_STATUS); vvm_util_parse_status_sms_message (ios_status_sms_3, sms_msg, "FreeMobileProprietary"); //st g_assert_cmpint (sms_msg->provision_status, ==, VVM_PROVISION_STATUS_BLOCKED); vvm_util_delete_status_message (sms_msg); sms_msg = g_try_new0 (struct sms_control_message, 1); g_assert_nonnull (sms_msg); g_assert_cmpint (vvm_util_parse_sms_message_type (ios_sync_sms_0, "//VVM", "FreeMobileProprietary"), ==, SMS_MESSAGE_SYNC); vvm_util_parse_sync_sms_message (ios_sync_sms_0, sms_msg, "FreeMobileProprietary"); //Mailbox Update g_assert_cmpint (sms_msg->sync_status_reason, ==, SYNC_SMS_MAILBOX_UPDATE); vvm_util_delete_status_message (sms_msg); //ios message sms_msg = g_try_new0 (struct sms_control_message, 1); g_assert_nonnull (sms_msg); g_assert_cmpint (vvm_util_parse_sms_message_type (ios_status_sms_0, "//vvm", "ios"), ==, SMS_MESSAGE_STATUS); vvm_util_parse_status_sms_message (ios_status_sms_0, sms_msg, "ios"); g_assert_cmpint (sms_msg->provision_status, ==, VVM_PROVISION_STATUS_NEW); g_assert_cmpstr (sms_msg->mailbox_hostname, ==, "vvm.mobile.att.net"); g_assert_cmpstr (sms_msg->mailbox_port, ==, "143"); g_assert_cmpstr (sms_msg->mailbox_username, ==, "1235550000"); g_assert_cmpstr (sms_msg->mailbox_password, ==, "5550000"); vvm_util_delete_status_message (sms_msg); sms_msg = g_try_new0 (struct sms_control_message, 1); g_assert_nonnull (sms_msg); g_assert_cmpint (vvm_util_parse_sms_message_type (ios_status_sms_1, "//VVM", "ios"), ==, SMS_MESSAGE_STATUS); vvm_util_parse_status_sms_message (ios_status_sms_1, sms_msg, "ios"); //st g_assert_cmpint (sms_msg->provision_status, ==, VVM_PROVISION_STATUS_NEW); //server g_assert_cmpstr (sms_msg->mailbox_hostname, ==, "vvm.proxad.net"); //port g_assert_cmpstr (sms_msg->mailbox_port, ==, "993"); //name g_assert_cmpstr (sms_msg->mailbox_username, ==, "1234567"); //password g_assert_cmpstr (sms_msg->mailbox_password, ==, "PASSWORD"); vvm_util_delete_status_message (sms_msg); sms_msg = g_try_new0 (struct sms_control_message, 1); g_assert_nonnull (sms_msg); g_assert_cmpint (vvm_util_parse_sms_message_type (ios_status_sms_2, "//VVM", "ios"), ==, SMS_MESSAGE_STATUS); vvm_util_parse_status_sms_message (ios_status_sms_2, sms_msg, "ios"); //st g_assert_cmpint (sms_msg->provision_status, ==, VVM_PROVISION_STATUS_READY); //server g_assert_cmpstr (sms_msg->mailbox_hostname, ==, "vvm.proxad.net"); //port g_assert_cmpstr (sms_msg->mailbox_port, ==, "993"); //name g_assert_cmpstr (sms_msg->mailbox_username, ==, "1234567"); //password g_assert_cmpstr (sms_msg->mailbox_password, ==, "PASSWORD"); vvm_util_delete_status_message (sms_msg); sms_msg = g_try_new0 (struct sms_control_message, 1); g_assert_nonnull (sms_msg); g_assert_cmpint (vvm_util_parse_sms_message_type (ios_status_sms_3, "//VVM", "ios"), ==, SMS_MESSAGE_STATUS); vvm_util_parse_status_sms_message (ios_status_sms_3, sms_msg, "ios"); //st g_assert_cmpint (sms_msg->provision_status, ==, VVM_PROVISION_STATUS_BLOCKED); vvm_util_delete_status_message (sms_msg); sms_msg = g_try_new0 (struct sms_control_message, 1); g_assert_nonnull (sms_msg); g_assert_cmpint (vvm_util_parse_sms_message_type (ios_status_sms_4, "//vvm", "ios"), ==, SMS_MESSAGE_STATUS); vvm_util_parse_status_sms_message (ios_status_sms_4, sms_msg, "ios"); g_assert_cmpint (sms_msg->provision_status, ==, VVM_PROVISION_STATUS_NEW); g_assert_cmpstr (sms_msg->mailbox_hostname, ==, "prodvvm.vodafone.com.au"); g_assert_cmpstr (sms_msg->mailbox_port, ==, "993"); g_assert_cmpstr (sms_msg->mailbox_username, ==, "username"); g_assert_cmpstr (sms_msg->mailbox_password, ==, "pass"); vvm_util_delete_status_message (sms_msg); sms_msg = g_try_new0 (struct sms_control_message, 1); g_assert_nonnull (sms_msg); g_assert_cmpint (vvm_util_parse_sms_message_type (ios_status_sms_5, "//vvm", "ios"), ==, SMS_MESSAGE_STATUS); vvm_util_parse_status_sms_message (ios_status_sms_5, sms_msg, "ios"); g_assert_cmpint (sms_msg->provision_status, ==, VVM_PROVISION_STATUS_NEW); g_assert_cmpstr (sms_msg->mailbox_hostname, ==, "vvm.mobile.att.net"); g_assert_cmpstr (sms_msg->mailbox_port, ==, "143"); g_assert_cmpstr (sms_msg->mailbox_username, ==, "2015550123"); g_assert_cmpstr (sms_msg->mailbox_password, ==, "1234567"); vvm_util_delete_status_message (sms_msg); sms_msg = g_try_new0 (struct sms_control_message, 1); g_assert_nonnull (sms_msg); g_assert_cmpint (vvm_util_parse_sms_message_type (ios_sync_sms_0, "//VVM", "ios"), ==, SMS_MESSAGE_SYNC); vvm_util_parse_sync_sms_message (ios_sync_sms_0, sms_msg, "ios"); //Mailbox Update g_assert_cmpint (sms_msg->sync_status_reason, ==, SYNC_SMS_MAILBOX_UPDATE); vvm_util_delete_status_message (sms_msg); sms_msg = g_try_new0 (struct sms_control_message, 1); g_assert_nonnull (sms_msg); g_assert_cmpint (vvm_util_parse_sms_message_type (ios_sync_sms_1, "//VVM", "ios"), ==, SMS_MESSAGE_SYNC); vvm_util_parse_sync_sms_message (ios_sync_sms_1, sms_msg, "ios"); //Mailbox Update g_assert_cmpint (sms_msg->sync_status_reason, ==, SYNC_SMS_MAILBOX_UPDATE); vvm_util_delete_status_message (sms_msg); } int main (int argc, char **argv) { g_test_init (&argc, &argv, NULL); g_test_add_data_func ("/vvmutil/SMS Test", NULL, test_sms); return g_test_run (); } vvmd-0.18/unit/test-vvm3.c000066400000000000000000000136421456374475000154360ustar00rootroot00000000000000/* * * Visual Voicemail Daemon * * Copyright (C) 2022, 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 "resolve.h" #include "vvmutil.h" #include "vvm3-activation.h" #define SPGURL_TO_TEST_ONE "https://spg.vzw.com/newSelfProvisioning/SelfProvisioning" #define BUTTONURL_TO_TEST_ONE "https://spg.vzw.com/newSelfProvisioning/bvvmprovision?sfo=XXXXX" static const char FIRST_REQUEST_0[] = "./unit/vvm3_activation/first request.xml"; static const char SECOND_REQUEST_0[] = "./unit/vvm3_activation/second request.html"; static const char SECOND_REQUEST_1[] = "./unit/vvm3_activation/second request 1.html"; // Valid URLs static const char url_0[] = "https://spg.vzw.com/newSelfProvisioning/SelfProvisioning"; static const char url_1[] = "https://spg.vzw.com:443/newSelfProvisioning/bvvmdecline"; static const char url_2[] = "http://spg.vzw.com/newSelfProvisioning/SelfProvisioning"; static const char url_3[] = "http://spg.vzw.com:443/newSelfProvisioning/bvvmdecline"; static const char url_4[] = "http://spg.dsfsdf.dgssfdds.vzw.com/newSelfProvisioning/SelfProvisioning"; static const char url_5[] = "http://spgrrfbnjt.ssxvcr.45td.vzw.com:443/newSelfProvisioning/bvvmdecline"; // Invalid Domain static const char url_6[] = "http://spg.not_vzw.com/newSelfProvisioning/SelfProvisioning"; static const char url_7[] = "http://spg.not_vzw.com:443/newSelfProvisioning/bvvmdecline"; // Not http:// or https:// static const char url_8[] = "ftp://spg.vzw.com:443/newSelfProvisioning/bvvmdecline"; static const char url_9[] = "spg.vzw.com:443/newSelfProvisioning/bvvmdecline"; // Valid URL, Activate URL static const char url_10[] = "https://vmg.vzw.com/VMGIMS/VMServices"; // Put .vzw.com not in domain static const char url_11[] = "http://spg.example.com/.vzw.com/newSelfProvisioning/SelfProvisioning"; static const char url_12[] = "http://spg.example.com/.vzw.com:443/newSelfProvisioning/SelfProvisioning"; // .vzw.com is a subdommain, not root domain static const char url_13[] = "https://vmg.vzw.com.example.com/VMGIMS/VMServices"; static void append_file_to_g_string (const char *file_to_copy, GString *data_to_parse) { unsigned int len; struct stat st; unsigned char *pdu; int fd; fd = open (file_to_copy, O_RDONLY); if (fd < 0) { g_printerr ("Failed to open path %s\n", file_to_copy); goto out; } if (fstat (fd, &st) < 0) { g_printerr ("Failed to stat %s\n", file_to_copy); 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", file_to_copy); close (fd); goto out; } data_to_parse = g_string_append (data_to_parse, (char *) pdu); munmap (pdu, len); close (fd); return; out: g_assert (FALSE); return; } static void test_vvm3 (gconstpointer data) { g_autofree char *buttonUrl = NULL; g_autofree char *spgUrl = NULL; GString *data_to_parse; data_to_parse = g_string_new (NULL); append_file_to_g_string (FIRST_REQUEST_0, data_to_parse); spgUrl = vvm3_xml_find_spgUrl (data_to_parse); if (g_strcmp0 (spgUrl, SPGURL_TO_TEST_ONE) != 0) g_printerr ("Captured URL id %s not the same as original %s\n", spgUrl, SPGURL_TO_TEST_ONE); g_assert (g_strcmp0 (spgUrl, SPGURL_TO_TEST_ONE) == 0); g_string_free (data_to_parse, TRUE); data_to_parse = g_string_new (NULL); append_file_to_g_string (SECOND_REQUEST_0, data_to_parse); buttonUrl = vvm3_html_find_buttonUrl (data_to_parse); if (g_strcmp0 (buttonUrl, BUTTONURL_TO_TEST_ONE) != 0) g_printerr ("Captured URL id %s not the same as original %s\n", buttonUrl, BUTTONURL_TO_TEST_ONE); g_assert (g_strcmp0 (buttonUrl, BUTTONURL_TO_TEST_ONE) == 0); g_string_free (data_to_parse, TRUE); data_to_parse = g_string_new (NULL); append_file_to_g_string (SECOND_REQUEST_1, data_to_parse); buttonUrl = vvm3_html_find_buttonUrl (data_to_parse); if (g_strcmp0 (buttonUrl, BUTTONURL_TO_TEST_ONE) != 0) g_printerr ("Captured URL id %s not the same as original %s\n", buttonUrl, BUTTONURL_TO_TEST_ONE); g_assert (g_strcmp0 (buttonUrl, BUTTONURL_TO_TEST_ONE) == 0); g_string_free (data_to_parse, TRUE); g_assert (validate_vvm3_host (SPGURL_TO_TEST_ONE)); g_assert (validate_vvm3_host (BUTTONURL_TO_TEST_ONE)); g_assert (validate_vvm3_host (url_0)); g_assert (validate_vvm3_host (url_1)); g_assert (validate_vvm3_host (url_2)); g_assert (validate_vvm3_host (url_3)); g_assert (validate_vvm3_host (url_4)); g_assert (validate_vvm3_host (url_5)); g_assert (!validate_vvm3_host (url_6)); g_assert (!validate_vvm3_host (url_7)); g_assert (!validate_vvm3_host (url_8)); g_assert (!validate_vvm3_host (url_9)); g_assert (validate_vvm3_host (url_10)); g_assert (!validate_vvm3_host (url_11)); g_assert (!validate_vvm3_host (url_12)); g_assert (!validate_vvm3_host (url_13)); return; } int main (int argc, char **argv) { g_test_init (&argc, &argv, NULL); g_test_add_data_func ("/vvmutil/vvm3 Activation Test", NULL, test_vvm3); return g_test_run (); } vvmd-0.18/unit/test-vvmparse.c000066400000000000000000000146021456374475000164030ustar00rootroot00000000000000/* * * 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; struct stat st; unsigned char *pdu; int fd; vvm_msg = g_try_new0 (struct voicemail, 1); if (vvm_msg == NULL) { g_printerr ("Failed allocate message"); return; } 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.18/unit/test-vvmutil.c000066400000000000000000000145671456374475000162600ustar00rootroot00000000000000/* * * 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" #include "store.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; struct stat st; unsigned char *pdu; int fd; vvm_msg = g_try_new0 (struct voicemail, 1); if (vvm_msg == NULL) { g_printerr ("Failed allocate message"); return; } 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 (); } vvmd-0.18/unit/vvm3_activation/000077500000000000000000000000001456374475000165305ustar00rootroot00000000000000vvmd-0.18/unit/vvm3_activation/first request.xml000066400000000000000000000005611456374475000220540ustar00rootroot000000000000002628369071234567890retrieveSPGURL200SUCCESShttps://spg.vzw.com/newSelfProvisioning/SelfProvisioning vvmd-0.18/unit/vvm3_activation/log.txt000066400000000000000000000226201456374475000200540ustar00rootroot00000000000000** (vvmd:10656): DEBUG: 18:54:11.375: ../src/main.c:on_bus_acquired() Dbus Bus acquired! ** (vvmd:10656): DEBUG: 18:54:11.375: ../src/service.c:__vvm_service_init() Starting Up VVMD Service Manager ** (vvmd:10656): DEBUG: 18:54:11.375: ../src/plugin.c:__vvm_plugin_init() ** (vvmd:10656): DEBUG: 18:54:11.375: ../plugins/modemmanager.c:modemmanager_init() Starting Modem Manager Plugin! ** (vvmd:10656): DEBUG: 18:54:11.375: ../src/service.c:vvm_service_create() service 0xffffb9ed3e50 ** (vvmd:10656): DEBUG: 18:54:11.375: ../src/service.c:vvm_service_set_identity() service 0xffffb9ed3e50 identity modemmanager ** (vvmd:10656): DEBUG: 18:54:11.391: ../src/plugin.c:add_plugin() Plugin modemmanager loaded ** (vvmd:10656): DEBUG: 18:54:11.393: ../src/main.c:on_name_acquired() Dbus name acquired! ** (vvmd:10656): DEBUG: 18:54:11.398: ../plugins/modemmanager.c:mm_appeared_cb() Modem Manager appeared ** (vvmd:10656): DEBUG: 18:54:11.420: ../plugins/modemmanager.c:vvmd_mm_state() VVMD_MM_STATE_MANAGER_FOUND ** (vvmd:10656): DEBUG: 18:54:11.420: ../plugins/modemmanager.c:cb_mm_manager_new() ModemManager found: :1.6 ** (vvmd:10656): DEBUG: 18:54:11.420: ../plugins/modemmanager.c:vvmd_mm_add_object() Not checking for a default Modem ** (vvmd:10656): DEBUG: 18:54:11.420: ../plugins/modemmanager.c:vvmd_mm_add_object() Added device at: /org/freedesktop/ModemManager1/Modem/1 ** (vvmd:10656): DEBUG: 18:54:11.420: ../plugins/modemmanager.c:vvmd_mm_init_modem() vvmd_mm_init_modem ** (vvmd:10656): DEBUG: 18:54:11.420: ../plugins/modemmanager.c:vvmd_mm_state() VVMD_MM_STATE_MODEM_FOUND ** (vvmd:10656): DEBUG: 18:54:11.420: ../plugins/modemmanager.c:vvmd_modem_available() Registering Modem Manager VVM Service ** (vvmd:10656): DEBUG: 18:54:11.420: ../src/service.c:vvm_service_register() service 0xffffb9ed3e50 ** (vvmd:10656): DEBUG: 18:54:11.420: ../src/service.c:emit_service_added() Service Added 0xffffb9ed3e50 ** (vvmd:10656): DEBUG: 18:54:11.427: ../src/service.c:vvm_service_set_country_code() Setting Service Country Code... ** (vvmd:10656): DEBUG: 18:54:11.427: ../plugins/modemmanager.c:vvmd_modem_available() VVM carrier settings already set ** (vvmd:10656): DEBUG: 18:54:11.427: ../src/service.c:vvm_service_set_own_number() Setting own Number... ** (vvmd:10656): DEBUG: 18:54:11.485: ../plugins/modemmanager.c:vvmd_connect_to_sms_wap() Watching for new SMS WAPs ** (vvmd:10656): DEBUG: 18:54:11.485: ../plugins/modemmanager.c:vvmd_modem_available() Checking status of carrier subscription ** (vvmd:10656): DEBUG: 18:54:11.485: ../plugins/modemmanager.c:vvmd_check_subscription_status() Checking your carrier's VVM service ** (vvmd:10656): DEBUG: 18:54:11.485: ../plugins/modemmanager.c:vvmd_mm_get_modem_state() MM_MODEM_GOOD_STATE: 11 ** (vvmd:10656): DEBUG: 18:54:11.485: ../plugins/modemmanager.c:vvmd_mm_state() VVMD_MM_STATE_READY ** (vvmd:10656): DEBUG: 18:54:11.485: ../plugins/modemmanager.c:vvmd_mm_state() Setting Bearer Handler ** (vvmd:10656): DEBUG: 18:54:11.486: ../plugins/modemmanager.c:vvmd_get_all_sms() Searching for any new SMS WAPs... ** (vvmd:10656): DEBUG: 18:54:12.486: ../plugins/modemmanager.c:vvmd_get_all_sms() Adding timeout to vvmd_get_all_sms() ** (vvmd:10656): DEBUG: 18:54:12.497: ../src/service.c:vvm_service_set_interface() Modem interface set to wwan0 ** (vvmd:10656): DEBUG: 18:54:12.497: ../plugins/modemmanager.c:vvmd_check_subscription_status() Checking your carrier's VVM service ** (vvmd:10656): DEBUG: 18:54:13.498: ../src/vvmutil.c:vvm_util_create_status_sms() VVM type: vvm3 ** (vvmd:10656): DEBUG: 18:54:13.498: ../plugins/modemmanager.c:vvmd_check_subscription_status() Message: STATUS ** (vvmd:10656): DEBUG: 18:54:13.498: ../plugins/modemmanager.c:vvmd_mm_create_sms() Creating new SMS ** (vvmd:10656): DEBUG: 18:54:13.551: ../plugins/modemmanager.c:vvmd_mm_create_sms() Successfully created new SMS: /org/freedesktop/ModemManager1/SMS/9 ** (vvmd:10656): DEBUG: 18:54:13.552: Couldn't get SMS list - error: unknown ** (vvmd:10656): DEBUG: 18:54:13.566: ../plugins/modemmanager.c:vvmd_check_pdu_type() This is not an SMS being received, do not care ** (vvmd:10656): DEBUG: 18:54:13.815: ../plugins/modemmanager.c:cb_sms_send_finish() Successfully sent SMS: /org/freedesktop/ModemManager1/SMS/9 ** (vvmd:10656): DEBUG: 18:54:13.850: Message delete finish ** (vvmd:10656): DEBUG: 18:54:14.352: ../src/vvmutil.c:vvm_util_parse_otmp_sms_message_type() This is a status SMS message ** (vvmd:10656): DEBUG: 18:54:14.352: ../plugins/modemmanager.c:vvmd_process_sms() Processing VVM status message. ** (vvmd:10656): DEBUG: 18:54:14.352: ../src/vvmutil.c:vvm_util_parse_status_sms_message() Parsing VVM status message. ** (vvmd:10656): DEBUG: 18:54:14.352: ../src/vvmutil.c:vvm_util_parse_status_otmp_sms_message() Return Code: Mailbox unknown. ** (vvmd:10656): DEBUG: 18:54:14.353: ../src/vvmutil.c:vvm_util_parse_status_otmp_sms_message() Provisioning Status: Unknown. ** (vvmd:10656): DEBUG: 18:54:14.353: ../plugins/modemmanager.c:vvm_process_status_message() Previous Provisioning Status to 4 ** (vvmd:10656): DEBUG: 18:54:14.353: ../plugins/modemmanager.c:vvm_process_status_message() Setting Provisioning Status to 4 ** (vvmd:10656): DEBUG: 18:54:14.369: The XML request that was transmitted was 2628369071234567890retrieveSPGURLDevicePixel 5 ** (vvmd:10656): DEBUG: 18:54:15.005: The response to the request was: 2628369071234567890retrieveSPGURL200SUCCESShttps://spg.vzw.com/newSelfProvisioning/SelfProvisioning ** (vvmd:10656): DEBUG: 18:54:15.006: URL grabbed! ** (vvmd:10656): DEBUG: 18:54:15.006: Captured URL is https://spg.vzw.com/newSelfProvisioning/SelfProvisioning ** (vvmd:10656): DEBUG: 18:54:15.006: The second request is being sent with the following parameters: VZW_MDN=1234567890&VZW_SERVICE=BVVM&DEVICE_MODEL=DROID_4G&APP_TOKEN=q8e3t5u2o1&SPG_LANGUAGE_PARAM=ENGLISH ** (vvmd:10656): DEBUG: 18:54:16.204: The response to the second request was: bvvmsignup

Basic Visual Voice Mail

Included in your plan, Basic Visual Voice Mail lets you listen to, manage and delete your messages directly from your phone, in any order, without having to listen to instructions.

If you’re switching to Basic Visual Voice Mail from another voice mail service or ending the Premium Free Trial*, you may have more than 20 messages in your inbox. You’ll have to save or delete some messages until you have fewer than 20 before new messages can be left for you.

* One Talk customers are not eligible for the Free Trial

Subscribe to Basic Visual Voice Mail


Cancel
** (vvmd:10656): DEBUG: 18:54:16.251: Couldn't delete SMS - error: GDBus.Error:org.freedesktop.ModemManager1.Error.Core.NotFound: No SMS found with path '/org/freedesktop/ModemManager1/SMS/10' ** (vvmd:10656): DEBUG: 18:54:17.452: ../plugins/modemmanager.c:vvmd_get_all_sms_timeout() Removing timeout to vvmd_get_all_sms() ** (vvmd:10656): DEBUG: 18:54:21.487: ../src/plugin.c:__vvm_plugin_cleanup() ** (vvmd:10656): DEBUG: 18:54:21.488: ../plugins/modemmanager.c:vvmd_disconnect_from_sms_wap() Stopping watching SMS WAPs ** (vvmd:10656): DEBUG: 18:54:21.489: ../plugins/modemmanager.c:vvmd_mm_state() Modem vanished, Disabling plugin ** (vvmd:10656): DEBUG: 18:54:21.489: ../plugins/modemmanager.c:vvmd_mm_state() VVMD_MM_STATE_NO_MODEM ** (vvmd:10656): DEBUG: 18:54:21.491: ../plugins/modemmanager.c:vvmd_mm_state() VVMD_MM_STATE_NO_MANAGER ** (vvmd:10656): DEBUG: 18:54:21.491: ../src/service.c:vvm_service_unregister() service 0xffffb9ed3e50 ** (vvmd:10656): DEBUG: 18:54:21.511: ../src/service.c:__vvm_service_cleanup() Cleaning Up VVMD Service Manager vvmd-0.18/unit/vvm3_activation/second request 1.html000066400000000000000000000041731456374475000224700ustar00rootroot00000000000000 bvvmsignup

Basic Visual Voice Mail

Included in your plan, Basic Visual Voice Mail lets you listen to, manage and delete your messages directly from your phone, in any order, without having to listen to instructions.

If you’re switching to Basic Visual Voice Mail from another voice mail service or ending the Premium Free Trial*, you may have more than 20 messages in your inbox. You’ll have to save or delete some messages until you have fewer than 20 before new messages can be left for you.

* One Talk customers are not eligible for the Free Trial

Subscribe to Basic Visual Voicemail


Cancel
vvmd-0.18/unit/vvm3_activation/second request.html000066400000000000000000000041741456374475000223500ustar00rootroot00000000000000 bvvmsignup

Basic Visual Voice Mail

Included in your plan, Basic Visual Voice Mail lets you listen to, manage and delete your messages directly from your phone, in any order, without having to listen to instructions.

If you’re switching to Basic Visual Voice Mail from another voice mail service or ending the Premium Free Trial*, you may have more than 20 messages in your inbox. You’ll have to save or delete some messages until you have fewer than 20 before new messages can be left for you.

* One Talk customers are not eligible for the Free Trial

Subscribe to Basic Visual Voice Mail


Cancel