pax_global_header00006660000000000000000000000064121602455240014513gustar00rootroot0000000000000052 comment=c8cd60621d5996c5660cf897e97675e424a5b0a2 mmsd-0.1/000077500000000000000000000000001216024552400123135ustar00rootroot00000000000000mmsd-0.1/.gitignore000066400000000000000000000005131216024552400143020ustar00rootroot00000000000000*.o *.lo *.la *.so .deps .libs .dirstamp Makefile Makefile.in aclocal.m4 config.guess config.h config.h.in config.log config.status config.sub configure depcomp compile install-sh libtool ltmain.sh missing stamp-h1 autom4te.cache src/builtin.h src/mmsd unit/test-wsputil unit/test-mmsutil tools/decode-mms tools/create-hex-array mmsd-0.1/.mailmap000066400000000000000000000000761216024552400137370ustar00rootroot00000000000000Syam Sidhardhan mmsd-0.1/AUTHORS000066400000000000000000000004611216024552400133640ustar00rootroot00000000000000Marcel Holtmann Denis Kenzior Sébastien Bianti Christophe Guiraud Regis Merlino Ronald Tessier Jens Rehsack mmsd-0.1/COPYING000066400000000000000000000431261216024552400133540ustar00rootroot00000000000000 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. mmsd-0.1/ChangeLog000066400000000000000000000000001216024552400140530ustar00rootroot00000000000000mmsd-0.1/HACKING000066400000000000000000000057411216024552400133110ustar00rootroot00000000000000Hacking on Multimedia Messaging Service *************************************** Build tools requirements ======================== When building and testing directly from the repository it is important to have at least automake version 1.10 or later installed. All modern distributions should default to the latest version, but it seems that Debian's default is still an earlier version: Check version # dpkg -l '*automake*' Install new version # apt-get install automake1.10 # update-alternatives --config automake Working with the source code repository ======================================= The repository contains two extra scripts that accomplish the bootstrap process. One is called "bootstrap" which is the basic scripts that uses the autotools scripts to create the needed files for building and installing. It makes sure to call the right programs depending on the usage of shared or static libraries or translations etc. The second program is called "bootstrap-configure". This program will make sure to properly clean the repository, call the "bootstrap" script and then call configure with proper settings for development. It will use the best options and pass them over to configure. These options normally include the enabling the maintainer mode and the debugging features. So while in a normal source project the call "./configure ..." is used to configure the project with its settings like prefix and extra options. In case of bare repositories call "./bootstrap-configure" and it will bootstrap the repository and calls configure with all the correct options to make development easier. In case of preparing for a release with "make distcheck", don't use bootstrap-configure since it could export development specific settings. So the normal steps to checkout, build and install such a repository is like this: Checkout repository # git clone git://git.kernel.org/pub/scm/network/ofono/mmsd.git # cd mmsd Configure and build # ./bootstrap-configure # make Check installation # make install DESTDIR=$PWD/x # find x # rm -rf x Check distribution # make distcheck Final installation # sudo make install Remove autogenerated files # make maintainer-clean Running from within the source code repository ============================================== When using "./configure --enable-maintainer-mode" the automake scripts will use the plugins directly from within the repository. This removes the need to use "make install" when testing "mmsd". The "bootstrap-configure" automatically includes this option. Run daemon in foreground with debugging # ./src/mmsd -n -d 'src/*' For production installations or distribution packaging it is important that the "--enable-maintainer-mode" option is NOT used. The debugging option -d takes an argument. This argument can be a comma separated list of file names like 'src/main.c,src/manager.c' to enable debugs in these files. Simple glob style pattern matching is supported in this list. mmsd-0.1/INSTALL000066400000000000000000000224061216024552400133500ustar00rootroot00000000000000Installation Instructions ************************* Copyright (C) 1994, 1995, 1996, 1999, 2000, 2001, 2002, 2004, 2005 Free Software Foundation, Inc. This file is free documentation; the Free Software Foundation gives unlimited permission to copy, distribute and modify it. Basic Installation ================== These are generic installation instructions. The `configure' shell script attempts to guess correct values for various system-dependent variables used during compilation. It uses those values to create a `Makefile' in each directory of the package. It may also create one or more `.h' files containing system-dependent definitions. Finally, it creates a shell script `config.status' that you can run in the future to recreate the current configuration, and a file `config.log' containing compiler output (useful mainly for debugging `configure'). It can also use an optional file (typically called `config.cache' and enabled with `--cache-file=config.cache' or simply `-C') that saves the results of its tests to speed up reconfiguring. (Caching is disabled by default to prevent problems with accidental use of stale cache files.) If you need to do unusual things to compile the package, please try to figure out how `configure' could check whether to do them, and mail diffs or instructions to the address given in the `README' so they can be considered for the next release. If you are using the cache, and at some point `config.cache' contains results you don't want to keep, you may remove or edit it. The file `configure.ac' (or `configure.in') is used to create `configure' by a program called `autoconf'. You only need `configure.ac' if you want to change it or regenerate `configure' using a newer version of `autoconf'. The simplest way to compile this package is: 1. `cd' to the directory containing the package's source code and type `./configure' to configure the package for your system. If you're using `csh' on an old version of System V, you might need to type `sh ./configure' instead to prevent `csh' from trying to execute `configure' itself. Running `configure' takes awhile. While running, it prints some messages telling which features it is checking for. 2. Type `make' to compile the package. 3. Optionally, type `make check' to run any self-tests that come with the package. 4. Type `make install' to install the programs and any data files and documentation. 5. You can remove the program binaries and object files from the source code directory by typing `make clean'. To also remove the files that `configure' created (so you can compile the package for a different kind of computer), type `make distclean'. There is also a `make maintainer-clean' target, but that is intended mainly for the package's developers. If you use it, you may have to get all sorts of other programs in order to regenerate files that came with the distribution. Compilers and Options ===================== Some systems require unusual options for compilation or linking that the `configure' script does not know about. Run `./configure --help' for details on some of the pertinent environment variables. You can give `configure' initial values for configuration parameters by setting variables in the command line or in the environment. Here is an example: ./configure CC=c89 CFLAGS=-O2 LIBS=-lposix *Note Defining Variables::, for more details. Compiling For Multiple Architectures ==================================== You can compile the package for more than one kind of computer at the same time, by placing the object files for each architecture in their own directory. To do this, you must use a version of `make' that supports the `VPATH' variable, such as GNU `make'. `cd' to the directory where you want the object files and executables to go and run the `configure' script. `configure' automatically checks for the source code in the directory that `configure' is in and in `..'. If you have to use a `make' that does not support the `VPATH' variable, you have to compile the package for one architecture at a time in the source code directory. After you have installed the package for one architecture, use `make distclean' before reconfiguring for another architecture. Installation Names ================== By default, `make install' will install the package's files in `/usr/local/bin', `/usr/local/man', etc. You can specify an installation prefix other than `/usr/local' by giving `configure' the option `--prefix=PREFIX'. You can specify separate installation prefixes for architecture-specific files and architecture-independent files. If you give `configure' the option `--exec-prefix=PREFIX', the package will use PREFIX as the prefix for installing programs and libraries. Documentation and other data files will still use the regular prefix. In addition, if you use an unusual directory layout you can give options like `--bindir=DIR' to specify different values for particular kinds of files. Run `configure --help' for a list of the directories you can set and what kinds of files go in them. If the package supports it, you can cause programs to be installed with an extra prefix or suffix on their names by giving `configure' the option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'. Optional Features ================= Some packages pay attention to `--enable-FEATURE' options to `configure', where FEATURE indicates an optional part of the package. They may also pay attention to `--with-PACKAGE' options, where PACKAGE is something like `gnu-as' or `x' (for the X Window System). The `README' should mention any `--enable-' and `--with-' options that the package recognizes. For packages that use the X Window System, `configure' can usually find the X include and library files automatically, but if it doesn't, you can use the `configure' options `--x-includes=DIR' and `--x-libraries=DIR' to specify their locations. Specifying the System Type ========================== There may be some features `configure' cannot figure out automatically, but needs to determine by the type of machine the package will run on. Usually, assuming the package is built to be run on the _same_ architectures, `configure' can figure that out, but if it prints a message saying it cannot guess the machine type, give it the `--build=TYPE' option. TYPE can either be a short name for the system type, such as `sun4', or a canonical name which has the form: CPU-COMPANY-SYSTEM where SYSTEM can have one of these forms: OS KERNEL-OS See the file `config.sub' for the possible values of each field. If `config.sub' isn't included in this package, then this package doesn't need to know the machine type. If you are _building_ compiler tools for cross-compiling, you should use the `--target=TYPE' option to select the type of system they will produce code for. If you want to _use_ a cross compiler, that generates code for a platform different from the build platform, you should specify the "host" platform (i.e., that on which the generated programs will eventually be run) with `--host=TYPE'. Sharing Defaults ================ If you want to set default values for `configure' scripts to share, you can create a site shell script called `config.site' that gives default values for variables like `CC', `cache_file', and `prefix'. `configure' looks for `PREFIX/share/config.site' if it exists, then `PREFIX/etc/config.site' if it exists. Or, you can set the `CONFIG_SITE' environment variable to the location of the site script. A warning: not all `configure' scripts look for a site script. Defining Variables ================== Variables not defined in a site shell script can be set in the environment passed to `configure'. However, some packages may run configure again during the build, and the customized values of these variables may be lost. In order to avoid this problem, you should set them in the `configure' command line, using `VAR=value'. For example: ./configure CC=/usr/local2/bin/gcc causes the specified `gcc' to be used as the C compiler (unless it is overridden in the site shell script). Here is a another example: /bin/bash ./configure CONFIG_SHELL=/bin/bash Here the `CONFIG_SHELL=/bin/bash' operand causes subsequent configuration-related scripts to be executed by `/bin/bash'. `configure' Invocation ====================== `configure' recognizes the following options to control how it operates. `--help' `-h' Print a summary of the options to `configure', and exit. `--version' `-V' Print the version of Autoconf used to generate the `configure' script, and exit. `--cache-file=FILE' Enable the cache: use and save the results of the tests in FILE, traditionally `config.cache'. FILE defaults to `/dev/null' to disable caching. `--config-cache' `-C' Alias for `--cache-file=config.cache'. `--quiet' `--silent' `-q' Do not print messages saying which checks are being made. To suppress all normal output, redirect it to `/dev/null' (any error messages will still be shown). `--srcdir=DIR' Look for the package's source code in directory DIR. Usually `configure' can determine that directory automatically. `configure' also accepts some other, not widely useful, options. Run `configure --help' for more details. mmsd-0.1/Makefile.am000066400000000000000000000057641216024552400143630ustar00rootroot00000000000000 AM_MAKEFLAGS = --no-print-directory gdbus_sources = gdbus/gdbus.h gdbus/mainloop.c gdbus/watch.c \ gdbus/object.c gdbus/client.c gdbus/polkit.c gweb_sources = gweb/gweb.h gweb/gweb.c gweb/gresolv.h gweb/gresolv.c \ gweb/giognutls.h gweb/gionotls.c builtin_modules = builtin_sources = builtin_cflags = builtin_libadd = builtin_modules += ofono builtin_sources += plugins/ofono.c libexec_PROGRAMS = src/mmsd src_mmsd_SOURCES = $(gdbus_sources) $(gweb_sources) $(builtin_sources) \ src/main.c src/mms.h src/types.h \ src/log.h src/log.c src/plugin.h src/plugin.c \ src/dbus.h src/dbus.c src/service.h src/service.c \ src/push.h src/push.c src/store.h src/store.c \ src/wsputil.h src/wsputil.c src/mmsutil.h src/mmsutil.c src_mmsd_LDADD = $(builtin_libadd) @GLIB_LIBS@ @DBUS_LIBS@ -lresolv -ldl src_mmsd_LDFLAGS = -Wl,--export-dynamic nodist_src_mmsd_SOURCES = src/builtin.h plugindir = $(libdir)/mms/plugins pushconfdir = $(libdir)/mms/push.d pushconf_DATA = src/plugin.$(OBJEXT): src/builtin.h src/builtin.h: src/genbuiltin $(builtin_sources) $(AM_V_GEN)$(srcdir)/src/genbuiltin $(builtin_modules) > $@ AM_CFLAGS = @GLIB_CFLAGS@ @DBUS_CFLAGS@ $(builtin_cflags) \ -DMMS_PLUGIN_BUILTIN \ -DPLUGINDIR=\""$(plugindir)"\" \ -DPUSHCONFDIR=\""$(pushconfdir)"\" INCLUDES = -I$(builddir)/src -I$(srcdir)/src -I$(srcdir)/gdbus CLEANFILES = src/builtin.h test_scripts = test/monitor-mms test/get-services test/send-message \ test/delete-message test/mark-message-read test/get-messages \ test/get-conversation test/set-use-delivery-reports doc_files = doc/architecture.txt doc/consumer.txt doc/manager-api.txt \ doc/service-api.txt doc/message-api.txt \ doc/storage.txt unit_test_data_files = unit/ni-mms-1-3-con-271.mms \ unit/ni-mms-1-3-con-272.mms \ unit/rc-mms-1-3-con-210.mms \ unit/rc-mms-1-3-con-271.mms \ unit/rc-mms-1-3-con-212.mms \ unit/rc-mms-1-3-con-213.mms \ unit/rc-mms-1-3-con-214.mms \ unit/rc-mms-1-3-con-216.mms \ unit/rc-mms-1-3-con-220.mms \ unit/rc-mms-1-3-con-224.mms \ unit/rc-mms-1-3-con-228.mms \ unit/rc-mms-1-3-con-211.mms \ unit/rc-mms-1-3-con-282.mms \ unit/rc-mms-1-3-con-281.mms \ unit/sr-mms-mixed.mms \ unit/sr-mms-related-multi-to.mms EXTRA_DIST = src/genbuiltin $(test_scripts) $(doc_files) \ $(unit_test_data_files) noinst_PROGRAMS = tools/decode-mms tools/create-hex-array \ unit/test-wsputil unit/test-mmsutil tools_decode_mms_SOURCES = src/wsputil.h src/wsputil.c tools/decode-mms.c tools_decode_mms_LDADD = @GLIB_LIBS@ unit_test_wsputil_SOURCES = unit/test-wsputil.c src/wsputil.c src/wsputil.h unit_test_wsputil_LDADD = @GLIB_LIBS@ unit_test_mmsutil_SOURCES = unit/test-mmsutil.c src/mmsutil.c src/mmsutil.h \ src/wsputil.c src/wsputil.h unit_test_mmsutil_LDADD = @GLIB_LIBS@ TESTS = unit/test-wsputil unit/test-mmsutil MAINTAINERCLEANFILES = Makefile.in \ aclocal.m4 configure config.h.in config.sub config.guess \ ltmain.sh depcomp compile missing install-sh mkinstalldirs mmsd-0.1/NEWS000066400000000000000000000000001216024552400130000ustar00rootroot00000000000000mmsd-0.1/README000066400000000000000000000007361216024552400132010ustar00rootroot00000000000000Multimedia Messaging Service **************************** Copyright (C) 2010-2011 Intel Corporation. All rights reserved. Compilation and installation ============================ In order to compile proxy daemon you need following software packages: - GCC compiler - D-Bus library - GLib library To configure run: ./configure --prefix=/usr Configure automatically searches for all required components and packages. To compile and install run: make && make install mmsd-0.1/TODO000066400000000000000000000064041216024552400130070ustar00rootroot00000000000000Background ========== - Priority scale: High, Medium and Low - Complexity scale: C1, C2, C4 and C8. The complexity scale is exponential, with complexity 1 being the lowest complexity. Complexity is a function of both task 'complexity' and task 'scope'. The general rule of thumb is that a complexity 1 task should take 1-2 weeks for a person very familiar with mmsd codebase. Higher complexity tasks require more time and have higher uncertainty. Higher complexity tasks should be refined into several lower complexity tasks once the task is better understood. MMS Delivery Report =================== - Add delivery report request when sending message. Modify SendMessage D-Bus API to add delivery report request flag. Update message structure to manage this flag. Modify MMS M-Send.Req PDU encoder to encode delivery report request. Modify test script related to message sending to support new delivery flag. Priority: Medium Complexity: C2 Owner: Ronald Tessier - Update message status stored in its associated Meta file. Add a group [delivery_status] in the message status to handle delivery reports, this group contains an entry for each message recipients. Each recipient entry will be updated when the corresponding report will be received and then remove the temporary stored delivery report. Upon M-Send.Conf reception, store the received Message_ID to match delivery reports (needed only if delivery report was requested). Priority: Medium Complexity: C2 Depends: Owner: Sébastien Bianti - Add MMS M-Delivery.ind PDU decoding support. Modify mms_service_push_notify() to handle MMS Delivery Report WAP Push notification and temporary store the M-Delivery.ind PDU. Decode M-Delivery.ind PDU. Add MMS M-Delivery.ind PDU sample PDU to unit test. Priority: Medium Complexity: C2 Depends: Owner: Ronald Tessier - Update message Meta file. Upon MMS M-Delivery.ind PDU reception, update delivery_status recipient entry with the received status (this is described in doc/storage.txt). Priority: Medium Complexity: C2 Depends: Owner: Sébastien Bianti D-Bus API ========= - Add the "org.ofono.mms.Service" interface "DeleteMessages" method implementation. The D-Bus Message API documentation is available under /doc/service-api.txt. Priority: Medium Complexity: C2 Owner: Sébastien Bianti - Add the "org.ofono.mms.Service" interface "DeleteConversation" method implementation. The D-Bus Message API documentation is available under /doc/service-api.txt. Priority: Medium Complexity: C2 Owner: Ronald Tessier Miscellaneous ============= - Add OMA MMS M-Retrieve.conf decoding mandatory test cases to the unit tests. PDU for these tests cases should be added as data files to the unit tests. The test PDUs should use the media content sample provided by the OMA. Applicable test cases are available in "OMA Enabler Test Specification for (Conformance) for MMS" [OMA-ETS-MMS_CON-V1_3-20101015-C]. Complexity: C1 Priority: Low Owner: Christophe Guiraud mmsd-0.1/acinclude.m4000066400000000000000000000013271216024552400145070ustar00rootroot00000000000000AC_DEFUN([AC_PROG_CC_PIE], [ AC_CACHE_CHECK([whether ${CC-cc} accepts -fPIE], ac_cv_prog_cc_pie, [ echo 'void f(){}' > conftest.c if test -z "`${CC-cc} -fPIE -pie -c conftest.c 2>&1`"; then ac_cv_prog_cc_pie=yes else ac_cv_prog_cc_pie=no fi rm -rf conftest* ]) ]) AC_DEFUN([COMPILER_FLAGS], [ if (test "${CFLAGS}" = ""); then CFLAGS="-Wall -O2 -D_FORTIFY_SOURCE=2" fi if (test "$USE_MAINTAINER_MODE" = "yes"); then CFLAGS+=" -Werror -Wextra" CFLAGS+=" -Wno-unused-parameter" CFLAGS+=" -Wno-missing-field-initializers" CFLAGS+=" -Wdeclaration-after-statement" CFLAGS+=" -Wmissing-declarations" CFLAGS+=" -Wredundant-decls" CFLAGS+=" -Wcast-align" CFLAGS+=" -DG_DISABLE_DEPRECATED" fi ]) mmsd-0.1/bootstrap000077500000000000000000000002121216024552400142510ustar00rootroot00000000000000#!/bin/sh aclocal && \ autoheader && \ libtoolize --automake --copy --force && \ automake --add-missing --copy && \ autoconf mmsd-0.1/bootstrap-configure000077500000000000000000000002461216024552400162370ustar00rootroot00000000000000#!/bin/sh if [ -f config.status ]; then make maintainer-clean fi ./bootstrap && \ ./configure --enable-maintainer-mode \ --enable-debug \ --prefix=/usr $* mmsd-0.1/configure.ac000066400000000000000000000035241216024552400146050ustar00rootroot00000000000000AC_PREREQ(2.60) AC_INIT(mmsd, 0.0) AM_INIT_AUTOMAKE([foreign subdir-objects color-tests]) AM_CONFIG_HEADER(config.h) m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])]) AM_MAINTAINER_MODE AC_PREFIX_DEFAULT(/usr/local) if (test "${libdir}" = '${exec_prefix}/lib'); then libdir="${prefix}/lib" fi plugindir="${libdir}/mms/plugins" pushconfdir="${libdir}/mms/push.d" PKG_PROG_PKG_CONFIG COMPILER_FLAGS AC_LANG_C AC_PROG_CC AC_PROG_CC_PIE AC_PROG_INSTALL m4_define([_LT_AC_TAGCONFIG], []) m4_ifdef([AC_LIBTOOL_TAGS], [AC_LIBTOOL_TAGS([])]) AC_DISABLE_STATIC AC_PROG_LIBTOOL AC_ARG_ENABLE(optimization, AC_HELP_STRING([--disable-optimization], [disable code optimization through compiler]), [ if (test "${enableval}" = "no"); then CFLAGS="$CFLAGS -O0" fi ]) AC_ARG_ENABLE(debug, AC_HELP_STRING([--enable-debug], [enable compiling with debugging information]), [ if (test "${enableval}" = "yes" && test "${ac_cv_prog_cc_g}" = "yes"); then CFLAGS="$CFLAGS -g" fi ]) AC_ARG_ENABLE(pie, AC_HELP_STRING([--enable-pie], [enable position independent executables flag]), [ if (test "${enableval}" = "yes" && test "${ac_cv_prog_cc_pie}" = "yes"); then CFLAGS="$CFLAGS -fPIE" LDFLAGS="$LDFLAGS -pie" fi ]) AC_CHECK_HEADERS(resolv.h, dummy=yes, AC_MSG_ERROR(resolver header files are required)) AC_CHECK_LIB(resolv, ns_initparse, dummy=yes, [ AC_CHECK_LIB(resolv, __ns_initparse, dummy=yes, AC_MSG_ERROR(resolver library support is required)) ]) AC_CHECK_LIB(dl, dlopen, dummy=yes, AC_MSG_ERROR(dynamic linking loader is required)) PKG_CHECK_MODULES(GLIB, glib-2.0 >= 2.16, dummy=yes, AC_MSG_ERROR(GLib >= 2.16 is required)) AC_SUBST(GLIB_CFLAGS) AC_SUBST(GLIB_LIBS) PKG_CHECK_MODULES(DBUS, dbus-1 >= 1.2, dummy=yes, AC_MSG_ERROR(D-Bus >= 1.2 is required)) AC_SUBST(DBUS_CFLAGS) AC_SUBST(DBUS_LIBS) AC_OUTPUT(Makefile) mmsd-0.1/doc/000077500000000000000000000000001216024552400130605ustar00rootroot00000000000000mmsd-0.1/doc/architecture.txt000066400000000000000000000027001216024552400163020ustar00rootroot00000000000000Architecture overview ===================== The MMS daemon implements the Multimedia Message Service with integration into the oFono telephony stack. +-----------------------+ +-----------+ | Message application |<---------->| Tracker | +-----------------------+ +-----------+ | ^ | Session D-Bus | D-Bus V | +-----------------------+------------------------+ | | | | MMS daemon | Tracker plugin | | | | | +------------------------+ | | | | | oFono plugin | | | | +-----------------------+------------------------+ | | System D-Bus V +------------------------------------------------+ | | | oFono telephony stack | | | +------------------------------------------------+ mmsd-0.1/doc/consumer.txt000066400000000000000000000117151216024552400154610ustar00rootroot00000000000000mmsd consumers design ********************* mmsd handles all the SMS WAP Push notifications delivered by oFono. In addition to the processing of the MMS WAP Push notifications, it also provides a dispatch mechanism for any other kind of WAP Push notifications to the registered consumer. Consumer basic ============== In order to be identified by mmsd as a specific WAP Push notification consumer, a program does not need to register in a sense of having to be running and register. It just puts a D-Bus session activation configuration file in place and then lets mmsd knows what's the D-Bus service name to call out to. mmsd just has to send a D-Bus message to a target daemon and it will activate if configured that way. Each WAP Push notification consumer has to install (in /usr/lib/mms/push.d/ directory) their configuration file on what types of message they want to consume and how they want to consume them. In addition it needs to install the appropriate D-Bus session activation configuration file matching the same service name (in /usr/share/dbus-1/services/). Consumer configuration file =========================== The content of the configuration file is the following, it describes the D-Bus interface to be provided by a consumer (acting as a service provider): [OMA-DM] # simple match on the content type MatchContentType=application/vnd.syncml.notification # match on WAP application id MatchApplicationId=x-wap-application:syncml.dm # D-Bus session or system bus TargetBus=session # D-Bus service name TargetService=com.meego.omadm # D-Bus target path TargetPath=/com/meego/omadm You can have as many of group (i.e. [OMA-DM]) you want in one file or multiple files. They will all be read on mmsd startup and can then be progressed. This file must be located in /usr/lib/mms/push.d/ directory, its name extension must be ".conf". TargetPath, TargetService keys are mandatory. TargetBus key is optional, if it is present the currently only supported value is "session", if it is not present then the default value "session" will be used. MatchApplicationId key is optional, MatchContentType key is mandatory. If only the MatchContentType key is present without the MatchApplicationId key, then the consumer will be notified of all the received WAP push messages having the same content type. If the MatchApplicationId key is present, then the consumer will be notified of all the received WAP push messages having both the same content type AND the same application Id. The D-Bus target method is named Notify, see the [Notify API] section below for its signature details. D-Bus activation service file ============================= D-Bus needs a ".service" file to know what program to launch for a given service, that file has to be installed in /usr/share/dbus-1/services/. The content of the service file is the following: [D-BUS Service] Name=com.meego.omadm Exec=/usr/bin/omadm_consumer Notify API ========== Service unique name (must match the consumer's TargetService) Interface org.ofono.mms.PushConsumer Object path freely definable Method void Notify(array{byte} header, array{byte} body) Requests the consumer to process a new WAP Push notification that has arrived containing an appropriate message. Possible Errors: None Python consumer sample ====================== The sample location and name must match the 'Exec' key entry value of the D-Bus activation service file. This consumer receives the WAP Push Notification and dumps the WAP Push Notifcation's header and body in 2 separates files. The "Release" method is only present to gracefully exit the sample using the shell command below: # dbus-send --print-reply --session --dest="com.meego.omadm" /com/meego/omadm org.ofono.mms.PushConsumer.Release #!/usr/bin/env python import gobject import dbus import dbus.service import dbus.mainloop.glib if getattr(dbus, 'version', (0,0,0)) >= (0,41,0): import dbus.glib class ServerObject(dbus.service.Object): def __init__(self): # Here the service name bus_name = dbus.service.BusName("com.meego.omadm", bus=dbus.SessionBus()) # Here the object path dbus.service.Object.__init__(self, bus_name, "/com/meego/omadm") # Here the interface name, and the method is named same as on dbus. @dbus.service.method("org.ofono.mms.PushConsumer", in_signature="ayay", out_signature="i") def Notify(self, hdr, body): hdrstr = "" for hbyte in hdr: hdrstr += str(hbyte) print "hdr length = %d, hdr : %s" % (len (hdr), hdrstr) bodystr = "" for bbyte in body: bodystr += str(bbyte) print "body length = %d, body : %s" % (len(body), bodystr) f = open("/tmp/dm_header.out", "w") f.write("%s" % (hdrstr)) f.close() f = open("/tmp/dm_body.out", "w") f.write("%s" % (bodystr)) f.close() return 0 @dbus.service.method("org.ofono.mms.PushConsumer") def Release(self): mainloop.quit() if __name__ == '__main__': dbus.mainloop.glib.DBusGMainLoop(set_as_default=True) server = ServerObject() mainloop = gobject.MainLoop() mainloop.run() mmsd-0.1/doc/manager-api.txt000066400000000000000000000015521216024552400160050ustar00rootroot00000000000000Manager hierarchy ================= Service org.ofono.mms Interface org.ofono.mms.Manager Object path /org/ofono/mms 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. mmsd-0.1/doc/message-api.txt000066400000000000000000000026631216024552400160230ustar00rootroot00000000000000Message hierarchy ================= Service org.ofono.mms Interface org.ofono.mms.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. Either when it was received or sent. string Subject [readonly, optional] Contains the optional subject of a MMS. string Sender [readonly, optional] Number of sender. array{string} Recipients [readonly] Numbers of recipients. string Smil [readonly, optional] SMIL body in UTF-8 format. array{string id, string content-type, string filename, uint64 offset, uint64 len} Attachments [readonly] List of attachement content types, identifiers, offset and length of data in the stored file. mmsd-0.1/doc/service-api.txt000066400000000000000000000071651216024552400160410ustar00rootroot00000000000000Service hierarchy ================= Service org.ofono.mms Interface org.ofono.mms.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 array{object,dict} GetConversation(string number, uint32 count) Get an array of message objects and properties that are part of a conversation between the service entity and the number provided. The number parameter contains digits to look for (i.e.: n last digits of the phone number), only messages with a recipient which ends with the given number will be part of the GetConversation result. The count parameter can either be 0 for unlimited messages in the conversation or limit the conversation to count last messages. Possible Errors: [service].Error.InvalidArguments [service].Error.TransientFailure dict GetProperties() Returns properties for the manager object. See the properties section for available properties. Possible Errors: [service].Error.NotImplemented object SendMessage(array{string} recipients, string smil, array{string id, string content-type, string filename}) Send a new message to recipients with SMIL body and list of attachement content types, identifiers and filenames. The SMIL body is required to be in UTF-8 format. On success a message object will be created and also signaled by MessageAdded signal. The initial status of the message is a draft and and will be updated once sending has succeeded. Possible Errors: [service].Error.InvalidArguments [service].Error.UnsupportedMessage [service].Error.TransientFailure [service].Error.TransientNetworkProblem [service].Error.PermanentFailure [service].Error.PermanentServiceDenied [service].Error.PermanentMessageFormatCorrupt [service].Error.PermanentInvalidAddress [service].Error.PermanentContentNotAccepted [service].Error.PermanentLackOfPrepaid void DeleteMessages(array{object}) Delete an array of message objects that represents the currently stored messages to delete. Possible Errors: [service].Error.InvalidArguments void DeleteConversation(string number) Delete the messages that belongs to the conversation defined by the given number. The number parameter contains digits to look for (i.e.: n last digits of the phone number), only messages with a recipient which ends with the given number will be member of the conversation and thus will be deleted. Possible Errors: [service].Error.InvalidArguments void SetProperty(string name, variant value) Changes the value of the specified property. Possible Errors: [service].Error.InvalidArguments Signals MessageAdded(object path, dict properties) Signal that is sent when a new message is added. It contains the object path of new message, its properties. MessageRemoved(object path) Signal that is sent when a message has been removed. The object path is no longer accessible after this signal and only emitted for reference. Properties boolean UseDeliveryReports This property controls whether MMS Status Reports, sometimes better known as Delivery Reports are to be used. If enabled, all outgoing MMS messages will be flagged to request a status report from the MMSC. mmsd-0.1/doc/standards.txt000066400000000000000000000022601216024552400156040ustar00rootroot00000000000000Referencing 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 mmsd-0.1/doc/storage.txt000066400000000000000000000052651216024552400152750ustar00rootroot00000000000000mmsd storage design ******************* The mmsd persists the mms messages on the file system in a directory named ".mms/" under the user home directory. (e.g: /home//.mms/246813579/ -> "246813579" is the service identifier) A mms message is stored in its raw PDU format in a file named with a generated against its content (e.g: D1D3D50E0571C68E18628CFC6FAC9A75F5BAD2B5). Another file with the same prefix, named .status (e.g: D1D3D50E0571C68E18628CFC6FAC9A75F5BAD2B5.status) contains meta information related to the raw PDU. Meta file Example ================= [info] read=false state=notification id=0123456789ABCDEF Meta file Keys/Values details ============================= read: The message local "Read" status, possible values: "true" or "false". state: The message local state, possible values can be: - "notification": m-Notify.Ind PDU not yet downloaded. - "downloaded": m-Retrieve.Conf PDU downloaded, but not yet acknowledged. - "received": m-Retrieve.Conf PDU downloaded and successfully acknowledged. - "draft": m-Send.Req PDU ready for sending. - "sent": m-Send.Req PDU successfully sent. id: this is the value provided in the M-Send.conf PDU (assigned by MMSC in response to a M-Send.req message), this entry will only be created upon M-Send.conf message reception if the delivery report was requested. For sent messages, a group [delivery_status] could take place in addition to [info] if delivery report is requested. It will be used to manage the received delivery_report sent by each recipients. This group will have an entry per recipient, the associated value will be set to "none" (which means no report has been received yet) and updated upon report reception. The stored "id" (provided by the MMSC in the Send.conf msg) must match the received "id" in the delivery.ind push msg sent by each recipients. In this group, every recipient has a MMS Delivery status value which can be one of the following: - "none": no report has been received yet. - "expired": recipient did not retrieve the MMS before expiration. - "retrieved": MMS successfully retrieved by the recipient. - "rejected": recipient rejected the MMS. - "deferred": recipient decided to retrieve the MMS at a later time. - "indeterminate": cannot determine if the MMS reached its destination. - "forwarded": recipient forwarded the MMS without retrieving it first. - "unreachable": recipient is not reachable. Example of a sent_message meta file with delivery report requested ================================================================== [info] state=sent id=0123456789ABCDEF [delivery_status] +33612345678=retrieved +4915187654321=none mmsd-0.1/gdbus/000077500000000000000000000000001216024552400134175ustar00rootroot00000000000000mmsd-0.1/gdbus/client.c000066400000000000000000000764251216024552400150570ustar00rootroot00000000000000/* * * D-Bus helper library * * Copyright (C) 2004-2011 Marcel Holtmann * * * 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 * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include "gdbus.h" #define METHOD_CALL_TIMEOUT (300 * 1000) struct GDBusClient { int ref_count; DBusConnection *dbus_conn; char *service_name; char *unique_name; char *base_path; GPtrArray *match_rules; DBusPendingCall *pending_call; DBusPendingCall *get_objects_call; GDBusWatchFunction connect_func; void *connect_data; GDBusWatchFunction disconn_func; void *disconn_data; GDBusMessageFunction signal_func; void *signal_data; GDBusProxyFunction proxy_added; GDBusProxyFunction proxy_removed; GDBusPropertyFunction property_changed; void *user_data; GList *proxy_list; }; struct GDBusProxy { int ref_count; GDBusClient *client; char *obj_path; char *interface; GHashTable *prop_list; char *match_rule; GDBusPropertyFunction prop_func; void *prop_data; GDBusProxyFunction removed_func; void *removed_data; }; struct prop_entry { char *name; int type; DBusMessage *msg; }; static void modify_match_reply(DBusPendingCall *call, void *user_data) { DBusMessage *reply = dbus_pending_call_steal_reply(call); DBusError error; dbus_error_init(&error); if (dbus_set_error_from_message(&error, reply) == TRUE) dbus_error_free(&error); dbus_message_unref(reply); } static gboolean modify_match(DBusConnection *conn, const char *member, const char *rule) { DBusMessage *msg; DBusPendingCall *call; msg = dbus_message_new_method_call(DBUS_SERVICE_DBUS, DBUS_PATH_DBUS, DBUS_INTERFACE_DBUS, member); if (msg == NULL) return FALSE; dbus_message_append_args(msg, DBUS_TYPE_STRING, &rule, DBUS_TYPE_INVALID); if (dbus_connection_send_with_reply(conn, msg, &call, -1) == FALSE) { dbus_message_unref(msg); return FALSE; } if (call == NULL) { dbus_message_unref(msg); return FALSE; } dbus_pending_call_set_notify(call, modify_match_reply, NULL, NULL); dbus_pending_call_unref(call); dbus_message_unref(msg); return TRUE; } static void iter_append_iter(DBusMessageIter *base, DBusMessageIter *iter) { int type; type = dbus_message_iter_get_arg_type(iter); if (dbus_type_is_basic(type)) { const void *value; dbus_message_iter_get_basic(iter, &value); dbus_message_iter_append_basic(base, type, &value); } else if (dbus_type_is_container(type)) { DBusMessageIter iter_sub, base_sub; char *sig; dbus_message_iter_recurse(iter, &iter_sub); switch (type) { case DBUS_TYPE_ARRAY: case DBUS_TYPE_VARIANT: sig = dbus_message_iter_get_signature(&iter_sub); break; default: sig = NULL; break; } dbus_message_iter_open_container(base, type, sig, &base_sub); if (sig != NULL) dbus_free(sig); while (dbus_message_iter_get_arg_type(&iter_sub) != DBUS_TYPE_INVALID) { iter_append_iter(&base_sub, &iter_sub); dbus_message_iter_next(&iter_sub); } dbus_message_iter_close_container(base, &base_sub); } } static void prop_entry_update(struct prop_entry *prop, DBusMessageIter *iter) { DBusMessage *msg; DBusMessageIter base; msg = dbus_message_new(DBUS_MESSAGE_TYPE_METHOD_RETURN); if (msg == NULL) return; dbus_message_iter_init_append(msg, &base); iter_append_iter(&base, iter); if (prop->msg != NULL) dbus_message_unref(prop->msg); prop->msg = dbus_message_copy(msg); dbus_message_unref(msg); } static struct prop_entry *prop_entry_new(const char *name, DBusMessageIter *iter) { struct prop_entry *prop; prop = g_try_new0(struct prop_entry, 1); if (prop == NULL) return NULL; prop->name = g_strdup(name); prop->type = dbus_message_iter_get_arg_type(iter); prop_entry_update(prop, iter); return prop; } static void prop_entry_free(gpointer data) { struct prop_entry *prop = data; if (prop->msg != NULL) dbus_message_unref(prop->msg); g_free(prop->name); g_free(prop); } static void add_property(GDBusProxy *proxy, const char *name, DBusMessageIter *iter, gboolean send_changed) { GDBusClient *client = proxy->client; DBusMessageIter value; struct prop_entry *prop; if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_VARIANT) return; dbus_message_iter_recurse(iter, &value); prop = g_hash_table_lookup(proxy->prop_list, name); if (prop != NULL) { prop_entry_update(prop, &value); goto done; } prop = prop_entry_new(name, &value); if (prop == NULL) return; g_hash_table_replace(proxy->prop_list, prop->name, prop); done: if (proxy->prop_func) proxy->prop_func(proxy, name, &value, proxy->prop_data); if (client == NULL || send_changed == FALSE) return; if (client->property_changed) client->property_changed(proxy, name, &value, client->user_data); } static void update_properties(GDBusProxy *proxy, DBusMessageIter *iter, gboolean send_changed) { DBusMessageIter dict; if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY) return; dbus_message_iter_recurse(iter, &dict); while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) { DBusMessageIter entry; const char *name; dbus_message_iter_recurse(&dict, &entry); if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_STRING) break; dbus_message_iter_get_basic(&entry, &name); dbus_message_iter_next(&entry); add_property(proxy, name, &entry, send_changed); dbus_message_iter_next(&dict); } } static void get_all_properties_reply(DBusPendingCall *call, void *user_data) { GDBusProxy *proxy = user_data; GDBusClient *client = proxy->client; DBusMessage *reply = dbus_pending_call_steal_reply(call); DBusMessageIter iter; DBusError error; dbus_error_init(&error); if (dbus_set_error_from_message(&error, reply) == TRUE) { dbus_error_free(&error); goto done; } dbus_message_iter_init(reply, &iter); update_properties(proxy, &iter, FALSE); done: if (g_list_find(client->proxy_list, proxy) == NULL) { if (client->proxy_added) client->proxy_added(proxy, client->user_data); client->proxy_list = g_list_append(client->proxy_list, proxy); } dbus_message_unref(reply); g_dbus_client_unref(client); } static void get_all_properties(GDBusProxy *proxy) { GDBusClient *client = proxy->client; const char *service_name = client->service_name; DBusMessage *msg; DBusPendingCall *call; msg = dbus_message_new_method_call(service_name, proxy->obj_path, DBUS_INTERFACE_PROPERTIES, "GetAll"); if (msg == NULL) return; dbus_message_append_args(msg, DBUS_TYPE_STRING, &proxy->interface, DBUS_TYPE_INVALID); if (dbus_connection_send_with_reply(client->dbus_conn, msg, &call, -1) == FALSE) { dbus_message_unref(msg); return; } g_dbus_client_ref(client); dbus_pending_call_set_notify(call, get_all_properties_reply, proxy, NULL); dbus_pending_call_unref(call); dbus_message_unref(msg); } static GDBusProxy *proxy_lookup(GDBusClient *client, const char *path, const char *interface) { GList *list; for (list = g_list_first(client->proxy_list); list; list = g_list_next(list)) { GDBusProxy *proxy = list->data; if (g_str_equal(proxy->interface, interface) == TRUE && g_str_equal(proxy->obj_path, path) == TRUE) return proxy; } return NULL; } static GDBusProxy *proxy_new(GDBusClient *client, const char *path, const char *interface) { GDBusProxy *proxy; proxy = g_try_new0(GDBusProxy, 1); if (proxy == NULL) return NULL; proxy->client = client; proxy->obj_path = g_strdup(path); proxy->interface = g_strdup(interface); proxy->prop_list = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, prop_entry_free); proxy->match_rule = g_strdup_printf("type='signal'," "sender='%s',path='%s',interface='%s'," "member='PropertiesChanged',arg0='%s'", client->service_name, proxy->obj_path, DBUS_INTERFACE_PROPERTIES, proxy->interface); modify_match(client->dbus_conn, "AddMatch", proxy->match_rule); return g_dbus_proxy_ref(proxy); } static void proxy_free(gpointer data) { GDBusProxy *proxy = data; if (proxy->client) { GDBusClient *client = proxy->client; if (client->proxy_removed) client->proxy_removed(proxy, client->user_data); modify_match(client->dbus_conn, "RemoveMatch", proxy->match_rule); g_free(proxy->match_rule); proxy->match_rule = NULL; g_hash_table_remove_all(proxy->prop_list); proxy->client = NULL; } if (proxy->removed_func) proxy->removed_func(proxy, proxy->removed_data); g_dbus_proxy_unref(proxy); } static void proxy_remove(GDBusClient *client, const char *path, const char *interface) { GList *list; for (list = g_list_first(client->proxy_list); list; list = g_list_next(list)) { GDBusProxy *proxy = list->data; if (g_str_equal(proxy->interface, interface) == TRUE && g_str_equal(proxy->obj_path, path) == TRUE) { client->proxy_list = g_list_delete_link(client->proxy_list, list); proxy_free(proxy); break; } } } GDBusProxy *g_dbus_proxy_new(GDBusClient *client, const char *path, const char *interface) { GDBusProxy *proxy; if (client == NULL) return NULL; proxy = proxy_lookup(client, path, interface); if (proxy) return g_dbus_proxy_ref(proxy); proxy = proxy_new(client, path, interface); if (proxy == NULL) return NULL; get_all_properties(proxy); return g_dbus_proxy_ref(proxy); } GDBusProxy *g_dbus_proxy_ref(GDBusProxy *proxy) { if (proxy == NULL) return NULL; __sync_fetch_and_add(&proxy->ref_count, 1); return proxy; } void g_dbus_proxy_unref(GDBusProxy *proxy) { if (proxy == NULL) return; if (__sync_sub_and_fetch(&proxy->ref_count, 1) > 0) return; g_hash_table_destroy(proxy->prop_list); g_free(proxy->obj_path); g_free(proxy->interface); g_free(proxy); } const char *g_dbus_proxy_get_path(GDBusProxy *proxy) { if (proxy == NULL) return NULL; return proxy->obj_path; } const char *g_dbus_proxy_get_interface(GDBusProxy *proxy) { if (proxy == NULL) return NULL; return proxy->interface; } gboolean g_dbus_proxy_get_property(GDBusProxy *proxy, const char *name, DBusMessageIter *iter) { struct prop_entry *prop; if (proxy == NULL || name == NULL) return FALSE; prop = g_hash_table_lookup(proxy->prop_list, name); if (prop == NULL) return FALSE; if (prop->msg == NULL) return FALSE; if (dbus_message_iter_init(prop->msg, iter) == FALSE) return FALSE; return TRUE; } struct refresh_property_data { GDBusProxy *proxy; char *name; }; static void refresh_property_free(gpointer user_data) { struct refresh_property_data *data = user_data; g_free(data->name); g_free(data); } static void refresh_property_reply(DBusPendingCall *call, void *user_data) { struct refresh_property_data *data = user_data; DBusMessage *reply = dbus_pending_call_steal_reply(call); DBusError error; dbus_error_init(&error); if (dbus_set_error_from_message(&error, reply) == FALSE) { DBusMessageIter iter; dbus_message_iter_init(reply, &iter); add_property(data->proxy, data->name, &iter, TRUE); } else dbus_error_free(&error); dbus_message_unref(reply); } gboolean g_dbus_proxy_refresh_property(GDBusProxy *proxy, const char *name) { struct refresh_property_data *data; GDBusClient *client; DBusMessage *msg; DBusMessageIter iter; DBusPendingCall *call; if (proxy == NULL || name == NULL) return FALSE; client = proxy->client; if (client == NULL) return FALSE; data = g_try_new0(struct refresh_property_data, 1); if (data == NULL) return FALSE; data->proxy = proxy; data->name = g_strdup(name); msg = dbus_message_new_method_call(client->service_name, proxy->obj_path, DBUS_INTERFACE_PROPERTIES, "Get"); if (msg == NULL) { refresh_property_free(data); return FALSE; } dbus_message_iter_init_append(msg, &iter); dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &proxy->interface); dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &name); if (dbus_connection_send_with_reply(client->dbus_conn, msg, &call, -1) == FALSE) { dbus_message_unref(msg); refresh_property_free(data); return FALSE; } dbus_pending_call_set_notify(call, refresh_property_reply, data, refresh_property_free); dbus_pending_call_unref(call); dbus_message_unref(msg); return TRUE; } struct set_property_data { GDBusResultFunction function; void *user_data; GDBusDestroyFunction destroy; }; static void set_property_reply(DBusPendingCall *call, void *user_data) { struct set_property_data *data = user_data; DBusMessage *reply = dbus_pending_call_steal_reply(call); DBusError error; dbus_error_init(&error); dbus_set_error_from_message(&error, reply); if (data->function) data->function(&error, data->user_data); if (data->destroy) data->destroy(data->user_data); dbus_error_free(&error); dbus_message_unref(reply); } gboolean g_dbus_proxy_set_property_basic(GDBusProxy *proxy, const char *name, int type, const void *value, GDBusResultFunction function, void *user_data, GDBusDestroyFunction destroy) { struct set_property_data *data; GDBusClient *client; DBusMessage *msg; DBusMessageIter iter, variant; DBusPendingCall *call; char type_as_str[2]; if (proxy == NULL || name == NULL || value == NULL) return FALSE; if (dbus_type_is_basic(type) == FALSE) return FALSE; client = proxy->client; if (client == NULL) return FALSE; data = g_try_new0(struct set_property_data, 1); if (data == NULL) return FALSE; data->function = function; data->user_data = user_data; data->destroy = destroy; msg = dbus_message_new_method_call(client->service_name, proxy->obj_path, DBUS_INTERFACE_PROPERTIES, "Set"); if (msg == NULL) { g_free(data); return FALSE; } type_as_str[0] = (char) type; type_as_str[1] = '\0'; dbus_message_iter_init_append(msg, &iter); dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &proxy->interface); dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &name); dbus_message_iter_open_container(&iter, DBUS_TYPE_VARIANT, type_as_str, &variant); dbus_message_iter_append_basic(&variant, type, value); dbus_message_iter_close_container(&iter, &variant); if (dbus_connection_send_with_reply(client->dbus_conn, msg, &call, -1) == FALSE) { dbus_message_unref(msg); g_free(data); return FALSE; } dbus_pending_call_set_notify(call, set_property_reply, data, g_free); dbus_pending_call_unref(call); dbus_message_unref(msg); return TRUE; } struct method_call_data { GDBusReturnFunction function; void *user_data; GDBusDestroyFunction destroy; }; static void method_call_reply(DBusPendingCall *call, void *user_data) { struct method_call_data *data = user_data; DBusMessage *reply = dbus_pending_call_steal_reply(call); if (data->function) data->function(reply, data->user_data); if (data->destroy) data->destroy(data->user_data); dbus_message_unref(reply); } gboolean g_dbus_proxy_method_call(GDBusProxy *proxy, const char *method, GDBusSetupFunction setup, GDBusReturnFunction function, void *user_data, GDBusDestroyFunction destroy) { struct method_call_data *data; GDBusClient *client; DBusMessage *msg; DBusPendingCall *call; if (proxy == NULL || method == NULL) return FALSE; client = proxy->client; if (client == NULL) return FALSE; data = g_try_new0(struct method_call_data, 1); if (data == NULL) return FALSE; data->function = function; data->user_data = user_data; data->destroy = destroy; msg = dbus_message_new_method_call(client->service_name, proxy->obj_path, proxy->interface, method); if (msg == NULL) { g_free(data); return FALSE; } if (setup) { DBusMessageIter iter; dbus_message_iter_init_append(msg, &iter); setup(&iter, data->user_data); } if (dbus_connection_send_with_reply(client->dbus_conn, msg, &call, METHOD_CALL_TIMEOUT) == FALSE) { dbus_message_unref(msg); g_free(data); return FALSE; } dbus_pending_call_set_notify(call, method_call_reply, data, g_free); dbus_pending_call_unref(call); dbus_message_unref(msg); return TRUE; } gboolean g_dbus_proxy_set_property_watch(GDBusProxy *proxy, GDBusPropertyFunction function, void *user_data) { if (proxy == NULL) return FALSE; proxy->prop_func = function; proxy->prop_data = user_data; return TRUE; } gboolean g_dbus_proxy_set_removed_watch(GDBusProxy *proxy, GDBusProxyFunction function, void *user_data) { if (proxy == NULL) return FALSE; proxy->removed_func = function; proxy->removed_data = user_data; return TRUE; } static void refresh_properties(GDBusClient *client) { GList *list; for (list = g_list_first(client->proxy_list); list; list = g_list_next(list)) { GDBusProxy *proxy = list->data; get_all_properties(proxy); } } static void properties_changed(GDBusClient *client, const char *path, DBusMessage *msg) { GDBusProxy *proxy = NULL; DBusMessageIter iter, entry; const char *interface; GList *list; if (dbus_message_iter_init(msg, &iter) == FALSE) return; if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING) return; dbus_message_iter_get_basic(&iter, &interface); dbus_message_iter_next(&iter); for (list = g_list_first(client->proxy_list); list; list = g_list_next(list)) { GDBusProxy *data = list->data; if (g_str_equal(data->interface, interface) == TRUE && g_str_equal(data->obj_path, path) == TRUE) { proxy = data; break; } } if (proxy == NULL) return; update_properties(proxy, &iter, TRUE); dbus_message_iter_next(&iter); if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY) return; dbus_message_iter_recurse(&iter, &entry); while (dbus_message_iter_get_arg_type(&entry) == DBUS_TYPE_STRING) { const char *name; dbus_message_iter_get_basic(&entry, &name); g_hash_table_remove(proxy->prop_list, name); if (proxy->prop_func) proxy->prop_func(proxy, name, NULL, proxy->prop_data); if (client->property_changed) client->property_changed(proxy, name, NULL, client->user_data); dbus_message_iter_next(&entry); } } static void parse_properties(GDBusClient *client, const char *path, const char *interface, DBusMessageIter *iter) { GDBusProxy *proxy; if (g_str_equal(interface, DBUS_INTERFACE_INTROSPECTABLE) == TRUE) return; if (g_str_equal(interface, DBUS_INTERFACE_PROPERTIES) == TRUE) return; proxy = proxy_lookup(client, path, interface); if (proxy) { update_properties(proxy, iter, FALSE); return; } proxy = proxy_new(client, path, interface); if (proxy == NULL) return; update_properties(proxy, iter, FALSE); if (client->proxy_added) client->proxy_added(proxy, client->user_data); client->proxy_list = g_list_append(client->proxy_list, proxy); } static void parse_interfaces(GDBusClient *client, const char *path, DBusMessageIter *iter) { DBusMessageIter dict; if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY) return; dbus_message_iter_recurse(iter, &dict); while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) { DBusMessageIter entry; const char *interface; dbus_message_iter_recurse(&dict, &entry); if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_STRING) break; dbus_message_iter_get_basic(&entry, &interface); dbus_message_iter_next(&entry); parse_properties(client, path, interface, &entry); dbus_message_iter_next(&dict); } } static void interfaces_added(GDBusClient *client, DBusMessage *msg) { DBusMessageIter iter; const char *path; if (dbus_message_iter_init(msg, &iter) == FALSE) return; if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_OBJECT_PATH) return; dbus_message_iter_get_basic(&iter, &path); dbus_message_iter_next(&iter); g_dbus_client_ref(client); parse_interfaces(client, path, &iter); g_dbus_client_unref(client); } static void interfaces_removed(GDBusClient *client, DBusMessage *msg) { DBusMessageIter iter, entry; const char *path; if (dbus_message_iter_init(msg, &iter) == FALSE) return; if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_OBJECT_PATH) return; dbus_message_iter_get_basic(&iter, &path); dbus_message_iter_next(&iter); if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY) return; dbus_message_iter_recurse(&iter, &entry); g_dbus_client_ref(client); while (dbus_message_iter_get_arg_type(&entry) == DBUS_TYPE_STRING) { const char *interface; dbus_message_iter_get_basic(&entry, &interface); proxy_remove(client, path, interface); dbus_message_iter_next(&entry); } g_dbus_client_unref(client); } static void parse_managed_objects(GDBusClient *client, DBusMessage *msg) { DBusMessageIter iter, dict; if (dbus_message_iter_init(msg, &iter) == FALSE) return; if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY) return; dbus_message_iter_recurse(&iter, &dict); while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) { DBusMessageIter entry; const char *path; dbus_message_iter_recurse(&dict, &entry); if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_OBJECT_PATH) break; dbus_message_iter_get_basic(&entry, &path); dbus_message_iter_next(&entry); parse_interfaces(client, path, &entry); dbus_message_iter_next(&dict); } } static void get_managed_objects_reply(DBusPendingCall *call, void *user_data) { GDBusClient *client = user_data; DBusMessage *reply = dbus_pending_call_steal_reply(call); DBusError error; g_dbus_client_ref(client); dbus_error_init(&error); if (dbus_set_error_from_message(&error, reply) == TRUE) { dbus_error_free(&error); goto done; } parse_managed_objects(client, reply); done: dbus_message_unref(reply); dbus_pending_call_unref(client->get_objects_call); client->get_objects_call = NULL; g_dbus_client_unref(client); } static void get_managed_objects(GDBusClient *client) { DBusMessage *msg; if (!client->proxy_added && !client->proxy_removed) { refresh_properties(client); return; } if (client->get_objects_call != NULL) return; msg = dbus_message_new_method_call(client->service_name, "/", DBUS_INTERFACE_DBUS ".ObjectManager", "GetManagedObjects"); if (msg == NULL) return; dbus_message_append_args(msg, DBUS_TYPE_INVALID); if (dbus_connection_send_with_reply(client->dbus_conn, msg, &client->get_objects_call, -1) == FALSE) { dbus_message_unref(msg); return; } dbus_pending_call_set_notify(client->get_objects_call, get_managed_objects_reply, client, NULL); dbus_message_unref(msg); } static void get_name_owner_reply(DBusPendingCall *call, void *user_data) { GDBusClient *client = user_data; DBusMessage *reply = dbus_pending_call_steal_reply(call); DBusError error; const char *name; g_dbus_client_ref(client); dbus_error_init(&error); if (dbus_set_error_from_message(&error, reply) == TRUE) { dbus_error_free(&error); goto done; } if (dbus_message_get_args(reply, NULL, DBUS_TYPE_STRING, &name, DBUS_TYPE_INVALID) == FALSE) goto done; if (client->unique_name == NULL) { client->unique_name = g_strdup(name); if (client->connect_func) client->connect_func(client->dbus_conn, client->connect_data); get_managed_objects(client); } done: dbus_message_unref(reply); dbus_pending_call_unref(client->pending_call); client->pending_call = NULL; g_dbus_client_unref(client); } static void get_name_owner(GDBusClient *client, const char *name) { DBusMessage *msg; msg = dbus_message_new_method_call(DBUS_SERVICE_DBUS, DBUS_PATH_DBUS, DBUS_INTERFACE_DBUS, "GetNameOwner"); if (msg == NULL) return; dbus_message_append_args(msg, DBUS_TYPE_STRING, &name, DBUS_TYPE_INVALID); if (dbus_connection_send_with_reply(client->dbus_conn, msg, &client->pending_call, -1) == FALSE) { dbus_message_unref(msg); return; } dbus_pending_call_set_notify(client->pending_call, get_name_owner_reply, client, NULL); dbus_message_unref(msg); } static DBusHandlerResult message_filter(DBusConnection *connection, DBusMessage *message, void *user_data) { GDBusClient *client = user_data; const char *sender; if (dbus_message_get_type(message) != DBUS_MESSAGE_TYPE_SIGNAL) return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; sender = dbus_message_get_sender(message); if (sender == NULL) return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; if (g_str_equal(sender, DBUS_SERVICE_DBUS) == TRUE) { const char *interface, *member; const char *name, *old, *new; interface = dbus_message_get_interface(message); if (g_str_equal(interface, DBUS_INTERFACE_DBUS) == FALSE) return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; member = dbus_message_get_member(message); if (g_str_equal(member, "NameOwnerChanged") == FALSE) return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; if (dbus_message_get_args(message, NULL, DBUS_TYPE_STRING, &name, DBUS_TYPE_STRING, &old, DBUS_TYPE_STRING, &new, DBUS_TYPE_INVALID) == FALSE) return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; if (g_str_equal(name, client->service_name) == FALSE) return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; if (*new == '\0' && client->unique_name != NULL && g_str_equal(old, client->unique_name) == TRUE) { g_list_free_full(client->proxy_list, proxy_free); client->proxy_list = NULL; if (client->disconn_func) client->disconn_func(client->dbus_conn, client->disconn_data); g_free(client->unique_name); client->unique_name = NULL; } else if (*old == '\0' && client->unique_name == NULL) { client->unique_name = g_strdup(new); if (client->connect_func) client->connect_func(client->dbus_conn, client->connect_data); get_managed_objects(client); } return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; } if (client->unique_name == NULL) return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; if (g_str_equal(sender, client->unique_name) == TRUE) { const char *path, *interface, *member; path = dbus_message_get_path(message); interface = dbus_message_get_interface(message); member = dbus_message_get_member(message); if (g_str_equal(path, "/") == TRUE) { if (g_str_equal(interface, DBUS_INTERFACE_DBUS ".ObjectManager") == FALSE) return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; if (g_str_equal(member, "InterfacesAdded") == TRUE) { interfaces_added(client, message); return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; } if (g_str_equal(member, "InterfacesRemoved") == TRUE) { interfaces_removed(client, message); return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; } return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; } if (g_str_has_prefix(path, client->base_path) == FALSE) return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; if (g_str_equal(interface, DBUS_INTERFACE_PROPERTIES) == TRUE) { if (g_str_equal(member, "PropertiesChanged") == TRUE) properties_changed(client, path, message); return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; } if (client->signal_func) client->signal_func(client->dbus_conn, message, client->signal_data); } return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; } GDBusClient *g_dbus_client_new(DBusConnection *connection, const char *service, const char *path) { GDBusClient *client; unsigned int i; if (connection == NULL) return NULL; client = g_try_new0(GDBusClient, 1); if (client == NULL) return NULL; if (dbus_connection_add_filter(connection, message_filter, client, NULL) == FALSE) { g_free(client); return NULL; } client->dbus_conn = dbus_connection_ref(connection); client->service_name = g_strdup(service); client->base_path = g_strdup(path); get_name_owner(client, client->service_name); client->match_rules = g_ptr_array_sized_new(4); g_ptr_array_set_free_func(client->match_rules, g_free); g_ptr_array_add(client->match_rules, g_strdup_printf("type='signal'," "sender='%s',path='%s',interface='%s'," "member='NameOwnerChanged',arg0='%s'", DBUS_SERVICE_DBUS, DBUS_PATH_DBUS, DBUS_INTERFACE_DBUS, client->service_name)); g_ptr_array_add(client->match_rules, g_strdup_printf("type='signal'," "sender='%s'," "path='/',interface='%s.ObjectManager'," "member='InterfacesAdded'", client->service_name, DBUS_INTERFACE_DBUS)); g_ptr_array_add(client->match_rules, g_strdup_printf("type='signal'," "sender='%s'," "path='/',interface='%s.ObjectManager'," "member='InterfacesRemoved'", client->service_name, DBUS_INTERFACE_DBUS)); g_ptr_array_add(client->match_rules, g_strdup_printf("type='signal'," "sender='%s',path_namespace='%s'", client->service_name, client->base_path)); for (i = 0; i < client->match_rules->len; i++) { modify_match(client->dbus_conn, "AddMatch", g_ptr_array_index(client->match_rules, i)); } return g_dbus_client_ref(client); } GDBusClient *g_dbus_client_ref(GDBusClient *client) { if (client == NULL) return NULL; __sync_fetch_and_add(&client->ref_count, 1); return client; } void g_dbus_client_unref(GDBusClient *client) { unsigned int i; if (client == NULL) return; if (__sync_sub_and_fetch(&client->ref_count, 1) > 0) return; if (client->pending_call != NULL) { dbus_pending_call_cancel(client->pending_call); dbus_pending_call_unref(client->pending_call); } if (client->get_objects_call != NULL) { dbus_pending_call_cancel(client->get_objects_call); dbus_pending_call_unref(client->get_objects_call); } for (i = 0; i < client->match_rules->len; i++) { modify_match(client->dbus_conn, "RemoveMatch", g_ptr_array_index(client->match_rules, i)); } g_ptr_array_free(client->match_rules, TRUE); dbus_connection_remove_filter(client->dbus_conn, message_filter, client); g_list_free_full(client->proxy_list, proxy_free); if (client->disconn_func) client->disconn_func(client->dbus_conn, client->disconn_data); dbus_connection_unref(client->dbus_conn); g_free(client->service_name); g_free(client->unique_name); g_free(client->base_path); g_free(client); } gboolean g_dbus_client_set_connect_watch(GDBusClient *client, GDBusWatchFunction function, void *user_data) { if (client == NULL) return FALSE; client->connect_func = function; client->connect_data = user_data; return TRUE; } gboolean g_dbus_client_set_disconnect_watch(GDBusClient *client, GDBusWatchFunction function, void *user_data) { if (client == NULL) return FALSE; client->disconn_func = function; client->disconn_data = user_data; return TRUE; } gboolean g_dbus_client_set_signal_watch(GDBusClient *client, GDBusMessageFunction function, void *user_data) { if (client == NULL) return FALSE; client->signal_func = function; client->signal_data = user_data; return TRUE; } gboolean g_dbus_client_set_proxy_handlers(GDBusClient *client, GDBusProxyFunction proxy_added, GDBusProxyFunction proxy_removed, GDBusPropertyFunction property_changed, void *user_data) { if (client == NULL) return FALSE; client->proxy_added = proxy_added; client->proxy_removed = proxy_removed; client->property_changed = property_changed; client->user_data = user_data; get_managed_objects(client); return TRUE; } mmsd-0.1/gdbus/gdbus.h000066400000000000000000000304171216024552400147010ustar00rootroot00000000000000/* * * D-Bus helper library * * Copyright (C) 2004-2011 Marcel Holtmann * * * 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 * */ #ifndef __GDBUS_H #define __GDBUS_H #ifdef __cplusplus extern "C" { #endif #include #include typedef enum GDBusMethodFlags GDBusMethodFlags; typedef enum GDBusSignalFlags GDBusSignalFlags; typedef enum GDBusPropertyFlags GDBusPropertyFlags; typedef enum GDBusSecurityFlags GDBusSecurityFlags; typedef struct GDBusArgInfo GDBusArgInfo; typedef struct GDBusMethodTable GDBusMethodTable; typedef struct GDBusSignalTable GDBusSignalTable; typedef struct GDBusPropertyTable GDBusPropertyTable; typedef struct GDBusSecurityTable GDBusSecurityTable; typedef void (* GDBusWatchFunction) (DBusConnection *connection, void *user_data); typedef void (* GDBusMessageFunction) (DBusConnection *connection, DBusMessage *message, void *user_data); typedef gboolean (* GDBusSignalFunction) (DBusConnection *connection, DBusMessage *message, void *user_data); DBusConnection *g_dbus_setup_bus(DBusBusType type, const char *name, DBusError *error); DBusConnection *g_dbus_setup_private(DBusBusType type, const char *name, DBusError *error); gboolean g_dbus_request_name(DBusConnection *connection, const char *name, DBusError *error); gboolean g_dbus_set_disconnect_function(DBusConnection *connection, GDBusWatchFunction function, void *user_data, DBusFreeFunction destroy); typedef void (* GDBusDestroyFunction) (void *user_data); typedef DBusMessage * (* GDBusMethodFunction) (DBusConnection *connection, DBusMessage *message, void *user_data); typedef gboolean (*GDBusPropertyGetter)(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data); typedef guint32 GDBusPendingPropertySet; typedef void (*GDBusPropertySetter)(const GDBusPropertyTable *property, DBusMessageIter *value, GDBusPendingPropertySet id, void *data); typedef gboolean (*GDBusPropertyExists)(const GDBusPropertyTable *property, void *data); typedef guint32 GDBusPendingReply; typedef void (* GDBusSecurityFunction) (DBusConnection *connection, const char *action, gboolean interaction, GDBusPendingReply pending); enum GDBusFlags { G_DBUS_FLAG_ENABLE_EXPERIMENTAL = (1 << 0), }; enum GDBusMethodFlags { G_DBUS_METHOD_FLAG_DEPRECATED = (1 << 0), G_DBUS_METHOD_FLAG_NOREPLY = (1 << 1), G_DBUS_METHOD_FLAG_ASYNC = (1 << 2), G_DBUS_METHOD_FLAG_EXPERIMENTAL = (1 << 3), }; enum GDBusSignalFlags { G_DBUS_SIGNAL_FLAG_DEPRECATED = (1 << 0), G_DBUS_SIGNAL_FLAG_EXPERIMENTAL = (1 << 1), }; enum GDBusPropertyFlags { G_DBUS_PROPERTY_FLAG_DEPRECATED = (1 << 0), G_DBUS_PROPERTY_FLAG_EXPERIMENTAL = (1 << 1), }; enum GDBusSecurityFlags { G_DBUS_SECURITY_FLAG_DEPRECATED = (1 << 0), G_DBUS_SECURITY_FLAG_BUILTIN = (1 << 1), G_DBUS_SECURITY_FLAG_ALLOW_INTERACTION = (1 << 2), }; struct GDBusArgInfo { const char *name; const char *signature; }; struct GDBusMethodTable { const char *name; GDBusMethodFunction function; GDBusMethodFlags flags; unsigned int privilege; const GDBusArgInfo *in_args; const GDBusArgInfo *out_args; }; struct GDBusSignalTable { const char *name; GDBusSignalFlags flags; const GDBusArgInfo *args; }; struct GDBusPropertyTable { const char *name; const char *type; GDBusPropertyGetter get; GDBusPropertySetter set; GDBusPropertyExists exists; GDBusPropertyFlags flags; }; struct GDBusSecurityTable { unsigned int privilege; const char *action; GDBusSecurityFlags flags; GDBusSecurityFunction function; }; #define GDBUS_ARGS(args...) (const GDBusArgInfo[]) { args, { } } #define GDBUS_METHOD(_name, _in_args, _out_args, _function) \ .name = _name, \ .in_args = _in_args, \ .out_args = _out_args, \ .function = _function #define GDBUS_ASYNC_METHOD(_name, _in_args, _out_args, _function) \ .name = _name, \ .in_args = _in_args, \ .out_args = _out_args, \ .function = _function, \ .flags = G_DBUS_METHOD_FLAG_ASYNC #define GDBUS_DEPRECATED_METHOD(_name, _in_args, _out_args, _function) \ .name = _name, \ .in_args = _in_args, \ .out_args = _out_args, \ .function = _function, \ .flags = G_DBUS_METHOD_FLAG_DEPRECATED #define GDBUS_DEPRECATED_ASYNC_METHOD(_name, _in_args, _out_args, _function) \ .name = _name, \ .in_args = _in_args, \ .out_args = _out_args, \ .function = _function, \ .flags = G_DBUS_METHOD_FLAG_ASYNC | G_DBUS_METHOD_FLAG_DEPRECATED #define GDBUS_EXPERIMENTAL_METHOD(_name, _in_args, _out_args, _function) \ .name = _name, \ .in_args = _in_args, \ .out_args = _out_args, \ .function = _function, \ .flags = G_DBUS_METHOD_FLAG_EXPERIMENTAL #define GDBUS_EXPERIMENTAL_ASYNC_METHOD(_name, _in_args, _out_args, _function) \ .name = _name, \ .in_args = _in_args, \ .out_args = _out_args, \ .function = _function, \ .flags = G_DBUS_METHOD_FLAG_ASYNC | G_DBUS_METHOD_FLAG_EXPERIMENTAL #define GDBUS_NOREPLY_METHOD(_name, _in_args, _out_args, _function) \ .name = _name, \ .in_args = _in_args, \ .out_args = _out_args, \ .function = _function, \ .flags = G_DBUS_METHOD_FLAG_NOREPLY #define GDBUS_SIGNAL(_name, _args) \ .name = _name, \ .args = _args #define GDBUS_DEPRECATED_SIGNAL(_name, _args) \ .name = _name, \ .args = _args, \ .flags = G_DBUS_SIGNAL_FLAG_DEPRECATED #define GDBUS_EXPERIMENTAL_SIGNAL(_name, _args) \ .name = _name, \ .args = _args, \ .flags = G_DBUS_SIGNAL_FLAG_EXPERIMENTAL void g_dbus_set_flags(int flags); gboolean g_dbus_register_interface(DBusConnection *connection, const char *path, const char *name, const GDBusMethodTable *methods, const GDBusSignalTable *signals, const GDBusPropertyTable *properties, void *user_data, GDBusDestroyFunction destroy); gboolean g_dbus_unregister_interface(DBusConnection *connection, const char *path, const char *name); gboolean g_dbus_register_security(const GDBusSecurityTable *security); gboolean g_dbus_unregister_security(const GDBusSecurityTable *security); void g_dbus_pending_success(DBusConnection *connection, GDBusPendingReply pending); void g_dbus_pending_error(DBusConnection *connection, GDBusPendingReply pending, const char *name, const char *format, ...) __attribute__((format(printf, 4, 5))); void g_dbus_pending_error_valist(DBusConnection *connection, GDBusPendingReply pending, const char *name, const char *format, va_list args); DBusMessage *g_dbus_create_error(DBusMessage *message, const char *name, const char *format, ...) __attribute__((format(printf, 3, 4))); DBusMessage *g_dbus_create_error_valist(DBusMessage *message, const char *name, const char *format, va_list args); DBusMessage *g_dbus_create_reply(DBusMessage *message, int type, ...); DBusMessage *g_dbus_create_reply_valist(DBusMessage *message, int type, va_list args); gboolean g_dbus_send_message(DBusConnection *connection, DBusMessage *message); gboolean g_dbus_send_error(DBusConnection *connection, DBusMessage *message, const char *name, const char *format, ...) __attribute__((format(printf, 4, 5))); gboolean g_dbus_send_error_valist(DBusConnection *connection, DBusMessage *message, const char *name, const char *format, va_list args); gboolean g_dbus_send_reply(DBusConnection *connection, DBusMessage *message, int type, ...); gboolean g_dbus_send_reply_valist(DBusConnection *connection, DBusMessage *message, int type, va_list args); gboolean g_dbus_emit_signal(DBusConnection *connection, const char *path, const char *interface, const char *name, int type, ...); gboolean g_dbus_emit_signal_valist(DBusConnection *connection, const char *path, const char *interface, const char *name, int type, va_list args); guint g_dbus_add_service_watch(DBusConnection *connection, const char *name, GDBusWatchFunction connect, GDBusWatchFunction disconnect, void *user_data, GDBusDestroyFunction destroy); guint g_dbus_add_disconnect_watch(DBusConnection *connection, const char *name, GDBusWatchFunction function, void *user_data, GDBusDestroyFunction destroy); guint g_dbus_add_signal_watch(DBusConnection *connection, const char *sender, const char *path, const char *interface, const char *member, GDBusSignalFunction function, void *user_data, GDBusDestroyFunction destroy); guint g_dbus_add_properties_watch(DBusConnection *connection, const char *sender, const char *path, const char *interface, GDBusSignalFunction function, void *user_data, GDBusDestroyFunction destroy); gboolean g_dbus_remove_watch(DBusConnection *connection, guint tag); void g_dbus_remove_all_watches(DBusConnection *connection); void g_dbus_pending_property_success(GDBusPendingPropertySet id); void g_dbus_pending_property_error_valist(GDBusPendingReply id, const char *name, const char *format, va_list args); void g_dbus_pending_property_error(GDBusPendingReply id, const char *name, const char *format, ...); void g_dbus_emit_property_changed(DBusConnection *connection, const char *path, const char *interface, const char *name); gboolean g_dbus_get_properties(DBusConnection *connection, const char *path, const char *interface, DBusMessageIter *iter); gboolean g_dbus_attach_object_manager(DBusConnection *connection); gboolean g_dbus_detach_object_manager(DBusConnection *connection); typedef struct GDBusClient GDBusClient; typedef struct GDBusProxy GDBusProxy; GDBusProxy *g_dbus_proxy_new(GDBusClient *client, const char *path, const char *interface); GDBusProxy *g_dbus_proxy_ref(GDBusProxy *proxy); void g_dbus_proxy_unref(GDBusProxy *proxy); const char *g_dbus_proxy_get_path(GDBusProxy *proxy); const char *g_dbus_proxy_get_interface(GDBusProxy *proxy); gboolean g_dbus_proxy_get_property(GDBusProxy *proxy, const char *name, DBusMessageIter *iter); gboolean g_dbus_proxy_refresh_property(GDBusProxy *proxy, const char *name); typedef void (* GDBusResultFunction) (const DBusError *error, void *user_data); gboolean g_dbus_proxy_set_property_basic(GDBusProxy *proxy, const char *name, int type, const void *value, GDBusResultFunction function, void *user_data, GDBusDestroyFunction destroy); typedef void (* GDBusSetupFunction) (DBusMessageIter *iter, void *user_data); typedef void (* GDBusReturnFunction) (DBusMessage *message, void *user_data); gboolean g_dbus_proxy_method_call(GDBusProxy *proxy, const char *method, GDBusSetupFunction setup, GDBusReturnFunction function, void *user_data, GDBusDestroyFunction destroy); typedef void (* GDBusProxyFunction) (GDBusProxy *proxy, void *user_data); typedef void (* GDBusPropertyFunction) (GDBusProxy *proxy, const char *name, DBusMessageIter *iter, void *user_data); gboolean g_dbus_proxy_set_property_watch(GDBusProxy *proxy, GDBusPropertyFunction function, void *user_data); gboolean g_dbus_proxy_set_removed_watch(GDBusProxy *proxy, GDBusProxyFunction destroy, void *user_data); GDBusClient *g_dbus_client_new(DBusConnection *connection, const char *service, const char *path); GDBusClient *g_dbus_client_ref(GDBusClient *client); void g_dbus_client_unref(GDBusClient *client); gboolean g_dbus_client_set_connect_watch(GDBusClient *client, GDBusWatchFunction function, void *user_data); gboolean g_dbus_client_set_disconnect_watch(GDBusClient *client, GDBusWatchFunction function, void *user_data); gboolean g_dbus_client_set_signal_watch(GDBusClient *client, GDBusMessageFunction function, void *user_data); gboolean g_dbus_client_set_proxy_handlers(GDBusClient *client, GDBusProxyFunction proxy_added, GDBusProxyFunction proxy_removed, GDBusPropertyFunction property_changed, void *user_data); #ifdef __cplusplus } #endif #endif /* __GDBUS_H */ mmsd-0.1/gdbus/mainloop.c000066400000000000000000000177521216024552400154150ustar00rootroot00000000000000/* * * D-Bus helper library * * Copyright (C) 2004-2011 Marcel Holtmann * * * 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 * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include "gdbus.h" #define DISPATCH_TIMEOUT 0 #define info(fmt...) #define error(fmt...) #define debug(fmt...) struct timeout_handler { guint id; DBusTimeout *timeout; }; struct watch_info { guint id; DBusWatch *watch; DBusConnection *conn; }; struct disconnect_data { GDBusWatchFunction function; void *user_data; }; static gboolean disconnected_signal(DBusConnection *conn, DBusMessage *msg, void *data) { struct disconnect_data *dc_data = data; error("Got disconnected from the system message bus"); dc_data->function(conn, dc_data->user_data); dbus_connection_unref(conn); return TRUE; } static gboolean message_dispatch(void *data) { DBusConnection *conn = data; dbus_connection_ref(conn); /* Dispatch messages */ while (dbus_connection_dispatch(conn) == DBUS_DISPATCH_DATA_REMAINS); dbus_connection_unref(conn); return FALSE; } static inline void queue_dispatch(DBusConnection *conn, DBusDispatchStatus status) { if (status == DBUS_DISPATCH_DATA_REMAINS) g_timeout_add(DISPATCH_TIMEOUT, message_dispatch, conn); } static gboolean watch_func(GIOChannel *chan, GIOCondition cond, gpointer data) { struct watch_info *info = data; unsigned int flags = 0; DBusDispatchStatus status; DBusConnection *conn; conn = dbus_connection_ref(info->conn); if (cond & G_IO_IN) flags |= DBUS_WATCH_READABLE; if (cond & G_IO_OUT) flags |= DBUS_WATCH_WRITABLE; if (cond & G_IO_HUP) flags |= DBUS_WATCH_HANGUP; if (cond & G_IO_ERR) flags |= DBUS_WATCH_ERROR; dbus_watch_handle(info->watch, flags); status = dbus_connection_get_dispatch_status(conn); queue_dispatch(conn, status); dbus_connection_unref(conn); return TRUE; } static void watch_info_free(void *data) { struct watch_info *info = data; if (info->id > 0) { g_source_remove(info->id); info->id = 0; } dbus_connection_unref(info->conn); g_free(info); } static dbus_bool_t add_watch(DBusWatch *watch, void *data) { DBusConnection *conn = data; GIOCondition cond = G_IO_HUP | G_IO_ERR; GIOChannel *chan; struct watch_info *info; unsigned int flags; int fd; if (!dbus_watch_get_enabled(watch)) return TRUE; info = g_new0(struct watch_info, 1); fd = dbus_watch_get_unix_fd(watch); chan = g_io_channel_unix_new(fd); info->watch = watch; info->conn = dbus_connection_ref(conn); dbus_watch_set_data(watch, info, watch_info_free); flags = dbus_watch_get_flags(watch); if (flags & DBUS_WATCH_READABLE) cond |= G_IO_IN; if (flags & DBUS_WATCH_WRITABLE) cond |= G_IO_OUT; info->id = g_io_add_watch(chan, cond, watch_func, info); g_io_channel_unref(chan); return TRUE; } static void remove_watch(DBusWatch *watch, void *data) { if (dbus_watch_get_enabled(watch)) return; /* will trigger watch_info_free() */ dbus_watch_set_data(watch, NULL, NULL); } static void watch_toggled(DBusWatch *watch, void *data) { /* Because we just exit on OOM, enable/disable is * no different from add/remove */ if (dbus_watch_get_enabled(watch)) add_watch(watch, data); else remove_watch(watch, data); } static gboolean timeout_handler_dispatch(gpointer data) { struct timeout_handler *handler = data; handler->id = 0; /* if not enabled should not be polled by the main loop */ if (!dbus_timeout_get_enabled(handler->timeout)) return FALSE; dbus_timeout_handle(handler->timeout); return FALSE; } static void timeout_handler_free(void *data) { struct timeout_handler *handler = data; if (handler->id > 0) { g_source_remove(handler->id); handler->id = 0; } g_free(handler); } static dbus_bool_t add_timeout(DBusTimeout *timeout, void *data) { int interval = dbus_timeout_get_interval(timeout); struct timeout_handler *handler; if (!dbus_timeout_get_enabled(timeout)) return TRUE; handler = g_new0(struct timeout_handler, 1); handler->timeout = timeout; dbus_timeout_set_data(timeout, handler, timeout_handler_free); handler->id = g_timeout_add(interval, timeout_handler_dispatch, handler); return TRUE; } static void remove_timeout(DBusTimeout *timeout, void *data) { /* will trigger timeout_handler_free() */ dbus_timeout_set_data(timeout, NULL, NULL); } static void timeout_toggled(DBusTimeout *timeout, void *data) { if (dbus_timeout_get_enabled(timeout)) add_timeout(timeout, data); else remove_timeout(timeout, data); } static void dispatch_status(DBusConnection *conn, DBusDispatchStatus status, void *data) { if (!dbus_connection_get_is_connected(conn)) return; queue_dispatch(conn, status); } static inline void setup_dbus_with_main_loop(DBusConnection *conn) { dbus_connection_set_watch_functions(conn, add_watch, remove_watch, watch_toggled, conn, NULL); dbus_connection_set_timeout_functions(conn, add_timeout, remove_timeout, timeout_toggled, NULL, NULL); dbus_connection_set_dispatch_status_function(conn, dispatch_status, NULL, NULL); } static gboolean setup_bus(DBusConnection *conn, const char *name, DBusError *error) { gboolean result; DBusDispatchStatus status; if (name != NULL) { result = g_dbus_request_name(conn, name, error); if (error != NULL) { if (dbus_error_is_set(error) == TRUE) return FALSE; } if (result == FALSE) return FALSE; } setup_dbus_with_main_loop(conn); status = dbus_connection_get_dispatch_status(conn); queue_dispatch(conn, status); return TRUE; } DBusConnection *g_dbus_setup_bus(DBusBusType type, const char *name, DBusError *error) { DBusConnection *conn; conn = dbus_bus_get(type, error); if (error != NULL) { if (dbus_error_is_set(error) == TRUE) return NULL; } if (conn == NULL) return NULL; if (setup_bus(conn, name, error) == FALSE) { dbus_connection_unref(conn); return NULL; } return conn; } DBusConnection *g_dbus_setup_private(DBusBusType type, const char *name, DBusError *error) { DBusConnection *conn; conn = dbus_bus_get_private(type, error); if (error != NULL) { if (dbus_error_is_set(error) == TRUE) return NULL; } if (conn == NULL) return NULL; if (setup_bus(conn, name, error) == FALSE) { dbus_connection_unref(conn); return NULL; } return conn; } gboolean g_dbus_request_name(DBusConnection *connection, const char *name, DBusError *error) { int result; result = dbus_bus_request_name(connection, name, DBUS_NAME_FLAG_DO_NOT_QUEUE, error); if (error != NULL) { if (dbus_error_is_set(error) == TRUE) return FALSE; } if (result != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) { if (error != NULL) dbus_set_error(error, name, "Name already in use"); return FALSE; } return TRUE; } gboolean g_dbus_set_disconnect_function(DBusConnection *connection, GDBusWatchFunction function, void *user_data, DBusFreeFunction destroy) { struct disconnect_data *dc_data; dc_data = g_new0(struct disconnect_data, 1); dc_data->function = function; dc_data->user_data = user_data; dbus_connection_set_exit_on_disconnect(connection, FALSE); if (g_dbus_add_signal_watch(connection, NULL, NULL, DBUS_INTERFACE_LOCAL, "Disconnected", disconnected_signal, dc_data, g_free) == 0) { error("Failed to add watch for D-Bus Disconnected signal"); g_free(dc_data); return FALSE; } return TRUE; } mmsd-0.1/gdbus/object.c000066400000000000000000001254371216024552400150450ustar00rootroot00000000000000/* * * D-Bus helper library * * Copyright (C) 2004-2011 Marcel Holtmann * * * 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 * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include "gdbus.h" #define info(fmt...) #define error(fmt...) #define debug(fmt...) #define DBUS_INTERFACE_OBJECT_MANAGER "org.freedesktop.DBus.ObjectManager" #ifndef DBUS_ERROR_UNKNOWN_PROPERTY #define DBUS_ERROR_UNKNOWN_PROPERTY "org.freedesktop.DBus.Error.UnknownProperty" #endif #ifndef DBUS_ERROR_PROPERTY_READ_ONLY #define DBUS_ERROR_PROPERTY_READ_ONLY "org.freedesktop.DBus.Error.PropertyReadOnly" #endif struct generic_data { unsigned int refcount; DBusConnection *conn; char *path; GSList *interfaces; GSList *objects; GSList *added; GSList *removed; guint process_id; gboolean pending_prop; char *introspect; struct generic_data *parent; }; struct interface_data { char *name; const GDBusMethodTable *methods; const GDBusSignalTable *signals; const GDBusPropertyTable *properties; GSList *pending_prop; void *user_data; GDBusDestroyFunction destroy; }; struct security_data { GDBusPendingReply pending; DBusMessage *message; const GDBusMethodTable *method; void *iface_user_data; }; struct property_data { DBusConnection *conn; GDBusPendingPropertySet id; DBusMessage *message; }; static int global_flags = 0; static struct generic_data *root; static gboolean process_changes(gpointer user_data); static void process_properties_from_interface(struct generic_data *data, struct interface_data *iface); static void process_property_changes(struct generic_data *data); static void print_arguments(GString *gstr, const GDBusArgInfo *args, const char *direction) { for (; args && args->name; args++) { g_string_append_printf(gstr, "name, args->signature); if (direction) g_string_append_printf(gstr, " direction=\"%s\"/>\n", direction); else g_string_append_printf(gstr, "/>\n"); } } #define G_DBUS_ANNOTATE(name_, value_) \ "" #define G_DBUS_ANNOTATE_DEPRECATED \ G_DBUS_ANNOTATE("Deprecated", "true") #define G_DBUS_ANNOTATE_NOREPLY \ G_DBUS_ANNOTATE("Method.NoReply", "true") static gboolean check_experimental(int flags, int flag) { if (!(flags & flag)) return FALSE; return !(global_flags & G_DBUS_FLAG_ENABLE_EXPERIMENTAL); } static void generate_interface_xml(GString *gstr, struct interface_data *iface) { const GDBusMethodTable *method; const GDBusSignalTable *signal; const GDBusPropertyTable *property; for (method = iface->methods; method && method->name; method++) { if (check_experimental(method->flags, G_DBUS_METHOD_FLAG_EXPERIMENTAL)) continue; g_string_append_printf(gstr, "", method->name); print_arguments(gstr, method->in_args, "in"); print_arguments(gstr, method->out_args, "out"); if (method->flags & G_DBUS_METHOD_FLAG_DEPRECATED) g_string_append_printf(gstr, G_DBUS_ANNOTATE_DEPRECATED); if (method->flags & G_DBUS_METHOD_FLAG_NOREPLY) g_string_append_printf(gstr, G_DBUS_ANNOTATE_NOREPLY); g_string_append_printf(gstr, ""); } for (signal = iface->signals; signal && signal->name; signal++) { if (check_experimental(signal->flags, G_DBUS_SIGNAL_FLAG_EXPERIMENTAL)) continue; g_string_append_printf(gstr, "", signal->name); print_arguments(gstr, signal->args, NULL); if (signal->flags & G_DBUS_SIGNAL_FLAG_DEPRECATED) g_string_append_printf(gstr, G_DBUS_ANNOTATE_DEPRECATED); g_string_append_printf(gstr, "\n"); } for (property = iface->properties; property && property->name; property++) { if (check_experimental(property->flags, G_DBUS_PROPERTY_FLAG_EXPERIMENTAL)) continue; g_string_append_printf(gstr, "", property->name, property->type, property->get ? "read" : "", property->set ? "write" : ""); if (property->flags & G_DBUS_PROPERTY_FLAG_DEPRECATED) g_string_append_printf(gstr, G_DBUS_ANNOTATE_DEPRECATED); g_string_append_printf(gstr, ""); } } static void generate_introspection_xml(DBusConnection *conn, struct generic_data *data, const char *path) { GSList *list; GString *gstr; char **children; int i; g_free(data->introspect); gstr = g_string_new(DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE); g_string_append_printf(gstr, ""); for (list = data->interfaces; list; list = list->next) { struct interface_data *iface = list->data; g_string_append_printf(gstr, "", iface->name); generate_interface_xml(gstr, iface); g_string_append_printf(gstr, ""); } if (!dbus_connection_list_registered(conn, path, &children)) goto done; for (i = 0; children[i]; i++) g_string_append_printf(gstr, "", children[i]); dbus_free_string_array(children); done: g_string_append_printf(gstr, ""); data->introspect = g_string_free(gstr, FALSE); } static DBusMessage *introspect(DBusConnection *connection, DBusMessage *message, void *user_data) { struct generic_data *data = user_data; DBusMessage *reply; if (data->introspect == NULL) generate_introspection_xml(connection, data, dbus_message_get_path(message)); reply = dbus_message_new_method_return(message); if (reply == NULL) return NULL; dbus_message_append_args(reply, DBUS_TYPE_STRING, &data->introspect, DBUS_TYPE_INVALID); return reply; } static DBusHandlerResult process_message(DBusConnection *connection, DBusMessage *message, const GDBusMethodTable *method, void *iface_user_data) { DBusMessage *reply; reply = method->function(connection, message, iface_user_data); if (method->flags & G_DBUS_METHOD_FLAG_NOREPLY) { if (reply != NULL) dbus_message_unref(reply); return DBUS_HANDLER_RESULT_HANDLED; } if (method->flags & G_DBUS_METHOD_FLAG_ASYNC) { if (reply == NULL) return DBUS_HANDLER_RESULT_HANDLED; } if (reply == NULL) return DBUS_HANDLER_RESULT_NEED_MEMORY; dbus_connection_send(connection, reply, NULL); dbus_message_unref(reply); return DBUS_HANDLER_RESULT_HANDLED; } static GDBusPendingReply next_pending = 1; static GSList *pending_security = NULL; static const GDBusSecurityTable *security_table = NULL; void g_dbus_pending_success(DBusConnection *connection, GDBusPendingReply pending) { GSList *list; for (list = pending_security; list; list = list->next) { struct security_data *secdata = list->data; if (secdata->pending != pending) continue; pending_security = g_slist_remove(pending_security, secdata); process_message(connection, secdata->message, secdata->method, secdata->iface_user_data); dbus_message_unref(secdata->message); g_free(secdata); return; } } void g_dbus_pending_error_valist(DBusConnection *connection, GDBusPendingReply pending, const char *name, const char *format, va_list args) { GSList *list; for (list = pending_security; list; list = list->next) { struct security_data *secdata = list->data; DBusMessage *reply; if (secdata->pending != pending) continue; pending_security = g_slist_remove(pending_security, secdata); reply = g_dbus_create_error_valist(secdata->message, name, format, args); if (reply != NULL) { dbus_connection_send(connection, reply, NULL); dbus_message_unref(reply); } dbus_message_unref(secdata->message); g_free(secdata); return; } } void g_dbus_pending_error(DBusConnection *connection, GDBusPendingReply pending, const char *name, const char *format, ...) { va_list args; va_start(args, format); g_dbus_pending_error_valist(connection, pending, name, format, args); va_end(args); } int polkit_check_authorization(DBusConnection *conn, const char *action, gboolean interaction, void (*function) (dbus_bool_t authorized, void *user_data), void *user_data, int timeout); struct builtin_security_data { DBusConnection *conn; GDBusPendingReply pending; }; static void builtin_security_result(dbus_bool_t authorized, void *user_data) { struct builtin_security_data *data = user_data; if (authorized == TRUE) g_dbus_pending_success(data->conn, data->pending); else g_dbus_pending_error(data->conn, data->pending, DBUS_ERROR_AUTH_FAILED, NULL); g_free(data); } static void builtin_security_function(DBusConnection *conn, const char *action, gboolean interaction, GDBusPendingReply pending) { struct builtin_security_data *data; data = g_new0(struct builtin_security_data, 1); data->conn = conn; data->pending = pending; if (polkit_check_authorization(conn, action, interaction, builtin_security_result, data, 30000) < 0) g_dbus_pending_error(conn, pending, NULL, NULL); } static gboolean check_privilege(DBusConnection *conn, DBusMessage *msg, const GDBusMethodTable *method, void *iface_user_data) { const GDBusSecurityTable *security; for (security = security_table; security && security->privilege; security++) { struct security_data *secdata; gboolean interaction; if (security->privilege != method->privilege) continue; secdata = g_new(struct security_data, 1); secdata->pending = next_pending++; secdata->message = dbus_message_ref(msg); secdata->method = method; secdata->iface_user_data = iface_user_data; pending_security = g_slist_prepend(pending_security, secdata); if (security->flags & G_DBUS_SECURITY_FLAG_ALLOW_INTERACTION) interaction = TRUE; else interaction = FALSE; if (!(security->flags & G_DBUS_SECURITY_FLAG_BUILTIN) && security->function) security->function(conn, security->action, interaction, secdata->pending); else builtin_security_function(conn, security->action, interaction, secdata->pending); return TRUE; } return FALSE; } static GDBusPendingPropertySet next_pending_property = 1; static GSList *pending_property_set; static struct property_data *remove_pending_property_data( GDBusPendingPropertySet id) { struct property_data *propdata; GSList *l; for (l = pending_property_set; l != NULL; l = l->next) { propdata = l->data; if (propdata->id != id) continue; break; } if (l == NULL) return NULL; pending_property_set = g_slist_delete_link(pending_property_set, l); return propdata; } void g_dbus_pending_property_success(GDBusPendingPropertySet id) { struct property_data *propdata; propdata = remove_pending_property_data(id); if (propdata == NULL) return; g_dbus_send_reply(propdata->conn, propdata->message, DBUS_TYPE_INVALID); dbus_message_unref(propdata->message); g_free(propdata); } void g_dbus_pending_property_error_valist(GDBusPendingReply id, const char *name, const char *format, va_list args) { struct property_data *propdata; DBusMessage *reply; propdata = remove_pending_property_data(id); if (propdata == NULL) return; reply = g_dbus_create_error_valist(propdata->message, name, format, args); if (reply != NULL) { dbus_connection_send(propdata->conn, reply, NULL); dbus_message_unref(reply); } dbus_message_unref(propdata->message); g_free(propdata); } void g_dbus_pending_property_error(GDBusPendingReply id, const char *name, const char *format, ...) { va_list args; va_start(args, format); g_dbus_pending_property_error_valist(id, name, format, args); va_end(args); } static void reset_parent(gpointer data, gpointer user_data) { struct generic_data *child = data; struct generic_data *parent = user_data; child->parent = parent; } static void append_property(struct interface_data *iface, const GDBusPropertyTable *p, DBusMessageIter *dict) { DBusMessageIter entry, value; dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY, NULL, &entry); dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &p->name); dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT, p->type, &value); p->get(p, &value, iface->user_data); dbus_message_iter_close_container(&entry, &value); dbus_message_iter_close_container(dict, &entry); } static void append_properties(struct interface_data *data, DBusMessageIter *iter) { DBusMessageIter dict; const GDBusPropertyTable *p; dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict); for (p = data->properties; p && p->name; p++) { if (check_experimental(p->flags, G_DBUS_PROPERTY_FLAG_EXPERIMENTAL)) continue; if (p->get == NULL) continue; if (p->exists != NULL && !p->exists(p, data->user_data)) continue; append_property(data, p, &dict); } dbus_message_iter_close_container(iter, &dict); } static void append_interface(gpointer data, gpointer user_data) { struct interface_data *iface = data; DBusMessageIter *array = user_data; DBusMessageIter entry; dbus_message_iter_open_container(array, DBUS_TYPE_DICT_ENTRY, NULL, &entry); dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &iface->name); append_properties(data, &entry); dbus_message_iter_close_container(array, &entry); } static void emit_interfaces_added(struct generic_data *data) { DBusMessage *signal; DBusMessageIter iter, array; if (root == NULL || data == root) return; signal = dbus_message_new_signal(root->path, DBUS_INTERFACE_OBJECT_MANAGER, "InterfacesAdded"); if (signal == NULL) return; dbus_message_iter_init_append(signal, &iter); dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH, &data->path); dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_ARRAY_AS_STRING DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING DBUS_DICT_ENTRY_END_CHAR_AS_STRING DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &array); g_slist_foreach(data->added, append_interface, &array); g_slist_free(data->added); data->added = NULL; dbus_message_iter_close_container(&iter, &array); g_dbus_send_message(data->conn, signal); } static struct interface_data *find_interface(GSList *interfaces, const char *name) { GSList *list; if (name == NULL) return NULL; for (list = interfaces; list; list = list->next) { struct interface_data *iface = list->data; if (!strcmp(name, iface->name)) return iface; } return NULL; } static gboolean g_dbus_args_have_signature(const GDBusArgInfo *args, DBusMessage *message) { const char *sig = dbus_message_get_signature(message); const char *p = NULL; for (; args && args->signature && *sig; args++) { p = args->signature; for (; *sig && *p; sig++, p++) { if (*p != *sig) return FALSE; } } if (*sig || (p && *p) || (args && args->signature)) return FALSE; return TRUE; } static gboolean remove_interface(struct generic_data *data, const char *name) { struct interface_data *iface; iface = find_interface(data->interfaces, name); if (iface == NULL) return FALSE; process_properties_from_interface(data, iface); data->interfaces = g_slist_remove(data->interfaces, iface); if (iface->destroy) { iface->destroy(iface->user_data); iface->user_data = NULL; } /* * Interface being removed was just added, on the same mainloop * iteration? Don't send any signal */ if (g_slist_find(data->added, iface)) { data->added = g_slist_remove(data->added, iface); g_free(iface->name); g_free(iface); return TRUE; } if (data->parent == NULL) { g_free(iface->name); g_free(iface); return TRUE; } data->removed = g_slist_prepend(data->removed, iface->name); g_free(iface); if (data->process_id > 0) return TRUE; data->process_id = g_idle_add(process_changes, data); return TRUE; } static struct generic_data *invalidate_parent_data(DBusConnection *conn, const char *child_path) { struct generic_data *data = NULL, *child = NULL, *parent = NULL; char *parent_path, *slash; parent_path = g_strdup(child_path); slash = strrchr(parent_path, '/'); if (slash == NULL) goto done; if (slash == parent_path && parent_path[1] != '\0') parent_path[1] = '\0'; else *slash = '\0'; if (!strlen(parent_path)) goto done; if (dbus_connection_get_object_path_data(conn, parent_path, (void *) &data) == FALSE) { goto done; } parent = invalidate_parent_data(conn, parent_path); if (data == NULL) { data = parent; if (data == NULL) goto done; } g_free(data->introspect); data->introspect = NULL; if (!dbus_connection_get_object_path_data(conn, child_path, (void *) &child)) goto done; if (child == NULL || g_slist_find(data->objects, child) != NULL) goto done; data->objects = g_slist_prepend(data->objects, child); child->parent = data; done: g_free(parent_path); return data; } static inline const GDBusPropertyTable *find_property(const GDBusPropertyTable *properties, const char *name) { const GDBusPropertyTable *p; for (p = properties; p && p->name; p++) { if (strcmp(name, p->name) != 0) continue; if (check_experimental(p->flags, G_DBUS_PROPERTY_FLAG_EXPERIMENTAL)) break; return p; } return NULL; } static DBusMessage *properties_get(DBusConnection *connection, DBusMessage *message, void *user_data) { struct generic_data *data = user_data; struct interface_data *iface; const GDBusPropertyTable *property; const char *interface, *name; DBusMessageIter iter, value; DBusMessage *reply; if (!dbus_message_get_args(message, NULL, DBUS_TYPE_STRING, &interface, DBUS_TYPE_STRING, &name, DBUS_TYPE_INVALID)) return NULL; iface = find_interface(data->interfaces, interface); if (iface == NULL) return g_dbus_create_error(message, DBUS_ERROR_INVALID_ARGS, "No such interface '%s'", interface); property = find_property(iface->properties, name); if (property == NULL) return g_dbus_create_error(message, DBUS_ERROR_INVALID_ARGS, "No such property '%s'", name); if (property->exists != NULL && !property->exists(property, iface->user_data)) return g_dbus_create_error(message, DBUS_ERROR_INVALID_ARGS, "No such property '%s'", name); if (property->get == NULL) return g_dbus_create_error(message, DBUS_ERROR_INVALID_ARGS, "Property '%s' is not readable", name); reply = dbus_message_new_method_return(message); if (reply == NULL) return NULL; dbus_message_iter_init_append(reply, &iter); dbus_message_iter_open_container(&iter, DBUS_TYPE_VARIANT, property->type, &value); if (!property->get(property, &value, iface->user_data)) { dbus_message_unref(reply); return NULL; } dbus_message_iter_close_container(&iter, &value); return reply; } static DBusMessage *properties_get_all(DBusConnection *connection, DBusMessage *message, void *user_data) { struct generic_data *data = user_data; struct interface_data *iface; const char *interface; DBusMessageIter iter; DBusMessage *reply; if (!dbus_message_get_args(message, NULL, DBUS_TYPE_STRING, &interface, DBUS_TYPE_INVALID)) return NULL; iface = find_interface(data->interfaces, interface); if (iface == NULL) return g_dbus_create_error(message, DBUS_ERROR_INVALID_ARGS, "No such interface '%s'", interface); reply = dbus_message_new_method_return(message); if (reply == NULL) return NULL; dbus_message_iter_init_append(reply, &iter); append_properties(iface, &iter); return reply; } static DBusMessage *properties_set(DBusConnection *connection, DBusMessage *message, void *user_data) { struct generic_data *data = user_data; DBusMessageIter iter, sub; struct interface_data *iface; const GDBusPropertyTable *property; const char *name, *interface; struct property_data *propdata; gboolean valid_signature; char *signature; if (!dbus_message_iter_init(message, &iter)) return g_dbus_create_error(message, DBUS_ERROR_INVALID_ARGS, "No arguments given"); if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING) return g_dbus_create_error(message, DBUS_ERROR_INVALID_ARGS, "Invalid argument type: '%c'", dbus_message_iter_get_arg_type(&iter)); dbus_message_iter_get_basic(&iter, &interface); dbus_message_iter_next(&iter); if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING) return g_dbus_create_error(message, DBUS_ERROR_INVALID_ARGS, "Invalid argument type: '%c'", dbus_message_iter_get_arg_type(&iter)); dbus_message_iter_get_basic(&iter, &name); dbus_message_iter_next(&iter); if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT) return g_dbus_create_error(message, DBUS_ERROR_INVALID_ARGS, "Invalid argument type: '%c'", dbus_message_iter_get_arg_type(&iter)); dbus_message_iter_recurse(&iter, &sub); iface = find_interface(data->interfaces, interface); if (iface == NULL) return g_dbus_create_error(message, DBUS_ERROR_INVALID_ARGS, "No such interface '%s'", interface); property = find_property(iface->properties, name); if (property == NULL) return g_dbus_create_error(message, DBUS_ERROR_UNKNOWN_PROPERTY, "No such property '%s'", name); if (property->set == NULL) return g_dbus_create_error(message, DBUS_ERROR_PROPERTY_READ_ONLY, "Property '%s' is not writable", name); if (property->exists != NULL && !property->exists(property, iface->user_data)) return g_dbus_create_error(message, DBUS_ERROR_UNKNOWN_PROPERTY, "No such property '%s'", name); signature = dbus_message_iter_get_signature(&sub); valid_signature = strcmp(signature, property->type) ? FALSE : TRUE; dbus_free(signature); if (!valid_signature) return g_dbus_create_error(message, DBUS_ERROR_INVALID_SIGNATURE, "Invalid signature for '%s'", name); propdata = g_new(struct property_data, 1); propdata->id = next_pending_property++; propdata->message = dbus_message_ref(message); propdata->conn = connection; pending_property_set = g_slist_prepend(pending_property_set, propdata); property->set(property, &sub, propdata->id, iface->user_data); return NULL; } static const GDBusMethodTable properties_methods[] = { { GDBUS_METHOD("Get", GDBUS_ARGS({ "interface", "s" }, { "name", "s" }), GDBUS_ARGS({ "value", "v" }), properties_get) }, { GDBUS_ASYNC_METHOD("Set", GDBUS_ARGS({ "interface", "s" }, { "name", "s" }, { "value", "v" }), NULL, properties_set) }, { GDBUS_METHOD("GetAll", GDBUS_ARGS({ "interface", "s" }), GDBUS_ARGS({ "properties", "a{sv}" }), properties_get_all) }, { } }; static const GDBusSignalTable properties_signals[] = { { GDBUS_SIGNAL("PropertiesChanged", GDBUS_ARGS({ "interface", "s" }, { "changed_properties", "a{sv}" }, { "invalidated_properties", "as"})) }, { } }; static void append_name(gpointer data, gpointer user_data) { char *name = data; DBusMessageIter *iter = user_data; dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &name); } static void emit_interfaces_removed(struct generic_data *data) { DBusMessage *signal; DBusMessageIter iter, array; if (root == NULL || data == root) return; signal = dbus_message_new_signal(root->path, DBUS_INTERFACE_OBJECT_MANAGER, "InterfacesRemoved"); if (signal == NULL) return; dbus_message_iter_init_append(signal, &iter); dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH, &data->path); dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, DBUS_TYPE_STRING_AS_STRING, &array); g_slist_foreach(data->removed, append_name, &array); g_slist_free_full(data->removed, g_free); data->removed = NULL; dbus_message_iter_close_container(&iter, &array); g_dbus_send_message(data->conn, signal); } static gboolean process_changes(gpointer user_data) { struct generic_data *data = user_data; data->process_id = 0; if (data->added != NULL) emit_interfaces_added(data); /* Flush pending properties */ if (data->pending_prop == TRUE) process_property_changes(data); if (data->removed != NULL) emit_interfaces_removed(data); return FALSE; } static void generic_unregister(DBusConnection *connection, void *user_data) { struct generic_data *data = user_data; struct generic_data *parent = data->parent; if (parent != NULL) parent->objects = g_slist_remove(parent->objects, data); if (data->process_id > 0) { g_source_remove(data->process_id); process_changes(data); } g_slist_foreach(data->objects, reset_parent, data->parent); g_slist_free(data->objects); dbus_connection_unref(data->conn); g_free(data->introspect); g_free(data->path); g_free(data); } static DBusHandlerResult generic_message(DBusConnection *connection, DBusMessage *message, void *user_data) { struct generic_data *data = user_data; struct interface_data *iface; const GDBusMethodTable *method; const char *interface; interface = dbus_message_get_interface(message); iface = find_interface(data->interfaces, interface); if (iface == NULL) return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; for (method = iface->methods; method && method->name && method->function; method++) { if (dbus_message_is_method_call(message, iface->name, method->name) == FALSE) continue; if (check_experimental(method->flags, G_DBUS_METHOD_FLAG_EXPERIMENTAL)) return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; if (g_dbus_args_have_signature(method->in_args, message) == FALSE) continue; if (check_privilege(connection, message, method, iface->user_data) == TRUE) return DBUS_HANDLER_RESULT_HANDLED; return process_message(connection, message, method, iface->user_data); } return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; } static DBusObjectPathVTable generic_table = { .unregister_function = generic_unregister, .message_function = generic_message, }; static const GDBusMethodTable introspect_methods[] = { { GDBUS_METHOD("Introspect", NULL, GDBUS_ARGS({ "xml", "s" }), introspect) }, { } }; static void append_interfaces(struct generic_data *data, DBusMessageIter *iter) { DBusMessageIter array; GSList *l; dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_ARRAY_AS_STRING DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING DBUS_DICT_ENTRY_END_CHAR_AS_STRING DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &array); for (l = data->interfaces; l != NULL; l = l->next) { if (g_slist_find(data->added, l->data)) continue; append_interface(l->data, &array); } dbus_message_iter_close_container(iter, &array); } static void append_object(gpointer data, gpointer user_data) { struct generic_data *child = data; DBusMessageIter *array = user_data; DBusMessageIter entry; dbus_message_iter_open_container(array, DBUS_TYPE_DICT_ENTRY, NULL, &entry); dbus_message_iter_append_basic(&entry, DBUS_TYPE_OBJECT_PATH, &child->path); append_interfaces(child, &entry); dbus_message_iter_close_container(array, &entry); g_slist_foreach(child->objects, append_object, user_data); } static DBusMessage *get_objects(DBusConnection *connection, DBusMessage *message, void *user_data) { struct generic_data *data = user_data; DBusMessage *reply; DBusMessageIter iter; DBusMessageIter array; reply = dbus_message_new_method_return(message); if (reply == NULL) return NULL; dbus_message_iter_init_append(reply, &iter); dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING DBUS_TYPE_OBJECT_PATH_AS_STRING DBUS_TYPE_ARRAY_AS_STRING DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_ARRAY_AS_STRING DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING DBUS_DICT_ENTRY_END_CHAR_AS_STRING DBUS_DICT_ENTRY_END_CHAR_AS_STRING DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &array); g_slist_foreach(data->objects, append_object, &array); dbus_message_iter_close_container(&iter, &array); return reply; } static const GDBusMethodTable manager_methods[] = { { GDBUS_METHOD("GetManagedObjects", NULL, GDBUS_ARGS({ "objects", "a{oa{sa{sv}}}" }), get_objects) }, { } }; static const GDBusSignalTable manager_signals[] = { { GDBUS_SIGNAL("InterfacesAdded", GDBUS_ARGS({ "object", "o" }, { "interfaces", "a{sa{sv}}" })) }, { GDBUS_SIGNAL("InterfacesRemoved", GDBUS_ARGS({ "object", "o" }, { "interfaces", "as" })) }, { } }; static gboolean add_interface(struct generic_data *data, const char *name, const GDBusMethodTable *methods, const GDBusSignalTable *signals, const GDBusPropertyTable *properties, void *user_data, GDBusDestroyFunction destroy) { struct interface_data *iface; const GDBusMethodTable *method; const GDBusSignalTable *signal; const GDBusPropertyTable *property; for (method = methods; method && method->name; method++) { if (!check_experimental(method->flags, G_DBUS_METHOD_FLAG_EXPERIMENTAL)) goto done; } for (signal = signals; signal && signal->name; signal++) { if (!check_experimental(signal->flags, G_DBUS_SIGNAL_FLAG_EXPERIMENTAL)) goto done; } for (property = properties; property && property->name; property++) { if (!check_experimental(property->flags, G_DBUS_PROPERTY_FLAG_EXPERIMENTAL)) goto done; } /* Nothing to register */ return FALSE; done: iface = g_new0(struct interface_data, 1); iface->name = g_strdup(name); iface->methods = methods; iface->signals = signals; iface->properties = properties; iface->user_data = user_data; iface->destroy = destroy; data->interfaces = g_slist_append(data->interfaces, iface); if (data->parent == NULL) return TRUE; data->added = g_slist_append(data->added, iface); if (data->process_id > 0) return TRUE; data->process_id = g_idle_add(process_changes, data); return TRUE; } static struct generic_data *object_path_ref(DBusConnection *connection, const char *path) { struct generic_data *data; if (dbus_connection_get_object_path_data(connection, path, (void *) &data) == TRUE) { if (data != NULL) { data->refcount++; return data; } } data = g_new0(struct generic_data, 1); data->conn = dbus_connection_ref(connection); data->path = g_strdup(path); data->refcount = 1; data->introspect = g_strdup(DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE ""); if (!dbus_connection_register_object_path(connection, path, &generic_table, data)) { g_free(data->introspect); g_free(data); return NULL; } invalidate_parent_data(connection, path); add_interface(data, DBUS_INTERFACE_INTROSPECTABLE, introspect_methods, NULL, NULL, data, NULL); return data; } static void object_path_unref(DBusConnection *connection, const char *path) { struct generic_data *data = NULL; if (dbus_connection_get_object_path_data(connection, path, (void *) &data) == FALSE) return; if (data == NULL) return; data->refcount--; if (data->refcount > 0) return; remove_interface(data, DBUS_INTERFACE_INTROSPECTABLE); remove_interface(data, DBUS_INTERFACE_PROPERTIES); invalidate_parent_data(data->conn, data->path); dbus_connection_unregister_object_path(data->conn, data->path); } static gboolean check_signal(DBusConnection *conn, const char *path, const char *interface, const char *name, const GDBusArgInfo **args) { struct generic_data *data = NULL; struct interface_data *iface; const GDBusSignalTable *signal; *args = NULL; if (!dbus_connection_get_object_path_data(conn, path, (void *) &data) || data == NULL) { error("dbus_connection_emit_signal: path %s isn't registered", path); return FALSE; } iface = find_interface(data->interfaces, interface); if (iface == NULL) { error("dbus_connection_emit_signal: %s does not implement %s", path, interface); return FALSE; } for (signal = iface->signals; signal && signal->name; signal++) { if (strcmp(signal->name, name) != 0) continue; if (signal->flags & G_DBUS_SIGNAL_FLAG_EXPERIMENTAL) { const char *env = g_getenv("GDBUS_EXPERIMENTAL"); if (g_strcmp0(env, "1") != 0) break; } *args = signal->args; return TRUE; } error("No signal named %s on interface %s", name, interface); return FALSE; } static dbus_bool_t emit_signal_valist(DBusConnection *conn, const char *path, const char *interface, const char *name, int first, va_list var_args) { DBusMessage *signal; dbus_bool_t ret; const GDBusArgInfo *args; if (!check_signal(conn, path, interface, name, &args)) return FALSE; signal = dbus_message_new_signal(path, interface, name); if (signal == NULL) { error("Unable to allocate new %s.%s signal", interface, name); return FALSE; } ret = dbus_message_append_args_valist(signal, first, var_args); if (!ret) goto fail; if (g_dbus_args_have_signature(args, signal) == FALSE) { error("%s.%s: got unexpected signature '%s'", interface, name, dbus_message_get_signature(signal)); ret = FALSE; goto fail; } ret = dbus_connection_send(conn, signal, NULL); fail: dbus_message_unref(signal); return ret; } gboolean g_dbus_register_interface(DBusConnection *connection, const char *path, const char *name, const GDBusMethodTable *methods, const GDBusSignalTable *signals, const GDBusPropertyTable *properties, void *user_data, GDBusDestroyFunction destroy) { struct generic_data *data; data = object_path_ref(connection, path); if (data == NULL) return FALSE; if (find_interface(data->interfaces, name)) { object_path_unref(connection, path); return FALSE; } if (!add_interface(data, name, methods, signals, properties, user_data, destroy)) { object_path_unref(connection, path); return FALSE; } if (properties != NULL && !find_interface(data->interfaces, DBUS_INTERFACE_PROPERTIES)) add_interface(data, DBUS_INTERFACE_PROPERTIES, properties_methods, properties_signals, NULL, data, NULL); g_free(data->introspect); data->introspect = NULL; return TRUE; } gboolean g_dbus_unregister_interface(DBusConnection *connection, const char *path, const char *name) { struct generic_data *data = NULL; if (path == NULL) return FALSE; if (dbus_connection_get_object_path_data(connection, path, (void *) &data) == FALSE) return FALSE; if (data == NULL) return FALSE; if (remove_interface(data, name) == FALSE) return FALSE; g_free(data->introspect); data->introspect = NULL; object_path_unref(connection, data->path); return TRUE; } gboolean g_dbus_register_security(const GDBusSecurityTable *security) { if (security_table != NULL) return FALSE; security_table = security; return TRUE; } gboolean g_dbus_unregister_security(const GDBusSecurityTable *security) { security_table = NULL; return TRUE; } DBusMessage *g_dbus_create_error_valist(DBusMessage *message, const char *name, const char *format, va_list args) { char str[1024]; vsnprintf(str, sizeof(str), format, args); return dbus_message_new_error(message, name, str); } DBusMessage *g_dbus_create_error(DBusMessage *message, const char *name, const char *format, ...) { va_list args; DBusMessage *reply; va_start(args, format); reply = g_dbus_create_error_valist(message, name, format, args); va_end(args); return reply; } DBusMessage *g_dbus_create_reply_valist(DBusMessage *message, int type, va_list args) { DBusMessage *reply; reply = dbus_message_new_method_return(message); if (reply == NULL) return NULL; if (dbus_message_append_args_valist(reply, type, args) == FALSE) { dbus_message_unref(reply); return NULL; } return reply; } DBusMessage *g_dbus_create_reply(DBusMessage *message, int type, ...) { va_list args; DBusMessage *reply; va_start(args, type); reply = g_dbus_create_reply_valist(message, type, args); va_end(args); return reply; } gboolean g_dbus_send_message(DBusConnection *connection, DBusMessage *message) { dbus_bool_t result = FALSE; if (dbus_message_get_type(message) == DBUS_MESSAGE_TYPE_METHOD_CALL) dbus_message_set_no_reply(message, TRUE); else if (dbus_message_get_type(message) == DBUS_MESSAGE_TYPE_SIGNAL) { const char *path = dbus_message_get_path(message); const char *interface = dbus_message_get_interface(message); const char *name = dbus_message_get_member(message); const GDBusArgInfo *args; if (!check_signal(connection, path, interface, name, &args)) goto out; } result = dbus_connection_send(connection, message, NULL); out: dbus_message_unref(message); return result; } gboolean g_dbus_send_error_valist(DBusConnection *connection, DBusMessage *message, const char *name, const char *format, va_list args) { DBusMessage *error; char str[1024]; vsnprintf(str, sizeof(str), format, args); error = dbus_message_new_error(message, name, str); if (error == NULL) return FALSE; return g_dbus_send_message(connection, error); } gboolean g_dbus_send_error(DBusConnection *connection, DBusMessage *message, const char *name, const char *format, ...) { va_list args; gboolean result; va_start(args, format); result = g_dbus_send_error_valist(connection, message, name, format, args); va_end(args); return result; } gboolean g_dbus_send_reply_valist(DBusConnection *connection, DBusMessage *message, int type, va_list args) { DBusMessage *reply; reply = dbus_message_new_method_return(message); if (reply == NULL) return FALSE; if (dbus_message_append_args_valist(reply, type, args) == FALSE) { dbus_message_unref(reply); return FALSE; } return g_dbus_send_message(connection, reply); } gboolean g_dbus_send_reply(DBusConnection *connection, DBusMessage *message, int type, ...) { va_list args; gboolean result; va_start(args, type); result = g_dbus_send_reply_valist(connection, message, type, args); va_end(args); return result; } gboolean g_dbus_emit_signal(DBusConnection *connection, const char *path, const char *interface, const char *name, int type, ...) { va_list args; gboolean result; va_start(args, type); result = emit_signal_valist(connection, path, interface, name, type, args); va_end(args); return result; } gboolean g_dbus_emit_signal_valist(DBusConnection *connection, const char *path, const char *interface, const char *name, int type, va_list args) { return emit_signal_valist(connection, path, interface, name, type, args); } static void process_properties_from_interface(struct generic_data *data, struct interface_data *iface) { GSList *l; DBusMessage *signal; DBusMessageIter iter, dict, array; GSList *invalidated; if (iface->pending_prop == NULL) return; signal = dbus_message_new_signal(data->path, DBUS_INTERFACE_PROPERTIES, "PropertiesChanged"); if (signal == NULL) { error("Unable to allocate new " DBUS_INTERFACE_PROPERTIES ".PropertiesChanged signal"); return; } iface->pending_prop = g_slist_reverse(iface->pending_prop); dbus_message_iter_init_append(signal, &iter); dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &iface->name); dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict); invalidated = NULL; for (l = iface->pending_prop; l != NULL; l = l->next) { GDBusPropertyTable *p = l->data; if (p->get == NULL) continue; if (p->exists != NULL && !p->exists(p, iface->user_data)) { invalidated = g_slist_prepend(invalidated, p); continue; } append_property(iface, p, &dict); } dbus_message_iter_close_container(&iter, &dict); dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, DBUS_TYPE_STRING_AS_STRING, &array); for (l = invalidated; l != NULL; l = g_slist_next(l)) { GDBusPropertyTable *p = l->data; dbus_message_iter_append_basic(&array, DBUS_TYPE_STRING, &p->name); } g_slist_free(invalidated); dbus_message_iter_close_container(&iter, &array); g_dbus_send_message(data->conn, signal); g_slist_free(iface->pending_prop); iface->pending_prop = NULL; } static void process_property_changes(struct generic_data *data) { GSList *l; for (l = data->interfaces; l != NULL; l = l->next) { struct interface_data *iface = l->data; process_properties_from_interface(data, iface); } data->pending_prop = FALSE; } void g_dbus_emit_property_changed(DBusConnection *connection, const char *path, const char *interface, const char *name) { const GDBusPropertyTable *property; struct generic_data *data; struct interface_data *iface; if (path == NULL) return; if (!dbus_connection_get_object_path_data(connection, path, (void **) &data) || data == NULL) return; iface = find_interface(data->interfaces, interface); if (iface == NULL) return; /* * If ObjectManager is attached, don't emit property changed if * interface is not yet published */ if (root && g_slist_find(data->added, iface)) return; property = find_property(iface->properties, name); if (property == NULL) { error("Could not find property %s in %p", name, iface->properties); return; } if (g_slist_find(iface->pending_prop, (void *) property) != NULL) return; data->pending_prop = TRUE; iface->pending_prop = g_slist_prepend(iface->pending_prop, (void *) property); if (!data->process_id) { data->process_id = g_idle_add(process_changes, data); return; } } gboolean g_dbus_get_properties(DBusConnection *connection, const char *path, const char *interface, DBusMessageIter *iter) { struct generic_data *data; struct interface_data *iface; if (path == NULL) return FALSE; if (!dbus_connection_get_object_path_data(connection, path, (void **) &data) || data == NULL) return FALSE; iface = find_interface(data->interfaces, interface); if (iface == NULL) return FALSE; append_properties(iface, iter); return TRUE; } gboolean g_dbus_attach_object_manager(DBusConnection *connection) { struct generic_data *data; data = object_path_ref(connection, "/"); if (data == NULL) return FALSE; add_interface(data, DBUS_INTERFACE_OBJECT_MANAGER, manager_methods, manager_signals, NULL, data, NULL); root = data; return TRUE; } gboolean g_dbus_detach_object_manager(DBusConnection *connection) { if (!g_dbus_unregister_interface(connection, "/", DBUS_INTERFACE_OBJECT_MANAGER)) return FALSE; root = NULL; return TRUE; } void g_dbus_set_flags(int flags) { global_flags = flags; } mmsd-0.1/gdbus/polkit.c000066400000000000000000000126201216024552400150660ustar00rootroot00000000000000/* * * D-Bus helper library * * Copyright (C) 2004-2011 Marcel Holtmann * * * 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 * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include int polkit_check_authorization(DBusConnection *conn, const char *action, gboolean interaction, void (*function) (dbus_bool_t authorized, void *user_data), void *user_data, int timeout); static void add_dict_with_string_value(DBusMessageIter *iter, const char *key, const char *str) { DBusMessageIter dict, entry, value; dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict); dbus_message_iter_open_container(&dict, DBUS_TYPE_DICT_ENTRY, NULL, &entry); dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key); dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT, DBUS_TYPE_STRING_AS_STRING, &value); dbus_message_iter_append_basic(&value, DBUS_TYPE_STRING, &str); dbus_message_iter_close_container(&entry, &value); dbus_message_iter_close_container(&dict, &entry); dbus_message_iter_close_container(iter, &dict); } static void add_empty_string_dict(DBusMessageIter *iter) { DBusMessageIter dict; dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_STRING_AS_STRING DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict); dbus_message_iter_close_container(iter, &dict); } static void add_arguments(DBusConnection *conn, DBusMessageIter *iter, const char *action, dbus_uint32_t flags) { const char *busname = dbus_bus_get_unique_name(conn); const char *kind = "system-bus-name"; const char *cancel = ""; DBusMessageIter subject; dbus_message_iter_open_container(iter, DBUS_TYPE_STRUCT, NULL, &subject); dbus_message_iter_append_basic(&subject, DBUS_TYPE_STRING, &kind); add_dict_with_string_value(&subject, "name", busname); dbus_message_iter_close_container(iter, &subject); dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &action); add_empty_string_dict(iter); dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT32, &flags); dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &cancel); } static dbus_bool_t parse_result(DBusMessageIter *iter) { DBusMessageIter result; dbus_bool_t authorized, challenge; dbus_message_iter_recurse(iter, &result); dbus_message_iter_get_basic(&result, &authorized); dbus_message_iter_get_basic(&result, &challenge); return authorized; } struct authorization_data { void (*function) (dbus_bool_t authorized, void *user_data); void *user_data; }; static void authorization_reply(DBusPendingCall *call, void *user_data) { struct authorization_data *data = user_data; DBusMessage *reply; DBusMessageIter iter; dbus_bool_t authorized = FALSE; reply = dbus_pending_call_steal_reply(call); if (dbus_message_get_type(reply) == DBUS_MESSAGE_TYPE_ERROR) goto done; if (dbus_message_has_signature(reply, "(bba{ss})") == FALSE) goto done; dbus_message_iter_init(reply, &iter); authorized = parse_result(&iter); done: if (data->function != NULL) data->function(authorized, data->user_data); dbus_message_unref(reply); dbus_pending_call_unref(call); } #define AUTHORITY_DBUS "org.freedesktop.PolicyKit1" #define AUTHORITY_INTF "org.freedesktop.PolicyKit1.Authority" #define AUTHORITY_PATH "/org/freedesktop/PolicyKit1/Authority" int polkit_check_authorization(DBusConnection *conn, const char *action, gboolean interaction, void (*function) (dbus_bool_t authorized, void *user_data), void *user_data, int timeout) { struct authorization_data *data; DBusMessage *msg; DBusMessageIter iter; DBusPendingCall *call; dbus_uint32_t flags = 0x00000000; if (conn == NULL) return -EINVAL; data = dbus_malloc0(sizeof(*data)); if (data == NULL) return -ENOMEM; msg = dbus_message_new_method_call(AUTHORITY_DBUS, AUTHORITY_PATH, AUTHORITY_INTF, "CheckAuthorization"); if (msg == NULL) { dbus_free(data); return -ENOMEM; } if (interaction == TRUE) flags |= 0x00000001; if (action == NULL) action = "org.freedesktop.policykit.exec"; dbus_message_iter_init_append(msg, &iter); add_arguments(conn, &iter, action, flags); if (dbus_connection_send_with_reply(conn, msg, &call, timeout) == FALSE) { dbus_message_unref(msg); dbus_free(data); return -EIO; } if (call == NULL) { dbus_message_unref(msg); dbus_free(data); return -EIO; } data->function = function; data->user_data = user_data; dbus_pending_call_set_notify(call, authorization_reply, data, dbus_free); dbus_message_unref(msg); return 0; } mmsd-0.1/gdbus/watch.c000066400000000000000000000444061216024552400147010ustar00rootroot00000000000000/* * * D-Bus helper library * * Copyright (C) 2004-2011 Marcel Holtmann * * * 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 * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include "gdbus.h" #define info(fmt...) #define error(fmt...) #define debug(fmt...) static DBusHandlerResult message_filter(DBusConnection *connection, DBusMessage *message, void *user_data); static guint listener_id = 0; static GSList *listeners = NULL; struct service_data { DBusConnection *conn; DBusPendingCall *call; char *name; const char *owner; guint id; struct filter_callback *callback; }; struct filter_callback { GDBusWatchFunction conn_func; GDBusWatchFunction disc_func; GDBusSignalFunction signal_func; GDBusDestroyFunction destroy_func; struct service_data *data; void *user_data; guint id; }; struct filter_data { DBusConnection *connection; DBusHandleMessageFunction handle_func; char *name; char *owner; char *path; char *interface; char *member; char *argument; GSList *callbacks; GSList *processed; guint name_watch; gboolean lock; gboolean registered; }; static struct filter_data *filter_data_find_match(DBusConnection *connection, const char *name, const char *owner, const char *path, const char *interface, const char *member, const char *argument) { GSList *current; for (current = listeners; current != NULL; current = current->next) { struct filter_data *data = current->data; if (connection != data->connection) continue; if (g_strcmp0(name, data->name) != 0) continue; if (g_strcmp0(owner, data->owner) != 0) continue; if (g_strcmp0(path, data->path) != 0) continue; if (g_strcmp0(interface, data->interface) != 0) continue; if (g_strcmp0(member, data->member) != 0) continue; if (g_strcmp0(argument, data->argument) != 0) continue; return data; } return NULL; } static struct filter_data *filter_data_find(DBusConnection *connection) { GSList *current; for (current = listeners; current != NULL; current = current->next) { struct filter_data *data = current->data; if (connection != data->connection) continue; return data; } return NULL; } static void format_rule(struct filter_data *data, char *rule, size_t size) { const char *sender; int offset; offset = snprintf(rule, size, "type='signal'"); sender = data->name ? : data->owner; if (sender) offset += snprintf(rule + offset, size - offset, ",sender='%s'", sender); if (data->path) offset += snprintf(rule + offset, size - offset, ",path='%s'", data->path); if (data->interface) offset += snprintf(rule + offset, size - offset, ",interface='%s'", data->interface); if (data->member) offset += snprintf(rule + offset, size - offset, ",member='%s'", data->member); if (data->argument) snprintf(rule + offset, size - offset, ",arg0='%s'", data->argument); } static gboolean add_match(struct filter_data *data, DBusHandleMessageFunction filter) { DBusError err; char rule[DBUS_MAXIMUM_MATCH_RULE_LENGTH]; format_rule(data, rule, sizeof(rule)); dbus_error_init(&err); dbus_bus_add_match(data->connection, rule, &err); if (dbus_error_is_set(&err)) { error("Adding match rule \"%s\" failed: %s", rule, err.message); dbus_error_free(&err); return FALSE; } data->handle_func = filter; data->registered = TRUE; return TRUE; } static gboolean remove_match(struct filter_data *data) { DBusError err; char rule[DBUS_MAXIMUM_MATCH_RULE_LENGTH]; format_rule(data, rule, sizeof(rule)); dbus_error_init(&err); dbus_bus_remove_match(data->connection, rule, &err); if (dbus_error_is_set(&err)) { error("Removing owner match rule for %s failed: %s", rule, err.message); dbus_error_free(&err); return FALSE; } return TRUE; } static struct filter_data *filter_data_get(DBusConnection *connection, DBusHandleMessageFunction filter, const char *sender, const char *path, const char *interface, const char *member, const char *argument) { struct filter_data *data; const char *name = NULL, *owner = NULL; if (filter_data_find(connection) == NULL) { if (!dbus_connection_add_filter(connection, message_filter, NULL, NULL)) { error("dbus_connection_add_filter() failed"); return NULL; } } if (sender == NULL) goto proceed; if (sender[0] == ':') owner = sender; else name = sender; proceed: data = filter_data_find_match(connection, name, owner, path, interface, member, argument); if (data) return data; data = g_new0(struct filter_data, 1); data->connection = dbus_connection_ref(connection); data->name = g_strdup(name); data->owner = g_strdup(owner); data->path = g_strdup(path); data->interface = g_strdup(interface); data->member = g_strdup(member); data->argument = g_strdup(argument); if (!add_match(data, filter)) { g_free(data); return NULL; } listeners = g_slist_append(listeners, data); return data; } static struct filter_callback *filter_data_find_callback( struct filter_data *data, guint id) { GSList *l; for (l = data->callbacks; l; l = l->next) { struct filter_callback *cb = l->data; if (cb->id == id) return cb; } for (l = data->processed; l; l = l->next) { struct filter_callback *cb = l->data; if (cb->id == id) return cb; } return NULL; } static void filter_data_free(struct filter_data *data) { GSList *l; for (l = data->callbacks; l != NULL; l = l->next) g_free(l->data); g_slist_free(data->callbacks); g_dbus_remove_watch(data->connection, data->name_watch); g_free(data->name); g_free(data->owner); g_free(data->path); g_free(data->interface); g_free(data->member); g_free(data->argument); dbus_connection_unref(data->connection); g_free(data); } static void filter_data_call_and_free(struct filter_data *data) { GSList *l; for (l = data->callbacks; l != NULL; l = l->next) { struct filter_callback *cb = l->data; if (cb->disc_func) cb->disc_func(data->connection, cb->user_data); if (cb->destroy_func) cb->destroy_func(cb->user_data); g_free(cb); } filter_data_free(data); } static struct filter_callback *filter_data_add_callback( struct filter_data *data, GDBusWatchFunction connect, GDBusWatchFunction disconnect, GDBusSignalFunction signal, GDBusDestroyFunction destroy, void *user_data) { struct filter_callback *cb = NULL; cb = g_new0(struct filter_callback, 1); cb->conn_func = connect; cb->disc_func = disconnect; cb->signal_func = signal; cb->destroy_func = destroy; cb->user_data = user_data; cb->id = ++listener_id; if (data->lock) data->processed = g_slist_append(data->processed, cb); else data->callbacks = g_slist_append(data->callbacks, cb); return cb; } static void service_data_free(struct service_data *data) { struct filter_callback *callback = data->callback; dbus_connection_unref(data->conn); if (data->call) dbus_pending_call_unref(data->call); if (data->id) g_source_remove(data->id); g_free(data->name); g_free(data); callback->data = NULL; } static gboolean filter_data_remove_callback(struct filter_data *data, struct filter_callback *cb) { DBusConnection *connection; data->callbacks = g_slist_remove(data->callbacks, cb); data->processed = g_slist_remove(data->processed, cb); /* Cancel pending operations */ if (cb->data) { if (cb->data->call) dbus_pending_call_cancel(cb->data->call); service_data_free(cb->data); } if (cb->destroy_func) cb->destroy_func(cb->user_data); g_free(cb); /* Don't remove the filter if other callbacks exist or data is lock * processing callbacks */ if (data->callbacks || data->lock) return TRUE; if (data->registered && !remove_match(data)) return FALSE; connection = dbus_connection_ref(data->connection); listeners = g_slist_remove(listeners, data); /* Remove filter if there are no listeners left for the connection */ if (filter_data_find(connection) == NULL) dbus_connection_remove_filter(connection, message_filter, NULL); filter_data_free(data); dbus_connection_unref(connection); return TRUE; } static DBusHandlerResult signal_filter(DBusConnection *connection, DBusMessage *message, void *user_data) { struct filter_data *data = user_data; struct filter_callback *cb; while (data->callbacks) { cb = data->callbacks->data; if (cb->signal_func && !cb->signal_func(connection, message, cb->user_data)) { filter_data_remove_callback(data, cb); continue; } /* Check if the watch was removed/freed by the callback * function */ if (!g_slist_find(data->callbacks, cb)) continue; data->callbacks = g_slist_remove(data->callbacks, cb); data->processed = g_slist_append(data->processed, cb); } return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; } static void update_name_cache(const char *name, const char *owner) { GSList *l; for (l = listeners; l != NULL; l = l->next) { struct filter_data *data = l->data; if (g_strcmp0(data->name, name) != 0) continue; g_free(data->owner); data->owner = g_strdup(owner); } } static const char *check_name_cache(const char *name) { GSList *l; for (l = listeners; l != NULL; l = l->next) { struct filter_data *data = l->data; if (g_strcmp0(data->name, name) != 0) continue; return data->owner; } return NULL; } static DBusHandlerResult service_filter(DBusConnection *connection, DBusMessage *message, void *user_data) { struct filter_data *data = user_data; struct filter_callback *cb; char *name, *old, *new; if (!dbus_message_get_args(message, NULL, DBUS_TYPE_STRING, &name, DBUS_TYPE_STRING, &old, DBUS_TYPE_STRING, &new, DBUS_TYPE_INVALID)) { error("Invalid arguments for NameOwnerChanged signal"); return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; } update_name_cache(name, new); while (data->callbacks) { cb = data->callbacks->data; if (*new == '\0') { if (cb->disc_func) cb->disc_func(connection, cb->user_data); } else { if (cb->conn_func) cb->conn_func(connection, cb->user_data); } /* Check if the watch was removed/freed by the callback * function */ if (!g_slist_find(data->callbacks, cb)) continue; /* Only auto remove if it is a bus name watch */ if (data->argument[0] == ':' && (cb->conn_func == NULL || cb->disc_func == NULL)) { filter_data_remove_callback(data, cb); continue; } data->callbacks = g_slist_remove(data->callbacks, cb); data->processed = g_slist_append(data->processed, cb); } return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; } static DBusHandlerResult message_filter(DBusConnection *connection, DBusMessage *message, void *user_data) { struct filter_data *data; const char *sender, *path, *iface, *member, *arg = NULL; GSList *current, *delete_listener = NULL; /* Only filter signals */ if (dbus_message_get_type(message) != DBUS_MESSAGE_TYPE_SIGNAL) return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; sender = dbus_message_get_sender(message); path = dbus_message_get_path(message); iface = dbus_message_get_interface(message); member = dbus_message_get_member(message); dbus_message_get_args(message, NULL, DBUS_TYPE_STRING, &arg, DBUS_TYPE_INVALID); /* Sender is always the owner */ for (current = listeners; current != NULL; current = current->next) { data = current->data; if (connection != data->connection) continue; if (data->owner && g_str_equal(sender, data->owner) == FALSE) continue; if (data->path && g_str_equal(path, data->path) == FALSE) continue; if (data->interface && g_str_equal(iface, data->interface) == FALSE) continue; if (data->member && g_str_equal(member, data->member) == FALSE) continue; if (data->argument && g_str_equal(arg, data->argument) == FALSE) continue; if (data->handle_func) { data->lock = TRUE; data->handle_func(connection, message, data); data->callbacks = data->processed; data->processed = NULL; data->lock = FALSE; } if (!data->callbacks) delete_listener = g_slist_prepend(delete_listener, current); } for (current = delete_listener; current != NULL; current = delete_listener->next) { GSList *l = current->data; data = l->data; /* Has any other callback added callbacks back to this data? */ if (data->callbacks != NULL) continue; remove_match(data); listeners = g_slist_delete_link(listeners, l); filter_data_free(data); } g_slist_free(delete_listener); /* Remove filter if there are no listeners left for the connection */ if (filter_data_find(connection) == NULL) dbus_connection_remove_filter(connection, message_filter, NULL); return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; } static gboolean update_service(void *user_data) { struct service_data *data = user_data; struct filter_callback *cb = data->callback; update_name_cache(data->name, data->owner); if (cb->conn_func) cb->conn_func(data->conn, cb->user_data); service_data_free(data); return FALSE; } static void service_reply(DBusPendingCall *call, void *user_data) { struct service_data *data = user_data; DBusMessage *reply; DBusError err; reply = dbus_pending_call_steal_reply(call); if (reply == NULL) return; dbus_error_init(&err); if (dbus_set_error_from_message(&err, reply)) goto fail; if (dbus_message_get_args(reply, &err, DBUS_TYPE_STRING, &data->owner, DBUS_TYPE_INVALID) == FALSE) goto fail; update_service(data); goto done; fail: error("%s", err.message); dbus_error_free(&err); service_data_free(data); done: dbus_message_unref(reply); } static void check_service(DBusConnection *connection, const char *name, struct filter_callback *callback) { DBusMessage *message; struct service_data *data; data = g_try_malloc0(sizeof(*data)); if (data == NULL) { error("Can't allocate data structure"); return; } data->conn = dbus_connection_ref(connection); data->name = g_strdup(name); data->callback = callback; callback->data = data; data->owner = check_name_cache(name); if (data->owner != NULL) { data->id = g_idle_add(update_service, data); return; } message = dbus_message_new_method_call(DBUS_SERVICE_DBUS, DBUS_PATH_DBUS, DBUS_INTERFACE_DBUS, "GetNameOwner"); if (message == NULL) { error("Can't allocate new message"); g_free(data); return; } dbus_message_append_args(message, DBUS_TYPE_STRING, &name, DBUS_TYPE_INVALID); if (dbus_connection_send_with_reply(connection, message, &data->call, -1) == FALSE) { error("Failed to execute method call"); g_free(data); goto done; } if (data->call == NULL) { error("D-Bus connection not available"); g_free(data); goto done; } dbus_pending_call_set_notify(data->call, service_reply, data, NULL); done: dbus_message_unref(message); } guint g_dbus_add_service_watch(DBusConnection *connection, const char *name, GDBusWatchFunction connect, GDBusWatchFunction disconnect, void *user_data, GDBusDestroyFunction destroy) { struct filter_data *data; struct filter_callback *cb; if (name == NULL) return 0; data = filter_data_get(connection, service_filter, NULL, NULL, DBUS_INTERFACE_DBUS, "NameOwnerChanged", name); if (data == NULL) return 0; cb = filter_data_add_callback(data, connect, disconnect, NULL, destroy, user_data); if (cb == NULL) return 0; if (connect) check_service(connection, name, cb); return cb->id; } guint g_dbus_add_disconnect_watch(DBusConnection *connection, const char *name, GDBusWatchFunction func, void *user_data, GDBusDestroyFunction destroy) { return g_dbus_add_service_watch(connection, name, NULL, func, user_data, destroy); } guint g_dbus_add_signal_watch(DBusConnection *connection, const char *sender, const char *path, const char *interface, const char *member, GDBusSignalFunction function, void *user_data, GDBusDestroyFunction destroy) { struct filter_data *data; struct filter_callback *cb; data = filter_data_get(connection, signal_filter, sender, path, interface, member, NULL); if (data == NULL) return 0; cb = filter_data_add_callback(data, NULL, NULL, function, destroy, user_data); if (cb == NULL) return 0; if (data->name != NULL && data->name_watch == 0) data->name_watch = g_dbus_add_service_watch(connection, data->name, NULL, NULL, NULL, NULL); return cb->id; } guint g_dbus_add_properties_watch(DBusConnection *connection, const char *sender, const char *path, const char *interface, GDBusSignalFunction function, void *user_data, GDBusDestroyFunction destroy) { struct filter_data *data; struct filter_callback *cb; data = filter_data_get(connection, signal_filter, sender, path, DBUS_INTERFACE_PROPERTIES, "PropertiesChanged", interface); if (data == NULL) return 0; cb = filter_data_add_callback(data, NULL, NULL, function, destroy, user_data); if (cb == NULL) return 0; if (data->name != NULL && data->name_watch == 0) data->name_watch = g_dbus_add_service_watch(connection, data->name, NULL, NULL, NULL, NULL); return cb->id; } gboolean g_dbus_remove_watch(DBusConnection *connection, guint id) { struct filter_data *data; struct filter_callback *cb; GSList *ldata; if (id == 0) return FALSE; for (ldata = listeners; ldata; ldata = ldata->next) { data = ldata->data; cb = filter_data_find_callback(data, id); if (cb) { filter_data_remove_callback(data, cb); return TRUE; } } return FALSE; } void g_dbus_remove_all_watches(DBusConnection *connection) { struct filter_data *data; while ((data = filter_data_find(connection))) { listeners = g_slist_remove(listeners, data); filter_data_call_and_free(data); } dbus_connection_remove_filter(connection, message_filter, NULL); } mmsd-0.1/gweb/000077500000000000000000000000001216024552400132375ustar00rootroot00000000000000mmsd-0.1/gweb/giognutls.c000066400000000000000000000251071216024552400154230ustar00rootroot00000000000000/* * * Web service library with GLib integration * * Copyright (C) 2009-2012 Intel Corporation. All rights reserved. * * 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 "giognutls.h" //#define DBG(fmt, arg...) printf("%s: " fmt "\n" , __func__ , ## arg) #define DBG(fmt, arg...) typedef struct _GIOGnuTLSChannel GIOGnuTLSChannel; typedef struct _GIOGnuTLSWatch GIOGnuTLSWatch; struct _GIOGnuTLSChannel { GIOChannel channel; gint fd; gnutls_certificate_credentials_t cred; gnutls_session_t session; gboolean established; gboolean again; }; struct _GIOGnuTLSWatch { GSource source; GPollFD pollfd; GIOChannel *channel; GIOCondition condition; }; static volatile int global_init_done = 0; static inline void g_io_gnutls_global_init(void) { if (__sync_bool_compare_and_swap(&global_init_done, 0, 1) == TRUE) gnutls_global_init(); } static GIOStatus check_handshake(GIOChannel *channel, GError **err) { GIOGnuTLSChannel *gnutls_channel = (GIOGnuTLSChannel *) channel; int result; DBG("channel %p", channel); if (gnutls_channel->established == TRUE) return G_IO_STATUS_NORMAL; again: result = gnutls_handshake(gnutls_channel->session); if (result == GNUTLS_E_INTERRUPTED || result == GNUTLS_E_AGAIN) { GIOFlags flags = g_io_channel_get_flags(channel); if (gnutls_channel->again == TRUE) return G_IO_STATUS_AGAIN; if (flags & G_IO_FLAG_NONBLOCK) return G_IO_STATUS_AGAIN; goto again; } if (result < 0) { g_set_error(err, G_IO_CHANNEL_ERROR, G_IO_CHANNEL_ERROR_FAILED, "Handshake failed"); return G_IO_STATUS_ERROR; } gnutls_channel->established = TRUE; DBG("handshake done"); return G_IO_STATUS_NORMAL; } static GIOStatus g_io_gnutls_read(GIOChannel *channel, gchar *buf, gsize count, gsize *bytes_read, GError **err) { GIOGnuTLSChannel *gnutls_channel = (GIOGnuTLSChannel *) channel; GIOStatus status; ssize_t result; DBG("channel %p count %zu", channel, count); *bytes_read = 0; again: status = check_handshake(channel, err); if (status != G_IO_STATUS_NORMAL) return status; result = gnutls_record_recv(gnutls_channel->session, buf, count); DBG("result %zd", result); if (result == GNUTLS_E_REHANDSHAKE) { gnutls_channel->established = FALSE; goto again; } if (result == GNUTLS_E_INTERRUPTED || result == GNUTLS_E_AGAIN) { GIOFlags flags = g_io_channel_get_flags(channel); if (gnutls_channel->again == TRUE) return G_IO_STATUS_AGAIN; if (flags & G_IO_FLAG_NONBLOCK) return G_IO_STATUS_AGAIN; goto again; } if (result == GNUTLS_E_UNEXPECTED_PACKET_LENGTH) return G_IO_STATUS_EOF; if (result < 0) { g_set_error(err, G_IO_CHANNEL_ERROR, G_IO_CHANNEL_ERROR_FAILED, "Stream corrupted"); return G_IO_STATUS_ERROR; } *bytes_read = result; return (result > 0) ? G_IO_STATUS_NORMAL : G_IO_STATUS_EOF; } static GIOStatus g_io_gnutls_write(GIOChannel *channel, const gchar *buf, gsize count, gsize *bytes_written, GError **err) { GIOGnuTLSChannel *gnutls_channel = (GIOGnuTLSChannel *) channel; GIOStatus status; ssize_t result; DBG("channel %p count %zu", channel, count); *bytes_written = 0; again: status = check_handshake(channel, err); if (status != G_IO_STATUS_NORMAL) return status; result = gnutls_record_send(gnutls_channel->session, buf, count); DBG("result %zd", result); if (result == GNUTLS_E_REHANDSHAKE) { gnutls_channel->established = FALSE; goto again; } if (result == GNUTLS_E_INTERRUPTED || result == GNUTLS_E_AGAIN) { GIOFlags flags = g_io_channel_get_flags(channel); if (gnutls_channel->again == TRUE) return G_IO_STATUS_AGAIN; if (flags & G_IO_FLAG_NONBLOCK) return G_IO_STATUS_AGAIN; goto again; } if (result < 0) { g_set_error(err, G_IO_CHANNEL_ERROR, G_IO_CHANNEL_ERROR_FAILED, "Stream corrupted"); return G_IO_STATUS_ERROR; } *bytes_written = result; return (result > 0) ? G_IO_STATUS_NORMAL : G_IO_STATUS_EOF; } static GIOStatus g_io_gnutls_seek(GIOChannel *channel, gint64 offset, GSeekType type, GError **err) { DBG("channel %p", channel); g_set_error_literal(err, G_IO_CHANNEL_ERROR, G_IO_CHANNEL_ERROR_FAILED, "Not supported"); return G_IO_STATUS_ERROR; } static GIOStatus g_io_gnutls_close(GIOChannel *channel, GError **err) { GIOGnuTLSChannel *gnutls_channel = (GIOGnuTLSChannel *) channel; DBG("channel %p", channel); if (gnutls_channel->established == TRUE) gnutls_bye(gnutls_channel->session, GNUTLS_SHUT_RDWR); if (close(gnutls_channel->fd) < 0) { g_set_error_literal(err, G_IO_CHANNEL_ERROR, G_IO_CHANNEL_ERROR_FAILED, "Closing failed"); return G_IO_STATUS_ERROR; } return G_IO_STATUS_NORMAL; } static void g_io_gnutls_free(GIOChannel *channel) { GIOGnuTLSChannel *gnutls_channel = (GIOGnuTLSChannel *) channel; DBG("channel %p", channel); gnutls_deinit(gnutls_channel->session); gnutls_certificate_free_credentials(gnutls_channel->cred); g_free(gnutls_channel); } static GIOStatus g_io_gnutls_set_flags(GIOChannel *channel, GIOFlags flags, GError **err) { GIOGnuTLSChannel *gnutls_channel = (GIOGnuTLSChannel *) channel; glong fcntl_flags = 0; DBG("channel %p flags %u", channel, flags); if (flags & G_IO_FLAG_NONBLOCK) fcntl_flags |= O_NONBLOCK; if (fcntl(gnutls_channel->fd, F_SETFL, fcntl_flags) < 0) { g_set_error_literal(err, G_IO_CHANNEL_ERROR, G_IO_CHANNEL_ERROR_FAILED, "Setting flags failed"); return G_IO_STATUS_ERROR; } return G_IO_STATUS_NORMAL; } static GIOFlags g_io_gnutls_get_flags(GIOChannel *channel) { GIOGnuTLSChannel *gnutls_channel = (GIOGnuTLSChannel *) channel; GIOFlags flags = 0; glong fcntl_flags; DBG("channel %p", channel); fcntl_flags = fcntl(gnutls_channel->fd, F_GETFL); if (fcntl_flags < 0) return 0; if (fcntl_flags & O_NONBLOCK) flags |= G_IO_FLAG_NONBLOCK; return flags; } static gboolean g_io_gnutls_prepare(GSource *source, gint *timeout) { DBG("source %p", source); *timeout = -1; return FALSE; } static gboolean g_io_gnutls_check(GSource *source) { GIOGnuTLSWatch *watch = (GIOGnuTLSWatch *) source; GIOCondition condition = watch->pollfd.revents; DBG("source %p condition %u", source, condition); if (condition & watch->condition) return TRUE; return FALSE; } static gboolean g_io_gnutls_dispatch(GSource *source, GSourceFunc callback, gpointer user_data) { GIOGnuTLSWatch *watch = (GIOGnuTLSWatch *) source; GIOFunc func = (GIOFunc) callback; GIOCondition condition = watch->pollfd.revents; DBG("source %p condition %u", source, condition); if (func == NULL) return FALSE; return func(watch->channel, condition & watch->condition, user_data); } static void g_io_gnutls_finalize(GSource *source) { GIOGnuTLSWatch *watch = (GIOGnuTLSWatch *) source; DBG("source %p", source); g_io_channel_unref(watch->channel); } static GSourceFuncs gnutls_watch_funcs = { g_io_gnutls_prepare, g_io_gnutls_check, g_io_gnutls_dispatch, g_io_gnutls_finalize, }; static GSource *g_io_gnutls_create_watch(GIOChannel *channel, GIOCondition condition) { GIOGnuTLSChannel *gnutls_channel = (GIOGnuTLSChannel *) channel; GIOGnuTLSWatch *watch; GSource *source; DBG("channel %p condition %u", channel, condition); source = g_source_new(&gnutls_watch_funcs, sizeof(GIOGnuTLSWatch)); watch = (GIOGnuTLSWatch *) source; watch->channel = channel; g_io_channel_ref(channel); watch->condition = condition; watch->pollfd.fd = gnutls_channel->fd; watch->pollfd.events = condition; g_source_add_poll(source, &watch->pollfd); return source; } static GIOFuncs gnutls_channel_funcs = { g_io_gnutls_read, g_io_gnutls_write, g_io_gnutls_seek, g_io_gnutls_close, g_io_gnutls_create_watch, g_io_gnutls_free, g_io_gnutls_set_flags, g_io_gnutls_get_flags, }; static ssize_t g_io_gnutls_push_func(gnutls_transport_ptr_t transport_data, const void *buf, size_t count) { GIOGnuTLSChannel *gnutls_channel = transport_data; ssize_t result; DBG("count %zu", count); result = write(gnutls_channel->fd, buf, count); if (result < 0 && errno == EAGAIN) gnutls_channel->again = TRUE; else gnutls_channel->again = FALSE; DBG("result %zd", result); return result; } static ssize_t g_io_gnutls_pull_func(gnutls_transport_ptr_t transport_data, void *buf, size_t count) { GIOGnuTLSChannel *gnutls_channel = transport_data; ssize_t result; DBG("count %zu", count); result = read(gnutls_channel->fd, buf, count); if (result < 0 && errno == EAGAIN) gnutls_channel->again = TRUE; else gnutls_channel->again = FALSE; DBG("result %zd", result); return result; } gboolean g_io_channel_supports_tls(void) { return TRUE; } GIOChannel *g_io_channel_gnutls_new(int fd) { GIOGnuTLSChannel *gnutls_channel; GIOChannel *channel; int err; DBG(""); gnutls_channel = g_new(GIOGnuTLSChannel, 1); channel = (GIOChannel *) gnutls_channel; g_io_channel_init(channel); channel->funcs = &gnutls_channel_funcs; gnutls_channel->fd = fd; channel->is_seekable = FALSE; channel->is_readable = TRUE; channel->is_writeable = TRUE; channel->do_encode = FALSE; g_io_gnutls_global_init(); err = gnutls_init(&gnutls_channel->session, GNUTLS_CLIENT); if (err < 0) { g_free(gnutls_channel); return NULL; } gnutls_transport_set_ptr(gnutls_channel->session, gnutls_channel); gnutls_transport_set_push_function(gnutls_channel->session, g_io_gnutls_push_func); gnutls_transport_set_pull_function(gnutls_channel->session, g_io_gnutls_pull_func); #if GNUTLS_VERSION_NUMBER < 0x020c00 gnutls_transport_set_lowat(gnutls_channel->session, 0); gnutls_priority_set_direct(gnutls_channel->session, "NORMAL:%COMPAT", NULL); #else gnutls_priority_set_direct(gnutls_channel->session, "NORMAL:-VERS-TLS-ALL:+VERS-TLS1.0:+VERS-SSL3.0:%COMPAT", NULL); #endif gnutls_certificate_allocate_credentials(&gnutls_channel->cred); gnutls_credentials_set(gnutls_channel->session, GNUTLS_CRD_CERTIFICATE, gnutls_channel->cred); DBG("channel %p", channel); return channel; } mmsd-0.1/gweb/giognutls.h000066400000000000000000000016051216024552400154250ustar00rootroot00000000000000/* * * Web service library with GLib integration * * Copyright (C) 2009-2012 Intel Corporation. All rights reserved. * * 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 gboolean g_io_channel_supports_tls(void); GIOChannel *g_io_channel_gnutls_new(int fd); mmsd-0.1/gweb/gionotls.c000066400000000000000000000017361216024552400152500ustar00rootroot00000000000000/* * * Web service library with GLib integration * * Copyright (C) 2009-2012 Intel Corporation. All rights reserved. * * 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 "giognutls.h" gboolean g_io_channel_supports_tls(void) { return FALSE; } GIOChannel *g_io_channel_gnutls_new(int fd) { return NULL; } mmsd-0.1/gweb/gresolv.c000066400000000000000000000564151216024552400150770ustar00rootroot00000000000000/* * * Resolver library with GLib integration * * Copyright (C) 2009-2012 Intel Corporation. All rights reserved. * * 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 #include #include "gresolv.h" struct sort_result { int precedence; int src_scope; int dst_scope; int src_label; int dst_label; gboolean reachable; union { struct sockaddr sa; struct sockaddr_in sin; struct sockaddr_in6 sin6; } src; union { struct sockaddr sa; struct sockaddr_in sin; struct sockaddr_in6 sin6; } dst; }; struct resolv_query; struct resolv_lookup { GResolv *resolv; guint id; int nr_results; struct sort_result *results; struct resolv_query *ipv4_query; struct resolv_query *ipv6_query; guint ipv4_status; guint ipv6_status; GResolvResultFunc result_func; gpointer result_data; }; struct resolv_query { GResolv *resolv; guint timeout; uint16_t msgid; struct resolv_lookup *lookup; }; struct resolv_nameserver { GResolv *resolv; char *address; uint16_t port; unsigned long flags; GIOChannel *udp_channel; guint udp_watch; }; struct _GResolv { int ref_count; int result_family; guint next_lookup_id; GQueue *lookup_queue; GQueue *query_queue; int index; GList *nameserver_list; struct __res_state res; GResolvDebugFunc debug_func; gpointer debug_data; }; static inline void debug(GResolv *resolv, const char *format, ...) { char str[256]; va_list ap; if (resolv->debug_func == NULL) return; va_start(ap, format); if (vsnprintf(str, sizeof(str), format, ap) > 0) resolv->debug_func(str, resolv->debug_data); va_end(ap); } static void destroy_query(struct resolv_query *query) { if (query->timeout > 0) g_source_remove(query->timeout); g_free(query); } static void destroy_lookup(struct resolv_lookup *lookup) { if (lookup->ipv4_query != NULL) { g_queue_remove(lookup->resolv->query_queue, lookup->ipv4_query); destroy_query(lookup->ipv4_query); } if (lookup->ipv6_query != NULL) { g_queue_remove(lookup->resolv->query_queue, lookup->ipv6_query); destroy_query(lookup->ipv6_query); } g_free(lookup->results); g_free(lookup); } static void find_srcaddr(struct sort_result *res) { socklen_t sl = sizeof(res->src); int fd; fd = socket(res->dst.sa.sa_family, SOCK_DGRAM | SOCK_CLOEXEC, IPPROTO_IP); if (fd < 0) return; if (connect(fd, &res->dst.sa, sizeof(res->dst)) < 0) { close(fd); return; } if (getsockname(fd, &res->src.sa, &sl) < 0) { close(fd); return; } res->reachable = TRUE; close(fd); } struct gai_table { unsigned char addr[NS_IN6ADDRSZ]; int mask; int value; }; static const struct gai_table gai_labels[] = { { .addr = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 }, .mask = 128, .value = 0, }, { .addr = { 0x20, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, .mask = 16, .value = 2, }, { .addr = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, .mask = 96, .value = 3, }, { .addr = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00 }, .mask = 96, .value = 4, }, { /* Variations from RFC 3484, matching glibc behaviour */ .addr = { 0xfe, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, .mask = 10, .value = 5, }, { .addr = { 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, .mask = 7, .value = 6, }, { .addr = { 0x20, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, .mask = 32, .value = 7, }, { /* catch-all */ .addr = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, .mask = 0, .value = 1, } }; static const struct gai_table gai_precedences[] = { { .addr = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 }, .mask = 128, .value = 50, }, { .addr = { 0x20, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, .mask = 16, .value = 30, }, { .addr = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, .mask = 96, .value = 20, }, { .addr = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00 }, .mask = 96, .value = 10, }, { .addr = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, .mask = 0, .value = 40, } }; static unsigned char v4mapped[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00 }; static gboolean mask_compare(const unsigned char *one, const unsigned char *two, int mask) { if (mask > 8) { if (memcmp(one, two, mask / 8)) return FALSE; one += mask / 8; two += mask / 8; mask %= 8; } if (mask && ((*one ^ *two) >> (8 - mask))) return FALSE; return TRUE; } static int match_gai_table(struct sockaddr *sa, const struct gai_table *tbl) { struct sockaddr_in *sin = (void *)sa; struct sockaddr_in6 *sin6 = (void *)sa; void *addr; if (sa->sa_family == AF_INET) { addr = v4mapped; memcpy(v4mapped+12, &sin->sin_addr, NS_INADDRSZ); } else addr = &sin6->sin6_addr; while (1) { if (mask_compare(addr, tbl->addr, tbl->mask)) return tbl->value; tbl++; } } #define DQUAD(_a,_b,_c,_d) ( ((_a)<<24) | ((_b)<<16) | ((_c)<<8) | (_d) ) #define V4MATCH(addr, a,b,c,d, m) ( ((addr) ^ DQUAD(a,b,c,d)) >> (32 - (m)) ) #define RFC3484_SCOPE_LINK 2 #define RFC3484_SCOPE_SITE 5 #define RFC3484_SCOPE_GLOBAL 14 static int addr_scope(struct sockaddr *sa) { if (sa->sa_family == AF_INET) { struct sockaddr_in *sin = (void *)sa; guint32 addr = ntohl(sin->sin_addr.s_addr); if (V4MATCH(addr, 169,254,0,0, 16) || V4MATCH(addr, 127,0,0,0, 8)) return RFC3484_SCOPE_LINK; /* Site-local */ if (V4MATCH(addr, 10,0,0,0, 8) || V4MATCH(addr, 172,16,0,0, 12) || V4MATCH(addr, 192,168,0,0, 16)) return RFC3484_SCOPE_SITE; /* Global */ return RFC3484_SCOPE_GLOBAL; } else { struct sockaddr_in6 *sin6 = (void *)sa; /* Multicast addresses have a 4-bit scope field */ if (IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr)) return sin6->sin6_addr.s6_addr[1] & 0xf; if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr) || IN6_IS_ADDR_LOOPBACK(&sin6->sin6_addr)) return RFC3484_SCOPE_LINK; if (IN6_IS_ADDR_SITELOCAL(&sin6->sin6_addr)) return RFC3484_SCOPE_SITE; return RFC3484_SCOPE_GLOBAL; } } static int rfc3484_compare(const void *__one, const void *__two) { const struct sort_result *one = __one; const struct sort_result *two = __two; /* Rule 1: Avoid unusable destinations */ if (one->reachable && !two->reachable) return -1; else if (two->reachable && !one->reachable) return 1; /* Rule 2: Prefer matching scope */ if (one->dst_scope == one->src_scope && two->dst_scope != two->src_scope) return -1; else if (two->dst_scope == two->src_scope && one->dst_scope != one->src_scope) return 1; /* Rule 3: Avoid deprecated addresses */ /* Rule 4: Prefer home addresses */ /* Rule 5: Prefer matching label */ if (one->dst_label == one->src_label && two->dst_label != two->src_label) return -1; else if (two->dst_label == two->src_label && one->dst_label != one->src_label) return 1; /* Rule 6: Prefer higher precedence */ if (one->precedence > two->precedence) return -1; else if (two->precedence > one->precedence) return 1; /* Rule 7: Prefer native transport */ /* Rule 8: Prefer smaller scope */ if (one->dst_scope != two->dst_scope) return one->dst_scope - two->dst_scope; /* Rule 9: Use longest matching prefix */ if (one->dst.sa.sa_family == AF_INET) { /* * Rule 9 is meaningless and counterproductive for Legacy IP * unless perhaps we can tell that it's actually on the local * subnet. But we don't (yet) have local interface config * information, so do nothing here for Legacy IP for now. */ } else { int i; for (i = 0; i < 4; i++) { guint32 cmp_one, cmp_two; cmp_one = one->src.sin6.sin6_addr.s6_addr32[i] ^ one->dst.sin6.sin6_addr.s6_addr32[i]; cmp_two = two->src.sin6.sin6_addr.s6_addr32[i] ^ two->dst.sin6.sin6_addr.s6_addr32[i]; if (!cmp_two && !cmp_one) continue; if (cmp_one && !cmp_two) return 1; if (cmp_two && !cmp_one) return -1; /* g_bit_storage() is effectively fls() */ cmp_one = g_bit_storage(ntohl(cmp_one)); cmp_two = g_bit_storage(ntohl(cmp_two)); if (cmp_one == cmp_two) break; return cmp_one - cmp_two; } } /* Rule 10: Otherwise, leave the order unchanged */ if (one < two) return -1; else return 1; } static void rfc3484_sort_results(struct resolv_lookup *lookup) { int i; for (i = 0; i < lookup->nr_results; i++) { struct sort_result *res = &lookup->results[i]; find_srcaddr(res); res->precedence = match_gai_table(&res->dst.sa, gai_precedences); res->dst_label = match_gai_table(&res->dst.sa, gai_labels); res->src_label = match_gai_table(&res->src.sa, gai_labels); res->dst_scope = addr_scope(&res->dst.sa); res->src_scope = addr_scope(&res->src.sa); } qsort(lookup->results, lookup->nr_results, sizeof(struct sort_result), rfc3484_compare); } static void sort_and_return_results(struct resolv_lookup *lookup) { char buf[INET6_ADDRSTRLEN + 1]; GResolvResultStatus status; char **results = g_try_new0(char *, lookup->nr_results + 1); int i, n = 0; if (!results) return; memset(buf, 0, INET6_ADDRSTRLEN + 1); rfc3484_sort_results(lookup); for (i = 0; i < lookup->nr_results; i++) { if (lookup->results[i].dst.sa.sa_family == AF_INET) { if (inet_ntop(AF_INET, &lookup->results[i].dst.sin.sin_addr, buf, sizeof(buf) - 1) == NULL) continue; } else if (lookup->results[i].dst.sa.sa_family == AF_INET6) { if (inet_ntop(AF_INET6, &lookup->results[i].dst.sin6.sin6_addr, buf, sizeof(buf) - 1) == NULL) continue; } else continue; results[n++] = strdup(buf); } results[n++] = NULL; if (lookup->resolv->result_family == AF_INET) status = lookup->ipv4_status; else if (lookup->resolv->result_family == AF_INET6) status = lookup->ipv6_status; else { if (lookup->ipv6_status == G_RESOLV_RESULT_STATUS_SUCCESS) status = lookup->ipv6_status; else status = lookup->ipv4_status; } lookup->result_func(status, results, lookup->result_data); g_strfreev(results); g_queue_remove(lookup->resolv->lookup_queue, lookup); destroy_lookup(lookup); } static gboolean query_timeout(gpointer user_data) { struct resolv_query *query = user_data; struct resolv_lookup *lookup = query->lookup; GResolv *resolv = query->resolv; query->timeout = 0; if (query == lookup->ipv4_query) { lookup->ipv4_status = G_RESOLV_RESULT_STATUS_NO_RESPONSE; lookup->ipv4_query = NULL; } else if (query == lookup->ipv6_query) { lookup->ipv6_status = G_RESOLV_RESULT_STATUS_NO_RESPONSE; lookup->ipv6_query = NULL; } if (lookup->ipv4_query == NULL && lookup->ipv6_query == NULL) sort_and_return_results(lookup); destroy_query(query); g_queue_remove(resolv->query_queue, query); return FALSE; } static void free_nameserver(struct resolv_nameserver *nameserver) { if (nameserver == NULL) return; if (nameserver->udp_watch > 0) g_source_remove(nameserver->udp_watch); if (nameserver->udp_channel != NULL) g_io_channel_unref(nameserver->udp_channel); g_free(nameserver->address); g_free(nameserver); } static void flush_nameservers(GResolv *resolv) { GList *list; for (list = g_list_first(resolv->nameserver_list); list; list = g_list_next(list)) free_nameserver(list->data); g_list_free(resolv->nameserver_list); resolv->nameserver_list = NULL; } static int send_query(GResolv *resolv, const unsigned char *buf, int len) { GList *list; if (resolv->nameserver_list == NULL) return -ENOENT; for (list = g_list_first(resolv->nameserver_list); list; list = g_list_next(list)) { struct resolv_nameserver *nameserver = list->data; int sk, sent; if (nameserver->udp_channel == NULL) continue; sk = g_io_channel_unix_get_fd(nameserver->udp_channel); sent = send(sk, buf, len, 0); if (sent < 0) continue; } return 0; } static gint compare_lookup_id(gconstpointer a, gconstpointer b) { const struct resolv_lookup *lookup = a; guint id = GPOINTER_TO_UINT(b); if (lookup->id < id) return -1; if (lookup->id > id) return 1; return 0; } static gint compare_query_msgid(gconstpointer a, gconstpointer b) { const struct resolv_query *query = a; uint16_t msgid = GPOINTER_TO_UINT(b); if (query->msgid < msgid) return -1; if (query->msgid > msgid) return 1; return 0; } static void add_result(struct resolv_lookup *lookup, int family, const void *data) { int n = lookup->nr_results++; lookup->results = g_try_realloc(lookup->results, sizeof(struct sort_result) * (n + 1)); if (lookup->results == NULL) return; memset(&lookup->results[n], 0, sizeof(struct sort_result)); lookup->results[n].dst.sa.sa_family = family; if (family == AF_INET) memcpy(&lookup->results[n].dst.sin.sin_addr, data, NS_INADDRSZ); else memcpy(&lookup->results[n].dst.sin6.sin6_addr, data, NS_IN6ADDRSZ); } static void parse_response(struct resolv_nameserver *nameserver, const unsigned char *buf, int len) { GResolv *resolv = nameserver->resolv; GResolvResultStatus status; struct resolv_query *query; struct resolv_lookup *lookup; GList *list; ns_msg msg; ns_rr rr; int i, rcode, count; debug(resolv, "response from %s", nameserver->address); ns_initparse(buf, len, &msg); rcode = ns_msg_getflag(msg, ns_f_rcode); count = ns_msg_count(msg, ns_s_an); debug(resolv, "msg id: 0x%04x rcode: %d count: %d", ns_msg_id(msg), rcode, count); switch (rcode) { case ns_r_noerror: status = G_RESOLV_RESULT_STATUS_SUCCESS; break; case ns_r_formerr: status = G_RESOLV_RESULT_STATUS_FORMAT_ERROR; break; case ns_r_servfail: status = G_RESOLV_RESULT_STATUS_SERVER_FAILURE; break; case ns_r_nxdomain: status = G_RESOLV_RESULT_STATUS_NAME_ERROR; break; case ns_r_notimpl: status = G_RESOLV_RESULT_STATUS_NOT_IMPLEMENTED; break; case ns_r_refused: status = G_RESOLV_RESULT_STATUS_REFUSED; break; default: status = G_RESOLV_RESULT_STATUS_ERROR; break; } list = g_queue_find_custom(resolv->query_queue, GUINT_TO_POINTER(ns_msg_id(msg)), compare_query_msgid); if (!list) return; query = list->data; lookup = query->lookup; if (query == lookup->ipv6_query) { lookup->ipv6_status = status; lookup->ipv6_query = NULL; } else if (query == lookup->ipv4_query) { lookup->ipv4_status = status; lookup->ipv4_query = NULL; } for (i = 0; i < count; i++) { ns_parserr(&msg, ns_s_an, i, &rr); if (ns_rr_class(rr) != ns_c_in) continue; g_assert(offsetof(struct sockaddr_in, sin_addr) == offsetof(struct sockaddr_in6, sin6_flowinfo)); if (ns_rr_type(rr) == ns_t_a && ns_rr_rdlen(rr) == NS_INADDRSZ) { add_result(lookup, AF_INET, ns_rr_rdata(rr)); } else if (ns_rr_type(rr) == ns_t_aaaa && ns_rr_rdlen(rr) == NS_IN6ADDRSZ) { add_result(lookup, AF_INET6, ns_rr_rdata(rr)); } } if (lookup->ipv4_query == NULL && lookup->ipv6_query == NULL) sort_and_return_results(lookup); destroy_query(query); g_queue_remove(resolv->query_queue, query); } static gboolean received_udp_data(GIOChannel *channel, GIOCondition cond, gpointer user_data) { struct resolv_nameserver *nameserver = user_data; unsigned char buf[4096]; int sk, len; if (cond & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) { nameserver->udp_watch = 0; return FALSE; } sk = g_io_channel_unix_get_fd(nameserver->udp_channel); len = recv(sk, buf, sizeof(buf), 0); if (len < 12) return TRUE; parse_response(nameserver, buf, len); return TRUE; } static int connect_udp_channel(struct resolv_nameserver *nameserver) { struct addrinfo hints, *rp; char portnr[6]; int err, sk; memset(&hints, 0, sizeof(hints)); hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_DGRAM; hints.ai_flags = AI_PASSIVE | AI_NUMERICSERV | AI_NUMERICHOST; sprintf(portnr, "%d", nameserver->port); err = getaddrinfo(nameserver->address, portnr, &hints, &rp); if (err) return -EINVAL; /* * Do not blindly copy this code elsewhere; it doesn't loop over the * results using ->ai_next as it should. That's OK in *this* case * because it was a numeric lookup; we *know* there's only one. */ if (!rp) return -EINVAL; sk = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); if (sk < 0) { freeaddrinfo(rp); return -EIO; } /* * If nameserver points to localhost ip, their is no need to * bind the socket on any interface. */ if (nameserver->resolv->index > 0 && strncmp(nameserver->address, "127.0.0.1", 9) != 0) { char interface[IF_NAMESIZE]; memset(interface, 0, IF_NAMESIZE); if (if_indextoname(nameserver->resolv->index, interface) != NULL) { if (setsockopt(sk, SOL_SOCKET, SO_BINDTODEVICE, interface, IF_NAMESIZE) < 0) { close(sk); freeaddrinfo(rp); return -EIO; } } } if (connect(sk, rp->ai_addr, rp->ai_addrlen) < 0) { close(sk); freeaddrinfo(rp); return -EIO; } freeaddrinfo(rp); nameserver->udp_channel = g_io_channel_unix_new(sk); if (nameserver->udp_channel == NULL) { close(sk); return -ENOMEM; } g_io_channel_set_close_on_unref(nameserver->udp_channel, TRUE); nameserver->udp_watch = g_io_add_watch(nameserver->udp_channel, G_IO_IN | G_IO_NVAL | G_IO_ERR | G_IO_HUP, received_udp_data, nameserver); return 0; } GResolv *g_resolv_new(int index) { GResolv *resolv; if (index < 0) return NULL; resolv = g_try_new0(GResolv, 1); if (resolv == NULL) return NULL; resolv->ref_count = 1; resolv->result_family = AF_UNSPEC; resolv->next_lookup_id = 1; resolv->query_queue = g_queue_new(); if (resolv->query_queue == NULL) { g_free(resolv); return NULL; } resolv->lookup_queue = g_queue_new(); if (resolv->lookup_queue == NULL) { g_queue_free(resolv->query_queue); g_free(resolv); return NULL; } resolv->index = index; resolv->nameserver_list = NULL; res_ninit(&resolv->res); return resolv; } GResolv *g_resolv_ref(GResolv *resolv) { if (resolv == NULL) return NULL; __sync_fetch_and_add(&resolv->ref_count, 1); return resolv; } void g_resolv_unref(GResolv *resolv) { struct resolv_query *query; if (resolv == NULL) return; if (__sync_fetch_and_sub(&resolv->ref_count, 1) != 1) return; while ((query = g_queue_pop_head(resolv->query_queue))) destroy_query(query); g_queue_free(resolv->query_queue); g_queue_free(resolv->lookup_queue); flush_nameservers(resolv); res_nclose(&resolv->res); g_free(resolv); } void g_resolv_set_debug(GResolv *resolv, GResolvDebugFunc func, gpointer user_data) { if (resolv == NULL) return; resolv->debug_func = func; resolv->debug_data = user_data; } gboolean g_resolv_add_nameserver(GResolv *resolv, const char *address, uint16_t port, unsigned long flags) { struct resolv_nameserver *nameserver; if (resolv == NULL) return FALSE; nameserver = g_try_new0(struct resolv_nameserver, 1); if (nameserver == NULL) return FALSE; nameserver->address = g_strdup(address); nameserver->port = port; nameserver->flags = flags; nameserver->resolv = resolv; if (connect_udp_channel(nameserver) < 0) { free_nameserver(nameserver); return FALSE; } resolv->nameserver_list = g_list_append(resolv->nameserver_list, nameserver); debug(resolv, "setting nameserver %s", address); return TRUE; } void g_resolv_flush_nameservers(GResolv *resolv) { if (resolv == NULL) return; flush_nameservers(resolv); } static gint add_query(struct resolv_lookup *lookup, const char *hostname, int type) { struct resolv_query *query = g_try_new0(struct resolv_query, 1); unsigned char buf[4096]; int len; if (query == NULL) return -ENOMEM; len = res_mkquery(ns_o_query, hostname, ns_c_in, type, NULL, 0, NULL, buf, sizeof(buf)); query->msgid = buf[0] << 8 | buf[1]; if (send_query(lookup->resolv, buf, len) < 0) { g_free(query); return -EIO; } query->resolv = lookup->resolv; query->lookup = lookup; g_queue_push_tail(lookup->resolv->query_queue, query); query->timeout = g_timeout_add_seconds(5, query_timeout, query); if (type == ns_t_aaaa) lookup->ipv6_query = query; else lookup->ipv4_query = query; return 0; } guint g_resolv_lookup_hostname(GResolv *resolv, const char *hostname, GResolvResultFunc func, gpointer user_data) { struct resolv_lookup *lookup; debug(resolv, "lookup hostname %s", hostname); if (resolv == NULL) return 0; if (resolv->nameserver_list == NULL) { int i; for (i = 0; i < resolv->res.nscount; i++) { char buf[100]; int family = resolv->res.nsaddr_list[i].sin_family; void *sa_addr = &resolv->res.nsaddr_list[i].sin_addr; if (family != AF_INET && resolv->res._u._ext.nsaddrs[i]) { family = AF_INET6; sa_addr = &resolv->res._u._ext.nsaddrs[i]->sin6_addr; } if (family != AF_INET && family != AF_INET6) continue; if (inet_ntop(family, sa_addr, buf, sizeof(buf))) g_resolv_add_nameserver(resolv, buf, 53, 0); } if (resolv->nameserver_list == NULL) g_resolv_add_nameserver(resolv, "127.0.0.1", 53, 0); } lookup = g_try_new0(struct resolv_lookup, 1); if (lookup == NULL) return 0; lookup->resolv = resolv; lookup->result_func = func; lookup->result_data = user_data; lookup->id = resolv->next_lookup_id++; if (resolv->result_family != AF_INET6) { if (add_query(lookup, hostname, ns_t_a)) { g_free(lookup); return -EIO; } } if (resolv->result_family != AF_INET) { if (add_query(lookup, hostname, ns_t_aaaa)) { if (resolv->result_family != AF_INET6) { destroy_query(lookup->ipv4_query); g_queue_remove(resolv->query_queue, lookup->ipv4_query); } g_free(lookup); return -EIO; } } g_queue_push_tail(resolv->lookup_queue, lookup); return lookup->id; } gboolean g_resolv_cancel_lookup(GResolv *resolv, guint id) { GList *list; list = g_queue_find_custom(resolv->lookup_queue, GUINT_TO_POINTER(id), compare_lookup_id); if (list == NULL) return FALSE; destroy_lookup(list->data); g_queue_remove(resolv->query_queue, list->data); return TRUE; } gboolean g_resolv_set_address_family(GResolv *resolv, int family) { if (resolv == NULL) return FALSE; if (family != AF_UNSPEC && family != AF_INET && family != AF_INET6) return FALSE; resolv->result_family = family; return TRUE; } mmsd-0.1/gweb/gresolv.h000066400000000000000000000041371216024552400150760ustar00rootroot00000000000000/* * * Resolver library with GLib integration * * Copyright (C) 2009-2012 Intel Corporation. All rights reserved. * * 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 * */ #ifndef __G_RESOLV_H #define __G_RESOLV_H #include #include #ifdef __cplusplus extern "C" { #endif struct _GResolv; typedef struct _GResolv GResolv; typedef enum { G_RESOLV_RESULT_STATUS_SUCCESS, G_RESOLV_RESULT_STATUS_ERROR, G_RESOLV_RESULT_STATUS_NO_RESPONSE, G_RESOLV_RESULT_STATUS_FORMAT_ERROR, G_RESOLV_RESULT_STATUS_SERVER_FAILURE, G_RESOLV_RESULT_STATUS_NAME_ERROR, G_RESOLV_RESULT_STATUS_NOT_IMPLEMENTED, G_RESOLV_RESULT_STATUS_REFUSED, } GResolvResultStatus; typedef void (*GResolvResultFunc)(GResolvResultStatus status, char **results, gpointer user_data); typedef void (*GResolvDebugFunc)(const char *str, gpointer user_data); GResolv *g_resolv_new(int index); GResolv *g_resolv_ref(GResolv *resolv); void g_resolv_unref(GResolv *resolv); void g_resolv_set_debug(GResolv *resolv, GResolvDebugFunc func, gpointer user_data); gboolean g_resolv_add_nameserver(GResolv *resolv, const char *address, uint16_t port, unsigned long flags); void g_resolv_flush_nameservers(GResolv *resolv); guint g_resolv_lookup_hostname(GResolv *resolv, const char *hostname, GResolvResultFunc func, gpointer user_data); gboolean g_resolv_cancel_lookup(GResolv *resolv, guint id); gboolean g_resolv_set_address_family(GResolv *resolv, int family); #ifdef __cplusplus } #endif #endif /* __G_RESOLV_H */ mmsd-0.1/gweb/gweb.c000066400000000000000000000774361216024552400143500ustar00rootroot00000000000000/* * * Web service library with GLib integration * * Copyright (C) 2009-2012 Intel Corporation. All rights reserved. * * 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 #include #include #include #include #include "giognutls.h" #include "gresolv.h" #include "gweb.h" #define DEFAULT_BUFFER_SIZE 2048 #define SESSION_FLAG_USE_TLS (1 << 0) enum chunk_state { CHUNK_SIZE, CHUNK_R_BODY, CHUNK_N_BODY, CHUNK_DATA, }; struct _GWebResult { guint16 status; const guint8 *buffer; gsize length; gboolean use_chunk; gchar *last_key; GHashTable *headers; }; struct web_session { GWeb *web; char *address; char *host; uint16_t port; unsigned long flags; struct addrinfo *addr; char *content_type; GIOChannel *transport_channel; guint transport_watch; guint send_watch; guint resolv_action; char *request; guint8 *receive_buffer; gsize receive_space; GString *send_buffer; GString *current_header; gboolean header_done; gboolean body_done; gboolean more_data; gboolean request_started; enum chunk_state chunck_state; gsize chunk_size; gsize chunk_left; gsize total_len; GWebResult result; GWebResultFunc result_func; GWebRouteFunc route_func; GWebInputFunc input_func; int fd; gsize length; gsize offset; gpointer user_data; }; struct _GWeb { int ref_count; guint next_query_id; int family; int index; GList *session_list; GResolv *resolv; char *proxy; char *accept_option; char *user_agent; char *user_agent_profile; char *http_version; gboolean close_connection; GWebDebugFunc debug_func; gpointer debug_data; }; static inline void debug(GWeb *web, const char *format, ...) { char str[256]; va_list ap; if (web->debug_func == NULL) return; va_start(ap, format); if (vsnprintf(str, sizeof(str), format, ap) > 0) web->debug_func(str, web->debug_data); va_end(ap); } static void free_session(struct web_session *session) { GWeb *web; if (session == NULL) return; g_free(session->request); web = session->web; if (session->resolv_action > 0) g_resolv_cancel_lookup(web->resolv, session->resolv_action); if (session->transport_watch > 0) g_source_remove(session->transport_watch); if (session->send_watch > 0) g_source_remove(session->send_watch); if (session->transport_channel != NULL) g_io_channel_unref(session->transport_channel); g_free(session->result.last_key); if (session->result.headers != NULL) g_hash_table_destroy(session->result.headers); if (session->send_buffer != NULL) g_string_free(session->send_buffer, TRUE); if (session->current_header != NULL) g_string_free(session->current_header, TRUE); g_free(session->receive_buffer); g_free(session->content_type); g_free(session->host); g_free(session->address); if (session->addr != NULL) freeaddrinfo(session->addr); g_free(session); } static void flush_sessions(GWeb *web) { GList *list; for (list = g_list_first(web->session_list); list; list = g_list_next(list)) free_session(list->data); g_list_free(web->session_list); web->session_list = NULL; } GWeb *g_web_new(int index) { GWeb *web; if (index < 0) return NULL; web = g_try_new0(GWeb, 1); if (web == NULL) return NULL; web->ref_count = 1; web->next_query_id = 1; web->family = AF_UNSPEC; web->index = index; web->session_list = NULL; web->resolv = g_resolv_new(index); if (web->resolv == NULL) { g_free(web); return NULL; } web->accept_option = g_strdup("*/*"); web->user_agent = g_strdup_printf("GWeb/%s", VERSION); web->close_connection = FALSE; return web; } GWeb *g_web_ref(GWeb *web) { if (web == NULL) return NULL; __sync_fetch_and_add(&web->ref_count, 1); return web; } void g_web_unref(GWeb *web) { if (web == NULL) return; if (__sync_fetch_and_sub(&web->ref_count, 1) != 1) return; flush_sessions(web); g_resolv_unref(web->resolv); g_free(web->proxy); g_free(web->accept_option); g_free(web->user_agent); g_free(web->user_agent_profile); g_free(web->http_version); g_free(web); } gboolean g_web_supports_tls(void) { return g_io_channel_supports_tls(); } void g_web_set_debug(GWeb *web, GWebDebugFunc func, gpointer user_data) { if (web == NULL) return; web->debug_func = func; web->debug_data = user_data; g_resolv_set_debug(web->resolv, func, user_data); } gboolean g_web_set_proxy(GWeb *web, const char *proxy) { if (web == NULL) return FALSE; g_free(web->proxy); if (proxy == NULL) { web->proxy = NULL; debug(web, "clearing proxy"); } else { web->proxy = g_strdup(proxy); debug(web, "setting proxy %s", web->proxy); } return TRUE; } gboolean g_web_set_address_family(GWeb *web, int family) { if (web == NULL) return FALSE; if (family != AF_UNSPEC && family != AF_INET && family != AF_INET6) return FALSE; web->family = family; g_resolv_set_address_family(web->resolv, family); return TRUE; } gboolean g_web_add_nameserver(GWeb *web, const char *address) { if (web == NULL) return FALSE; g_resolv_add_nameserver(web->resolv, address, 53, 0); return TRUE; } static gboolean set_accept_option(GWeb *web, const char *format, va_list args) { g_free(web->accept_option); if (format == NULL) { web->accept_option = NULL; debug(web, "clearing accept option"); } else { web->accept_option = g_strdup_vprintf(format, args); debug(web, "setting accept %s", web->accept_option); } return TRUE; } gboolean g_web_set_accept(GWeb *web, const char *format, ...) { va_list args; gboolean result; if (web == NULL) return FALSE; va_start(args, format); result = set_accept_option(web, format, args); va_end(args); return result; } static gboolean set_user_agent(GWeb *web, const char *format, va_list args) { g_free(web->user_agent); if (format == NULL) { web->user_agent = NULL; debug(web, "clearing user agent"); } else { web->user_agent = g_strdup_vprintf(format, args); debug(web, "setting user agent %s", web->user_agent); } return TRUE; } gboolean g_web_set_user_agent(GWeb *web, const char *format, ...) { va_list args; gboolean result; if (web == NULL) return FALSE; va_start(args, format); result = set_user_agent(web, format, args); va_end(args); return result; } gboolean g_web_set_ua_profile(GWeb *web, const char *profile) { if (web == NULL) return FALSE; g_free(web->user_agent_profile); web->user_agent_profile = g_strdup(profile); debug(web, "setting user agent profile %s", web->user_agent); return TRUE; } gboolean g_web_set_http_version(GWeb *web, const char *version) { if (web == NULL) return FALSE; g_free(web->http_version); if (version == NULL) { web->http_version = NULL; debug(web, "clearing HTTP version"); } else { web->http_version = g_strdup(version); debug(web, "setting HTTP version %s", web->http_version); } return TRUE; } void g_web_set_close_connection(GWeb *web, gboolean enabled) { if (web == NULL) return; web->close_connection = enabled; } gboolean g_web_get_close_connection(GWeb *web) { if (web == NULL) return FALSE; return web->close_connection; } static inline void call_result_func(struct web_session *session, guint16 status) { if (session->result_func == NULL) return; if (status != 0) session->result.status = status; session->result_func(&session->result, session->user_data); } static inline void call_route_func(struct web_session *session) { if (session->route_func != NULL) session->route_func(session->address, session->addr->ai_family, session->web->index, session->user_data); } static gboolean process_send_buffer(struct web_session *session) { GString *buf; gsize count, bytes_written; GIOStatus status; if (session == NULL) return FALSE; buf = session->send_buffer; count = buf->len; if (count == 0) { if (session->request_started == TRUE && session->more_data == FALSE && session->fd == -1) session->body_done = TRUE; return FALSE; } status = g_io_channel_write_chars(session->transport_channel, buf->str, count, &bytes_written, NULL); debug(session->web, "status %u bytes to write %zu bytes written %zu", status, count, bytes_written); if (status != G_IO_STATUS_NORMAL && status != G_IO_STATUS_AGAIN) return FALSE; g_string_erase(buf, 0, bytes_written); return TRUE; } static gboolean process_send_file(struct web_session *session) { int sk; off_t offset; ssize_t bytes_sent; if (session->fd == -1) return FALSE; if (session->request_started == FALSE || session->more_data == TRUE) return FALSE; sk = g_io_channel_unix_get_fd(session->transport_channel); if (sk < 0) return FALSE; offset = session->offset; bytes_sent = sendfile(sk, session->fd, &offset, session->length); debug(session->web, "errno: %d, bytes to send %zu / bytes sent %zu", errno, session->length, bytes_sent); if (bytes_sent < 0 && errno != EAGAIN) return FALSE; session->offset = offset; session->length -= bytes_sent; if (session->length == 0) { session->body_done = TRUE; return FALSE; } return TRUE; } static void process_next_chunk(struct web_session *session) { GString *buf = session->send_buffer; const guint8 *body; gsize length; if (session->input_func == NULL) { session->more_data = FALSE; return; } session->more_data = session->input_func(&body, &length, session->user_data); if (length > 0) { g_string_append_printf(buf, "%zx\r\n", length); g_string_append_len(buf, (char *) body, length); g_string_append(buf, "\r\n"); } if (session->more_data == FALSE) g_string_append(buf, "0\r\n\r\n"); } static void start_request(struct web_session *session) { GString *buf = session->send_buffer; const char *version; const guint8 *body; gsize length; debug(session->web, "request %s from %s", session->request, session->host); g_string_truncate(buf, 0); if (session->web->http_version == NULL) version = "1.1"; else version = session->web->http_version; if (session->content_type == NULL) g_string_append_printf(buf, "GET %s HTTP/%s\r\n", session->request, version); else g_string_append_printf(buf, "POST %s HTTP/%s\r\n", session->request, version); g_string_append_printf(buf, "Host: %s\r\n", session->host); if (session->web->user_agent != NULL) g_string_append_printf(buf, "User-Agent: %s\r\n", session->web->user_agent); if (session->web->user_agent_profile != NULL) { g_string_append_printf(buf, "x-wap-profile: %s\r\n", session->web->user_agent_profile); } if (session->web->accept_option != NULL) g_string_append_printf(buf, "Accept: %s\r\n", session->web->accept_option); if (session->content_type != NULL) { g_string_append_printf(buf, "Content-Type: %s\r\n", session->content_type); if (session->input_func == NULL) { session->more_data = FALSE; length = session->length; } else session->more_data = session->input_func(&body, &length, session->user_data); if (session->more_data == FALSE) g_string_append_printf(buf, "Content-Length: %zu\r\n", length); else g_string_append(buf, "Transfer-Encoding: chunked\r\n"); } if (session->web->close_connection == TRUE) g_string_append(buf, "Connection: close\r\n"); g_string_append(buf, "\r\n"); if (session->content_type != NULL && length > 0) { if (session->more_data == TRUE) { g_string_append_printf(buf, "%zx\r\n", length); g_string_append_len(buf, (char *) body, length); g_string_append(buf, "\r\n"); } else if (session->fd == -1) g_string_append_len(buf, (char *) body, length); } } static gboolean send_data(GIOChannel *channel, GIOCondition cond, gpointer user_data) { struct web_session *session = user_data; if (cond & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) { session->send_watch = 0; return FALSE; } if (process_send_buffer(session) == TRUE) return TRUE; if (process_send_file(session) == TRUE) return TRUE; if (session->request_started == FALSE) { session->request_started = TRUE; start_request(session); } else if (session->more_data == TRUE) process_next_chunk(session); process_send_buffer(session); if (session->body_done == TRUE) { session->send_watch = 0; return FALSE; } return TRUE; } static int decode_chunked(struct web_session *session, const guint8 *buf, gsize len) { const guint8 *ptr = buf; gsize counter; while (len > 0) { guint8 *pos; gsize count; char *str; switch (session->chunck_state) { case CHUNK_SIZE: pos = memchr(ptr, '\n', len); if (pos == NULL) { g_string_append_len(session->current_header, (gchar *) ptr, len); return 0; } count = pos - ptr; if (count < 1 || ptr[count - 1] != '\r') return -EILSEQ; g_string_append_len(session->current_header, (gchar *) ptr, count); len -= count + 1; ptr = pos + 1; str = session->current_header->str; counter = strtoul(str, NULL, 16); if ((counter == 0 && errno == EINVAL) || counter == ULONG_MAX) return -EILSEQ; session->chunk_size = counter; session->chunk_left = counter; session->chunck_state = CHUNK_DATA; break; case CHUNK_R_BODY: if (*ptr != '\r') return -EILSEQ; ptr++; len--; session->chunck_state = CHUNK_N_BODY; break; case CHUNK_N_BODY: if (*ptr != '\n') return -EILSEQ; ptr++; len--; session->chunck_state = CHUNK_SIZE; break; case CHUNK_DATA: if (session->chunk_size == 0) { debug(session->web, "Download Done in chunk"); g_string_truncate(session->current_header, 0); return 0; } if (session->chunk_left <= len) { session->result.buffer = ptr; session->result.length = session->chunk_left; call_result_func(session, 0); len -= session->chunk_left; ptr += session->chunk_left; session->total_len += session->chunk_left; session->chunk_left = 0; g_string_truncate(session->current_header, 0); session->chunck_state = CHUNK_R_BODY; break; } /* more data */ session->result.buffer = ptr; session->result.length = len; call_result_func(session, 0); session->chunk_left -= len; session->total_len += len; len -= len; ptr += len; break; } } return 0; } static int handle_body(struct web_session *session, const guint8 *buf, gsize len) { int err; debug(session->web, "[body] length %zu", len); if (session->result.use_chunk == FALSE) { if (len > 0) { session->result.buffer = buf; session->result.length = len; call_result_func(session, 0); } return 0; } err = decode_chunked(session, buf, len); if (err < 0) { debug(session->web, "Error in chunk decode %d", err); session->result.buffer = NULL; session->result.length = 0; call_result_func(session, 400); } return err; } static void handle_multi_line(struct web_session *session) { gsize count; char *str; gchar *value; str = session->current_header->str; if (str[0] != ' ' && str[0] != '\t') return; while (str[0] == ' ' || str[0] == '\t') str++; count = str - session->current_header->str; if (count > 0) { g_string_erase(session->current_header, 0, count); g_string_insert_c(session->current_header, 0, ' '); } value = g_hash_table_lookup(session->result.headers, session->result.last_key); if (value != NULL) { g_string_insert(session->current_header, 0, value); str = session->current_header->str; g_hash_table_replace(session->result.headers, g_strdup(session->result.last_key), g_strdup(str)); } } static void add_header_field(struct web_session *session) { gsize count; guint8 *pos; char *str; gchar *value; gchar *key; str = session->current_header->str; pos = memchr(str, ':', session->current_header->len); if (pos != NULL) { *pos = '\0'; pos++; key = g_strdup(str); /* remove preceding white spaces */ while (*pos == ' ') pos++; count = (char *) pos - str; g_string_erase(session->current_header, 0, count); value = g_hash_table_lookup(session->result.headers, key); if (value != NULL) { g_string_insert_c(session->current_header, 0, ' '); g_string_insert_c(session->current_header, 0, ';'); g_string_insert(session->current_header, 0, value); } str = session->current_header->str; g_hash_table_replace(session->result.headers, key, g_strdup(str)); g_free(session->result.last_key); session->result.last_key = g_strdup(key); } } static gboolean received_data(GIOChannel *channel, GIOCondition cond, gpointer user_data) { struct web_session *session = user_data; guint8 *ptr = session->receive_buffer; gsize bytes_read; GIOStatus status; if (cond & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) { session->transport_watch = 0; session->result.buffer = NULL; session->result.length = 0; call_result_func(session, 400); return FALSE; } status = g_io_channel_read_chars(channel, (gchar *) session->receive_buffer, session->receive_space - 1, &bytes_read, NULL); debug(session->web, "bytes read %zu", bytes_read); if (status != G_IO_STATUS_NORMAL && status != G_IO_STATUS_AGAIN) { session->transport_watch = 0; session->result.buffer = NULL; session->result.length = 0; call_result_func(session, 0); return FALSE; } session->receive_buffer[bytes_read] = '\0'; if (session->header_done == TRUE) { if (handle_body(session, session->receive_buffer, bytes_read) < 0) { session->transport_watch = 0; return FALSE; } return TRUE; } while (bytes_read > 0) { guint8 *pos; gsize count; char *str; pos = memchr(ptr, '\n', bytes_read); if (pos == NULL) { g_string_append_len(session->current_header, (gchar *) ptr, bytes_read); return TRUE; } *pos = '\0'; count = strlen((char *) ptr); if (count > 0 && ptr[count - 1] == '\r') { ptr[--count] = '\0'; bytes_read--; } g_string_append_len(session->current_header, (gchar *) ptr, count); bytes_read -= count + 1; if (bytes_read > 0) ptr = pos + 1; else ptr = NULL; if (session->current_header->len == 0) { char *val; session->header_done = TRUE; val = g_hash_table_lookup(session->result.headers, "Transfer-Encoding"); if (val != NULL) { val = g_strrstr(val, "chunked"); if (val != NULL) { session->result.use_chunk = TRUE; session->chunck_state = CHUNK_SIZE; session->chunk_left = 0; session->total_len = 0; } } if (handle_body(session, ptr, bytes_read) < 0) { session->transport_watch = 0; return FALSE; } break; } str = session->current_header->str; if (session->result.status == 0) { unsigned int code; if (sscanf(str, "HTTP/%*s %u %*s", &code) == 1) session->result.status = code; } debug(session->web, "[header] %s", str); /* handle multi-line header */ if (str[0] == ' ' || str[0] == '\t') handle_multi_line(session); else add_header_field(session); g_string_truncate(session->current_header, 0); } return TRUE; } static int bind_to_address(int sk, const char *interface, int family) { struct ifaddrs *ifaddr_list, *ifaddr; int size, err = -1; if (getifaddrs(&ifaddr_list) < 0) return err; for (ifaddr = ifaddr_list; ifaddr != NULL; ifaddr = ifaddr->ifa_next) { if (g_strcmp0(ifaddr->ifa_name, interface) != 0) continue; if (ifaddr->ifa_addr == NULL || ifaddr->ifa_addr->sa_family != family) continue; switch (family) { case AF_INET: size = sizeof(struct sockaddr_in); break; case AF_INET6: size = sizeof(struct sockaddr_in6); break; default: continue; } err = bind(sk, (struct sockaddr *) ifaddr->ifa_addr, size); break; } freeifaddrs(ifaddr_list); return err; } static inline int bind_socket(int sk, int index, int family) { char interface[IF_NAMESIZE]; int err; if (if_indextoname(index, interface) == NULL) return -1; err = setsockopt(sk, SOL_SOCKET, SO_BINDTODEVICE, interface, IF_NAMESIZE); if (err < 0) err = bind_to_address(sk, interface, family); return err; } static int connect_session_transport(struct web_session *session) { GIOFlags flags; int sk; sk = socket(session->addr->ai_family, SOCK_STREAM | SOCK_CLOEXEC, IPPROTO_TCP); if (sk < 0) return -EIO; if (session->web->index > 0) { if (bind_socket(sk, session->web->index, session->addr->ai_family) < 0) { debug(session->web, "bind() %s", strerror(errno)); close(sk); return -EIO; } } if (session->flags & SESSION_FLAG_USE_TLS) { debug(session->web, "using TLS encryption"); session->transport_channel = g_io_channel_gnutls_new(sk); } else { debug(session->web, "no encryption"); session->transport_channel = g_io_channel_unix_new(sk); } if (session->transport_channel == NULL) { debug(session->web, "channel missing"); close(sk); return -ENOMEM; } flags = g_io_channel_get_flags(session->transport_channel); g_io_channel_set_flags(session->transport_channel, flags | G_IO_FLAG_NONBLOCK, NULL); g_io_channel_set_encoding(session->transport_channel, NULL, NULL); g_io_channel_set_buffered(session->transport_channel, FALSE); g_io_channel_set_close_on_unref(session->transport_channel, TRUE); if (connect(sk, session->addr->ai_addr, session->addr->ai_addrlen) < 0) { if (errno != EINPROGRESS) { debug(session->web, "connect() %s", strerror(errno)); close(sk); return -EIO; } } session->transport_watch = g_io_add_watch(session->transport_channel, G_IO_IN | G_IO_HUP | G_IO_NVAL | G_IO_ERR, received_data, session); session->send_watch = g_io_add_watch(session->transport_channel, G_IO_OUT | G_IO_HUP | G_IO_NVAL | G_IO_ERR, send_data, session); return 0; } static int create_transport(struct web_session *session) { int err; err = connect_session_transport(session); if (err < 0) return err; debug(session->web, "creating session %s:%u", session->address, session->port); return 0; } static int parse_url(struct web_session *session, const char *url, const char *proxy) { char *scheme, *host, *port, *path; scheme = g_strdup(url); if (scheme == NULL) return -EINVAL; host = strstr(scheme, "://"); if (host != NULL) { *host = '\0'; host += 3; if (strcasecmp(scheme, "https") == 0) { session->port = 443; session->flags |= SESSION_FLAG_USE_TLS; } else if (strcasecmp(scheme, "http") == 0) { session->port = 80; } else { g_free(scheme); return -EINVAL; } } else { host = scheme; session->port = 80; } path = strchr(host, '/'); if (path != NULL) *(path++) = '\0'; if (proxy == NULL) session->request = g_strdup_printf("/%s", path ? path : ""); else session->request = g_strdup(url); port = strrchr(host, ':'); if (port != NULL) { char *end; int tmp = strtol(port + 1, &end, 10); if (*end == '\0') { *port = '\0'; session->port = tmp; } if (proxy == NULL) session->host = g_strdup(host); else session->host = g_strdup_printf("%s:%u", host, tmp); } else session->host = g_strdup(host); g_free(scheme); if (proxy == NULL) return 0; scheme = g_strdup(proxy); if (scheme == NULL) return -EINVAL; host = strstr(proxy, "://"); if (host != NULL) { *host = '\0'; host += 3; if (strcasecmp(scheme, "http") != 0) { g_free(scheme); return -EINVAL; } } else host = scheme; path = strchr(host, '/'); if (path != NULL) *(path++) = '\0'; port = strrchr(host, ':'); if (port != NULL) { char *end; int tmp = strtol(port + 1, &end, 10); if (*end == '\0') { *port = '\0'; session->port = tmp; } } session->address = g_strdup(host); g_free(scheme); return 0; } static void resolv_result(GResolvResultStatus status, char **results, gpointer user_data) { struct web_session *session = user_data; struct addrinfo hints; char *port; int ret; if (results == NULL || results[0] == NULL) { call_result_func(session, 404); return; } debug(session->web, "address %s", results[0]); memset(&hints, 0, sizeof(struct addrinfo)); hints.ai_flags = AI_NUMERICHOST; hints.ai_family = session->web->family; if (session->addr != NULL) { freeaddrinfo(session->addr); session->addr = NULL; } port = g_strdup_printf("%u", session->port); ret = getaddrinfo(results[0], port, &hints, &session->addr); g_free(port); if (ret != 0 || session->addr == NULL) { call_result_func(session, 400); return; } session->address = g_strdup(results[0]); call_route_func(session); if (create_transport(session) < 0) { call_result_func(session, 409); return; } } static guint do_request(GWeb *web, const char *url, const char *type, GWebInputFunc input, int fd, gsize length, GWebResultFunc func, GWebRouteFunc route, gpointer user_data) { struct web_session *session; if (web == NULL || url == NULL) return 0; debug(web, "request %s", url); session = g_try_new0(struct web_session, 1); if (session == NULL) return 0; if (parse_url(session, url, web->proxy) < 0) { free_session(session); return 0; } debug(web, "address %s", session->address); debug(web, "port %u", session->port); debug(web, "host %s", session->host); debug(web, "flags %lu", session->flags); debug(web, "request %s", session->request); if (type != NULL) { session->content_type = g_strdup(type); debug(web, "content-type %s", session->content_type); } session->web = web; session->result_func = func; session->route_func = route; session->input_func = input; session->fd = fd; session->length = length; session->offset = 0; session->user_data = user_data; session->receive_buffer = g_try_malloc(DEFAULT_BUFFER_SIZE); if (session->receive_buffer == NULL) { free_session(session); return 0; } session->result.headers = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free); if (session->result.headers == NULL) { free_session(session); return 0; } session->receive_space = DEFAULT_BUFFER_SIZE; session->send_buffer = g_string_sized_new(0); session->current_header = g_string_sized_new(0); session->header_done = FALSE; session->body_done = FALSE; if (session->address == NULL && inet_aton(session->host, NULL) == 0) { session->resolv_action = g_resolv_lookup_hostname(web->resolv, session->host, resolv_result, session); if (session->resolv_action == 0) { free_session(session); return 0; } } else { struct addrinfo hints; char *port; int ret; if (session->address == NULL) session->address = g_strdup(session->host); memset(&hints, 0, sizeof(struct addrinfo)); hints.ai_flags = AI_NUMERICHOST; hints.ai_family = session->web->family; if (session->addr != NULL) { freeaddrinfo(session->addr); session->addr = NULL; } port = g_strdup_printf("%u", session->port); ret = getaddrinfo(session->address, port, &hints, &session->addr); g_free(port); if (ret != 0 || session->addr == NULL) { free_session(session); return 0; } if (create_transport(session) < 0) { free_session(session); return 0; } } web->session_list = g_list_append(web->session_list, session); return web->next_query_id++; } guint g_web_request_get(GWeb *web, const char *url, GWebResultFunc func, GWebRouteFunc route, gpointer user_data) { return do_request(web, url, NULL, NULL, -1, 0, func, route, user_data); } guint g_web_request_post(GWeb *web, const char *url, const char *type, GWebInputFunc input, GWebResultFunc func, gpointer user_data) { return do_request(web, url, type, input, -1, 0, func, NULL, user_data); } guint g_web_request_post_file(GWeb *web, const char *url, const char *type, const char *file, GWebResultFunc func, gpointer user_data) { struct stat st; int fd; guint ret; if (stat(file, &st) < 0) return 0; fd = open(file, O_RDONLY); if (fd < 0) return 0; ret = do_request(web, url, type, NULL, fd, st.st_size, func, NULL, user_data); if (ret == 0) close(fd); return ret; } gboolean g_web_cancel_request(GWeb *web, guint id) { if (web == NULL) return FALSE; return TRUE; } guint16 g_web_result_get_status(GWebResult *result) { if (result == NULL) return 0; return result->status; } gboolean g_web_result_get_chunk(GWebResult *result, const guint8 **chunk, gsize *length) { if (result == NULL) return FALSE; if (chunk == NULL) return FALSE; *chunk = result->buffer; if (length != NULL) *length = result->length; return TRUE; } gboolean g_web_result_get_header(GWebResult *result, const char *header, const char **value) { if (result == NULL) return FALSE; if (value == NULL) return FALSE; *value = g_hash_table_lookup(result->headers, header); if (*value == NULL) return FALSE; return TRUE; } struct _GWebParser { gint ref_count; char *begin_token; char *end_token; const char *token_str; size_t token_len; size_t token_pos; gboolean intoken; GString *content; GWebParserFunc func; gpointer user_data; }; GWebParser *g_web_parser_new(const char *begin, const char *end, GWebParserFunc func, gpointer user_data) { GWebParser *parser; parser = g_try_new0(GWebParser, 1); if (parser == NULL) return NULL; parser->ref_count = 1; parser->begin_token = g_strdup(begin); parser->end_token = g_strdup(end); if (parser->begin_token == NULL) { g_free(parser); return NULL; } parser->func = func; parser->user_data = user_data; parser->token_str = parser->begin_token; parser->token_len = strlen(parser->token_str); parser->token_pos = 0; parser->intoken = FALSE; parser->content = g_string_sized_new(0); return parser; } GWebParser *g_web_parser_ref(GWebParser *parser) { if (parser == NULL) return NULL; __sync_fetch_and_add(&parser->ref_count, 1); return parser; } void g_web_parser_unref(GWebParser *parser) { if (parser == NULL) return; if (__sync_fetch_and_sub(&parser->ref_count, 1) != 1) return; g_string_free(parser->content, TRUE); g_free(parser->begin_token); g_free(parser->end_token); g_free(parser); } void g_web_parser_feed_data(GWebParser *parser, const guint8 *data, gsize length) { const guint8 *ptr = data; if (parser == NULL) return; while (length > 0) { guint8 chr = parser->token_str[parser->token_pos]; if (parser->token_pos == 0) { guint8 *pos; pos = memchr(ptr, chr, length); if (pos == NULL) { if (parser->intoken == TRUE) g_string_append_len(parser->content, (gchar *) ptr, length); break; } if (parser->intoken == TRUE) g_string_append_len(parser->content, (gchar *) ptr, (pos - ptr) + 1); length -= (pos - ptr) + 1; ptr = pos + 1; parser->token_pos++; continue; } if (parser->intoken == TRUE) g_string_append_c(parser->content, ptr[0]); if (ptr[0] != chr) { length--; ptr++; parser->token_pos = 0; continue; } length--; ptr++; parser->token_pos++; if (parser->token_pos == parser->token_len) { if (parser->intoken == FALSE) { g_string_append(parser->content, parser->token_str); parser->intoken = TRUE; parser->token_str = parser->end_token; parser->token_len = strlen(parser->end_token); parser->token_pos = 0; } else { char *str; str = g_string_free(parser->content, FALSE); parser->content = g_string_sized_new(0); if (parser->func) parser->func(str, parser->user_data); g_free(str); parser->intoken = FALSE; parser->token_str = parser->begin_token; parser->token_len = strlen(parser->begin_token); parser->token_pos = 0; } } } } void g_web_parser_end_data(GWebParser *parser) { if (parser == NULL) return; } mmsd-0.1/gweb/gweb.h000066400000000000000000000065461216024552400143470ustar00rootroot00000000000000/* * * Web service library with GLib integration * * Copyright (C) 2009-2012 Intel Corporation. All rights reserved. * * 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 * */ #ifndef __G_WEB_H #define __G_WEB_H #include #include #ifdef __cplusplus extern "C" { #endif struct _GWeb; struct _GWebResult; struct _GWebParser; typedef struct _GWeb GWeb; typedef struct _GWebResult GWebResult; typedef struct _GWebParser GWebParser; typedef gboolean (*GWebResultFunc)(GWebResult *result, gpointer user_data); typedef gboolean (*GWebRouteFunc)(const char *addr, int ai_family, int if_index, gpointer user_data); typedef gboolean (*GWebInputFunc)(const guint8 **data, gsize *length, gpointer user_data); typedef void (*GWebDebugFunc)(const char *str, gpointer user_data); GWeb *g_web_new(int index); GWeb *g_web_ref(GWeb *web); void g_web_unref(GWeb *web); void g_web_set_debug(GWeb *web, GWebDebugFunc func, gpointer user_data); gboolean g_web_supports_tls(void); gboolean g_web_set_proxy(GWeb *web, const char *proxy); gboolean g_web_set_address_family(GWeb *web, int family); gboolean g_web_add_nameserver(GWeb *web, const char *address); gboolean g_web_set_accept(GWeb *web, const char *format, ...) __attribute__((format(printf, 2, 3))); gboolean g_web_set_user_agent(GWeb *web, const char *format, ...) __attribute__((format(printf, 2, 3))); gboolean g_web_set_ua_profile(GWeb *web, const char *profile); gboolean g_web_set_http_version(GWeb *web, const char *version); void g_web_set_close_connection(GWeb *web, gboolean enabled); gboolean g_web_get_close_connection(GWeb *web); guint g_web_request_get(GWeb *web, const char *url, GWebResultFunc func, GWebRouteFunc route, gpointer user_data); guint g_web_request_post(GWeb *web, const char *url, const char *type, GWebInputFunc input, GWebResultFunc func, gpointer user_data); guint g_web_request_post_file(GWeb *web, const char *url, const char *type, const char *file, GWebResultFunc func, gpointer user_data); gboolean g_web_cancel_request(GWeb *web, guint id); guint16 g_web_result_get_status(GWebResult *result); gboolean g_web_result_get_header(GWebResult *result, const char *header, const char **value); gboolean g_web_result_get_chunk(GWebResult *result, const guint8 **chunk, gsize *length); typedef void (*GWebParserFunc)(const char *str, gpointer user_data); GWebParser *g_web_parser_new(const char *begin, const char *end, GWebParserFunc func, gpointer user_data); GWebParser *g_web_parser_ref(GWebParser *parser); void g_web_parser_unref(GWebParser *parser); void g_web_parser_feed_data(GWebParser *parser, const guint8 *data, gsize length); void g_web_parser_end_data(GWebParser *parser); #ifdef __cplusplus } #endif #endif /* __G_WEB_H */ mmsd-0.1/plugins/000077500000000000000000000000001216024552400137745ustar00rootroot00000000000000mmsd-0.1/plugins/ofono.c000066400000000000000000000712671216024552400152750ustar00rootroot00000000000000/* * * Multimedia Messaging Service * * Copyright (C) 2010-2011 Intel Corporation. All rights reserved. * * 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 "plugin.h" #include "log.h" #include "dbus.h" #include "service.h" #define OFONO_SERVICE "org.ofono" #define OFONO_MANAGER_INTERFACE OFONO_SERVICE ".Manager" #define OFONO_MODEM_INTERFACE OFONO_SERVICE ".Modem" #define OFONO_SIM_INTERFACE OFONO_SERVICE ".SimManager" #define OFONO_GPRS_INTERFACE OFONO_SERVICE ".ConnectionManager" #define OFONO_CONTEXT_INTERFACE OFONO_SERVICE ".ConnectionContext" #define OFONO_PUSH_INTERFACE OFONO_SERVICE ".PushNotification" #define OFONO_AGENT_INTERFACE OFONO_SERVICE ".PushNotificationAgent" struct modem_data { char *path; DBusConnection *conn; gboolean has_sim; gboolean has_gprs; gboolean has_push; gboolean has_agent; struct mms_service *service; dbus_bool_t sim_present; char *sim_identity; dbus_bool_t gprs_attached; char *message_center; char *context_path; dbus_bool_t context_active; char *context_interface; char *context_proxy; }; static GHashTable *modem_list; static gboolean ofono_running = FALSE; static void remove_agent(struct modem_data *modem) { DBG("path %s", modem->path); if (g_dbus_unregister_interface(modem->conn, modem->path, OFONO_AGENT_INTERFACE) == FALSE) { mms_error("Failed to unregister notification agent"); return; } modem->has_agent = FALSE; } static DBusMessage *agent_receive(DBusConnection *conn, DBusMessage *msg, void *user_data) { struct modem_data *modem = user_data; DBusMessageIter iter, array; unsigned char *data; int data_len; DBG("path %s", modem->path); if (dbus_message_iter_init(msg, &iter) == FALSE) goto done; dbus_message_iter_recurse(&iter, &array); dbus_message_iter_get_fixed_array(&array, &data, &data_len); DBG("notification with %d bytes", data_len); mms_service_push_notify(modem->service, data, data_len); done: return g_dbus_create_reply(msg, DBUS_TYPE_INVALID); } static DBusMessage *agent_release(DBusConnection *conn, DBusMessage *msg, void *user_data) { struct modem_data *modem = user_data; DBG("path %s", modem->path); remove_agent(modem); return NULL; } static const GDBusMethodTable agent_methods[] = { { GDBUS_METHOD("ReceiveNotification", GDBUS_ARGS({ "notification", "ay" }, { "info", "a{sv}" }), NULL, agent_receive) }, { GDBUS_NOREPLY_METHOD("Release", NULL, NULL, agent_release) }, { } }; static void create_agent(struct modem_data *modem) { DBG("path %s", modem->path); if (g_dbus_register_interface(modem->conn, modem->path, OFONO_AGENT_INTERFACE, agent_methods, NULL, NULL, modem, NULL) == FALSE) { mms_error("Failed to register notification agent"); return; } modem->has_agent = TRUE; } static void remove_modem(gpointer data) { struct modem_data *modem = data; DBG("path %s", modem->path); if (modem->sim_present == TRUE && modem->sim_identity != NULL) mms_service_unregister(modem->service); mms_service_unref(modem->service); if (modem->has_agent == TRUE) remove_agent(modem); dbus_connection_unref(modem->conn); g_free(modem->message_center); g_free(modem->context_path); g_free(modem->context_interface); g_free(modem->context_proxy); g_free(modem->sim_identity); g_free(modem->path); g_free(modem); } static void register_agent_reply(DBusPendingCall *call, void *user_data) { struct modem_data *modem = user_data; DBusMessage *reply = dbus_pending_call_steal_reply(call); DBusError err; DBG("path %s", modem->path); dbus_error_init(&err); if (dbus_set_error_from_message(&err, reply) == TRUE) { dbus_error_free(&err); remove_agent(modem); goto done; } done: dbus_message_unref(reply); } static int register_agent(struct modem_data *modem) { DBusConnection *conn = modem->conn; DBusMessage *msg; DBusPendingCall *call; DBG("path %s", modem->path); msg = dbus_message_new_method_call(OFONO_SERVICE, modem->path, OFONO_PUSH_INTERFACE, "RegisterAgent"); if (msg == NULL) return -ENOMEM; dbus_message_set_auto_start(msg, FALSE); dbus_message_append_args(msg, DBUS_TYPE_OBJECT_PATH, &modem->path, DBUS_TYPE_INVALID); if (dbus_connection_send_with_reply(conn, msg, &call, -1) == FALSE) { dbus_message_unref(msg); return -EIO; } dbus_message_unref(msg); if (call == NULL) return -EINVAL; dbus_pending_call_set_notify(call, register_agent_reply, modem, NULL); dbus_pending_call_unref(call); return 0; } static void check_sim_present(struct modem_data *modem, DBusMessageIter *iter) { dbus_bool_t present; dbus_message_iter_get_basic(iter, &present); if (modem->sim_present == present) return; modem->sim_present = present; DBG("SIM present %d", modem->sim_present); if (modem->sim_identity == NULL) return; if (modem->sim_present == FALSE) { mms_service_unregister(modem->service); g_free(modem->sim_identity); modem->sim_identity = NULL; } else mms_service_register(modem->service); } static void check_sim_identity(struct modem_data *modem, DBusMessageIter *iter) { const char *identity; dbus_message_iter_get_basic(iter, &identity); g_free(modem->sim_identity); modem->sim_identity = g_strdup(identity); if (modem->sim_identity == NULL) return; mms_service_set_identity(modem->service, modem->sim_identity); if (modem->sim_present == TRUE) mms_service_register(modem->service); } static gboolean sim_changed(DBusConnection *connection, DBusMessage *message, void *user_data) { struct modem_data *modem; DBusMessageIter iter, value; const char *path, *key; if (dbus_message_iter_init(message, &iter) == FALSE) return TRUE; path = dbus_message_get_path(message); modem = g_hash_table_lookup(modem_list, path); if (modem == NULL) return TRUE; dbus_message_iter_get_basic(&iter, &key); dbus_message_iter_next(&iter); dbus_message_iter_recurse(&iter, &value); if (g_str_equal(key, "Present") == TRUE) check_sim_present(modem, &value); else if (g_str_equal(key, "SubscriberIdentity") == TRUE) check_sim_identity(modem, &value); return TRUE; } static void get_sim_properties_reply(DBusPendingCall *call, void *user_data) { struct modem_data *modem = user_data; DBusMessage *reply = dbus_pending_call_steal_reply(call); DBusMessageIter iter, dict; DBusError err; dbus_error_init(&err); if (dbus_set_error_from_message(&err, reply) == TRUE) { dbus_error_free(&err); goto done; } if (dbus_message_has_signature(reply, "a{sv}") == FALSE) goto done; if (dbus_message_iter_init(reply, &iter) == FALSE) goto done; dbus_message_iter_recurse(&iter, &dict); while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) { DBusMessageIter entry, value; const char *key; dbus_message_iter_recurse(&dict, &entry); dbus_message_iter_get_basic(&entry, &key); dbus_message_iter_next(&entry); dbus_message_iter_recurse(&entry, &value); if (g_str_equal(key, "Present") == TRUE) check_sim_present(modem, &value); else if (g_str_equal(key, "SubscriberIdentity") == TRUE) check_sim_identity(modem, &value); dbus_message_iter_next(&dict); } done: dbus_message_unref(reply); } static int get_sim_properties(struct modem_data *modem) { DBusConnection *conn = modem->conn; DBusMessage *msg; DBusPendingCall *call; msg = dbus_message_new_method_call(OFONO_SERVICE, modem->path, OFONO_SIM_INTERFACE, "GetProperties"); if (msg == NULL) return -ENOMEM; dbus_message_set_auto_start(msg, FALSE); if (dbus_connection_send_with_reply(conn, msg, &call, -1) == FALSE) { dbus_message_unref(msg); return -EIO; } dbus_message_unref(msg); if (call == NULL) return -EINVAL; dbus_pending_call_set_notify(call, get_sim_properties_reply, modem, NULL); dbus_pending_call_unref(call); return 0; } static void check_context_active(struct modem_data *modem, DBusMessageIter *iter) { dbus_bool_t active; dbus_message_iter_get_basic(iter, &active); if (modem->context_active == active) return; modem->context_active = active; DBG("Context active %d", modem->context_active); if (modem->context_active == FALSE) { g_free(modem->context_interface); modem->context_interface = NULL; g_free(modem->context_proxy); modem->context_proxy = NULL; mms_service_bearer_notify(modem->service, FALSE, NULL, NULL); } else if (modem->context_proxy != NULL) mms_service_bearer_notify(modem->service, TRUE, modem->context_interface, modem->context_proxy); } static void check_context_settings(struct modem_data *modem, DBusMessageIter *iter) { DBusMessageIter dict; g_free(modem->context_interface); modem->context_interface = NULL; g_free(modem->context_proxy); modem->context_proxy = NULL; if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY) return; dbus_message_iter_recurse(iter, &dict); while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) { DBusMessageIter entry, value; const char *key; dbus_message_iter_recurse(&dict, &entry); dbus_message_iter_get_basic(&entry, &key); dbus_message_iter_next(&entry); dbus_message_iter_recurse(&entry, &value); if (g_str_equal(key, "Interface") == TRUE) { const char *str; dbus_message_iter_get_basic(&value, &str); g_free(modem->context_interface); modem->context_interface = g_strdup(str); } else if (g_str_equal(key, "Proxy") == TRUE) { const char *str; dbus_message_iter_get_basic(&value, &str); g_free(modem->context_proxy); modem->context_proxy = g_strdup(str); } dbus_message_iter_next(&dict); } if (modem->context_active == FALSE) return; mms_service_bearer_notify(modem->service, TRUE, modem->context_interface, modem->context_proxy); } static void check_context_message_center(struct modem_data *modem, DBusMessageIter *iter) { char *message_center; dbus_message_iter_get_basic(iter, &message_center); g_free(modem->message_center); modem->message_center = g_strdup(message_center); DBG("Message center %s", modem->message_center); mms_service_set_mmsc(modem->service, modem->message_center); } static void create_context(struct modem_data *modem, const char *path, DBusMessageIter *iter) { DBusMessageIter dict; if (modem->context_path != NULL) return; dbus_message_iter_recurse(iter, &dict); while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) { DBusMessageIter entry, value; const char *key; dbus_message_iter_recurse(&dict, &entry); dbus_message_iter_get_basic(&entry, &key); dbus_message_iter_next(&entry); dbus_message_iter_recurse(&entry, &value); if (g_str_equal(key, "Type") == TRUE) { const char *type; dbus_message_iter_get_basic(&value, &type); if (g_str_equal(type, "mms") == FALSE) return; modem->context_path = g_strdup(path); DBG("path %s", modem->context_path); } else if (g_str_equal(key, "Active") == TRUE) check_context_active(modem, &value); else if (g_str_equal(key, "Settings") == TRUE) check_context_settings(modem, &value); else if (g_str_equal(key, "MessageCenter") == TRUE) check_context_message_center(modem, &value); dbus_message_iter_next(&dict); } } static gboolean context_added(DBusConnection *connection, DBusMessage *message, void *user_data) { struct modem_data *modem; DBusMessageIter iter; const char *path; if (dbus_message_iter_init(message, &iter) == FALSE) return TRUE; path = dbus_message_get_path(message); modem = g_hash_table_lookup(modem_list, path); if (modem == NULL) return TRUE; dbus_message_iter_get_basic(&iter, &path); dbus_message_iter_next(&iter); create_context(modem, path, &iter); return TRUE; } static gboolean context_removed(DBusConnection *connection, DBusMessage *message, void *user_data) { struct modem_data *modem; DBusMessageIter iter; const char *path; if (dbus_message_iter_init(message, &iter) == FALSE) return TRUE; path = dbus_message_get_path(message); modem = g_hash_table_lookup(modem_list, path); if (modem == NULL) return TRUE; if (modem->context_path == NULL) return TRUE; dbus_message_iter_get_basic(&iter, &path); if (g_str_equal(path, modem->context_path) == TRUE) { modem->context_path = NULL; modem->context_active = FALSE; DBG("Context active %d", modem->context_active); } return TRUE; } static gboolean context_match(gpointer key, gpointer value, gpointer user_data) { struct modem_data *modem = value; const char *path = user_data; if (modem->context_path == NULL) return FALSE; return g_str_equal(modem->context_path, path); } static gboolean context_changed(DBusConnection *connection, DBusMessage *message, void *user_data) { struct modem_data *modem; DBusMessageIter iter, value; const char *path, *key; if (dbus_message_iter_init(message, &iter) == FALSE) return TRUE; path = dbus_message_get_path(message); modem = g_hash_table_find(modem_list, context_match, (char *) path); if (modem == NULL) return TRUE; dbus_message_iter_get_basic(&iter, &key); dbus_message_iter_next(&iter); dbus_message_iter_recurse(&iter, &value); if (g_str_equal(key, "Active") == TRUE) check_context_active(modem, &value); else if (g_str_equal(key, "Settings") == TRUE) check_context_settings(modem, &value); else if (g_str_equal(key, "MessageCenter") == TRUE) check_context_message_center(modem, &value); return TRUE; } static void get_contexts_reply(DBusPendingCall *call, void *user_data) { struct modem_data *modem = user_data; DBusMessage *reply = dbus_pending_call_steal_reply(call); DBusMessageIter iter, list; DBusError err; dbus_error_init(&err); if (dbus_set_error_from_message(&err, reply) == TRUE) { dbus_error_free(&err); goto done; } if (dbus_message_has_signature(reply, "a(oa{sv})") == FALSE) goto done; if (dbus_message_iter_init(reply, &iter) == FALSE) goto done; dbus_message_iter_recurse(&iter, &list); while (dbus_message_iter_get_arg_type(&list) == DBUS_TYPE_STRUCT) { DBusMessageIter entry; const char *path; dbus_message_iter_recurse(&list, &entry); dbus_message_iter_get_basic(&entry, &path); dbus_message_iter_next(&entry); create_context(modem, path, &entry); dbus_message_iter_next(&list); } done: dbus_message_unref(reply); } static int get_contexts(struct modem_data *modem) { DBusConnection *conn = modem->conn; DBusMessage *msg; DBusPendingCall *call; msg = dbus_message_new_method_call(OFONO_SERVICE, modem->path, OFONO_GPRS_INTERFACE, "GetContexts"); if (msg == NULL) return -ENOMEM; dbus_message_set_auto_start(msg, FALSE); if (dbus_connection_send_with_reply(conn, msg, &call, -1) == FALSE) { dbus_message_unref(msg); return -EIO; } dbus_message_unref(msg); if (call == NULL) return -EINVAL; dbus_pending_call_set_notify(call, get_contexts_reply, modem, NULL); dbus_pending_call_unref(call); return 0; } static void set_context_reply(DBusPendingCall *call, void *user_data) { struct modem_data *modem = user_data; DBusMessage *reply = dbus_pending_call_steal_reply(call); DBusError err; dbus_error_init(&err); if (dbus_set_error_from_message(&err, reply) == TRUE) { dbus_error_free(&err); mms_service_bearer_notify(modem->service, FALSE, NULL, NULL); } dbus_message_unref(reply); } static int set_context(struct modem_data *modem, dbus_bool_t active) { DBusConnection *conn = modem->conn; DBusMessage *msg; DBusMessageIter iter; DBusPendingCall *call; msg = dbus_message_new_method_call(OFONO_SERVICE, modem->context_path, OFONO_CONTEXT_INTERFACE, "SetProperty"); if (msg == NULL) return -ENOMEM; dbus_message_set_auto_start(msg, FALSE); dbus_message_iter_init_append(msg, &iter); mms_dbus_property_append_basic(&iter, "Active", DBUS_TYPE_BOOLEAN, &active); if (dbus_connection_send_with_reply(conn, msg, &call, -1) == FALSE) { dbus_message_unref(msg); return -EIO; } dbus_message_unref(msg); if (call == NULL) return -EINVAL; dbus_pending_call_set_notify(call, set_context_reply, modem, NULL); dbus_pending_call_unref(call); return 0; } static void bearer_handler(mms_bool_t active, void *user_data) { struct modem_data *modem = user_data; DBG("path %s active %d", modem->path, active); if (active == TRUE && modem->context_active == TRUE) { mms_service_bearer_notify(modem->service, TRUE, modem->context_interface, modem->context_proxy); return; } if (active == FALSE && modem->context_active == FALSE) { mms_service_bearer_notify(modem->service, FALSE, NULL, NULL); return; } if (modem->gprs_attached == FALSE || modem->context_path == NULL) { mms_service_bearer_notify(modem->service, FALSE, NULL, NULL); return; } if (set_context(modem, active == TRUE ? TRUE : FALSE) < 0) mms_service_bearer_notify(modem->service, FALSE, NULL, NULL); } static void check_gprs_attached(struct modem_data *modem, DBusMessageIter *iter) { dbus_bool_t attached; dbus_message_iter_get_basic(iter, &attached); if (modem->gprs_attached == attached) return; modem->gprs_attached = attached; DBG("GPRS attached %d", modem->gprs_attached); if (modem->gprs_attached == FALSE) { g_free(modem->context_path); modem->context_path = NULL; modem->context_active = FALSE; DBG("Context active %d", modem->context_active); mms_service_set_bearer_handler(modem->service, NULL, NULL); } else mms_service_set_bearer_handler(modem->service, bearer_handler, modem); } static gboolean gprs_changed(DBusConnection *connection, DBusMessage *message, void *user_data) { struct modem_data *modem; DBusMessageIter iter, value; const char *path, *key; if (dbus_message_iter_init(message, &iter) == FALSE) return TRUE; path = dbus_message_get_path(message); modem = g_hash_table_lookup(modem_list, path); if (modem == NULL) return TRUE; dbus_message_iter_get_basic(&iter, &key); dbus_message_iter_next(&iter); dbus_message_iter_recurse(&iter, &value); if (g_str_equal(key, "Attached") == TRUE) check_gprs_attached(modem, &value); return TRUE; } static void get_gprs_properties_reply(DBusPendingCall *call, void *user_data) { struct modem_data *modem = user_data; DBusMessage *reply = dbus_pending_call_steal_reply(call); DBusMessageIter iter, dict; DBusError err; dbus_error_init(&err); if (dbus_set_error_from_message(&err, reply) == TRUE) { dbus_error_free(&err); goto done; } if (dbus_message_has_signature(reply, "a{sv}") == FALSE) goto done; if (dbus_message_iter_init(reply, &iter) == FALSE) goto done; dbus_message_iter_recurse(&iter, &dict); while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) { DBusMessageIter entry, value; const char *key; dbus_message_iter_recurse(&dict, &entry); dbus_message_iter_get_basic(&entry, &key); dbus_message_iter_next(&entry); dbus_message_iter_recurse(&entry, &value); if (g_str_equal(key, "Attached") == TRUE) check_gprs_attached(modem, &value); dbus_message_iter_next(&dict); } done: dbus_message_unref(reply); get_contexts(modem); } static int get_gprs_properties(struct modem_data *modem) { DBusConnection *conn = modem->conn; DBusMessage *msg; DBusPendingCall *call; msg = dbus_message_new_method_call(OFONO_SERVICE, modem->path, OFONO_GPRS_INTERFACE, "GetProperties"); if (msg == NULL) return -ENOMEM; dbus_message_set_auto_start(msg, FALSE); if (dbus_connection_send_with_reply(conn, msg, &call, -1) == FALSE) { dbus_message_unref(msg); return -EIO; } dbus_message_unref(msg); if (call == NULL) return -EINVAL; dbus_pending_call_set_notify(call, get_gprs_properties_reply, modem, NULL); dbus_pending_call_unref(call); return 0; } static void check_interfaces(struct modem_data *modem, DBusMessageIter *iter) { DBusMessageIter entry; gboolean has_sim = FALSE; gboolean has_gprs = FALSE; gboolean has_push = FALSE; dbus_message_iter_recurse(iter, &entry); while (dbus_message_iter_get_arg_type(&entry) == DBUS_TYPE_STRING) { const char *interface; dbus_message_iter_get_basic(&entry, &interface); if (g_str_equal(interface, OFONO_SIM_INTERFACE) == TRUE) has_sim = TRUE; else if (g_str_equal(interface, OFONO_GPRS_INTERFACE) == TRUE) has_gprs = TRUE; else if (g_str_equal(interface, OFONO_PUSH_INTERFACE) == TRUE) has_push = TRUE; dbus_message_iter_next(&entry); } if (modem->has_sim != has_sim) { modem->has_sim = has_sim; DBG("path %s sim %d", modem->path, modem->has_sim); if (modem->has_sim == FALSE) { mms_service_unregister(modem->service); g_free(modem->sim_identity); modem->sim_identity = NULL; modem->sim_present = FALSE; DBG("SIM present %d", modem->sim_present); } else get_sim_properties(modem); } if (modem->has_gprs != has_gprs) { modem->has_gprs = has_gprs; DBG("path %s gprs %d", modem->path, modem->has_gprs); if (modem->has_gprs == FALSE) { modem->gprs_attached = FALSE; DBG("GPRS attached %d", modem->gprs_attached); g_free(modem->context_path); modem->context_path = NULL; modem->context_active = FALSE; DBG("Context active %d", modem->context_active); mms_service_set_bearer_handler(modem->service, NULL, NULL); } else get_gprs_properties(modem); } if (modem->has_push != has_push) { modem->has_push = has_push; DBG("path %s push %d", modem->path, modem->has_push); if (modem->has_push == TRUE && modem->has_agent == FALSE) { create_agent(modem); register_agent(modem); } if (modem->has_push == FALSE && modem->has_agent == TRUE) remove_agent(modem); } } static void create_modem(DBusConnection *conn, const char *path, DBusMessageIter *iter) { struct modem_data *modem; DBusMessageIter dict; modem = g_try_new0(struct modem_data, 1); if (modem == NULL) return; modem->path = g_strdup(path); modem->conn = dbus_connection_ref(conn); modem->has_sim = FALSE; modem->has_gprs = FALSE; modem->has_push = FALSE; modem->has_agent = FALSE; modem->service = mms_service_create(); DBG("path %s", modem->path); g_hash_table_replace(modem_list, modem->path, modem); dbus_message_iter_recurse(iter, &dict); while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) { DBusMessageIter entry, value; const char *key; dbus_message_iter_recurse(&dict, &entry); dbus_message_iter_get_basic(&entry, &key); dbus_message_iter_next(&entry); dbus_message_iter_recurse(&entry, &value); if (g_str_equal(key, "Interfaces") == TRUE) check_interfaces(modem, &value); dbus_message_iter_next(&dict); } } static gboolean modem_added(DBusConnection *connection, DBusMessage *message, void *user_data) { DBusMessageIter iter; const char *path; if (dbus_message_iter_init(message, &iter) == FALSE) return TRUE; dbus_message_iter_get_basic(&iter, &path); dbus_message_iter_next(&iter); create_modem(connection, path, &iter); return TRUE; } static gboolean modem_removed(DBusConnection *connection, DBusMessage *message, void *user_data) { DBusMessageIter iter; const char *path; if (dbus_message_iter_init(message, &iter) == FALSE) return TRUE; dbus_message_iter_get_basic(&iter, &path); g_hash_table_remove(modem_list, path); return TRUE; } static gboolean modem_changed(DBusConnection *connection, DBusMessage *message, void *user_data) { struct modem_data *modem; DBusMessageIter iter, value; const char *path, *key; if (dbus_message_iter_init(message, &iter) == FALSE) return TRUE; path = dbus_message_get_path(message); modem = g_hash_table_lookup(modem_list, path); if (modem == NULL) return TRUE; dbus_message_iter_get_basic(&iter, &key); dbus_message_iter_next(&iter); dbus_message_iter_recurse(&iter, &value); if (g_str_equal(key, "Interfaces") == TRUE) check_interfaces(modem, &value); return TRUE; } static void get_modems_reply(DBusPendingCall *call, void *user_data) { DBusConnection *conn = user_data; DBusMessage *reply = dbus_pending_call_steal_reply(call); DBusMessageIter iter, list; DBusError err; dbus_error_init(&err); if (dbus_set_error_from_message(&err, reply) == TRUE) { dbus_error_free(&err); goto done; } if (dbus_message_has_signature(reply, "a(oa{sv})") == FALSE) goto done; if (dbus_message_iter_init(reply, &iter) == FALSE) goto done; dbus_message_iter_recurse(&iter, &list); while (dbus_message_iter_get_arg_type(&list) == DBUS_TYPE_STRUCT) { DBusMessageIter entry; const char *path; dbus_message_iter_recurse(&list, &entry); dbus_message_iter_get_basic(&entry, &path); dbus_message_iter_next(&entry); create_modem(conn, path, &entry); dbus_message_iter_next(&list); } done: dbus_message_unref(reply); } static int get_modems(DBusConnection *conn) { DBusMessage *msg; DBusPendingCall *call; msg = dbus_message_new_method_call(OFONO_SERVICE, "/", OFONO_MANAGER_INTERFACE, "GetModems"); if (msg == NULL) return -ENOMEM; dbus_message_set_auto_start(msg, FALSE); if (dbus_connection_send_with_reply(conn, msg, &call, -1) == FALSE) { dbus_message_unref(msg); return -EIO; } dbus_message_unref(msg); if (call == NULL) return -EINVAL; dbus_pending_call_set_notify(call, get_modems_reply, conn, NULL); dbus_pending_call_unref(call); return 0; } static guint modem_added_watch; static guint modem_removed_watch; static guint modem_changed_watch; static guint sim_changed_watch; static guint gprs_changed_watch; static guint context_added_watch; static guint context_removed_watch; static guint context_changed_watch; static void ofono_connect(DBusConnection *conn, void *user_data) { DBG(""); ofono_running = TRUE; modem_list = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, remove_modem); modem_added_watch = g_dbus_add_signal_watch(conn, NULL, NULL, OFONO_MANAGER_INTERFACE, "ModemAdded", modem_added, NULL, NULL); modem_removed_watch = g_dbus_add_signal_watch(conn, NULL, NULL, OFONO_MANAGER_INTERFACE, "ModemRemoved", modem_removed, NULL, NULL); modem_changed_watch = g_dbus_add_signal_watch(conn, NULL, NULL, OFONO_MODEM_INTERFACE, "PropertyChanged", modem_changed, NULL, NULL); sim_changed_watch = g_dbus_add_signal_watch(conn, NULL, NULL, OFONO_SIM_INTERFACE, "PropertyChanged", sim_changed, NULL, NULL); gprs_changed_watch = g_dbus_add_signal_watch(conn, NULL, NULL, OFONO_GPRS_INTERFACE, "PropertyChanged", gprs_changed, NULL, NULL); context_added_watch = g_dbus_add_signal_watch(conn, NULL, NULL, OFONO_GPRS_INTERFACE, "ContextAdded", context_added, NULL, NULL); context_removed_watch = g_dbus_add_signal_watch(conn, NULL, NULL, OFONO_GPRS_INTERFACE, "ContextRemoved", context_removed, NULL, NULL); context_changed_watch = g_dbus_add_signal_watch(conn, NULL, NULL, OFONO_CONTEXT_INTERFACE, "PropertyChanged", context_changed, NULL, NULL); get_modems(conn); } static void ofono_disconnect(DBusConnection *conn, void *user_data) { DBG(""); ofono_running = FALSE; if (modem_added_watch > 0) { g_dbus_remove_watch(conn, modem_added_watch); modem_added_watch = 0; } if (modem_removed_watch > 0) { g_dbus_remove_watch(conn, modem_removed_watch); modem_removed_watch = 0; } if (modem_changed_watch > 0) { g_dbus_remove_watch(conn, modem_changed_watch); modem_changed_watch = 0; } if (sim_changed_watch > 0) { g_dbus_remove_watch(conn, sim_changed_watch); sim_changed_watch = 0; } if (gprs_changed_watch > 0) { g_dbus_remove_watch(conn, gprs_changed_watch); gprs_changed_watch = 0; } if (context_added_watch > 0) { g_dbus_remove_watch(conn, context_added_watch); context_added_watch = 0; } if (context_removed_watch > 0) { g_dbus_remove_watch(conn, context_removed_watch); context_removed_watch = 0; } if (context_changed_watch > 0) { g_dbus_remove_watch(conn, context_changed_watch); context_changed_watch = 0; } g_hash_table_destroy(modem_list); modem_list = NULL; } static DBusConnection *connection; static guint ofono_watch; static int ofono_init(void) { connection = g_dbus_setup_private(DBUS_BUS_SYSTEM, NULL, NULL); if (connection == NULL) return -EPERM; ofono_watch = g_dbus_add_service_watch(connection, OFONO_SERVICE, ofono_connect, ofono_disconnect, NULL, NULL); return 0; } static void ofono_exit(void) { if (ofono_watch > 0) g_dbus_remove_watch(connection, ofono_watch); if (ofono_running == TRUE) ofono_disconnect(connection, NULL); dbus_connection_unref(connection); } MMS_PLUGIN_DEFINE(ofono, ofono_init, ofono_exit) mmsd-0.1/src/000077500000000000000000000000001216024552400131025ustar00rootroot00000000000000mmsd-0.1/src/dbus.c000066400000000000000000000106421216024552400142060ustar00rootroot00000000000000/* * * Multimedia Messaging Service * * Copyright (C) 2010-2011 Intel Corporation. All rights reserved. * * 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 "mms.h" static DBusConnection *connection; DBusConnection *mms_dbus_get_connection(void) { return connection; } void __mms_dbus_set_connection(DBusConnection *conn) { connection = conn; } void mms_dbus_property_append_basic(DBusMessageIter *iter, const char *key, int type, void *val) { DBusMessageIter value; const char *signature; dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &key); switch (type) { case DBUS_TYPE_BOOLEAN: signature = DBUS_TYPE_BOOLEAN_AS_STRING; break; case DBUS_TYPE_STRING: signature = DBUS_TYPE_STRING_AS_STRING; break; case DBUS_TYPE_BYTE: signature = DBUS_TYPE_BYTE_AS_STRING; break; case DBUS_TYPE_UINT16: signature = DBUS_TYPE_UINT16_AS_STRING; break; case DBUS_TYPE_INT16: signature = DBUS_TYPE_INT16_AS_STRING; break; case DBUS_TYPE_UINT32: signature = DBUS_TYPE_UINT32_AS_STRING; break; case DBUS_TYPE_INT32: signature = DBUS_TYPE_INT32_AS_STRING; break; case DBUS_TYPE_OBJECT_PATH: signature = DBUS_TYPE_OBJECT_PATH_AS_STRING; break; default: signature = DBUS_TYPE_VARIANT_AS_STRING; break; } dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT, signature, &value); dbus_message_iter_append_basic(&value, type, val); dbus_message_iter_close_container(iter, &value); } DBusMessage *__mms_error_invalid_args(DBusMessage *msg) { return g_dbus_create_error(msg, MMS_ERROR_INTERFACE ".InvalidArguments", "Invalid arguments in method call"); } DBusMessage *__mms_error_unsupported_message(DBusMessage *msg) { return g_dbus_create_error(msg, MMS_ERROR_INTERFACE ".UnsupportedMessage", "The MMSC does not support the request"); } DBusMessage *__mms_error_trans_failure(DBusMessage *msg) { return g_dbus_create_error(msg, MMS_ERROR_INTERFACE ".TransientFailure", "Request is valid but the MMSC is unable to " "process it due to some temporary conditions"); } DBusMessage *__mms_error_trans_network_problem(DBusMessage *msg) { return g_dbus_create_error(msg, MMS_ERROR_INTERFACE ".TransientNetworkProblem", "The MMSC is unable to process the request " "because of capacity overload"); } DBusMessage *__mms_error_perm_failure(DBusMessage *msg) { return g_dbus_create_error(msg, MMS_ERROR_INTERFACE ".PermanentFailure", "An unspecified permanent error occured during" " the processing of the request by the MMSC"); } DBusMessage *__mms_error_perm_service_denied(DBusMessage *msg) { return g_dbus_create_error(msg, MMS_ERROR_INTERFACE ".PermanentServiceDenied", "The request is rejected because of service " "authentication or authorization failure(s)"); } DBusMessage *__mms_error_perm_message_format_corrupt(DBusMessage *msg) { return g_dbus_create_error(msg, MMS_ERROR_INTERFACE ".PermanentMessageFormatCorrupt", "The request is rejected by the MMSC because " "of an inconsistency with the message format"); } DBusMessage *__mms_error_perm_invalid_address(DBusMessage *msg) { return g_dbus_create_error(msg, MMS_ERROR_INTERFACE ".PermanentInvalidAddress", "No recipient address or none of them belongs " "to the recipient MMSC"); } DBusMessage *__mms_error_perm_content_not_accepted(DBusMessage *msg) { return g_dbus_create_error(msg, MMS_ERROR_INTERFACE ".PermanentContentNotAccepted", "The message content is not accepted because " "of message size, media type or copyrights " "issues"); } DBusMessage *__mms_error_perm_lack_of_prepaid(DBusMessage *msg) { return g_dbus_create_error(msg, MMS_ERROR_INTERFACE ".PermanentLackOfPrepaid", "The request is rejected due to insufficient " "credit of the user"); } mmsd-0.1/src/dbus.h000066400000000000000000000051321216024552400142110ustar00rootroot00000000000000/* * * Multimedia Messaging Service * * Copyright (C) 2010-2011 Intel Corporation. All rights reserved. * * 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 MMS_SERVICE "org.ofono.mms" #define MMS_PATH "/org/ofono/mms" #define MMS_MANAGER_INTERFACE MMS_SERVICE ".Manager" #define MMS_SERVICE_INTERFACE MMS_SERVICE ".Service" #define MMS_MESSAGE_INTERFACE MMS_SERVICE ".Message" #define MMS_ERROR_INTERFACE MMS_SERVICE ".Error" DBusConnection *mms_dbus_get_connection(void); void mms_dbus_property_append_basic(DBusMessageIter *iter, const char *key, int type, void *val); static inline void mms_dbus_dict_open(DBusMessageIter *iter, DBusMessageIter *dict) { dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING DBUS_DICT_ENTRY_END_CHAR_AS_STRING, dict); } static inline void mms_dbus_dict_close(DBusMessageIter *iter, DBusMessageIter *dict) { dbus_message_iter_close_container(iter, dict); } static inline void mms_dbus_dict_append_basic(DBusMessageIter *dict, const char *key, int type, void *val) { DBusMessageIter entry; dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY, NULL, &entry); mms_dbus_property_append_basic(&entry, key, type, val); dbus_message_iter_close_container(dict, &entry); } DBusMessage *__mms_error_invalid_args(DBusMessage *msg); DBusMessage *__mms_error_unsupported_message(DBusMessage *msg); DBusMessage *__mms_error_trans_failure(DBusMessage *msg); DBusMessage *__mms_error_trans_network_problem(DBusMessage *msg); DBusMessage *__mms_error_perm_failure(DBusMessage *msg); DBusMessage *__mms_error_perm_service_denied(DBusMessage *msg); DBusMessage *__mms_error_perm_message_format_corrupt(DBusMessage *msg); DBusMessage *__mms_error_perm_invalid_address(DBusMessage *msg); DBusMessage *__mms_error_perm_content_not_accepted(DBusMessage *msg); DBusMessage *__mms_error_perm_lack_of_prepaid(DBusMessage *msg); mmsd-0.1/src/genbuiltin000077500000000000000000000003421216024552400151670ustar00rootroot00000000000000#!/bin/sh for i in $* do echo "extern struct mms_plugin_desc __mms_builtin_$i;" done echo echo "static struct mms_plugin_desc *__mms_builtin[] = {" for i in $* do echo " &__mms_builtin_$i," done echo " NULL" echo "};" mmsd-0.1/src/log.c000066400000000000000000000052151216024552400140320ustar00rootroot00000000000000/* * * Multimedia Messaging Service * * Copyright (C) 2010-2011 Intel Corporation. All rights reserved. * * 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 "mms.h" void mms_info(const char *format, ...) { va_list ap; va_start(ap, format); vsyslog(LOG_INFO, format, ap); va_end(ap); } void mms_warn(const char *format, ...) { va_list ap; va_start(ap, format); vsyslog(LOG_WARNING, format, ap); va_end(ap); } void mms_error(const char *format, ...) { va_list ap; va_start(ap, format); vsyslog(LOG_ERR, format, ap); va_end(ap); } void mms_debug(const char *format, ...) { va_list ap; va_start(ap, format); vsyslog(LOG_DEBUG, format, ap); va_end(ap); } extern struct mms_debug_desc __start___debug[]; extern struct mms_debug_desc __stop___debug[]; static gchar **enabled = NULL; static gboolean is_enabled(struct mms_debug_desc *desc) { int i; if (enabled == NULL) return FALSE; for (i = 0; enabled[i] != NULL; i++) { if (desc->name != NULL && g_pattern_match_simple(enabled[i], desc->name) == TRUE) return TRUE; if (desc->file != NULL && g_pattern_match_simple(enabled[i], desc->file) == TRUE) return TRUE; } return FALSE; } int __mms_log_init(const char *debug, gboolean detach) { int option = LOG_NDELAY | LOG_PID; struct mms_debug_desc *desc; const char *name = NULL, *file = NULL; if (debug != NULL) enabled = g_strsplit_set(debug, ":, ", 0); for (desc = __start___debug; desc < __stop___debug; desc++) { if (file != NULL || name != NULL) { if (g_strcmp0(desc->file, file) == 0) { if (desc->name == NULL) desc->name = name; } else file = NULL; } if (is_enabled(desc) == TRUE) desc->flags |= MMS_DEBUG_FLAG_PRINT; } if (detach == FALSE) option |= LOG_PERROR; openlog("mmsd", option, LOG_DAEMON); syslog(LOG_INFO, "MMS daemon version %s", VERSION); return 0; } void __mms_log_cleanup(void) { syslog(LOG_INFO, "Exit"); closelog(); g_strfreev(enabled); } mmsd-0.1/src/log.h000066400000000000000000000031521216024552400140350ustar00rootroot00000000000000/* * * Multimedia Messaging Service * * Copyright (C) 2010-2011 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * */ void mms_info(const char *format, ...) __attribute__((format(printf, 1, 2))); void mms_warn(const char *format, ...) __attribute__((format(printf, 1, 2))); void mms_error(const char *format, ...) __attribute__((format(printf, 1, 2))); void mms_debug(const char *format, ...) __attribute__((format(printf, 1, 2))); struct mms_debug_desc { const char *name; const char *file; #define MMS_DEBUG_FLAG_DEFAULT (0) #define MMS_DEBUG_FLAG_PRINT (1 << 0) unsigned int flags; } __attribute__((aligned(8))); #define DBG(fmt, arg...) do { \ static struct mms_debug_desc __mms_debug_desc \ __attribute__((used, section("__debug"), aligned(8))) = { \ .file = __FILE__, .flags = MMS_DEBUG_FLAG_DEFAULT, \ }; \ if (__mms_debug_desc.flags & MMS_DEBUG_FLAG_PRINT) \ mms_debug("%s:%s() " fmt, \ __FILE__, __FUNCTION__ , ## arg); \ } while (0) mmsd-0.1/src/main.c000066400000000000000000000071271216024552400142010ustar00rootroot00000000000000/* * * Multimedia Messaging Service * * Copyright (C) 2010-2011 Intel Corporation. All rights reserved. * * 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 "mms.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; mms_info("Terminating"); g_main_loop_quit(main_loop); } static void disconnect_callback(DBusConnection *conn, void *user_data) { mms_error("D-Bus disconnect"); g_main_loop_quit(main_loop); } static gchar *option_debug = NULL; static gboolean option_detach = TRUE; static gboolean option_version = FALSE; static gboolean parse_debug(const char *key, const char *value, gpointer user_data, GError **error) { if (value) option_debug = g_strdup(value); else option_debug = g_strdup("*"); return TRUE; } static GOptionEntry options[] = { { "debug", 'd', G_OPTION_FLAG_OPTIONAL_ARG, G_OPTION_ARG_CALLBACK, parse_debug, "Specify debug options to enable", "DEBUG" }, { "nodaemon", 'n', G_OPTION_FLAG_REVERSE, G_OPTION_ARG_NONE, &option_detach, "Don't fork daemon to background" }, { "version", 'v', 0, G_OPTION_ARG_NONE, &option_version, "Show version information and exit" }, { NULL }, }; int main(int argc, char *argv[]) { GOptionContext *context; GError *error = NULL; DBusConnection *conn; DBusError err; struct sigaction sa; context = g_option_context_new(NULL); g_option_context_add_main_entries(context, options, NULL); if (g_option_context_parse(context, &argc, &argv, &error) == FALSE) { if (error != NULL) { g_printerr("%s\n", error->message); g_error_free(error); } else g_printerr("An unknown error occurred\n"); exit(1); } g_option_context_free(context); if (option_version == TRUE) { printf("%s\n", VERSION); exit(0); } if (option_detach == TRUE) { if (daemon(0, 0)) { perror("Can't start daemon"); exit(1); } } main_loop = g_main_loop_new(NULL, FALSE); dbus_error_init(&err); conn = g_dbus_setup_bus(DBUS_BUS_SESSION, MMS_SERVICE, &err); if (conn == NULL) { if (dbus_error_is_set(&err) == TRUE) { fprintf(stderr, "%s\n", err.message); dbus_error_free(&err); } else fprintf(stderr, "Can't register with session bus\n"); exit(1); } g_dbus_set_disconnect_function(conn, disconnect_callback, NULL, NULL); __mms_log_init(option_debug, option_detach); __mms_dbus_set_connection(conn); __mms_service_init(); __mms_plugin_init(); __mms_push_config_files_init(); memset(&sa, 0, sizeof(sa)); sa.sa_handler = sig_term; sigaction(SIGINT, &sa, NULL); sigaction(SIGTERM, &sa, NULL); g_main_loop_run(main_loop); __mms_push_config_files_cleanup(); __mms_plugin_cleanup(); __mms_service_cleanup(); __mms_log_cleanup(); dbus_connection_unref(conn); g_main_loop_unref(main_loop); return 0; } mmsd-0.1/src/mms.h000066400000000000000000000023711216024552400140520ustar00rootroot00000000000000/* * * Multimedia Messaging Service * * Copyright (C) 2010-2011 Intel Corporation. All rights reserved. * * 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 "log.h" int __mms_log_init(const char *debug, gboolean detach); void __mms_log_cleanup(void); #include "plugin.h" int __mms_plugin_init(void); void __mms_plugin_cleanup(void); #include "dbus.h" void __mms_dbus_set_connection(DBusConnection *conn); #include "service.h" int __mms_service_init(void); void __mms_service_cleanup(void); #include "push.h" int __mms_push_config_files_init(void); void __mms_push_config_files_cleanup(void); #include "store.h" mmsd-0.1/src/mmsutil.c000066400000000000000000001103531216024552400147430ustar00rootroot00000000000000/* * * Multimedia Messaging Service * * Copyright (C) 2010-2011 Intel Corporation. All rights reserved. * * 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 "wsputil.h" #include "mmsutil.h" #define MAX_ENC_VALUE_BYTES 6 #ifdef TEMP_FAILURE_RETRY #define TFR TEMP_FAILURE_RETRY #else #define TFR #endif #define uninitialized_var(x) x = x enum header_flag { HEADER_FLAG_MANDATORY = 1, HEADER_FLAG_ALLOW_MULTI = 2, HEADER_FLAG_PRESET_POS = 4, HEADER_FLAG_MARKED = 8, }; enum mms_header { MMS_HEADER_BCC = 0x01, MMS_HEADER_CC = 0x02, MMS_HEADER_CONTENT_LOCATION = 0x03, MMS_HEADER_CONTENT_TYPE = 0x04, MMS_HEADER_DATE = 0x05, MMS_HEADER_DELIVERY_REPORT = 0x06, MMS_HEADER_DELIVERY_TIME = 0x07, MMS_HEADER_EXPIRY = 0x08, MMS_HEADER_FROM = 0x09, MMS_HEADER_MESSAGE_CLASS = 0x0a, MMS_HEADER_MESSAGE_ID = 0x0b, MMS_HEADER_MESSAGE_TYPE = 0x0c, MMS_HEADER_MMS_VERSION = 0x0d, MMS_HEADER_MESSAGE_SIZE = 0x0e, MMS_HEADER_PRIORITY = 0x0f, MMS_HEADER_READ_REPLY = 0x10, MMS_HEADER_REPORT_ALLOWED = 0x11, MMS_HEADER_RESPONSE_STATUS = 0x12, MMS_HEADER_RESPONSE_TEXT = 0x13, MMS_HEADER_SENDER_VISIBILITY = 0x14, MMS_HEADER_STATUS = 0x15, MMS_HEADER_SUBJECT = 0x16, MMS_HEADER_TO = 0x17, MMS_HEADER_TRANSACTION_ID = 0x18, __MMS_HEADER_MAX = 0x19, MMS_HEADER_INVALID = 0x80, }; enum mms_part_header { MMS_PART_HEADER_CONTENT_LOCATION = 0x0e, MMS_PART_HEADER_CONTENT_ID = 0x40, }; /* * IANA Character Set Assignments (examples) used by WAPWSP * * Reference: WAP-230-WSP Appendix Table 42 Character Set Assignment Examples * Reference: IANA http://www.iana.org/assignments/character-sets */ static const struct { unsigned int mib_enum; const char *charset; } charset_assignments[] = { { 0x03, "us-ascii" }, { 0x6A, "utf-8" }, { 0x00, NULL } }; #define FB_SIZE 256 struct file_buffer { unsigned char buf[FB_SIZE]; unsigned int size; unsigned int fsize; int fd; }; typedef gboolean (*header_handler)(struct wsp_header_iter *, void *); typedef gboolean (*header_encoder)(struct file_buffer *, enum mms_header, void *); char *mms_content_type_get_param_value(const char *content_type, const char *param_name) { struct wsp_text_header_iter iter; if (wsp_text_header_iter_init(&iter, content_type) == FALSE) return NULL; while (wsp_text_header_iter_param_next(&iter) == TRUE) { const char *key = wsp_text_header_iter_get_key(&iter); if (g_str_equal(key, param_name) == TRUE) return g_strdup(wsp_text_header_iter_get_value(&iter)); } return NULL; } static const char *charset_index2string(unsigned int index) { unsigned int i = 0; for (i = 0; charset_assignments[i].charset; i++) { if (charset_assignments[i].mib_enum == index) return charset_assignments[i].charset; } return NULL; } static gboolean extract_short(struct wsp_header_iter *iter, void *user) { unsigned char *out = user; const unsigned char *p; if (wsp_header_iter_get_val_type(iter) != WSP_VALUE_TYPE_SHORT) return FALSE; p = wsp_header_iter_get_val(iter); *out = p[0]; return TRUE; } static const char *decode_text(struct wsp_header_iter *iter) { const unsigned char *p; unsigned int l; if (wsp_header_iter_get_val_type(iter) != WSP_VALUE_TYPE_TEXT) return NULL; p = wsp_header_iter_get_val(iter); l = wsp_header_iter_get_val_len(iter); return wsp_decode_text(p, l, NULL); } static gboolean extract_text(struct wsp_header_iter *iter, void *user) { char **out = user; const char *text; text = decode_text(iter); if (text == NULL) return FALSE; *out = g_strdup(text); return TRUE; } static gboolean extract_text_array_element(struct wsp_header_iter *iter, void *user) { char **out = user; const char *element; char *tmp; element = decode_text(iter); if (element == NULL) return FALSE; if (*out == NULL) { *out = g_strdup(element); return TRUE; } tmp = g_strjoin(",", *out, element, NULL); if (tmp == NULL) return FALSE; g_free(*out); *out = tmp; return TRUE; } static char *decode_encoded_string_with_mib_enum(const unsigned char *p, unsigned int l) { unsigned int mib_enum; unsigned int consumed; const char *text; const char *from_codeset; const char *to_codeset = "UTF-8"; gsize bytes_read; gsize bytes_written; if (wsp_decode_integer(p, l, &mib_enum, &consumed) == FALSE) return NULL; if (mib_enum == 106) { /* header is UTF-8 already */ text = wsp_decode_text(p + consumed, l - consumed, NULL); return g_strdup(text); } /* convert to UTF-8 */ from_codeset = charset_index2string(mib_enum); if (from_codeset == NULL) return NULL; return g_convert((const char *) p + consumed, l - consumed, to_codeset, from_codeset, &bytes_read, &bytes_written, NULL); } static gboolean extract_encoded_text(struct wsp_header_iter *iter, void *user) { char **out = user; const unsigned char *p; unsigned int l; const char *text; char *uninitialized_var(dec_text); p = wsp_header_iter_get_val(iter); l = wsp_header_iter_get_val_len(iter); switch (wsp_header_iter_get_val_type(iter)) { case WSP_VALUE_TYPE_TEXT: /* Text-string */ text = wsp_decode_text(p, l, NULL); dec_text = g_strdup(text); break; case WSP_VALUE_TYPE_LONG: /* (Value-len) Char-set Text-string */ dec_text = decode_encoded_string_with_mib_enum(p, l); break; case WSP_VALUE_TYPE_SHORT: dec_text = NULL; break; } if (dec_text == NULL) return FALSE; *out = dec_text; return TRUE; } static gboolean extract_date(struct wsp_header_iter *iter, void *user) { time_t *out = user; const unsigned char *p; unsigned int l; unsigned int i; if (wsp_header_iter_get_val_type(iter) != WSP_VALUE_TYPE_LONG) return FALSE; p = wsp_header_iter_get_val(iter); l = wsp_header_iter_get_val_len(iter); if (l > 4) return FALSE; for (i = 0, *out = 0; i < l; i++) *out = *out << 8 | p[i]; /* It is possible to overflow time_t on 32 bit systems */ *out = *out & 0x7fffffff; return TRUE; } static gboolean extract_absolute_relative_date(struct wsp_header_iter *iter, void *user) { time_t *out = user; const unsigned char *p; unsigned int l; unsigned int i; unsigned int seconds; if (wsp_header_iter_get_val_type(iter) != WSP_VALUE_TYPE_LONG) return FALSE; p = wsp_header_iter_get_val(iter); l = wsp_header_iter_get_val_len(iter); if (l < 2 || l > 5) return FALSE; if (p[0] != 128 && p[0] != 129) return FALSE; for (i = 2, seconds = 0; i < l; i++) seconds = seconds << 8 | p[i]; if (p[0] == 129) { *out = time(NULL); *out += seconds; } else *out = seconds; /* It is possible to overflow time_t on 32 bit systems */ *out = *out & 0x7fffffff; return TRUE; } static gboolean extract_boolean(struct wsp_header_iter *iter, void *user) { gboolean *out = user; const unsigned char *p; if (wsp_header_iter_get_val_type(iter) != WSP_VALUE_TYPE_SHORT) return FALSE; p = wsp_header_iter_get_val(iter); if (p[0] != 128 && p[0] != 129) return FALSE; *out = p[0] == 128; return TRUE; } static gboolean extract_from(struct wsp_header_iter *iter, void *user) { char **out = user; const unsigned char *p; unsigned int l; const char *text; if (wsp_header_iter_get_val_type(iter) != WSP_VALUE_TYPE_LONG) return FALSE; p = wsp_header_iter_get_val(iter); l = wsp_header_iter_get_val_len(iter); if (p[0] != 128 && p[0] != 129) return FALSE; if (p[0] == 129) { *out = NULL; return TRUE; } text = wsp_decode_text(p + 1, l - 1, NULL); if (text == NULL) return FALSE; *out = g_strdup(text); return TRUE; } static gboolean extract_message_class(struct wsp_header_iter *iter, void *user) { char **out = user; const unsigned char *p; unsigned int l; const char *text; if (wsp_header_iter_get_val_type(iter) == WSP_VALUE_TYPE_LONG) return FALSE; p = wsp_header_iter_get_val(iter); if (wsp_header_iter_get_val_type(iter) == WSP_VALUE_TYPE_SHORT) { switch (p[0]) { case 128: *out = g_strdup("Personal"); return TRUE; case 129: *out = g_strdup("Advertisement"); return TRUE; case 130: *out = g_strdup("Informational"); return TRUE; case 131: *out = g_strdup("Auto"); return TRUE; default: return FALSE; } } l = wsp_header_iter_get_val_len(iter); text = wsp_decode_token_text(p, l, NULL); if (text == NULL) return FALSE; *out = g_strdup(text); return TRUE; } static gboolean extract_sender_visibility(struct wsp_header_iter *iter, void *user) { enum mms_message_sender_visibility *out = user; const unsigned char *p; if (wsp_header_iter_get_val_type(iter) != WSP_VALUE_TYPE_SHORT) return FALSE; p = wsp_header_iter_get_val(iter); if (p[0] != 128 && p[0] != 129) return FALSE; *out = p[0]; return TRUE; } static gboolean extract_priority(struct wsp_header_iter *iter, void *user) { char **out = user; const unsigned char *p; if (wsp_header_iter_get_val_type(iter) != WSP_VALUE_TYPE_SHORT) return FALSE; p = wsp_header_iter_get_val(iter); switch (p[0]) { case 128: *out = g_strdup("Low"); return TRUE; case 129: *out = g_strdup("Normal"); return TRUE; case 130: *out = g_strdup("High"); return TRUE; default: return FALSE; } return TRUE; } static gboolean extract_rsp_status(struct wsp_header_iter *iter, void *user) { unsigned char *out = user; const unsigned char *p; if (wsp_header_iter_get_val_type(iter) != WSP_VALUE_TYPE_SHORT) return FALSE; p = wsp_header_iter_get_val(iter); switch (p[0]) { case MMS_MESSAGE_RSP_STATUS_OK: case MMS_MESSAGE_RSP_STATUS_ERR_UNSUPPORTED_MESSAGE: case MMS_MESSAGE_RSP_STATUS_ERR_TRANS_FAILURE: case MMS_MESSAGE_RSP_STATUS_ERR_TRANS_NETWORK_PROBLEM: case MMS_MESSAGE_RSP_STATUS_ERR_PERM_FAILURE: case MMS_MESSAGE_RSP_STATUS_ERR_PERM_SERVICE_DENIED: case MMS_MESSAGE_RSP_STATUS_ERR_PERM_MESSAGE_FORMAT_CORRUPT: case MMS_MESSAGE_RSP_STATUS_ERR_PERM_SENDING_ADDRESS_UNRESOLVED: case MMS_MESSAGE_RSP_STATUS_ERR_PERM_CONTENT_NOT_ACCEPTED: case MMS_MESSAGE_RSP_STATUS_ERR_PERM_LACK_OF_PREPAID: *out = p[0]; return TRUE; } return FALSE; } static gboolean extract_status(struct wsp_header_iter *iter, void *user) { enum mms_message_delivery_status *out = user; const unsigned char *p; if (wsp_header_iter_get_val_type(iter) != WSP_VALUE_TYPE_SHORT) return FALSE; p = wsp_header_iter_get_val(iter); switch (p[0]) { case MMS_MESSAGE_DELIVERY_STATUS_EXPIRED: case MMS_MESSAGE_DELIVERY_STATUS_RETRIEVED: case MMS_MESSAGE_DELIVERY_STATUS_REJECTED: case MMS_MESSAGE_DELIVERY_STATUS_DEFERRED: case MMS_MESSAGE_DELIVERY_STATUS_UNRECOGNISED: case MMS_MESSAGE_DELIVERY_STATUS_INDETERMINATE: case MMS_MESSAGE_DELIVERY_STATUS_FORWARDED: case MMS_MESSAGE_DELIVERY_STATUS_UNREACHABLE: *out = p[0]; return TRUE; } return FALSE; } static gboolean extract_unsigned(struct wsp_header_iter *iter, void *user) { unsigned long *out = user; const unsigned char *p; unsigned int l; unsigned int i; if (wsp_header_iter_get_val_type(iter) != WSP_VALUE_TYPE_LONG) return FALSE; p = wsp_header_iter_get_val(iter); l = wsp_header_iter_get_val_len(iter); if (l > sizeof(unsigned long)) return FALSE; for (i = 0, *out = 0; i < l; i++) *out = *out << 8 | p[i]; return TRUE; } static header_handler handler_for_type(enum mms_header header) { switch (header) { case MMS_HEADER_BCC: return extract_encoded_text; case MMS_HEADER_CC: return extract_encoded_text; case MMS_HEADER_CONTENT_LOCATION: return extract_text; case MMS_HEADER_CONTENT_TYPE: return extract_text; case MMS_HEADER_DATE: return extract_date; case MMS_HEADER_DELIVERY_REPORT: return extract_boolean; case MMS_HEADER_DELIVERY_TIME: return extract_absolute_relative_date; case MMS_HEADER_EXPIRY: return extract_absolute_relative_date; case MMS_HEADER_FROM: return extract_from; case MMS_HEADER_MESSAGE_CLASS: return extract_message_class; case MMS_HEADER_MESSAGE_ID: return extract_text; case MMS_HEADER_MESSAGE_TYPE: return extract_short; case MMS_HEADER_MMS_VERSION: return extract_short; case MMS_HEADER_MESSAGE_SIZE: return extract_unsigned; case MMS_HEADER_PRIORITY: return extract_priority; case MMS_HEADER_READ_REPLY: return extract_boolean; case MMS_HEADER_REPORT_ALLOWED: return extract_boolean; case MMS_HEADER_RESPONSE_STATUS: return extract_rsp_status; case MMS_HEADER_RESPONSE_TEXT: return extract_encoded_text; case MMS_HEADER_SENDER_VISIBILITY: return extract_sender_visibility; case MMS_HEADER_STATUS: return extract_status; case MMS_HEADER_SUBJECT: return extract_encoded_text; case MMS_HEADER_TO: return extract_text_array_element; case MMS_HEADER_TRANSACTION_ID: return extract_text; case MMS_HEADER_INVALID: case __MMS_HEADER_MAX: return NULL; } return NULL; } struct header_handler_entry { int flags; void *data; int pos; }; static gboolean mms_parse_headers(struct wsp_header_iter *iter, enum mms_header orig_header, ...) { struct header_handler_entry entries[__MMS_HEADER_MAX + 1]; va_list args; const unsigned char *p; unsigned int i; enum mms_header header; memset(&entries, 0, sizeof(entries)); va_start(args, orig_header); header = orig_header; while (header != MMS_HEADER_INVALID) { entries[header].flags = va_arg(args, int); entries[header].data = va_arg(args, void *); header = va_arg(args, enum mms_header); } va_end(args); for (i = 1; wsp_header_iter_next(iter); i++) { unsigned char h; header_handler handler; /* Skip application headers */ if (wsp_header_iter_get_hdr_type(iter) != WSP_HEADER_TYPE_WELL_KNOWN) continue; p = wsp_header_iter_get_hdr(iter); h = p[0] & 0x7f; handler = handler_for_type(h); if (handler == NULL) return FALSE; /* Unsupported header, skip */ if (entries[h].data == NULL) continue; /* Skip multiply present headers unless explicitly requested */ if ((entries[h].flags & HEADER_FLAG_MARKED) && !(entries[h].flags & HEADER_FLAG_ALLOW_MULTI)) continue; /* Parse the header */ if (handler(iter, entries[h].data) == FALSE) return FALSE; entries[h].pos = i; entries[h].flags |= HEADER_FLAG_MARKED; } for (i = 0; i < __MMS_HEADER_MAX + 1; i++) { if ((entries[i].flags & HEADER_FLAG_MANDATORY) && !(entries[i].flags & HEADER_FLAG_MARKED)) return FALSE; } /* * Here we check for header positions. This function assumes that * headers marked with PRESET_POS are in the beginning of the message * and follow the same order as given in the va_arg list. The headers * marked this way have to be contiguous. */ for (i = 0; i < __MMS_HEADER_MAX + 1; i++) { int check_flags = HEADER_FLAG_PRESET_POS | HEADER_FLAG_MARKED; int expected_pos = 1; if ((entries[i].flags & check_flags) != check_flags) continue; va_start(args, orig_header); header = orig_header; while (header != MMS_HEADER_INVALID && header != i) { va_arg(args, int); va_arg(args, void *); if (entries[header].flags & HEADER_FLAG_MARKED) expected_pos += 1; header = va_arg(args, enum mms_header); } va_end(args); if (entries[i].pos != expected_pos) return FALSE; } return TRUE; } static gboolean decode_notification_ind(struct wsp_header_iter *iter, struct mms_message *out) { return mms_parse_headers(iter, MMS_HEADER_TRANSACTION_ID, HEADER_FLAG_MANDATORY | HEADER_FLAG_PRESET_POS, &out->transaction_id, MMS_HEADER_MMS_VERSION, HEADER_FLAG_MANDATORY | HEADER_FLAG_PRESET_POS, &out->version, MMS_HEADER_FROM, 0, &out->ni.from, MMS_HEADER_SUBJECT, 0, &out->ni.subject, MMS_HEADER_MESSAGE_CLASS, HEADER_FLAG_MANDATORY, &out->ni.cls, MMS_HEADER_MESSAGE_SIZE, HEADER_FLAG_MANDATORY, &out->ni.size, MMS_HEADER_EXPIRY, HEADER_FLAG_MANDATORY, &out->ni.expiry, MMS_HEADER_CONTENT_LOCATION, HEADER_FLAG_MANDATORY, &out->ni.location, MMS_HEADER_INVALID); } static const char *decode_attachment_charset(const unsigned char *pdu, unsigned int len) { struct wsp_parameter_iter iter; struct wsp_parameter param; wsp_parameter_iter_init(&iter, pdu, len); while (wsp_parameter_iter_next(&iter, ¶m)) { if (param.type == WSP_PARAMETER_TYPE_CHARSET) return param.text; } return NULL; } static gboolean extract_content_id(struct wsp_header_iter *iter, void *user) { char **out = user; const unsigned char *p; unsigned int l; const char *text; p = wsp_header_iter_get_val(iter); l = wsp_header_iter_get_val_len(iter); if (wsp_header_iter_get_val_type(iter) != WSP_VALUE_TYPE_TEXT) return FALSE; text = wsp_decode_quoted_string(p, l, NULL); if (text == NULL) return FALSE; *out = g_strdup(text); return TRUE; } static gboolean attachment_parse_headers(struct wsp_header_iter *iter, struct mms_attachment *part) { while (wsp_header_iter_next(iter)) { const unsigned char *hdr = wsp_header_iter_get_hdr(iter); unsigned char h; /* Skip application headers */ if (wsp_header_iter_get_hdr_type(iter) != WSP_HEADER_TYPE_WELL_KNOWN) continue; h = hdr[0] & 0x7f; switch (h) { case MMS_PART_HEADER_CONTENT_ID: if (extract_content_id(iter, &part->content_id) == FALSE) return FALSE; break; case MMS_PART_HEADER_CONTENT_LOCATION: break; } } return TRUE; } static void free_attachment(gpointer data, gpointer user_data) { struct mms_attachment *attach = data; g_free(attach->content_type); g_free(attach->content_id); g_free(attach); } static gboolean mms_parse_attachments(struct wsp_header_iter *iter, struct mms_message *out) { struct wsp_multipart_iter mi; const void *ct; unsigned int ct_len; unsigned int consumed; if (wsp_multipart_iter_init(&mi, iter, &ct, &ct_len) == FALSE) return FALSE; while (wsp_multipart_iter_next(&mi) == TRUE) { struct mms_attachment *part; struct wsp_header_iter hi; const void *mimetype; const char *charset; ct = wsp_multipart_iter_get_content_type(&mi); ct_len = wsp_multipart_iter_get_content_type_len(&mi); if (wsp_decode_content_type(ct, ct_len, &mimetype, &consumed, NULL) == FALSE) return FALSE; charset = decode_attachment_charset(ct + consumed, ct_len - consumed); wsp_header_iter_init(&hi, wsp_multipart_iter_get_hdr(&mi), wsp_multipart_iter_get_hdr_len(&mi), 0); part = g_try_new0(struct mms_attachment, 1); if (part == NULL) return FALSE; if (attachment_parse_headers(&hi, part) == FALSE) { free_attachment(part, NULL); return FALSE; } if (wsp_header_iter_at_end(&hi) == FALSE) { free_attachment(part, NULL); return FALSE; } if (charset == NULL) part->content_type = g_strdup(mimetype); else part->content_type = g_strconcat(mimetype, ";charset=", charset, NULL); part->length = wsp_multipart_iter_get_body_len(&mi); part->offset = (const unsigned char *) wsp_multipart_iter_get_body(&mi) - wsp_header_iter_get_pdu(iter); out->attachments = g_slist_prepend(out->attachments, part); } if (wsp_multipart_iter_close(&mi, iter) == FALSE) return FALSE; out->attachments = g_slist_reverse(out->attachments); return TRUE; } static gboolean decode_retrieve_conf(struct wsp_header_iter *iter, struct mms_message *out) { if (mms_parse_headers(iter, MMS_HEADER_TRANSACTION_ID, HEADER_FLAG_PRESET_POS, &out->transaction_id, MMS_HEADER_MMS_VERSION, HEADER_FLAG_MANDATORY | HEADER_FLAG_PRESET_POS, &out->version, MMS_HEADER_FROM, 0, &out->rc.from, MMS_HEADER_TO, HEADER_FLAG_ALLOW_MULTI, &out->rc.to, MMS_HEADER_SUBJECT, 0, &out->rc.subject, MMS_HEADER_MESSAGE_CLASS, 0, &out->rc.cls, MMS_HEADER_PRIORITY, 0, &out->rc.priority, MMS_HEADER_MESSAGE_ID, 0, &out->rc.msgid, MMS_HEADER_DATE, HEADER_FLAG_MANDATORY, &out->rc.date, MMS_HEADER_INVALID) == FALSE) return FALSE; if (wsp_header_iter_at_end(iter) == TRUE) return TRUE; if (wsp_header_iter_is_multipart(iter) == FALSE) return FALSE; if (mms_parse_attachments(iter, out) == FALSE) return FALSE; if (wsp_header_iter_at_end(iter) == FALSE) return FALSE; return TRUE; } static gboolean decode_send_conf(struct wsp_header_iter *iter, struct mms_message *out) { return mms_parse_headers(iter, MMS_HEADER_TRANSACTION_ID, HEADER_FLAG_MANDATORY | HEADER_FLAG_PRESET_POS, &out->transaction_id, MMS_HEADER_MMS_VERSION, HEADER_FLAG_MANDATORY | HEADER_FLAG_PRESET_POS, &out->version, MMS_HEADER_RESPONSE_STATUS, HEADER_FLAG_MANDATORY, &out->sc.rsp_status, MMS_HEADER_MESSAGE_ID, 0, &out->sc.msgid, MMS_HEADER_INVALID); } static gboolean decode_send_req(struct wsp_header_iter *iter, struct mms_message *out) { if (mms_parse_headers(iter, MMS_HEADER_TRANSACTION_ID, HEADER_FLAG_MANDATORY | HEADER_FLAG_PRESET_POS, &out->transaction_id, MMS_HEADER_MMS_VERSION, HEADER_FLAG_MANDATORY | HEADER_FLAG_PRESET_POS, &out->version, MMS_HEADER_TO, HEADER_FLAG_ALLOW_MULTI, &out->sr.to, MMS_HEADER_INVALID) == FALSE) return FALSE; if (wsp_header_iter_at_end(iter) == TRUE) return TRUE; if (wsp_header_iter_is_multipart(iter) == FALSE) return FALSE; if (mms_parse_attachments(iter, out) == FALSE) return FALSE; if (wsp_header_iter_at_end(iter) == FALSE) return FALSE; return TRUE; } #define CHECK_WELL_KNOWN_HDR(hdr) \ if (wsp_header_iter_next(&iter) == FALSE) \ return FALSE; \ \ if (wsp_header_iter_get_hdr_type(&iter) != \ WSP_HEADER_TYPE_WELL_KNOWN) \ return FALSE; \ \ p = wsp_header_iter_get_hdr(&iter); \ \ if ((p[0] & 0x7f) != hdr) \ return FALSE \ gboolean mms_message_decode(const unsigned char *pdu, unsigned int len, struct mms_message *out) { unsigned int flags = 0; struct wsp_header_iter iter; const unsigned char *p; unsigned char octet; memset(out, 0, sizeof(*out)); flags |= WSP_HEADER_ITER_FLAG_REJECT_CP; flags |= WSP_HEADER_ITER_FLAG_DETECT_MMS_MULTIPART; wsp_header_iter_init(&iter, pdu, len, flags); CHECK_WELL_KNOWN_HDR(MMS_HEADER_MESSAGE_TYPE); if (extract_short(&iter, &octet) == FALSE) return FALSE; if (octet < MMS_MESSAGE_TYPE_SEND_REQ || octet > MMS_MESSAGE_TYPE_DELIVERY_IND) return FALSE; out->type = octet; switch (out->type) { case MMS_MESSAGE_TYPE_SEND_REQ: return decode_send_req(&iter, out); case MMS_MESSAGE_TYPE_SEND_CONF: return decode_send_conf(&iter, out); case MMS_MESSAGE_TYPE_NOTIFICATION_IND: return decode_notification_ind(&iter, out); case MMS_MESSAGE_TYPE_NOTIFYRESP_IND: return FALSE; case MMS_MESSAGE_TYPE_RETRIEVE_CONF: return decode_retrieve_conf(&iter, out); case MMS_MESSAGE_TYPE_ACKNOWLEDGE_IND: return FALSE; case MMS_MESSAGE_TYPE_DELIVERY_IND: return FALSE; } return FALSE; } void mms_message_free(struct mms_message *msg) { switch (msg->type) { case MMS_MESSAGE_TYPE_SEND_REQ: g_free(msg->sr.to); g_free(msg->sr.content_type); break; case MMS_MESSAGE_TYPE_SEND_CONF: g_free(msg->sc.msgid); break; case MMS_MESSAGE_TYPE_NOTIFICATION_IND: g_free(msg->ni.from); g_free(msg->ni.subject); g_free(msg->ni.cls); g_free(msg->ni.location); break; case MMS_MESSAGE_TYPE_NOTIFYRESP_IND: break; case MMS_MESSAGE_TYPE_RETRIEVE_CONF: g_free(msg->rc.from); g_free(msg->rc.to); g_free(msg->rc.subject); g_free(msg->rc.cls); g_free(msg->rc.priority); g_free(msg->rc.msgid); break; case MMS_MESSAGE_TYPE_ACKNOWLEDGE_IND: break; case MMS_MESSAGE_TYPE_DELIVERY_IND: break; } g_free(msg->uuid); g_free(msg->path); g_free(msg->transaction_id); if (msg->attachments != NULL) { g_slist_foreach(msg->attachments, free_attachment, NULL); g_slist_free(msg->attachments); } g_free(msg); } static void fb_init(struct file_buffer *fb, int fd) { fb->size = 0; fb->fsize = 0; fb->fd = fd; } static gboolean fb_flush(struct file_buffer *fb) { unsigned int size; ssize_t len; if (fb->size == 0) return TRUE; len = write(fb->fd, fb->buf, fb->size); if (len < 0) return FALSE; size = len; if (size != fb->size) return FALSE; fb->fsize += size; fb->size = 0; return TRUE; } static unsigned int fb_get_file_size(struct file_buffer *fb) { return fb->fsize + fb->size; } static void *fb_request(struct file_buffer *fb, unsigned int count) { if (fb->size + count < FB_SIZE) { void *ptr = fb->buf + fb->size; fb->size += count; return ptr; } if (fb_flush(fb) == FALSE) return NULL; if (count > FB_SIZE) return NULL; fb->size = count; return fb->buf; } static void *fb_request_field(struct file_buffer *fb, unsigned char token, unsigned int len) { unsigned char *ptr; ptr = fb_request(fb, len + 1); if (ptr == NULL) return NULL; ptr[0] = token | 0x80; return ptr + 1; } static gboolean fb_copy(struct file_buffer *fb, const void *buf, unsigned int c) { unsigned int written; ssize_t len; if (fb_flush(fb) == FALSE) return FALSE; len = TFR(write(fb->fd, buf, c)); if (len < 0) return FALSE; written = len; if (written != c) return FALSE; fb->fsize += written; return TRUE; } static gboolean fb_put_value_length(struct file_buffer *fb, unsigned int val) { unsigned int count; if (fb->size + MAX_ENC_VALUE_BYTES > FB_SIZE) { if (fb_flush(fb) == FALSE) return FALSE; } if (wsp_encode_value_length(val, fb->buf + fb->size, FB_SIZE - fb->size, &count) == FALSE) return FALSE; fb->size += count; return TRUE; } static gboolean fb_put_uintvar(struct file_buffer *fb, unsigned int val) { unsigned int count; if (fb->size + MAX_ENC_VALUE_BYTES > FB_SIZE) { if (fb_flush(fb) == FALSE) return FALSE; } if (wsp_encode_uintvar(val, fb->buf + fb->size, FB_SIZE - fb->size, &count) == FALSE) return FALSE; fb->size += count; return TRUE; } static gboolean encode_short(struct file_buffer *fb, enum mms_header header, void *user) { char *ptr; unsigned int *wk = user; ptr = fb_request_field(fb, header, 1); if (ptr == NULL) return FALSE; *ptr = *wk | 0x80; return TRUE; } static gboolean encode_from(struct file_buffer *fb, enum mms_header header, void *user) { char *ptr; char **text = user; if (strlen(*text) > 0) return FALSE; /* From: header token + value length + Insert-address-token */ ptr = fb_request_field(fb, header, 2); if (ptr == NULL) return FALSE; ptr[0] = 1; ptr[1] = 129; return TRUE; } static gboolean encode_text(struct file_buffer *fb, enum mms_header header, void *user) { char *ptr; char **text = user; unsigned int len; len = strlen(*text) + 1; ptr = fb_request_field(fb, header, len); if (ptr == NULL) return FALSE; strcpy(ptr, *text); return TRUE; } static gboolean encode_quoted_string(struct file_buffer *fb, enum mms_header header, void *user) { char *ptr; char **text = user; unsigned int len; len = strlen(*text) + 1; ptr = fb_request_field(fb, header, len + 3); if (ptr == NULL) return FALSE; ptr[0] = '"'; ptr[1] = '<'; strcpy(ptr + 2, *text); ptr[len + 1] = '>'; ptr[len + 2] = '\0'; return TRUE; } static gboolean encode_text_array_element(struct file_buffer *fb, enum mms_header header, void *user) { char **text = user; char **tos; int i; tos = g_strsplit(*text, ",", 0); for (i = 0; tos[i] != NULL; i++) { if (encode_text(fb, header, &tos[i]) == FALSE) { g_strfreev(tos); return FALSE; } } g_strfreev(tos); return TRUE; } static gboolean encode_content_type(struct file_buffer *fb, enum mms_header header, void *user) { char *ptr; char **hdr = user; unsigned int len; unsigned int ct; unsigned int ct_len; unsigned int type_len; unsigned int start_len; const char *ct_str; const char *uninitialized_var(type); const char *uninitialized_var(start); struct wsp_text_header_iter iter; if (wsp_text_header_iter_init(&iter, *hdr) == FALSE) return FALSE; if (g_ascii_strcasecmp("Content-Type", wsp_text_header_iter_get_key(&iter)) != 0) return FALSE; ct_str = wsp_text_header_iter_get_value(&iter); if (wsp_get_well_known_content_type(ct_str, &ct) == TRUE) ct_len = 1; else ct_len = strlen(ct_str) + 1; len = ct_len; type_len = 0; start_len = 0; while (wsp_text_header_iter_param_next(&iter) == TRUE) { if (g_ascii_strcasecmp("type", wsp_text_header_iter_get_key(&iter)) == 0) { type = wsp_text_header_iter_get_value(&iter); type_len = strlen(type) + 1; len += 1 + type_len; } else if (g_ascii_strcasecmp("start", wsp_text_header_iter_get_key(&iter)) == 0) { start = wsp_text_header_iter_get_value(&iter); start_len = strlen(start) + 1; len += 1 + start_len; } } if (len == 1) return encode_short(fb, header, &ct); ptr = fb_request(fb, 1); if (ptr == NULL) return FALSE; *ptr = header | 0x80; /* Encode content type value length */ if (fb_put_value_length(fb, len) == FALSE) return FALSE; /* Encode content type including parameters */ ptr = fb_request(fb, ct_len); if (ptr == NULL) return FALSE; if (ct_len == 1) *ptr = ct | 0x80; else strcpy(ptr, ct_str); if (type_len > 0) { ptr = fb_request_field(fb, WSP_PARAMETER_TYPE_CONTENT_TYPE, type_len); if (ptr == NULL) return FALSE; strcpy(ptr, type); } if (start_len > 0) { ptr = fb_request_field(fb, WSP_PARAMETER_TYPE_START_DEFUNCT, start_len); if (ptr == NULL) return FALSE; strcpy(ptr, start); } return TRUE; } static header_encoder encoder_for_type(enum mms_header header) { switch (header) { case MMS_HEADER_BCC: return NULL; case MMS_HEADER_CC: return NULL; case MMS_HEADER_CONTENT_LOCATION: return NULL; case MMS_HEADER_CONTENT_TYPE: return encode_content_type; case MMS_HEADER_DATE: return NULL; case MMS_HEADER_DELIVERY_REPORT: return encode_short; case MMS_HEADER_DELIVERY_TIME: return NULL; case MMS_HEADER_EXPIRY: return NULL; case MMS_HEADER_FROM: return encode_from; case MMS_HEADER_MESSAGE_CLASS: return NULL; case MMS_HEADER_MESSAGE_ID: return NULL; case MMS_HEADER_MESSAGE_TYPE: return encode_short; case MMS_HEADER_MMS_VERSION: return encode_short; case MMS_HEADER_MESSAGE_SIZE: return NULL; case MMS_HEADER_PRIORITY: return NULL; case MMS_HEADER_READ_REPLY: return NULL; case MMS_HEADER_REPORT_ALLOWED: return NULL; case MMS_HEADER_RESPONSE_STATUS: return NULL; case MMS_HEADER_RESPONSE_TEXT: return NULL; case MMS_HEADER_SENDER_VISIBILITY: return NULL; case MMS_HEADER_STATUS: return encode_short; case MMS_HEADER_SUBJECT: return NULL; case MMS_HEADER_TO: return encode_text_array_element; case MMS_HEADER_TRANSACTION_ID: return encode_text; case MMS_HEADER_INVALID: case __MMS_HEADER_MAX: return NULL; } return NULL; } static gboolean mms_encode_send_req_part_header(struct mms_attachment *part, struct file_buffer *fb) { char *ptr; unsigned int len; unsigned int ct; unsigned int ct_len; unsigned int cs_len; const char *ct_str; const char *uninitialized_var(cs_str); unsigned int ctp_len; unsigned int cid_len; unsigned char ctp_val[MAX_ENC_VALUE_BYTES]; unsigned char cs_val[MAX_ENC_VALUE_BYTES]; unsigned int cs; struct wsp_text_header_iter iter; /* * Compute Headers length: content-type [+ params] [+ content-id] * ex. : "Content-Type:text/plain; charset=us-ascii" */ if (wsp_text_header_iter_init(&iter, part->content_type) == FALSE) return FALSE; if (g_ascii_strcasecmp("Content-Type", wsp_text_header_iter_get_key(&iter)) != 0) return FALSE; ct_str = wsp_text_header_iter_get_value(&iter); if (wsp_get_well_known_content_type(ct_str, &ct) == TRUE) ct_len = 1; else ct_len = strlen(ct_str) + 1; len = ct_len; cs_len = 0; while (wsp_text_header_iter_param_next(&iter) == TRUE) { const char *key = wsp_text_header_iter_get_key(&iter); if (g_ascii_strcasecmp("charset", key) == 0) { cs_str = wsp_text_header_iter_get_value(&iter); if (cs_str == NULL) return FALSE; len += 1; if (wsp_get_well_known_charset(cs_str, &cs) == FALSE) return FALSE; if (wsp_encode_integer(cs, cs_val, MAX_ENC_VALUE_BYTES, &cs_len) == FALSE) return FALSE; len += cs_len; } } if (wsp_encode_value_length(len, ctp_val, MAX_ENC_VALUE_BYTES, &ctp_len) == FALSE) return FALSE; len += ctp_len; /* Compute content-id header length : token + (Quoted String) */ if (part->content_id != NULL) { cid_len = 1 + strlen(part->content_id) + 3 + 1; len += cid_len; } else cid_len = 0; /* Encode total headers length */ if (fb_put_uintvar(fb, len) == FALSE) return FALSE; /* Encode data length */ if (fb_put_uintvar(fb, part->length) == FALSE) return FALSE; /* Encode content-type */ ptr = fb_request(fb, ctp_len); if (ptr == NULL) return FALSE; memcpy(ptr, &ctp_val, ctp_len); ptr = fb_request(fb, ct_len); if (ptr == NULL) return FALSE; if (ct_len == 1) ptr[0] = ct | 0x80; else strcpy(ptr, ct_str); /* Encode "charset" param */ if (cs_len > 0) { ptr = fb_request_field(fb, WSP_PARAMETER_TYPE_CHARSET, cs_len); if (ptr == NULL) return FALSE; memcpy(ptr, &cs_val, cs_len); } /* Encode content-id */ if (part->content_id != NULL) { if (encode_quoted_string(fb, MMS_PART_HEADER_CONTENT_ID, &part->content_id) == FALSE) return FALSE; } return TRUE; } static gboolean mms_encode_send_req_part(struct mms_attachment *part, struct file_buffer *fb) { if (mms_encode_send_req_part_header(part, fb) == FALSE) return FALSE; part->offset = fb_get_file_size(fb); return fb_copy(fb, part->data, part->length); } static gboolean mms_encode_headers(struct file_buffer *fb, enum mms_header orig_header, ...) { va_list args; void *data; enum mms_header header; header_encoder encoder; va_start(args, orig_header); header = orig_header; while (header != MMS_HEADER_INVALID) { data = va_arg(args, void *); encoder = encoder_for_type(header); if (encoder == NULL) return FALSE; if (data && encoder(fb, header, data) == FALSE) return FALSE; header = va_arg(args, enum mms_header); } va_end(args); return TRUE; } static gboolean mms_encode_notify_resp_ind(struct mms_message *msg, struct file_buffer *fb) { if (mms_encode_headers(fb, MMS_HEADER_MESSAGE_TYPE, &msg->type, MMS_HEADER_TRANSACTION_ID, &msg->transaction_id, MMS_HEADER_MMS_VERSION, &msg->version, MMS_HEADER_STATUS, &msg->nri.notify_status, MMS_HEADER_INVALID) == FALSE) return FALSE; return fb_flush(fb); } static gboolean mms_encode_send_req(struct mms_message *msg, struct file_buffer *fb) { const char *empty_from = ""; GSList *item; enum mms_message_value_bool dr; if (msg->sr.dr == TRUE) dr = MMS_MESSAGE_VALUE_BOOL_YES; else dr = MMS_MESSAGE_VALUE_BOOL_NO; if (mms_encode_headers(fb, MMS_HEADER_MESSAGE_TYPE, &msg->type, MMS_HEADER_TRANSACTION_ID, &msg->transaction_id, MMS_HEADER_MMS_VERSION, &msg->version, MMS_HEADER_FROM, &empty_from, MMS_HEADER_TO, &msg->sr.to, MMS_HEADER_DELIVERY_REPORT, &dr, MMS_HEADER_CONTENT_TYPE, &msg->sr.content_type, MMS_HEADER_INVALID) == FALSE) return FALSE; if (msg->attachments == NULL) goto done; if (fb_put_uintvar(fb, g_slist_length(msg->attachments)) == FALSE) return FALSE; for (item = msg->attachments; item != NULL; item = g_slist_next(item)) { if (mms_encode_send_req_part(item->data, fb) == FALSE) return FALSE; } done: return fb_flush(fb); } gboolean mms_message_encode(struct mms_message *msg, int fd) { struct file_buffer fb; fb_init(&fb, fd); switch (msg->type) { case MMS_MESSAGE_TYPE_SEND_REQ: return mms_encode_send_req(msg, &fb); case MMS_MESSAGE_TYPE_SEND_CONF: case MMS_MESSAGE_TYPE_NOTIFICATION_IND: return FALSE; case MMS_MESSAGE_TYPE_NOTIFYRESP_IND: return mms_encode_notify_resp_ind(msg, &fb); case MMS_MESSAGE_TYPE_RETRIEVE_CONF: case MMS_MESSAGE_TYPE_ACKNOWLEDGE_IND: case MMS_MESSAGE_TYPE_DELIVERY_IND: return FALSE; } return FALSE; } const char *mms_message_status_get_string(enum mms_message_status status) { switch (status) { case MMS_MESSAGE_STATUS_DOWNLOADED: return "downloaded"; case MMS_MESSAGE_STATUS_RECEIVED: return "received"; case MMS_MESSAGE_STATUS_READ: return "read"; case MMS_MESSAGE_STATUS_SENT: return "sent"; case MMS_MESSAGE_STATUS_DRAFT: return "draft"; } return NULL; } mmsd-0.1/src/mmsutil.h000066400000000000000000000105151216024552400147470ustar00rootroot00000000000000/* * * Multimedia Messaging Service * * Copyright (C) 2010-2011 Intel Corporation. All rights reserved. * * 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 * */ enum mms_message_type { MMS_MESSAGE_TYPE_SEND_REQ = 128, MMS_MESSAGE_TYPE_SEND_CONF = 129, MMS_MESSAGE_TYPE_NOTIFICATION_IND = 130, MMS_MESSAGE_TYPE_NOTIFYRESP_IND = 131, MMS_MESSAGE_TYPE_RETRIEVE_CONF = 132, MMS_MESSAGE_TYPE_ACKNOWLEDGE_IND = 133, MMS_MESSAGE_TYPE_DELIVERY_IND = 134, }; enum mms_message_status { MMS_MESSAGE_STATUS_DOWNLOADED, MMS_MESSAGE_STATUS_RECEIVED, MMS_MESSAGE_STATUS_READ, MMS_MESSAGE_STATUS_SENT, MMS_MESSAGE_STATUS_DRAFT, }; enum mms_message_rsp_status { MMS_MESSAGE_RSP_STATUS_OK = 128, MMS_MESSAGE_RSP_STATUS_ERR_UNSUPPORTED_MESSAGE = 136, MMS_MESSAGE_RSP_STATUS_ERR_TRANS_FAILURE = 192, MMS_MESSAGE_RSP_STATUS_ERR_TRANS_NETWORK_PROBLEM = 195, MMS_MESSAGE_RSP_STATUS_ERR_PERM_FAILURE = 224, MMS_MESSAGE_RSP_STATUS_ERR_PERM_SERVICE_DENIED = 225, MMS_MESSAGE_RSP_STATUS_ERR_PERM_MESSAGE_FORMAT_CORRUPT = 226, MMS_MESSAGE_RSP_STATUS_ERR_PERM_SENDING_ADDRESS_UNRESOLVED = 227, MMS_MESSAGE_RSP_STATUS_ERR_PERM_CONTENT_NOT_ACCEPTED = 229, MMS_MESSAGE_RSP_STATUS_ERR_PERM_LACK_OF_PREPAID = 235, }; enum mms_message_notify_status { MMS_MESSAGE_NOTIFY_STATUS_RETRIEVED = 129, MMS_MESSAGE_NOTIFY_STATUS_REJECTED = 130, MMS_MESSAGE_NOTIFY_STATUS_DEFERRED = 131, MMS_MESSAGE_NOTIFY_STATUS_UNRECOGNISED = 132, }; enum mms_message_delivery_status { MMS_MESSAGE_DELIVERY_STATUS_EXPIRED = 128, MMS_MESSAGE_DELIVERY_STATUS_RETRIEVED = 129, MMS_MESSAGE_DELIVERY_STATUS_REJECTED = 130, MMS_MESSAGE_DELIVERY_STATUS_DEFERRED = 131, MMS_MESSAGE_DELIVERY_STATUS_UNRECOGNISED = 132, MMS_MESSAGE_DELIVERY_STATUS_INDETERMINATE = 133, MMS_MESSAGE_DELIVERY_STATUS_FORWARDED = 134, MMS_MESSAGE_DELIVERY_STATUS_UNREACHABLE = 135, }; enum mms_message_sender_visibility { MMS_MESSAGE_SENDER_VISIBILITY_HIDE = 128, MMS_MESSAGE_SENDER_VISIBILITY_SHOW = 129, }; enum mms_message_value_bool { MMS_MESSAGE_VALUE_BOOL_YES = 128, MMS_MESSAGE_VALUE_BOOL_NO = 129, }; enum mms_message_version { MMS_MESSAGE_VERSION_1_0 = 0x90, MMS_MESSAGE_VERSION_1_1 = 0x91, MMS_MESSAGE_VERSION_1_2 = 0x92, MMS_MESSAGE_VERSION_1_3 = 0x93, }; struct mms_notification_ind { char *from; char *subject; char *cls; unsigned int size; time_t expiry; char *location; }; struct mms_retrieve_conf { enum mms_message_status status; char *from; char *to; char *subject; char *cls; char *priority; char *msgid; time_t date; }; struct mms_send_req { enum mms_message_status status; char *to; time_t date; char *content_type; gboolean dr; }; struct mms_send_conf { enum mms_message_rsp_status rsp_status; char *msgid; }; struct mms_notification_resp_ind { enum mms_message_notify_status notify_status; }; struct mms_delivery_ind { enum mms_message_delivery_status dr_status; char *msgid; char *to; time_t date; }; struct mms_attachment { unsigned char *data; size_t offset; size_t length; char *content_type; char *content_id; }; struct mms_message { enum mms_message_type type; char *uuid; char *path; char *transaction_id; unsigned char version; GSList *attachments; union { struct mms_notification_ind ni; struct mms_retrieve_conf rc; struct mms_send_req sr; struct mms_send_conf sc; struct mms_notification_resp_ind nri; struct mms_delivery_ind di; }; }; char *mms_content_type_get_param_value(const char *content_type, const char *param_name); gboolean mms_message_decode(const unsigned char *pdu, unsigned int len, struct mms_message *out); gboolean mms_message_encode(struct mms_message *msg, int fd); void mms_message_free(struct mms_message *msg); const char *mms_message_status_get_string(enum mms_message_status status); mmsd-0.1/src/plugin.c000066400000000000000000000054421216024552400145510ustar00rootroot00000000000000/* * * Multimedia Messaging Service * * Copyright (C) 2010-2011 Intel Corporation. All rights reserved. * * 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 "mms.h" static GSList *plugins = NULL; struct mms_plugin { void *handle; struct mms_plugin_desc *desc; }; static gboolean add_plugin(void *handle, struct mms_plugin_desc *desc) { struct mms_plugin *plugin; if (desc->init == NULL) return FALSE; plugin = g_try_new0(struct mms_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 __mms_plugin_init(void) { GDir *dir; const char *file; unsigned int i; if (strlen(PLUGINDIR) == 0) return -EINVAL; DBG(""); for (i = 0; __mms_builtin[i]; i++) add_plugin(NULL, __mms_builtin[i]); dir = g_dir_open(PLUGINDIR, 0, NULL); if (dir == NULL) return -EIO; while ((file = g_dir_read_name(dir)) != NULL) { struct mms_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) { mms_error("Can't load plugin %s: %s", filename, dlerror()); g_free(filename); continue; } g_free(filename); desc = dlsym(handle, "mms_plugin_desc"); if (desc == NULL) { mms_error("Can't load plugin description: %s", dlerror()); dlclose(handle); continue; } if (add_plugin(handle, desc) == FALSE) dlclose(handle); } g_dir_close(dir); return 0; } void __mms_plugin_cleanup(void) { GSList *list; DBG(""); for (list = plugins; list; list = list->next) { struct mms_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); } mmsd-0.1/src/plugin.h000066400000000000000000000023511216024552400145520ustar00rootroot00000000000000/* * * Multimedia Messaging Service * * Copyright (C) 2010-2011 Intel Corporation. All rights reserved. * * 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 mms_plugin_desc { const char *name; int (*init) (void); void (*exit) (void); }; #ifdef MMS_PLUGIN_BUILTIN #define MMS_PLUGIN_DEFINE(name, init, exit) \ struct mms_plugin_desc __mms_builtin_ ## name = { \ #name, init, exit \ }; #else #define MMS_PLUGIN_DEFINE(name, init, exit) \ extern struct mms_plugin_desc mms_plugin_desc \ __attribute__ ((visibility("default"))); \ struct mms_plugin_desc mms_plugin_desc = { \ #name, init, exit \ }; #endif mmsd-0.1/src/push.c000066400000000000000000000227061216024552400142340ustar00rootroot00000000000000/* * * Multimedia Messaging Service * * Copyright (C) 2010-2011 Intel Corporation. All rights reserved. * * 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 "wsputil.h" #include "mms.h" #define MMS_CONTENT_TYPE "application/vnd.wap.mms-message" #define MMS_CONSUMER_INTERFACE "org.ofono.mms.PushConsumer" #define MMS_CONSUMER_METHOD "Notify" #define MMS_CONSUMER_KEY_MATCH_CONTENT_TYPE "MatchContentType" #define MMS_CONSUMER_KEY_MATCH_APPLICATION_ID "MatchApplicationId" #define MMS_CONSUMER_KEY_TARGET_BUS "TargetBus" #define MMS_CONSUMER_KEY_TARGET_SERVICE "TargetService" #define MMS_CONSUMER_KEY_TARGET_PATH "TargetPath" static const char *mms_consumer_possible_keys[] = { MMS_CONSUMER_KEY_MATCH_CONTENT_TYPE, MMS_CONSUMER_KEY_MATCH_APPLICATION_ID, MMS_CONSUMER_KEY_TARGET_BUS, MMS_CONSUMER_KEY_TARGET_SERVICE, MMS_CONSUMER_KEY_TARGET_PATH, NULL, }; struct push_consumer { char *group; char *type; char *app_id; char *bus; char *service; char *path; }; static GSList *pc_list; static void dump_push_consumer(struct push_consumer *pc) { mms_debug("consumer group: [%s] <%p>", pc->group, pc); mms_debug("type: %s", pc->type); mms_debug("app_id: %s", pc->app_id); mms_debug("targetbus: %s\n", pc->bus); mms_debug("service: %s\n", pc->service); mms_debug("path: %s\n", pc->path); } static void push_consumer_free(gpointer data, gpointer user_data) { struct push_consumer *pc = data; g_free(pc->group); g_free(pc->type); g_free(pc->app_id); g_free(pc->bus); g_free(pc->service); g_free(pc->path); g_free(pc); } static void check_keys(GKeyFile *keyfile, const char *group, const char **possible_keys) { char **avail_keys; gsize nb_avail_keys, i, j; avail_keys = g_key_file_get_keys(keyfile, group, &nb_avail_keys, NULL); if (avail_keys == NULL) return; /* * For each key in the configuration file, * verify it is understood by mmsd */ for (i = 0 ; i < nb_avail_keys; i++) { for (j = 0; possible_keys[j] ; j++) if (g_strcmp0(avail_keys[i], possible_keys[j]) == 0) break; if (possible_keys[j] == NULL) mms_warn("Unknown configuration key %s in [%s]", avail_keys[i], group); } g_strfreev(avail_keys); } static struct push_consumer *create_consumer(GKeyFile *keyfile, const char *group) { struct push_consumer *pc; pc = g_try_new0(struct push_consumer, 1); if (pc == NULL) return NULL; pc->group = g_strdup(group); pc->type = g_key_file_get_string(keyfile, group, MMS_CONSUMER_KEY_MATCH_CONTENT_TYPE, NULL); if (pc->type == NULL) goto out; pc->app_id = g_key_file_get_string(keyfile, group, MMS_CONSUMER_KEY_MATCH_APPLICATION_ID, NULL); pc->bus = g_key_file_get_string(keyfile, group, MMS_CONSUMER_KEY_TARGET_BUS, NULL); if (pc->bus == NULL) pc->bus = g_strdup("session"); else if (g_str_equal(pc->bus, "session") == FALSE) goto out; pc->service = g_key_file_get_string(keyfile, group, MMS_CONSUMER_KEY_TARGET_SERVICE, NULL); if (pc->service == NULL) goto out; pc->path = g_key_file_get_string(keyfile, group, MMS_CONSUMER_KEY_TARGET_PATH, NULL); if (pc->path == NULL) goto out; dump_push_consumer(pc); return pc; out: mms_warn("Invalid or missing mandatory information for %s", group); push_consumer_free(pc, NULL); return NULL; } static void parse_config_file(const char *filename) { GKeyFile *keyfile; GError *err = NULL; char **consumers; int i; DBG("filename %s", filename); keyfile = g_key_file_new(); g_key_file_set_list_separator(keyfile, ','); if (!g_key_file_load_from_file(keyfile, filename, 0, &err)) { mms_warn("Reading of %s failed: %s", filename, err->message); g_error_free(err); goto done; } consumers = g_key_file_get_groups(keyfile, NULL); for (i = 0; consumers[i]; i++) { struct push_consumer *pc; /* Verify that provided keys are good */ check_keys(keyfile, consumers[i], mms_consumer_possible_keys); pc = create_consumer(keyfile, consumers[i]); if (pc == NULL) continue; pc_list = g_slist_prepend(pc_list, pc); } g_strfreev(consumers); done: g_key_file_free(keyfile); } int __mms_push_config_files_init(void) { GDir *dir; const char *file; char *filename; dir = g_dir_open(PUSHCONFDIR, 0, NULL); if (dir == NULL) return -EIO; while ((file = g_dir_read_name(dir)) != NULL) { if (g_str_has_suffix(file, ".conf") == FALSE) continue; filename = g_build_filename(PUSHCONFDIR, file, NULL); if (filename == NULL) continue; parse_config_file(filename); g_free(filename); } g_dir_close(dir); return 0; } void __mms_push_config_files_cleanup(void) { g_slist_foreach(pc_list, push_consumer_free, NULL); g_slist_free(pc_list); pc_list = NULL; } static void mms_push_send_msg_reply(DBusPendingCall *call, void *user_data) { struct push_consumer *pc = user_data; DBusMessage *reply = dbus_pending_call_steal_reply(call); DBusError err; dbus_error_init(&err); if (dbus_set_error_from_message(&err, reply) == FALSE) goto done; mms_error("Consumer [%s] replied with error %s, %s", pc->group, err.name, err.message); dbus_error_free(&err); pc_list = g_slist_remove(pc_list, pc); push_consumer_free(pc, NULL); done: dbus_message_unref(reply); } static gboolean mms_push_send_msg(const unsigned char *pdu, unsigned int msglen, unsigned int hdrlen, struct push_consumer *hdlr) { DBusConnection *conn = mms_dbus_get_connection(); DBusMessage *msg; DBusPendingCall *call; DBusMessageIter iter; DBusMessageIter hdr_array; DBusMessageIter body_array; msg = dbus_message_new_method_call(hdlr->service, hdlr->path, MMS_CONSUMER_INTERFACE, MMS_CONSUMER_METHOD); if (msg == NULL) { mms_error("Can't allocate new message"); return FALSE; } dbus_message_iter_init_append(msg, &iter); dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE_AS_STRING, &hdr_array); dbus_message_iter_append_fixed_array(&hdr_array, DBUS_TYPE_BYTE, &pdu, hdrlen); dbus_message_iter_close_container(&iter, &hdr_array); dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE_AS_STRING, &body_array); pdu += hdrlen; dbus_message_iter_append_fixed_array(&body_array, DBUS_TYPE_BYTE, &pdu, msglen - hdrlen); dbus_message_iter_close_container(&iter, &body_array); if (dbus_connection_send_with_reply(conn, msg, &call, -1) == FALSE) { mms_error("Failed to execute method call"); dbus_message_unref(msg); return FALSE; } dbus_message_unref(msg); if (call == NULL) { mms_error("D-Bus connection not available"); return FALSE; } dbus_pending_call_set_notify(call, mms_push_send_msg_reply, hdlr, NULL); dbus_pending_call_unref(call); return TRUE; } gboolean mms_push_notify(unsigned char *pdu, unsigned int len, unsigned int *offset) { unsigned int headerslen; unsigned int param_len; const void *ct; const void *aid; struct wsp_header_iter iter; unsigned int nread; unsigned int consumed; struct push_consumer *hdlr; unsigned int i; GSList *elt; GString *hex; DBG("pdu %p len %d", pdu, len); hex = g_string_sized_new(len * 2); for (i = 0; i < len; i++) g_string_append_printf(hex, "%02X", pdu[i]); DBG("%s", hex->str); g_string_free(hex, TRUE); /* PUSH pdu ? */ if (pdu[1] != 0x06) return FALSE; /* Consume TID and Type */ nread = 2; if (wsp_decode_uintvar(pdu + nread, len, &headerslen, &consumed) == FALSE) return FALSE; /* Consume uintvar bytes */ nread += consumed; /* Try to decode content-type */ if (wsp_decode_content_type(pdu + nread, headerslen, &ct, &consumed, ¶m_len) == FALSE) return FALSE; if (ct == NULL) return FALSE; /* Consume Content Type bytes, including parameters */ consumed += param_len; nread += consumed; /* Parse header to decode application_id */ wsp_header_iter_init(&iter, pdu + nread, headerslen - consumed, 0); aid = NULL; while (wsp_header_iter_next(&iter)) { const unsigned char *wk; /* Skip application headers */ if (wsp_header_iter_get_hdr_type(&iter) != WSP_HEADER_TYPE_WELL_KNOWN) continue; wk = wsp_header_iter_get_hdr(&iter); if ((wk[0] & 0x7f) != WSP_HEADER_TOKEN_APP_ID) continue; if (wsp_decode_application_id(&iter, &aid) == FALSE) return FALSE; } if (wsp_header_iter_at_end(&iter) == FALSE) return FALSE; nread += headerslen - consumed; mms_info("Body Length: %d\n", len - nread); if (g_str_equal(ct, MMS_CONTENT_TYPE) == TRUE) { if (offset != NULL) *offset = nread; return TRUE; } /* Handle other consumers */ for (elt = pc_list; elt != NULL; elt = g_slist_next(elt)) { hdlr = elt->data; if (aid != NULL && hdlr->app_id != NULL) { if (g_str_equal(hdlr->app_id, aid) == FALSE) continue; } if (g_str_equal(hdlr->type, ct) == FALSE) continue; if (mms_push_send_msg(pdu, len, nread, hdlr) == FALSE) { mms_error("Failed to call consumer: [%s]\n", hdlr->group); } } return FALSE; } mmsd-0.1/src/push.h000066400000000000000000000015511216024552400142340ustar00rootroot00000000000000/* * * Multimedia Messaging Service * * Copyright (C) 2010-2011 Intel Corporation. All rights reserved. * * 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 * */ gboolean mms_push_notify(unsigned char *pdu, unsigned int len, unsigned int *offset); mmsd-0.1/src/service.c000066400000000000000000001633031216024552400147140ustar00rootroot00000000000000/* * * Multimedia Messaging Service * * Copyright (C) 2010-2011 Intel Corporation. All rights reserved. * * 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 #include #include "mmsutil.h" #include "mms.h" #define BEARER_SETUP_TIMEOUT 20 /* 20 seconds */ #define BEARER_IDLE_TIMEOUT 10 /* 10 seconds */ #define CHUNK_SIZE 2048 /* 2 Kib */ #define DEFAULT_CONTENT_TYPE "application/vnd.wap.mms-message" #define CT_MUTLIPART "Content-Type: \"application/vnd.wap.multipart." #define CT_TYPE ";type=\"application/smil\"" #define CT_START ";start=\"\"" #define CT_MULTIPART_RELATED CT_MUTLIPART "related\"" CT_TYPE CT_START #define CT_MULTIPART_MIXED CT_MUTLIPART "mixed\"" #define CONTENT_ID_SMIL "SMIL" #define CONTENT_TYPE_APP_SMIL "Content-Type: \"application/smil\";charset=utf-8" #define MAX_ATTACHMENTS_NUMBER 25 #define MAX_ATTEMPTS 3 #define SETTINGS_STORE "mms" #define SETTINGS_GROUP "Settings" #define uninitialized_var(x) x = x static const char *ctl_chars = "\x01\x02\x03\x04\x05\x06\x07\x08\x0A" "\x0B\x0C\x0D\x0E\x0F\x10\x11\x12\x13\x14" "\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E" "\x1F\x7F"; static const char *sep_chars = "()<>@,;:\\\"/[]?={} \t"; static const char *delivery_status[] = { "none", "expired", "retrieved", "rejected", "deferred", "indeterminate", "forwarded", "unreachable" }; struct mms_request; typedef gboolean (*mms_request_result_cb_t) (struct mms_request *request); struct mms_service { gint refcount; char *identity; char *path; char *mmsc; mms_service_bearer_handler_func_t bearer_handler; void *bearer_data; guint bearer_timeout; gboolean bearer_setup; gboolean bearer_active; GQueue *request_queue; guint current_request_id; GWeb *web; GHashTable *messages; GKeyFile *settings; gboolean use_delivery_reports; }; enum mms_request_type { MMS_REQUEST_TYPE_GET, MMS_REQUEST_TYPE_POST, MMS_REQUEST_TYPE_POST_TMP }; struct mms_request { enum mms_request_type type; char *data_path; char *location; gsize data_size; int fd; guint16 status; guint16 attempt; struct mms_service *service; mms_request_result_cb_t result_cb; struct mms_message *msg; }; static GList *service_list; static DBusConnection *connection; static guint32 transaction_id_start = 0; static void mms_load_settings(struct mms_service *service) { GError *error; service->settings = mms_settings_open(service->identity, SETTINGS_STORE); if (service->settings == NULL) return; error = NULL; service->use_delivery_reports = g_key_file_get_boolean(service->settings, SETTINGS_GROUP, "UseDeliveryReports", &error); if (error) { g_error_free(error); g_key_file_set_boolean(service->settings, SETTINGS_GROUP, "UseDeliveryReports", service->use_delivery_reports); } } static void mms_request_destroy(struct mms_request *request) { g_free(request->data_path); g_free(request->location); g_free(request); } static struct mms_message *mms_request_steal_message(struct mms_request *request) { struct mms_message *msg = request->msg; request->msg = NULL; return msg; } static DBusMessage *msg_delete(DBusConnection *conn, DBusMessage *msg, void *user_data) { struct mms_service *service = user_data; struct mms_message *mms; const char *path; char *uuid; path = dbus_message_get_path(msg); DBG("message path %s", path); mms = g_hash_table_lookup(service->messages, path); if (mms == NULL) return __mms_error_invalid_args(msg); uuid = g_strdup(mms->uuid); if (mms_message_unregister(service, path) < 0) return __mms_error_invalid_args(msg); mms_store_remove(service->identity, uuid); g_free(uuid); return g_dbus_create_reply(msg, DBUS_TYPE_INVALID); } static void emit_msg_status_changed(const char *path, const char *new_status) { DBusMessage *signal; DBusMessageIter iter; DBusMessageIter variant; const char *property = "status"; signal = dbus_message_new_signal(path, MMS_MESSAGE_INTERFACE, "PropertyChanged"); if (signal == NULL) return; dbus_message_iter_init_append(signal, &iter); dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &property); dbus_message_iter_open_container(&iter, DBUS_TYPE_VARIANT, DBUS_TYPE_STRING_AS_STRING, &variant); dbus_message_iter_append_basic(&variant, DBUS_TYPE_STRING, &new_status); dbus_message_iter_close_container(&iter, &variant); g_dbus_send_message(connection, signal); } static DBusMessage *msg_mark_read(DBusConnection *conn, DBusMessage *msg, void *user_data) { struct mms_service *service = user_data; struct mms_message *mms; const char *path; char *state; GKeyFile *meta; path = dbus_message_get_path(msg); DBG("message path %s", path); mms = g_hash_table_lookup(service->messages, path); if (mms == NULL) return __mms_error_invalid_args(msg); meta = mms_store_meta_open(service->identity, mms->uuid); state = g_key_file_get_string(meta, "info", "state", NULL); if (state == NULL) { mms_store_meta_close(service->identity, mms->uuid, meta, FALSE); return __mms_error_invalid_args(msg); } if (strcmp(state, "received") != 0 && strcmp(state, "sent") != 0) { mms_store_meta_close(service->identity, mms->uuid, meta, FALSE); g_free(state); return __mms_error_invalid_args(msg); } g_free(state); g_key_file_set_boolean(meta, "info", "read", TRUE); mms_store_meta_close(service->identity, mms->uuid, meta, TRUE); emit_msg_status_changed(path, "read"); return g_dbus_create_reply(msg, DBUS_TYPE_INVALID); } static const GDBusMethodTable message_methods[] = { { GDBUS_METHOD("MarkRead", NULL, NULL, msg_mark_read) }, { GDBUS_METHOD("Delete", NULL, NULL, msg_delete) }, { } }; static const GDBusSignalTable message_signals[] = { { GDBUS_SIGNAL("PropertyChanged", GDBUS_ARGS({ "name", "s" }, { "value", "v" })) }, { } }; static gboolean valid_number_format(const char *number) { int len = strlen(number); int begin = 0; unsigned int num_digits = 0; int i; if (len == 0) return FALSE; if (number[0] == '+') begin = 1; if (begin == len) return FALSE; for (i = begin; i < len; i++) { if (number[i] >= '0' && number[i] <= '9') { num_digits++; if (num_digits > 20) return FALSE; continue; } if (number[i] == '-' || number[i] == '.') continue; return FALSE; } return TRUE; } static gboolean valid_content_type(const char *ct) { if (strlen(ct) == 0) return FALSE; if (strpbrk(ct, ctl_chars) != NULL) return FALSE; if (isspace(*ct) == TRUE) return FALSE; ct = strpbrk(ct, sep_chars); if (ct == NULL) return FALSE; if (ct[0] != '/') return FALSE; ct += 1; ct = strpbrk(ct, sep_chars); if (ct == NULL) return TRUE; return FALSE; } static gboolean mmap_file(const char *path, void **out_pdu, size_t *out_len) { struct stat st; int fd; fd = open(path, O_RDONLY); if (fd < 0) { mms_error("Failed to open %s", path); return FALSE; } if (fstat(fd, &st) < 0) { mms_error("Failed to stat %s", path); close(fd); return FALSE; } *out_pdu = mmap(NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0); close(fd); if (*out_pdu == MAP_FAILED) { mms_error("Failed to mmap %s", path); return FALSE; } *out_len = st.st_size; return TRUE; } static const char *mms_address_to_string(char *mms_address) { unsigned int prefix_len; if (g_str_has_suffix(mms_address, "/TYPE=PLMN") == TRUE) { prefix_len = strlen(mms_address) - 10; mms_address[prefix_len] = '\0'; } return (const char *) mms_address; } static gboolean send_message_get_recipients(DBusMessageIter *top_iter, struct mms_message *msg) { DBusMessageIter recipients; dbus_message_iter_recurse(top_iter, &recipients); while (dbus_message_iter_get_arg_type(&recipients) == DBUS_TYPE_STRING) { const char *rec; char *tmp; dbus_message_iter_get_basic(&recipients, &rec); if (valid_number_format(rec) == FALSE) return FALSE; if (msg->sr.to != NULL) { tmp = g_strconcat(msg->sr.to, ",", rec, "/TYPE=PLMN", NULL); if (tmp == NULL) return FALSE; g_free(msg->sr.to); msg->sr.to = tmp; } else msg->sr.to = g_strdup_printf("%s/TYPE=PLMN", rec); dbus_message_iter_next(&recipients); } return TRUE; } static gboolean send_message_get_attachments(DBusMessageIter *top_iter, struct mms_message *msg) { DBusMessageIter attachments; unsigned int attach_num = 0; dbus_message_iter_recurse(top_iter, &attachments); while (dbus_message_iter_get_arg_type(&attachments) == DBUS_TYPE_STRUCT) { DBusMessageIter entry; const char *id; const char *ct; const char *filename; struct mms_attachment *attach; void *ptr; if (++attach_num > MAX_ATTACHMENTS_NUMBER) return FALSE; dbus_message_iter_recurse(&attachments, &entry); if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_STRING) return FALSE; dbus_message_iter_get_basic(&entry, &id); dbus_message_iter_next(&entry); if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_STRING) return FALSE; dbus_message_iter_get_basic(&entry, &ct); dbus_message_iter_next(&entry); if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_STRING) return FALSE; dbus_message_iter_get_basic(&entry, &filename); if (valid_content_type(ct) == FALSE) return FALSE; attach = g_try_new0(struct mms_attachment, 1); if (attach == NULL) return FALSE; if (mmap_file(filename, &ptr, &attach->length) == FALSE) { g_free(attach); return FALSE; } attach->data = ptr; attach->content_id = g_strdup(id); if (g_str_has_prefix(ct, "text/") == TRUE) attach->content_type = g_strconcat("Content-Type: \"", ct, "\";charset=utf-8", NULL); else attach->content_type = g_strconcat("Content-Type: \"", ct, "\"", NULL); msg->attachments = g_slist_append(msg->attachments, attach); dbus_message_iter_next(&attachments); } return TRUE; } static gboolean send_message_get_args(DBusMessage *dbus_msg, struct mms_message *msg) { DBusMessageIter top_iter; const char *smil; if (dbus_message_iter_init(dbus_msg, &top_iter) == FALSE) return FALSE; if (dbus_message_iter_get_arg_type(&top_iter) != DBUS_TYPE_ARRAY) return FALSE; if (send_message_get_recipients(&top_iter, msg) == FALSE) return FALSE; if (!dbus_message_iter_next(&top_iter)) return FALSE; if (dbus_message_iter_get_arg_type(&top_iter) != DBUS_TYPE_STRING) return FALSE; dbus_message_iter_get_basic(&top_iter, &smil); if (smil[0] != '\0') { struct mms_attachment *attach; attach = g_try_new0(struct mms_attachment, 1); if (attach == NULL) return FALSE; attach->content_id = g_strdup(CONTENT_ID_SMIL); attach->content_type = g_strdup(CONTENT_TYPE_APP_SMIL); attach->length = strlen(smil) + 1; attach->data = g_memdup(smil, attach->length); msg->attachments = g_slist_append(msg->attachments, attach); msg->sr.content_type = g_strdup(CT_MULTIPART_RELATED); } else msg->sr.content_type = g_strdup(CT_MULTIPART_MIXED); if (!dbus_message_iter_next(&top_iter)) return FALSE; if (dbus_message_iter_get_arg_type(&top_iter) != DBUS_TYPE_ARRAY) return FALSE; return send_message_get_attachments(&top_iter, msg); } static struct mms_request *create_request(enum mms_request_type type, mms_request_result_cb_t result_cb, char *location, struct mms_service *service, struct mms_message *msg) { struct mms_request *request; request = g_try_new0(struct mms_request, 1); if (request == NULL) return NULL; request->type = type; switch (request->type) { case MMS_REQUEST_TYPE_GET: request->data_path = g_strdup_printf("%s%s", g_get_home_dir(), "/.mms/receive.XXXXXX.mms"); break; case MMS_REQUEST_TYPE_POST: case MMS_REQUEST_TYPE_POST_TMP: request->data_path = g_strdup_printf("%s%s", g_get_home_dir(), "/.mms/send.XXXXXX.mms"); break; } request->fd = g_mkstemp_full(request->data_path, O_WRONLY | O_CREAT | O_TRUNC, S_IWUSR | S_IRUSR); if (request->fd < 0) { mms_request_destroy(request); return NULL; } request->result_cb = result_cb; request->location = g_strdup(location); request->service = service; request->msg = msg; request->status = 0; request->attempt = 0; return request; } static gboolean bearer_setup_timeout(gpointer user_data) { struct mms_service *service = user_data; DBG("service %p", service); service->bearer_timeout = 0; service->bearer_setup = FALSE; return FALSE; } static void process_request_queue(struct mms_service *service); static void emit_message_added(const struct mms_service *service, struct mms_message *msg); static void activate_bearer(struct mms_service *service) { DBG("service %p", service); if (service->bearer_setup == TRUE) return; if (service->bearer_active == TRUE) { process_request_queue(service); return; } if (service->bearer_handler == NULL) return; DBG("service %p", service); service->bearer_setup = TRUE; service->bearer_timeout = g_timeout_add_seconds(BEARER_SETUP_TIMEOUT, bearer_setup_timeout, service); service->bearer_handler(TRUE, service->bearer_data); } static inline char *create_transaction_id(void) { return g_strdup_printf("%08X%s", transaction_id_start++, "0123456789ABCDEF0123456789ABCDEF"); } static gboolean result_request_send_conf(struct mms_request *request) { struct mms_message *msg; struct mms_service *service = request->service; const char *uuid; GKeyFile *meta; void *pdu; size_t len; char *path; if (request->msg == NULL) return FALSE; uuid = request->msg->uuid; path = g_strdup_printf("%s/%s/%s", MMS_PATH, service->identity, uuid); if (request->status != 200) goto error; msg = g_try_new0(struct mms_message, 1); if (msg == NULL) goto error; if (mmap_file(request->data_path, &pdu, &len) == FALSE) { mms_message_free(msg); goto error; } if (mms_message_decode(pdu, len, msg) == FALSE) { mms_error("Failed to decode pdu %s", request->data_path); munmap(pdu, len); mms_message_free(msg); goto error; } mms_debug("response status : %d", msg->sc.rsp_status); munmap(pdu, len); unlink(request->data_path); meta = mms_store_meta_open(service->identity, uuid); if (meta == NULL) { mms_message_free(msg); goto error; } g_key_file_set_string(meta, "info", "state", "sent"); g_key_file_set_string(meta, "info", "id", msg->sc.msgid); mms_message_free(msg); mms_store_meta_close(service->identity, uuid, meta, TRUE); emit_msg_status_changed(path, "sent"); g_free(path); return TRUE; error: if (request->attempt == MAX_ATTEMPTS) emit_msg_status_changed(path, "draft"); g_free(path); return FALSE; } static void append_message(const char *path, const struct mms_service *service, struct mms_message *msg, DBusMessageIter *iter); static void append_message_entry(char *path, const struct mms_service *service, struct mms_message *msg, DBusMessageIter *iter) { DBusMessageIter entry; dbus_message_iter_open_container(iter, DBUS_TYPE_STRUCT, NULL, &entry); append_message(path, service, msg, &entry); dbus_message_iter_close_container(iter, &entry); } static DBusMessage *get_messages(DBusConnection *conn, DBusMessage *dbus_msg, void *data) { DBusMessage *reply; DBusMessageIter iter, array; const struct mms_service *service = data; GHashTableIter table_iter; gpointer key, value; reply = dbus_message_new_method_return(dbus_msg); if (reply == NULL) return NULL; dbus_message_iter_init_append(reply, &iter); dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "(oa{sv})", &array); g_hash_table_iter_init(&table_iter, service->messages); while (g_hash_table_iter_next(&table_iter, &key, &value)) append_message_entry(key, service, value, &array); dbus_message_iter_close_container(&iter, &array); return reply; } static gint fill_conversation_sort(gconstpointer a, gconstpointer b) { const struct mms_message *msg1 = a; const struct mms_message *msg2 = b; time_t date1, date2; if (msg1->type == MMS_MESSAGE_TYPE_SEND_REQ) date1 = msg1->sr.date; else date1 = msg1->rc.date; if (msg2->type == MMS_MESSAGE_TYPE_SEND_REQ) date2 = msg2->sr.date; else date2 = msg2->rc.date; if (date1 > date2) return 1; if (date1 < date2) return -1; return 0; } static gboolean is_recipient(const char *recipients, const char *number) { const char *subrecpts, *subnum; /* * Search for "number" substring in "recipients" string, * ignoring '-' and '.' which are valid chars in a phone number */ while (*recipients != '\0') { subrecpts = recipients; subnum = number; while (*subrecpts != '\0' && *subnum != '\0') { if (*subrecpts == *subnum) { subrecpts++; subnum++; } else { if (*subrecpts == '-' || *subrecpts == '.') { subrecpts++; continue; } if (*subnum == '-' || *subnum == '.') { subnum++; continue; } if (*subrecpts != *number) subrecpts++; break; } } /* * Phone numbers in recipients end with /TYPE=PLMN, so the * wanted number is found if the whole string number is found * and if the matched phone number ends with the wanted number * (i.e.: "/TYPE=PLMN" must follow). */ if (*subnum == '\0' && *subrecpts == '/') return TRUE; recipients = subrecpts; } return FALSE; } static GList *fill_conversation(const struct mms_service *service, GList *conversation, const char *number) { GHashTableIter table_iter; gpointer key, value; g_hash_table_iter_init(&table_iter, service->messages); while (g_hash_table_iter_next(&table_iter, &key, &value)) { struct mms_message *msg = value; char *recipients; if (msg->type == MMS_MESSAGE_TYPE_SEND_REQ) recipients = msg->sr.to; else if (msg->type == MMS_MESSAGE_TYPE_RETRIEVE_CONF) recipients = msg->rc.from; else continue; if (is_recipient(recipients, number) == TRUE) conversation = g_list_insert_sorted(conversation, value, fill_conversation_sort); } return conversation; } static DBusMessage *get_conversation(DBusConnection *conn, DBusMessage *dbus_msg, void *data) { DBusMessage *reply; DBusMessageIter iter, array; const struct mms_service *service = data; struct mms_message *msg; GList *msg_elt = NULL; GList *conversation = NULL; const char *number; unsigned int count, i; if (dbus_message_iter_init(dbus_msg, &iter) == FALSE) return __mms_error_invalid_args(dbus_msg); if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING) return __mms_error_invalid_args(dbus_msg); dbus_message_iter_get_basic(&iter, &number); if (number[0] == '\0') return __mms_error_invalid_args(dbus_msg); if (valid_number_format(number) == FALSE) return __mms_error_invalid_args(dbus_msg); if (!dbus_message_iter_next(&iter)) return __mms_error_invalid_args(dbus_msg); if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_UINT32) return __mms_error_invalid_args(dbus_msg); dbus_message_iter_get_basic(&iter, &count); reply = dbus_message_new_method_return(dbus_msg); if (reply == NULL) return __mms_error_trans_failure(dbus_msg); dbus_message_iter_init_append(reply, &iter); dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "(oa{sv})", &array); conversation = fill_conversation(service, conversation, number); if (conversation == NULL) goto out; i = 0; for (msg_elt = g_list_first(conversation); msg_elt != NULL; msg_elt = g_list_next(msg_elt), i++) { if (count != 0 && i == count) break; msg = msg_elt->data; append_message_entry(msg->path, service, msg, &array); } out: dbus_message_iter_close_container(&iter, &array); return reply; } static DBusMessage *get_properties(DBusConnection *conn, DBusMessage *msg, void *data) { struct mms_service *service = data; DBusMessage *reply; DBusMessageIter iter; DBusMessageIter dict; reply = dbus_message_new_method_return(msg); if (reply == NULL) return NULL; dbus_message_iter_init_append(reply, &iter); mms_dbus_dict_open(&iter, &dict); mms_dbus_dict_append_basic(&dict, "UseDeliveryReports", DBUS_TYPE_BOOLEAN, &service->use_delivery_reports); mms_dbus_dict_close(&iter, &dict); return reply; } static DBusMessage *set_property(DBusConnection *conn, DBusMessage *dbus_msg, void *data) { struct mms_service *service = data; DBusMessageIter iter; DBusMessageIter var; const char *property; if (!dbus_message_iter_init(dbus_msg, &iter)) return __mms_error_invalid_args(dbus_msg); if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING) return __mms_error_invalid_args(dbus_msg); dbus_message_iter_get_basic(&iter, &property); dbus_message_iter_next(&iter); if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT) return __mms_error_invalid_args(dbus_msg); dbus_message_iter_recurse(&iter, &var); if (!strcmp(property, "UseDeliveryReports")) { dbus_bool_t value; if (dbus_message_iter_get_arg_type(&var) != DBUS_TYPE_BOOLEAN) return __mms_error_invalid_args(dbus_msg); dbus_message_iter_get_basic(&var, &value); if (service->use_delivery_reports != (gboolean) value) { DBG("use_delivery_reports = %d", value); service->use_delivery_reports = value; } return g_dbus_create_reply(dbus_msg, DBUS_TYPE_INVALID); } return __mms_error_invalid_args(dbus_msg); } static gboolean mms_attachment_is_smil(const struct mms_attachment *part) { if (g_str_has_prefix(part->content_type, "application/smil")) return TRUE; return FALSE; } static void release_data(gpointer data, gpointer user_data) { struct mms_attachment *attach = data; if (mms_attachment_is_smil(attach)) g_free(attach->data); else munmap(attach->data, attach->length); } static void release_attachement_data(GSList *attach) { if (attach != NULL) g_slist_foreach(attach, release_data, NULL); } static DBusMessage *send_message(DBusConnection *conn, DBusMessage *dbus_msg, void *data) { DBusMessage *reply; DBusMessageIter iter; struct mms_message *msg; struct mms_service *service = data; struct mms_request *request; GKeyFile *meta; msg = g_new0(struct mms_message, 1); if (msg == NULL) return __mms_error_trans_failure(dbus_msg); msg->type = MMS_MESSAGE_TYPE_SEND_REQ; msg->version = MMS_MESSAGE_VERSION_1_0; msg->sr.status = MMS_MESSAGE_STATUS_DRAFT; msg->sr.dr = service->use_delivery_reports; if (send_message_get_args(dbus_msg, msg) == FALSE) { mms_debug("Invalid arguments"); release_attachement_data(msg->attachments); mms_message_free(msg); return __mms_error_invalid_args(dbus_msg); } msg->transaction_id = create_transaction_id(); if (msg->transaction_id == NULL) goto release_msg; request = create_request(MMS_REQUEST_TYPE_POST, result_request_send_conf, NULL, service, msg); if (request == NULL) goto release_msg; if (mms_message_encode(msg, request->fd) == FALSE) goto release_request; close(request->fd); request->fd = -1; msg->uuid = g_strdup(mms_store_file(service->identity, request->data_path)); if (msg->uuid == NULL) goto release_request; g_free(request->data_path); request->data_path = g_strdup_printf("%s/.mms/%s/%s", g_get_home_dir(), service->identity, msg->uuid); meta = mms_store_meta_open(service->identity, msg->uuid); if (meta == NULL) goto release_request; g_key_file_set_string(meta, "info", "state", "draft"); if (service->use_delivery_reports) { char **tos; int i; tos = g_strsplit(msg->sr.to, ",", 0); for (i = 0; tos[i] != NULL; i++) { char *to = g_strdup(tos[i]); mms_address_to_string(to); DBG("%s=%s", to, delivery_status[0]); g_key_file_set_string(meta, "delivery_status", to, delivery_status[0]); g_free(to); } g_strfreev(tos); } mms_store_meta_close(service->identity, msg->uuid, meta, TRUE); if (mms_message_register(service, msg) < 0) goto release_request; emit_message_added(service, msg); release_attachement_data(msg->attachments); g_queue_push_tail(service->request_queue, request); activate_bearer(service); reply = dbus_message_new_method_return(dbus_msg); if (reply == NULL) return NULL; dbus_message_iter_init_append(reply, &iter); dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH, &msg->path); return reply; release_request: unlink(request->data_path); mms_request_destroy(request); release_msg: release_attachement_data(msg->attachments); mms_message_free(msg); return __mms_error_trans_failure(dbus_msg); } static const GDBusMethodTable service_methods[] = { { GDBUS_METHOD("SendMessage", GDBUS_ARGS({ "recipients", "as" }, { "smil", "s" }, { "attachments", "a(sss)" }), GDBUS_ARGS({ "path", "o" }), send_message) }, { GDBUS_METHOD("GetMessages", NULL, GDBUS_ARGS({ "messages_with_properties", "a(oa{sv})" }), get_messages) }, { GDBUS_METHOD("GetConversation", GDBUS_ARGS({ "number", "s" }, { "count", "s" }), GDBUS_ARGS({ "messages_with_properties", "a(oa{sv}" }), get_conversation) }, { GDBUS_METHOD("SetProperty", GDBUS_ARGS({ "property", "s" }, { "value", "v" }), NULL, set_property) }, { GDBUS_METHOD("GetProperties", NULL, GDBUS_ARGS({ "properties", "a{sv}" }), get_properties) }, { } }; static const GDBusSignalTable service_signals[] = { { GDBUS_SIGNAL("MessageAdded", GDBUS_ARGS({ "path", "o" }, { "properties", "a{sv}" })) }, { GDBUS_SIGNAL("MessageRemoved", GDBUS_ARGS({ "path", "o" })) }, { } }; static void destroy_message(gpointer data) { struct mms_message *mms = data; mms_message_free(mms); } struct mms_service *mms_service_create(void) { struct mms_service *service; service = g_try_new0(struct mms_service, 1); if (service == NULL) return NULL; service->refcount = 1; service->request_queue = g_queue_new(); if (service->request_queue == NULL) { g_free(service); return NULL; } service->current_request_id = 0; service->messages = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, destroy_message); if (service->messages == NULL) { g_queue_free(service->request_queue); g_free(service); return NULL; } DBG("service %p", service); return service; } struct mms_service *mms_service_ref(struct mms_service *service) { if (service == NULL) return NULL; g_atomic_int_inc(&service->refcount); return service; } static void unregister_message(gpointer key, gpointer value, gpointer user_data) { struct mms_service *service = user_data; struct mms_message *msg = value; mms_message_unregister(service, msg->path); } static void destroy_message_table(struct mms_service *service) { if (service->messages == NULL) return; /* * Each message is first unregistered from dbus, then destroyed from * the hash table. * This step is required because we need access to mms_service when * unregistering the message object. */ g_hash_table_foreach(service->messages, unregister_message, service); g_hash_table_destroy(service->messages); service->messages = NULL; } void mms_service_unref(struct mms_service *service) { struct mms_request *request; if (service == NULL) return; if (g_atomic_int_dec_and_test(&service->refcount) == FALSE) return; DBG("service %p", service); while ((request = g_queue_pop_head(service->request_queue))) mms_request_destroy(request); g_queue_free(service->request_queue); destroy_message_table(service); if (service->web != NULL) g_web_unref(service->web); g_free(service->mmsc); g_free(service->identity); g_free(service->path); g_free(service); } static void append_properties(DBusMessageIter *dict, struct mms_service *service) { mms_dbus_dict_append_basic(dict, "Identity", DBUS_TYPE_STRING, &service->identity); } static void emit_service_added(struct mms_service *service) { DBusMessage *signal; DBusMessageIter iter, dict; DBG("service %p", service); signal = dbus_message_new_signal(MMS_PATH, MMS_MANAGER_INTERFACE, "ServiceAdded"); if (signal == NULL) return; dbus_message_iter_init_append(signal, &iter); dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH, &service->path); mms_dbus_dict_open(&iter, &dict); append_properties(&dict, service); mms_dbus_dict_close(&iter, &dict); g_dbus_send_message(connection, signal); } static void emit_service_removed(struct mms_service *service) { DBG("service %p", service); g_dbus_emit_signal(connection, MMS_PATH, MMS_MANAGER_INTERFACE, "ServiceRemoved", DBUS_TYPE_OBJECT_PATH, &service->path, DBUS_TYPE_INVALID); } static gboolean load_message_from_store(const char *service_id, const char *uuid, struct mms_message *msg) { GKeyFile *meta; char *state = NULL; gboolean read_status; char *data_path = NULL; gboolean success = FALSE; gboolean tainted = FALSE; void *pdu; size_t len; meta = mms_store_meta_open(service_id, uuid); if (meta == NULL) return FALSE; state = g_key_file_get_string(meta, "info", "state", NULL); if (state == NULL) goto out; read_status = g_key_file_get_boolean(meta, "info", "read", NULL); data_path = mms_store_get_path(service_id, uuid); if (data_path == NULL) goto out; if (mmap_file(data_path, &pdu, &len) == FALSE) goto out; if (mms_message_decode(pdu, len, msg) == FALSE) { mms_error("Failed to decode %s", data_path); munmap(pdu, len); tainted = TRUE; goto out; } munmap(pdu, len); msg->uuid = g_strdup(uuid); if (strcmp(state, "received") == 0 && msg->type == MMS_MESSAGE_TYPE_RETRIEVE_CONF) { if (read_status == TRUE) msg->rc.status = MMS_MESSAGE_STATUS_READ; else msg->rc.status = MMS_MESSAGE_STATUS_RECEIVED; } else if (strcmp(state, "downloaded") == 0 && msg->type == MMS_MESSAGE_TYPE_RETRIEVE_CONF) { msg->rc.status = MMS_MESSAGE_STATUS_DOWNLOADED; if (msg->transaction_id == NULL) msg->transaction_id = ""; } else if (strcmp(state, "sent") == 0 && msg->type == MMS_MESSAGE_TYPE_SEND_REQ) msg->sr.status = MMS_MESSAGE_STATUS_SENT; else if (strcmp(state, "draft") == 0 && msg->type == MMS_MESSAGE_TYPE_SEND_REQ) msg->sr.status = MMS_MESSAGE_STATUS_DRAFT; else if (msg->type != MMS_MESSAGE_TYPE_NOTIFICATION_IND && msg->type != MMS_MESSAGE_TYPE_DELIVERY_IND) goto out; success = TRUE; out: g_free(state); g_free(data_path); mms_store_meta_close(service_id, uuid, meta, FALSE); if (tainted == TRUE) mms_store_remove(service_id, uuid); return success; } static gboolean result_request_retrieve_conf(struct mms_request *request); static gboolean result_request_notify_resp(struct mms_request *request); static struct mms_request *build_notify_resp_ind(struct mms_service *service, enum mms_message_notify_status status, struct mms_message *rc_msg) { struct mms_message *ni_msg; struct mms_request *notify_request; gboolean result; ni_msg = g_try_new0(struct mms_message, 1); if (ni_msg == NULL) return NULL; ni_msg->type = MMS_MESSAGE_TYPE_NOTIFYRESP_IND; ni_msg->version = MMS_MESSAGE_VERSION_1_0; ni_msg->transaction_id = g_strdup(rc_msg->transaction_id); ni_msg->nri.notify_status = status; notify_request = create_request(MMS_REQUEST_TYPE_POST_TMP, result_request_notify_resp, NULL, service, rc_msg); if (notify_request == NULL) { mms_message_free(ni_msg); return NULL; } if (status == MMS_MESSAGE_NOTIFY_STATUS_UNRECOGNISED) notify_request->msg = NULL; result = mms_message_encode(ni_msg, notify_request->fd); close(notify_request->fd); notify_request->fd = -1; mms_message_free(ni_msg); if (result == FALSE) { unlink(notify_request->data_path); mms_request_destroy(notify_request); return NULL; } return notify_request; } static void process_message_on_start(struct mms_service *service, const char *uuid) { struct mms_message *msg; struct mms_request *request; const char *service_id = service->identity; msg = g_try_new0(struct mms_message, 1); if (msg == NULL) return; if (load_message_from_store(service_id, uuid, msg) == FALSE) { mms_message_free(msg); return; } if (msg->type == MMS_MESSAGE_TYPE_NOTIFICATION_IND) { char *location = g_strdup(msg->ni.location); request = create_request(MMS_REQUEST_TYPE_GET, result_request_retrieve_conf, location, service, msg); if (request == NULL) { mms_message_free(msg); g_free(location); return; } } else if (msg->type == MMS_MESSAGE_TYPE_SEND_REQ) { if (msg->sr.status == MMS_MESSAGE_STATUS_DRAFT) { request = create_request(MMS_REQUEST_TYPE_POST, result_request_send_conf, NULL, service, NULL); if (request == NULL) goto register_sr; close(request->fd); request->fd = -1; unlink(request->data_path); g_free(request->data_path); request->data_path = mms_store_get_path(service_id, uuid); request->msg = msg; } else request = NULL; register_sr: mms_message_register(service, msg); } else if (msg->type == MMS_MESSAGE_TYPE_RETRIEVE_CONF) { if (msg->rc.status == MMS_MESSAGE_STATUS_DOWNLOADED) { request = build_notify_resp_ind(service, MMS_MESSAGE_NOTIFY_STATUS_RETRIEVED, msg); if (request == NULL) mms_message_free(msg); } else { request = NULL; mms_message_register(service, msg); } } else if (msg->type == MMS_MESSAGE_TYPE_DELIVERY_IND) { request = NULL; } else request = NULL; if (request != NULL) { g_queue_push_tail(service->request_queue, request); activate_bearer(service); } } static void load_messages(struct mms_service *service) { GDir *dir; const char *file; const char *homedir; char *service_path; homedir = g_get_home_dir(); if (homedir == NULL) return; service_path = g_strdup_printf("%s/.mms/%s/", homedir, service->identity); dir = g_dir_open(service_path, 0, NULL); g_free(service_path); if (dir == NULL) return; while ((file = g_dir_read_name(dir)) != NULL) { const size_t suffix_len = 7; char *uuid; if (g_str_has_suffix(file, ".status") == FALSE) continue; if (strlen(file) - suffix_len == 0) continue; uuid = g_strndup(file, strlen(file) - suffix_len); process_message_on_start(service, uuid); g_free(uuid); } g_dir_close(dir); } int mms_service_register(struct mms_service *service) { 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", MMS_PATH, service->identity); if (service->path == NULL) return -ENOMEM; if (g_dbus_register_interface(connection, service->path, MMS_SERVICE_INTERFACE, service_methods, service_signals, NULL, service, NULL) == FALSE) { mms_error("Failed to register service interface"); g_free(service->path); service->path = NULL; return -EIO; } service_list = g_list_append(service_list, service); emit_service_added(service); mms_load_settings(service); load_messages(service); return 0; } int mms_service_unregister(struct mms_service *service) { 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) { g_key_file_set_boolean(service->settings, SETTINGS_GROUP, "UseDeliveryReports", service->use_delivery_reports); mms_settings_close(service->identity, SETTINGS_STORE, service->settings, TRUE); service->settings = NULL; } if (g_dbus_unregister_interface(connection, service->path, MMS_SERVICE_INTERFACE) == FALSE) { mms_error("Failed to unregister service interface"); return -EIO; } service_list = g_list_remove(service_list, service); emit_service_removed(service); g_free(service->path); service->path = NULL; return 0; } static const char *time_to_str(const time_t *t) { static char buf[128]; struct tm tm; strftime(buf, 127, "%Y-%m-%dT%H:%M:%S%z", localtime_r(t, &tm)); buf[127] = '\0'; return buf; } static void append_attachment_properties(struct mms_attachment *part, DBusMessageIter *dict, const char *path, DBusMessageIter *part_array) { DBusMessageIter entry; dbus_uint64_t val; dbus_message_iter_open_container(part_array, DBUS_TYPE_STRUCT, NULL, &entry); dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &part->content_id); dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &part->content_type); dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &path); val = part->offset; dbus_message_iter_append_basic(&entry, DBUS_TYPE_UINT64, &val); val = part->length; dbus_message_iter_append_basic(&entry, DBUS_TYPE_UINT64, &val); dbus_message_iter_close_container(part_array, &entry); } static void append_smil(DBusMessageIter *dict, const char *path, const struct mms_attachment *part) { const char *to_codeset = "utf-8"; char *from_codeset; void *data; size_t len; char *smil; if (mmap_file(path, &data, &len) == FALSE) return; from_codeset = mms_content_type_get_param_value(part->content_type, "charset"); if (from_codeset != NULL) { smil = g_convert((const char *) data + part->offset, part->length, to_codeset, from_codeset, NULL, NULL, NULL); g_free(from_codeset); } else smil = g_convert((const char *) data + part->offset, part->length, to_codeset, "us-ascii", NULL, NULL, NULL); munmap(data, len); if (smil == NULL) { mms_error("Failed to convert smil attachment\n"); return; } mms_dbus_dict_append_basic(dict, "Smil", DBUS_TYPE_STRING, &smil); g_free(smil); } static inline void check_null_content_id(struct mms_attachment *attachment) { if (attachment->content_id == NULL) attachment->content_id = g_strdup(""); } static void append_msg_attachments(DBusMessageIter *dict, const char *path, struct mms_message *msg) { const char *dict_entry = "Attachments"; DBusMessageIter array; DBusMessageIter entry; DBusMessageIter variant; GSList *part; struct mms_attachment *smil; dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY, NULL, &entry); dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &dict_entry); dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT, "a(ssstt)", &variant); dbus_message_iter_open_container(&variant, DBUS_TYPE_ARRAY, "(ssstt)", &array); smil = NULL; for (part = msg->attachments; part != NULL; part = g_slist_next(part)) { check_null_content_id(part->data); if (mms_attachment_is_smil(part->data)) smil = part->data; else append_attachment_properties(part->data, dict, path, &array); } dbus_message_iter_close_container(&variant, &array); dbus_message_iter_close_container(&entry, &variant); dbus_message_iter_close_container(dict, &entry); if (smil == NULL) return; switch (msg->type) { case MMS_MESSAGE_TYPE_SEND_REQ: append_smil(dict, path, smil); return; case MMS_MESSAGE_TYPE_SEND_CONF: return; case MMS_MESSAGE_TYPE_NOTIFICATION_IND: return; case MMS_MESSAGE_TYPE_NOTIFYRESP_IND: return; case MMS_MESSAGE_TYPE_RETRIEVE_CONF: append_smil(dict, path, smil); break; case MMS_MESSAGE_TYPE_ACKNOWLEDGE_IND: return; case MMS_MESSAGE_TYPE_DELIVERY_IND: return; } } static void append_msg_recipients(DBusMessageIter *dict, struct mms_message *msg) { const char *dict_entry = "Recipients"; DBusMessageIter array; DBusMessageIter entry; DBusMessageIter variant; gchar **uninitialized_var(tokens); unsigned int i; const char *rcpt; switch (msg->type) { case MMS_MESSAGE_TYPE_SEND_REQ: tokens = g_strsplit(msg->sr.to, ",", -1); break; case MMS_MESSAGE_TYPE_SEND_CONF: return; case MMS_MESSAGE_TYPE_NOTIFICATION_IND: return; case MMS_MESSAGE_TYPE_NOTIFYRESP_IND: return; case MMS_MESSAGE_TYPE_RETRIEVE_CONF: tokens = g_strsplit(msg->rc.to, ",", -1); break; case MMS_MESSAGE_TYPE_ACKNOWLEDGE_IND: return; case MMS_MESSAGE_TYPE_DELIVERY_IND: return; } dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY, NULL, &entry); dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &dict_entry); dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT, "as", &variant); dbus_message_iter_open_container(&variant, DBUS_TYPE_ARRAY, "s", &array); for (i = 0; tokens[i] != NULL; i++) { rcpt = mms_address_to_string(tokens[i]); dbus_message_iter_append_basic(&array, DBUS_TYPE_STRING, &rcpt); } g_strfreev(tokens); dbus_message_iter_close_container(&variant, &array); dbus_message_iter_close_container(&entry, &variant); dbus_message_iter_close_container(dict, &entry); } static void append_rc_msg_properties(DBusMessageIter *dict, struct mms_message *msg) { const char *date = time_to_str(&msg->rc.date); const char *status = "received"; const char *from_prefix; char *from; mms_dbus_dict_append_basic(dict, "Status", DBUS_TYPE_STRING, &status); mms_dbus_dict_append_basic(dict, "Date", DBUS_TYPE_STRING, &date); if (msg->rc.subject != NULL) mms_dbus_dict_append_basic(dict, "Subject", DBUS_TYPE_STRING, &msg->rc.subject); from = g_strdup(msg->rc.from); if (from != NULL) { from_prefix = mms_address_to_string(from); mms_dbus_dict_append_basic(dict, "Sender", DBUS_TYPE_STRING, &from_prefix); g_free(from); } if (msg->rc.to != NULL) append_msg_recipients(dict, msg); } static void append_sr_msg_properties(DBusMessageIter *dict, struct mms_message *msg) { const char *date = time_to_str(&msg->rc.date); const char *status = mms_message_status_get_string(msg->sr.status); mms_dbus_dict_append_basic(dict, "Status", DBUS_TYPE_STRING, &status); mms_dbus_dict_append_basic(dict, "Date", DBUS_TYPE_STRING, &date); if (msg->sr.to != NULL) append_msg_recipients(dict, msg); } static void append_message(const char *path, const struct mms_service *service, struct mms_message *msg, DBusMessageIter *iter) { DBusMessageIter dict; dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH, &path); mms_dbus_dict_open(iter, &dict); switch (msg->type) { case MMS_MESSAGE_TYPE_SEND_REQ: append_sr_msg_properties(&dict, msg); break; case MMS_MESSAGE_TYPE_SEND_CONF: break; case MMS_MESSAGE_TYPE_NOTIFICATION_IND: break; case MMS_MESSAGE_TYPE_NOTIFYRESP_IND: break; case MMS_MESSAGE_TYPE_RETRIEVE_CONF: append_rc_msg_properties(&dict, msg); break; case MMS_MESSAGE_TYPE_ACKNOWLEDGE_IND: break; case MMS_MESSAGE_TYPE_DELIVERY_IND: break; } if (msg->attachments != NULL) { char *pdu_path = mms_store_get_path(service->identity, msg->uuid); append_msg_attachments(&dict, pdu_path, msg); g_free(pdu_path); } mms_dbus_dict_close(iter, &dict); } static void emit_message_added(const struct mms_service *service, struct mms_message *msg) { DBusMessage *signal; DBusMessageIter iter; DBG("message %p", msg); signal = dbus_message_new_signal(service->path, MMS_SERVICE_INTERFACE, "MessageAdded"); if (signal == NULL) return; dbus_message_iter_init_append(signal, &iter); append_message(msg->path, service, msg, &iter); g_dbus_send_message(connection, signal); } int mms_message_register(struct mms_service *service, struct mms_message *msg) { msg->path = g_strdup_printf("%s/%s", service->path, msg->uuid); if (msg->path == NULL) return -ENOMEM; if (g_dbus_register_interface(connection, msg->path, MMS_MESSAGE_INTERFACE, message_methods, message_signals, NULL, service, NULL) == FALSE) { mms_error("Failed to register message interface"); g_free(msg->path); msg->path = NULL; return -EIO; } g_hash_table_replace(service->messages, msg->path, msg); DBG("message registered %s", msg->path); return 0; } static void emit_message_removed(const char *svc_path, const char *msg_path) { g_dbus_emit_signal(connection, svc_path, MMS_MESSAGE_INTERFACE, "MessageRemoved", DBUS_TYPE_OBJECT_PATH, &msg_path, DBUS_TYPE_INVALID); } int mms_message_unregister(const struct mms_service *service, const char *msg_path) { emit_message_removed(service->path, msg_path); if (g_dbus_unregister_interface(connection, msg_path, MMS_MESSAGE_INTERFACE) == FALSE) { mms_error("Failed to unregister message interface"); return -EIO; } DBG("message unregistered %s", msg_path); g_hash_table_remove(service->messages, msg_path); return 0; } int mms_service_set_identity(struct mms_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 mms_service_set_mmsc(struct mms_service *service, const char *mmsc) { DBG("service %p mmsc %s", service, mmsc); if (service == NULL) return -EINVAL; g_free(service->mmsc); service->mmsc = g_strdup(mmsc); return 0; } int mms_service_set_bearer_handler(struct mms_service *service, mms_service_bearer_handler_func_t handler, void *user_data) { DBG("service %p handler %p", service, handler); if (service == NULL) return -EINVAL; service->bearer_handler = handler; service->bearer_data = user_data; return 0; } static inline gboolean bearer_is_active(struct mms_service *service) { if (service->bearer_setup == TRUE) return FALSE; if (service->bearer_handler == NULL) return FALSE; return service->bearer_active; } static void deactivate_bearer(struct mms_service *service) { DBG("service %p", service); if (bearer_is_active(service) == FALSE) return; DBG("service %p", service); service->bearer_setup = TRUE; service->bearer_timeout = g_timeout_add_seconds(BEARER_SETUP_TIMEOUT, bearer_setup_timeout, service); service->bearer_handler(FALSE, service->bearer_data); } static gboolean bearer_idle_timeout(gpointer user_data) { struct mms_service *service = user_data; DBG("service %p", service); service->bearer_timeout = 0; deactivate_bearer(service); return FALSE; } static gboolean result_request_notify_resp(struct mms_request *request) { struct mms_message *msg; GKeyFile *meta; unlink(request->data_path); if (request->status != 200) { mms_error("POST m.notify.resp.ind failed with status %d", request->status); return FALSE; } if (request->msg == NULL) return FALSE; msg = mms_request_steal_message(request); if (mms_message_register(request->service, msg) != 0) { mms_message_free(msg); return FALSE; } emit_message_added(request->service, msg); meta = mms_store_meta_open(request->service->identity, msg->uuid); if (meta == NULL) return FALSE; g_key_file_set_string(meta, "info", "state", "received"); mms_store_meta_close(request->service->identity, msg->uuid, meta, TRUE); return TRUE; } static gboolean result_request_retrieve_conf(struct mms_request *request) { struct mms_message *msg; struct mms_service *service = request->service; const char *uuid; GKeyFile *meta; void *pdu; size_t len; struct mms_request *notify_request; gboolean decode_success; if (request->status != 200) return FALSE; if (mmap_file(request->data_path, &pdu, &len) == FALSE) return FALSE; uuid = mms_store_file(service->identity, request->data_path); if (uuid == NULL) goto exit; msg = g_try_new0(struct mms_message, 1); if (msg == NULL) goto exit; decode_success = mms_message_decode(pdu, len, msg); msg->transaction_id = g_strdup(request->msg->transaction_id); if (decode_success == TRUE) { msg->uuid = g_strdup(uuid); meta = mms_store_meta_open(service->identity, uuid); if (meta == NULL) goto error; g_key_file_set_boolean(meta, "info", "read", FALSE); g_key_file_set_string(meta, "info", "state", "downloaded"); mms_store_meta_close(service->identity, uuid, meta, TRUE); notify_request = build_notify_resp_ind(service, MMS_MESSAGE_NOTIFY_STATUS_RETRIEVED, msg); } else { mms_error("Failed to decode %s", request->data_path); notify_request = build_notify_resp_ind(service, MMS_MESSAGE_NOTIFY_STATUS_UNRECOGNISED, msg); } /* Remove notify.ind pdu */ mms_store_remove(service->identity, request->msg->uuid); if (notify_request == NULL) goto error; g_queue_push_tail(service->request_queue, notify_request); activate_bearer(service); if (decode_success == TRUE) goto exit; error: mms_store_remove(service->identity, uuid); mms_message_free(msg); exit: munmap(pdu, len); return TRUE; } static gboolean mms_requeue_request(struct mms_request *request) { request->attempt += 1; if (request->attempt == MAX_ATTEMPTS) return FALSE; if (request->type == MMS_REQUEST_TYPE_GET) { request->fd = open(request->data_path, O_WRONLY | O_TRUNC, S_IWUSR | S_IRUSR); if (request->fd < 0) return FALSE; } g_queue_push_tail(request->service->request_queue, request); return TRUE; } static gboolean web_get_cb(GWebResult *result, gpointer user_data) { gsize written; gsize chunk_size; struct mms_request *request = user_data; const guint8 *chunk; if (g_web_result_get_chunk(result, &chunk, &chunk_size) == FALSE) { mms_error("Fail to get data chunk"); goto complete; } if (chunk_size == 0) { request->status = g_web_result_get_status(result); DBG("status: %03u", request->status); DBG("data size = %zd", request->data_size); goto complete; } request->data_size += chunk_size; written = write(request->fd, chunk, chunk_size); if (written != chunk_size) { mms_error("only %zd/%zd bytes written\n", written, chunk_size); goto complete; } return TRUE; complete: close(request->fd); if (request->result_cb == NULL || request->result_cb(request) == TRUE) mms_request_destroy(request); else { mms_error("Fail to get data (http status = %03u)", request->status); if (mms_requeue_request(request) == TRUE) mms_error("retry later"); else { unlink(request->data_path); mms_request_destroy(request); } } request->service->current_request_id = 0; process_request_queue(request->service); return FALSE; } static gboolean web_post_result_cb(GWebResult *result, gpointer user_data) { struct mms_request *req = user_data; if (req->fd == -1) { if (req->type == MMS_REQUEST_TYPE_POST_TMP) unlink(req->data_path); DBG("Send <%s> complete", req->data_path); /* post complete, prepare for response reception */ g_free(req->data_path); req->data_path = g_strdup_printf("%s/.mms/post-rsp.XXXXXX.mms", g_get_home_dir()); req->fd = g_mkstemp_full(req->data_path, O_WRONLY | O_CREAT | O_TRUNC, S_IWUSR | S_IRUSR); } return web_get_cb(result, user_data); } static guint process_request(struct mms_request *request) { struct mms_service *service = request->service; guint id; if (request->data_path == NULL) return 0; switch (request->type) { case MMS_REQUEST_TYPE_GET: id = g_web_request_get(service->web, request->location, web_get_cb, NULL, request); if (id == 0) { close(request->fd); break; } return id; case MMS_REQUEST_TYPE_POST: case MMS_REQUEST_TYPE_POST_TMP: id = g_web_request_post_file(service->web, service->mmsc, DEFAULT_CONTENT_TYPE, request->data_path, web_post_result_cb, request); if (id == 0) break; return id; } mms_error("Cannot process request (request type: %d)", request->type); unlink(request->data_path); mms_request_destroy(request); return 0; } static void process_request_queue(struct mms_service *service) { struct mms_request *request; DBG("service %p", service); if (service->bearer_timeout > 0) { g_source_remove(service->bearer_timeout); service->bearer_timeout = 0; } if (service->current_request_id > 0) return; while (1) { if (bearer_is_active(service) == FALSE) return; request = g_queue_pop_head(service->request_queue); if (request == NULL) break; DBG("location %s", request->location); service->current_request_id = process_request(request); if (service->current_request_id > 0) return; } service->bearer_timeout = g_timeout_add_seconds(BEARER_IDLE_TIMEOUT, bearer_idle_timeout, service); } static void dump_delivery_ind(struct mms_message *msg) { char buf[128]; strftime(buf, 127, "%Y-%m-%dT%H:%M:%S%z", localtime(&msg->di.date)); buf[127] = '\0'; mms_info("MMS version: %u.%u\n", (msg->version & 0x70) >> 4, msg->version & 0x0f); mms_info("Msg ID: %s\n", msg->di.msgid); mms_info("To: %s\n", msg->di.to); mms_info("Date: %s\n", buf); mms_info("Delivery Report status: %d\n", msg->di.dr_status); } static void dump_notification_ind(struct mms_message *msg) { char buf[128]; strftime(buf, 127, "%Y-%m-%dT%H:%M:%S%z", localtime(&msg->ni.expiry)); buf[127] = '\0'; mms_info("MMS transaction id: %s\n", msg->transaction_id); mms_info("MMS version: %u.%u\n", (msg->version & 0x70) >> 4, msg->version & 0x0f); mms_info("From: %s\n", msg->ni.from); mms_info("Subject: %s\n", msg->ni.subject); mms_info("Class: %s\n", msg->ni.cls); mms_info("Size: %d\n", msg->ni.size); mms_info("Expiry: %s\n", buf); mms_info("Location: %s\n", msg->ni.location); } void mms_service_push_notify(struct mms_service *service, unsigned char *data, int len) { struct mms_request *request; struct mms_message *msg; unsigned int nread; const char *uuid; GKeyFile *meta; DBG("service %p data %p len %d", service, data, len); msg = g_try_new0(struct mms_message, 1); if (msg == NULL) { mms_error("Failed to allocate message"); return; } if (mms_push_notify(data, len, &nread) == FALSE) goto out; uuid = mms_store(service->identity, data + nread, len - nread); if (uuid == NULL) goto out; if (mms_message_decode(data + nread, len - nread, msg) == FALSE) goto error; if (msg->type == MMS_MESSAGE_TYPE_DELIVERY_IND) { msg->uuid = g_strdup(uuid); dump_delivery_ind(msg); meta = mms_store_meta_open(service->identity, uuid); if (meta == NULL) goto error; g_key_file_set_string(meta, "info", "state", "notification"); mms_store_meta_close(service->identity, uuid, meta, TRUE); return; } if (msg->type != MMS_MESSAGE_TYPE_NOTIFICATION_IND) goto error; msg->uuid = g_strdup(uuid); dump_notification_ind(msg); meta = mms_store_meta_open(service->identity, uuid); if (meta == NULL) goto error; g_key_file_set_boolean(meta, "info", "read", FALSE); g_key_file_set_string(meta, "info", "state", "notification"); mms_store_meta_close(service->identity, uuid, meta, TRUE); request = create_request(MMS_REQUEST_TYPE_GET, result_request_retrieve_conf, msg->ni.location, service, msg); if (request == NULL) goto out; g_queue_push_tail(service->request_queue, request); activate_bearer(service); return; error: mms_store_remove(service->identity, uuid); out: mms_message_free(msg); mms_error("Failed to handle incoming notification"); } void mms_service_bearer_notify(struct mms_service *service, mms_bool_t active, const char *interface, const char *proxy) { int ifindex; DBG("service %p active %d", service, active); if (service == NULL) return; if (service->bearer_timeout > 0) { g_source_remove(service->bearer_timeout); service->bearer_timeout = 0; } service->bearer_setup = FALSE; service->bearer_active = active; if (active == FALSE) goto interface_down; DBG("interface %s proxy %s", interface, proxy); if (service->web != NULL) { g_web_unref(service->web); service->web = NULL; } if (interface == NULL) goto interface_down; ifindex = if_nametoindex(interface); if (ifindex == 0) goto interface_down; service->web = g_web_new(ifindex); if (service->web == NULL) return; /* Sometimes no proxy is reported as string instead of NULL */ if (g_strcmp0(proxy, "") != 0) g_web_set_proxy(service->web, proxy); process_request_queue(service); return; interface_down: if (service->current_request_id > 0) g_web_cancel_request(service->web, service->current_request_id); } static void append_struct(gpointer value, gpointer user_data) { struct mms_service *service = value; DBusMessageIter *iter = user_data; DBusMessageIter entry, dict; dbus_message_iter_open_container(iter, DBUS_TYPE_STRUCT, NULL, &entry); dbus_message_iter_append_basic(&entry, DBUS_TYPE_OBJECT_PATH, &service->path); mms_dbus_dict_open(&entry, &dict); append_properties(&dict, service); mms_dbus_dict_close(&entry, &dict); dbus_message_iter_close_container(iter, &entry); } static DBusMessage *get_services(DBusConnection *conn, DBusMessage *msg, void *data) { DBusMessage *reply; DBusMessageIter iter, array; reply = dbus_message_new_method_return(msg); if (reply == NULL) return NULL; dbus_message_iter_init_append(reply, &iter); dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, DBUS_STRUCT_BEGIN_CHAR_AS_STRING DBUS_TYPE_OBJECT_PATH_AS_STRING DBUS_TYPE_ARRAY_AS_STRING DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING DBUS_DICT_ENTRY_END_CHAR_AS_STRING DBUS_STRUCT_END_CHAR_AS_STRING, &array); g_list_foreach(service_list, append_struct, &array); dbus_message_iter_close_container(&iter, &array); return reply; } static const GDBusMethodTable manager_methods[] = { { GDBUS_METHOD("GetServices", NULL, GDBUS_ARGS({ "services_with_properties", "a(oa{sv})" }), get_services) }, { } }; static const GDBusSignalTable manager_signals[] = { { GDBUS_SIGNAL("ServiceAdded", GDBUS_ARGS({ "path", "o" }, { "properties", "a{sv}" })) }, { GDBUS_SIGNAL("ServiceRemoved", GDBUS_ARGS({ "path", "o" })) }, { } }; int __mms_service_init(void) { DBG(""); connection = mms_dbus_get_connection(); if (g_dbus_register_interface(connection, MMS_PATH, MMS_MANAGER_INTERFACE, manager_methods, manager_signals, NULL, NULL, NULL) == FALSE) { mms_error("Failed to register manager interface"); return -EIO; } return 0; } void __mms_service_cleanup(void) { DBG(""); if (g_dbus_unregister_interface(connection, MMS_PATH, MMS_MANAGER_INTERFACE) == FALSE) mms_error("Failed to unregister manager interface"); } mmsd-0.1/src/service.h000066400000000000000000000035711216024552400147210ustar00rootroot00000000000000/* * * Multimedia Messaging Service * * Copyright (C) 2010-2011 Intel Corporation. All rights reserved. * * 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 "types.h" struct mms_service; struct mms_message; typedef void (*mms_service_bearer_handler_func_t) (mms_bool_t active, void *user_data); struct mms_service *mms_service_create(void); struct mms_service *mms_service_ref(struct mms_service *service); void mms_service_unref(struct mms_service *service); int mms_service_register(struct mms_service *service); int mms_service_unregister(struct mms_service *service); int mms_service_set_identity(struct mms_service *service, const char *identity); int mms_service_set_mmsc(struct mms_service *service, const char *mmsc); int mms_service_set_bearer_handler(struct mms_service *service, mms_service_bearer_handler_func_t handler, void *user_data); void mms_service_push_notify(struct mms_service *service, unsigned char *data, int len); void mms_service_bearer_notify(struct mms_service *service, mms_bool_t active, const char *interface, const char *proxy); int mms_message_register(struct mms_service *service, struct mms_message *msg); int mms_message_unregister(const struct mms_service *service, const char *msg_path); mmsd-0.1/src/store.c000066400000000000000000000201451216024552400144040ustar00rootroot00000000000000/* * * Multimedia Messaging Service * * Copyright (C) 2010-2011 Intel Corporation. All rights reserved. * * 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 "mms.h" #ifdef TEMP_FAILURE_RETRY #define TFR TEMP_FAILURE_RETRY #else #define TFR #endif static const char *digest_to_str(const unsigned char *digest) { static char buf[MMS_SHA1_UUID_LEN * 2 + 1]; unsigned int i; for (i = 0; i < MMS_SHA1_UUID_LEN; i++) sprintf(&buf[i * 2], "%02X", digest[i]); buf[MMS_SHA1_UUID_LEN * 2] = 0; return buf; } 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; } static const char *generate_uuid_from_pdu(unsigned char *pdu, unsigned int len) { GChecksum *checksum; guint8 digest[MMS_SHA1_UUID_LEN]; gsize digest_size = MMS_SHA1_UUID_LEN; DBG("pdu %p len %d", pdu, 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); } char *mms_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/.mms/%s/%s", homedir, service_id, uuid); } static char *generate_pdu_pathname(const char *service_id, const char *uuid) { char *pathname; pathname = mms_store_get_path(service_id, uuid); if (pathname == NULL) return NULL; if (create_dirs(pathname, S_IRUSR | S_IWUSR | S_IXUSR) != 0) { mms_error("Failed to create path %s", pathname); g_free(pathname); return NULL; } return pathname; } /* * Write a buffer to a file in a transactionally safe form * * Given a buffer, write it to a file named after * @filename. However, to make sure the file contents are * consistent (ie: a crash right after opening or during write() * doesn't leave a file half baked), the contents are written to a * file with a temporary name and when closed, it is renamed to the * specified name (@filename). */ static ssize_t write_file(const unsigned char *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; written = TFR(write(fd, 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; } const char *mms_store(const char *service_id, unsigned char *pdu, unsigned int len) { char *pathname; const char *uuid; uuid = generate_uuid_from_pdu(pdu, len); if (uuid == NULL) return NULL; pathname = generate_pdu_pathname(service_id, uuid); if (pathname == NULL) return NULL; if (write_file(pdu, len, pathname) < 0) { mms_error("Failed to write to %s", pathname); uuid = NULL; } DBG("pathname %s", pathname); g_free(pathname); return uuid; } const char *mms_store_file(const char *service_id, const char *path) { char *pathname; const char *uuid; int fd; struct stat st; unsigned char *pdu; fd = open(path, O_RDONLY); if (fd < 0) { mms_error("Failed to open %s\n", path); return NULL; } if (fstat(fd, &st) < 0) { mms_error("Failed to fstat %s\n", path); close(fd); return NULL; } pdu = mmap(NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0); if (pdu == NULL || pdu == MAP_FAILED) { mms_error("Failed to mmap %s\n", path); close(fd); return NULL; } uuid = generate_uuid_from_pdu(pdu, st.st_size); munmap(pdu, st.st_size); close(fd); if (uuid == NULL) return NULL; pathname = generate_pdu_pathname(service_id, uuid); if (pathname == NULL) return NULL; if (g_rename(path, pathname) < 0) { mms_error("Failed to rename %s to %s\n", path, pathname); uuid = NULL; } DBG("pathname %s", pathname); g_free(pathname); return uuid; } void mms_store_remove(const char *service_id, const char *uuid) { char *pdu_path; char *meta_path; pdu_path = mms_store_get_path(service_id, uuid); if (pdu_path == NULL) return; unlink(pdu_path); meta_path = g_strdup_printf("%s%s", pdu_path, MMS_META_UUID_SUFFIX); g_free(pdu_path); unlink(meta_path); g_free(meta_path); } GKeyFile *mms_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, MMS_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 = mms_store_get_path(service_id, uuid); if (pdu_path == NULL) return; meta_path = g_strdup_printf("%s%s", pdu_path, MMS_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 mms_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 *mms_settings_open(const char *service_id, const char *store) { GKeyFile *keyfile; char *path; if (store == NULL) return NULL; path = mms_store_get_path(service_id, store); if (path == NULL) return NULL; if (create_dirs(path, S_IRUSR | S_IWUSR | S_IXUSR) != 0) { mms_error("Failed to create path %s", path); g_free(path); return NULL; } keyfile = g_key_file_new(); g_key_file_load_from_file(keyfile, path, 0, NULL); g_free(path); return keyfile; } static void settings_sync(const char *service_id, const char *store, GKeyFile *keyfile) { char *path; char *data; gsize length = 0; path = mms_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 mms_settings_close(const char *service_id, const char *store, GKeyFile *keyfile, gboolean save) { if (save == TRUE) settings_sync(service_id, store, keyfile); g_key_file_free(keyfile); } mmsd-0.1/src/store.h000066400000000000000000000030701216024552400144070ustar00rootroot00000000000000/* * * Multimedia Messaging Service * * Copyright (C) 2010-2011 Intel Corporation. All rights reserved. * * 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 MMS_SHA1_UUID_LEN 20 #define MMS_META_UUID_SUFFIX ".status" #define MMS_META_UUID_SUFFIX_LEN 7 #define MMS_META_UUID_LEN (MMS_SHA1_UUID_LEN * 2) const char *mms_store(const char *service_id, unsigned char *pdu, unsigned int len); const char *mms_store_file(const char *service_id, const char *path); void mms_store_remove(const char *service_id, const char *uuid); char *mms_store_get_path(const char *service_id, const char *uuid); GKeyFile *mms_store_meta_open(const char *service_id, const char *uuid); void mms_store_meta_close(const char *service_id, const char *uuid, GKeyFile *keyfile, gboolean save); GKeyFile *mms_settings_open(const char *service_id, const char *store); void mms_settings_close(const char *service_id, const char *store, GKeyFile *keyfile, gboolean save); mmsd-0.1/src/types.h000066400000000000000000000015711216024552400144230ustar00rootroot00000000000000/* * * Multimedia Messaging Service * * Copyright (C) 2010-2011 Intel Corporation. All rights reserved. * * 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 * */ #ifndef FALSE #define FALSE (0) #endif #ifndef TRUE #define TRUE (!FALSE) #endif typedef int mms_bool_t; mmsd-0.1/src/wsputil.c000066400000000000000000000655031216024552400147660ustar00rootroot00000000000000/* * * Multimedia Messaging Service * * Copyright (C) 2010-2011 Intel Corporation. All rights reserved. * * 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 "wsputil.h" struct wsp_hex_str_entry { unsigned int type; const char *type_str; }; /* * http://www.wapforum.org/wina/wsp-content-type.htm */ static const char *content_types[] = { "*/*", "text/*", "text/html", "text/plain", "text/x-hdml", "text/x-ttml", "text/x-vCalendar", "text/x-vCard", "text/vnd.wap.wml", "text/vnd.wap.wmlscript", "text/vnd.wap.wta-event", "multipart/*", "multipart/mixed", "multipart/form-data", "multipart/byterantes", "multipart/alternative", "application/*", "application/java-vm", "application/x-www-form-urlencoded", "application/x-hdmlc", "application/vnd.wap.wmlc", "application/vnd.wap.wmlscriptc", "application/vnd.wap.wta-eventc", "application/vnd.wap.uaprof", "application/vnd.wap.wtls-ca-certificate", "application/vnd.wap.wtls-user-certificate", "application/x-x509-ca-cert", "application/x-x509-user-cert", "image/*", "image/gif", "image/jpeg", "image/tiff", "image/png", "image/vnd.wap.wbmp", "application/vnd.wap.multipart.*", "application/vnd.wap.multipart.mixed", "application/vnd.wap.multipart.form-data", "application/vnd.wap.multipart.byteranges", "application/vnd.wap.multipart.alternative", "application/xml", "text/xml", "application/vnd.wap.wbxml", "application/x-x968-cross-cert", "application/x-x968-ca-cert", "application/x-x968-user-cert", "text/vnd.wap.si", "application/vnd.wap.sic", "text/vnd.wap.sl", "application/vnd.wap.slc", "text/vnd.wap.co", "application/vnd.wap.coc", "application/vnd.wap.multipart.related", "application/vnd.wap.sia", "text/vnd.wap.connectivity-xml", "application/vnd.wap.connectivity-wbxml", "application/pkcs7-mime", "application/vnd.wap.hashed-certificate", "application/vnd.wap.signed-certificate", "application/vnd.wap.cert-response", "application/xhtml+xml", "application/wml+xml", "text/css", "application/vnd.wap.mms-message", "application/vnd.wap.rollover-certificate", "application/vnd.wap.locc+wbxml", "application/vnd.wap.loc+xml", "application/vnd.syncml.dm+wbxml", "application/vnd.syncml.dm+xml", "application/vnd.syncml.notification", "application/vnd.wap.xhtml+xml", "application/vnd.wv.csp.cir", "application/vnd.oma.dd+xml", "application/vnd.oma.drm.message", "application/vnd.oma.drm.content", "application/vnd.oma.drm.rights+xml", "application/vnd.oma.drm.rights+wbxml", }; #define LAST_CONTENT_TYPE (sizeof(content_types) / sizeof(const char *)) /* * http://www.wapforum.org/wina/push-app-id.htm */ static const struct wsp_hex_str_entry app_id[] = { { 0x04, "x-wap-application:mms.ua" }, { 0x07, "x-wap-application:syncml.dm" }, { 0x08, "x-wap-application:drm.ua" }, { 0xFF, NULL } }; /* http://www.wapforum.org/wina/wsp-content-type.htm */ static const struct wsp_hex_str_entry extension_mimetypes[] = { { 0x0201, "application/vnd.uplanet.cacheop-wbxml" }, { 0x0202, "application/vnd.uplanet.signal" }, { 0x0203, "application/vnd.uplanet.alert-wbxml" }, { 0x0204, "application/vnd.uplanet.list-wbxml" }, { 0x0205, "application/vnd.uplanet.listcmd-wbxml" }, { 0x0206, "application/vnd.uplanet.channel-wbxml" }, { 0x0207, "application/vnd.uplanet.provisioning-status-uri" }, { 0x0208, "x-wap.multipart/vnd.uplanet.header-set" }, { 0x0209, "application/vnd.uplanet.bearer-choice-wbxml" }, { 0x020A, "application/vnd.phonecom.mmc-wbxml" }, { 0x020B, "application/vnd.nokia.syncset+wbxml" }, { 0x020C, "image/x-up-wpng" }, { 0xFFFF, NULL }, }; static const struct wsp_hex_str_entry charset_assignments[] = { { 0x0000, "*" }, { 0x07EA, "big5" }, { 0x03E8, "iso-10646-ucs-2" }, { 0x0004, "iso-8859-1" }, { 0x0005, "iso-8859-2" }, { 0x0006, "iso-8859-3" }, { 0x0007, "iso-8859-4" }, { 0x0008, "iso-8859-5" }, { 0x0009, "iso-8859-6" }, { 0x000A, "iso-8859-7" }, { 0x000B, "iso-8859-8" }, { 0x000C, "iso-8859-9" }, { 0x0011, "shift_JIS" }, { 0x0003, "us-ascii" }, { 0x006A, "utf-8" }, { 0x03F7, "utf-16" }, { 0xFFFF, NULL }, }; /* * Control Characters 0-8, 10-31 and 127. The tab character is omitted * since it is included in the sep chars array and the most generic TEXT * type of RFC 2616 explicitly allows tabs */ static const char *ctl_chars = "\x01\x02\x03\x04\x05\x06\x07\x08\x0A" "\x0B\x0C\x0D\x0E\x0F\x10\x11\x12\x13\x14" "\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E" "\x1F\x7F"; static const char *sep_chars = "()<>@,;:\\\"/[]?={} \t"; static const char *decode_text_common(const unsigned char *pdu, unsigned int len, gboolean filter_ctl, gboolean filter_sep, unsigned int *consumed) { unsigned char *c; c = memchr(pdu, '\0', len); if (c == NULL) return NULL; c += 1; /* RFC 2616 Section 2.2 */ if (filter_ctl && strpbrk((const char *) pdu, ctl_chars) != NULL) return NULL; if (filter_sep && strpbrk((const char *) pdu, sep_chars) != NULL) return NULL; if (consumed) *consumed = c - pdu; return (const char *) pdu; } const char *wsp_decode_token_text(const unsigned char *pdu, unsigned int len, unsigned int *consumed) { return decode_text_common(pdu, len, TRUE, TRUE, consumed); } const char *wsp_decode_text(const unsigned char *pdu, unsigned int len, unsigned int *consumed) { const char *r; unsigned int fudge = 0; if (*pdu == 127) { pdu++; if (*pdu < 128) return NULL; len -= 1; fudge += 1; } r = decode_text_common(pdu, len, TRUE, FALSE, consumed); if (consumed) *consumed += fudge; return r; } const char *wsp_decode_quoted_string(const unsigned char *pdu, unsigned int len, unsigned int *consumed) { const char *text; text = wsp_decode_text(pdu, len, consumed); if (text == NULL) return NULL; if (*text != '"') return NULL; /* Skip initial quote */ text++; return text; } gboolean wsp_decode_uintvar(const unsigned char *pdu, unsigned int len, unsigned int *out_len, unsigned int *consumed) { unsigned int var; unsigned int i; unsigned int cont; for (i = 0, var = 0, cont = TRUE; i < 5 && i < len && cont; i++) { cont = pdu[i] & 0x80; var = (var << 7) | (pdu[i] & 0x7f); } if (cont) return FALSE; if (out_len) *out_len = var; if (consumed) *consumed = i; return TRUE; } gboolean wsp_decode_integer(const unsigned char *pdu, unsigned int len, unsigned int *out_val, unsigned int *consumed) { unsigned int var; unsigned int i; unsigned int count; if (pdu[0] & 0x80) { var = pdu[0] & 0x7f; count = 1; } else if (pdu[0] <= 30) { unsigned int value_len = *pdu; if (value_len > (len - 1)) return FALSE; if (value_len > sizeof(unsigned int)) return FALSE; var = 0; for (i = 0; i < value_len; i++) var = (var << 8) | pdu[i + 1]; count = value_len + 1; } else return FALSE; if (out_val) *out_val = var; if (consumed) *consumed = count; return TRUE; } gboolean wsp_decode_field(const unsigned char *pdu, unsigned int max, enum wsp_value_type *out_type, const void **out_value, unsigned int *out_len, unsigned int *out_read) { const unsigned char *end = pdu + max; const unsigned char *begin = pdu; unsigned int len; enum wsp_value_type value; unsigned int consumed; if (*pdu <= 30) { len = *pdu; pdu++; if (pdu + len > end) return FALSE; value = WSP_VALUE_TYPE_LONG; } else if (*pdu >= 128) { len = 1; value = WSP_VALUE_TYPE_SHORT; } else if (*pdu == 31) { pdu++; if (pdu == end) return FALSE; if (wsp_decode_uintvar(pdu, end - pdu, &len, &consumed) == FALSE) return FALSE; pdu += consumed; if (pdu + len > end) return FALSE; value = WSP_VALUE_TYPE_LONG; } else { if (decode_text_common(pdu, end - pdu, TRUE, FALSE, &len) == NULL) return FALSE; value = WSP_VALUE_TYPE_TEXT; } if (out_type) *out_type = value; if (out_value) *out_value = pdu; if (out_len) *out_len = len; if (out_read) *out_read = pdu - begin + len; return TRUE; } gboolean wsp_get_well_known_content_type(const char *text, unsigned int *out_val) { unsigned int i; for (i = 0; i < LAST_CONTENT_TYPE; i++) { if (g_str_equal(text, content_types[i]) == TRUE) { *out_val = i; return TRUE; } } return FALSE; } gboolean wsp_get_well_known_charset(const char *text, unsigned int *out_val) { unsigned int i; for (i = 0; charset_assignments[i].type_str != NULL; i++) { if (g_str_equal(charset_assignments[i].type_str, text) == TRUE) { *out_val = charset_assignments[i].type; return TRUE; } } return FALSE; } static const char *get_text_entry(unsigned int value, const struct wsp_hex_str_entry *table) { unsigned int i; for (i = 0; table[i].type_str != NULL; i++) { if (table[i].type == value) return table[i].type_str; } return NULL; } gboolean wsp_decode_content_type(const unsigned char *pdu, unsigned int max, const void **out_value, unsigned int *out_read, unsigned int *out_param_len) { unsigned int param_len = 0; unsigned int len; const void *data; enum wsp_value_type value_type; unsigned int consumed; if (wsp_decode_field(pdu, max, &value_type, &data, &len, &consumed) != TRUE) return FALSE; if (value_type == WSP_VALUE_TYPE_LONG) { unsigned int media_len; unsigned int value_len; if (wsp_decode_field(data, max, &value_type, &data, &value_len, &media_len) != TRUE) return FALSE; param_len = len - media_len; consumed -= param_len; /* Handle Well-Known-Media Long-Integer case */ if (value_type == WSP_VALUE_TYPE_LONG) { const unsigned char *pdu_val = data; unsigned int var = 0; unsigned int i; if (value_len > sizeof(unsigned int)) return FALSE; for (i = 0; i < value_len; i++) var = (var << 8) | pdu_val[i + 1]; data = get_text_entry(var, extension_mimetypes); } } if (value_type == WSP_VALUE_TYPE_SHORT) { const unsigned char *pdu_val = data; unsigned int val; val = *pdu_val & 0x7f; if (val >= LAST_CONTENT_TYPE) return FALSE; data = content_types[val]; } if (out_value) *out_value = data; if (out_read) *out_read = consumed; if (out_param_len) *out_param_len = param_len; return TRUE; } gboolean wsp_decode_application_id(struct wsp_header_iter *iter, const void **out_value) { const unsigned char *pdu_val = wsp_header_iter_get_val(iter); unsigned int val; unsigned int val_len; unsigned int i; switch (wsp_header_iter_get_val_type(iter)) { case WSP_VALUE_TYPE_TEXT: if (out_value) *out_value = pdu_val; break; /* * Well-known field values MUST be encoded using the * compact binary formats */ case WSP_VALUE_TYPE_SHORT: val = *pdu_val & 0x7f; if (out_value) *out_value = get_text_entry(val, app_id); break; case WSP_VALUE_TYPE_LONG: val_len = wsp_header_iter_get_val_len(iter); if (val_len > 2) return FALSE; for (i = 0, val = 0; i < val_len && i < sizeof(val); i++) val = (val << 8) | pdu_val[i]; if (out_value) *out_value = get_text_entry(val, app_id); break; } return TRUE; } static inline gboolean is_content_type_header(const unsigned char *pdu, unsigned char code_page, unsigned int flags) { /* Check for MMS Content-Type header */ if (flags & WSP_HEADER_ITER_FLAG_DETECT_MMS_MULTIPART) if (code_page == 1 && *pdu == 0x84) return TRUE; /* Check for WSP default Content-Type header */ if (code_page == 1 && *pdu == 0x91) return TRUE; return FALSE; } gboolean wsp_encode_uintvar(unsigned int value, unsigned char *dest, unsigned int dest_size, unsigned int *written) { unsigned char d[5]; unsigned int count = 0; /* Separate into 7-bit chunks, LS first */ while (value || !count) { d[count++] = value & 0x7F; value = value >> 7; } if (count > dest_size) return FALSE; *written = count; /* * Output to stream, MS first! * 0x80 flag = "continue". LS byte does not have this flag. */ while (--count) *dest++ = d[count] | 0x80; *dest = d[count]; return TRUE; } gboolean wsp_encode_value_length(unsigned int len, unsigned char *dest, unsigned int dest_size, unsigned int *written) { if (dest_size < 1) return FALSE; if (len <= 30) { *dest = len; *written = 1; return TRUE; } /* 31 is escape for variable length int */ *dest++ = 31; dest_size--; if (wsp_encode_uintvar(len, dest, dest_size, written) == FALSE) return FALSE; *written += 1; return TRUE; } gboolean wsp_encode_integer(unsigned int value, unsigned char *dest, unsigned int dest_size, unsigned int *written) { unsigned char moi[sizeof(unsigned int)]; unsigned int count; if (dest_size < 1) return FALSE; if (value < 0x80) { *dest = value | 0x80; *written = 1; return TRUE; } for (count = 0; count < sizeof(unsigned int) && value; count++) { moi[count] = value & 0xFF; value = value >> 8; } if (count + 1 > dest_size) return FALSE; *written = count + 1; *dest++ = count; for (; count > 0; count--) *dest++ = moi[count - 1]; return TRUE; } void wsp_header_iter_init(struct wsp_header_iter *iter, const unsigned char *pdu, unsigned int len, unsigned int flags) { iter->pdu = pdu; iter->pos = 0; iter->max = len; iter->code_page = 1; iter->flags = flags; } gboolean wsp_header_iter_next(struct wsp_header_iter *iter) { const unsigned char *pdu = iter->pdu + iter->pos; const unsigned char *end = iter->pdu + iter->max; enum wsp_header_type header; const void *hdr; unsigned int consumed; if (pdu == end) return FALSE; /* * 8.4.2.6 Header * The following rules are used to encode headers. * Header = Message-header | Shift-sequence * Shift-sequence = (Shift-delimiter Page-identity) | * Short-cut-shift-delimiter * Shift-delimiter = * Page-identity = * Short-cut-shift-delimiter = * Message-header = Well-known-header | Application-header * Well-known-header = Well-known-field-name Wap-value * Application-header = Token-text Application-specific-value * Well-known-field-name = Short-integer * Application-specific-value = Text-string */ while (*pdu == 127 || (*pdu >= 1 && *pdu <= 31)) { if (iter->flags & WSP_HEADER_ITER_FLAG_REJECT_CP) return FALSE; if (*pdu == 127) { pdu++; if (pdu == end) return FALSE; iter->code_page = *pdu; pdu++; } else iter->code_page = *pdu++; } if (pdu == end) return FALSE; if (*pdu >= 0x80) { if (is_content_type_header(pdu, iter->code_page, iter->flags)) return FALSE; header = WSP_HEADER_TYPE_WELL_KNOWN; hdr = pdu; pdu++; } else { if (wsp_decode_token_text(pdu, end - pdu, &consumed) == NULL) return FALSE; header = WSP_HEADER_TYPE_APPLICATION; hdr = pdu; pdu += consumed; } if (pdu == end) return FALSE; /* * Section 8.4.1.2 of WAP-230: * If the field name is encoded in text format, textual values MUST * be used. */ if ((*pdu < 32 || *pdu > 127) && header == WSP_HEADER_TYPE_APPLICATION) return FALSE; if (wsp_decode_field(pdu, end - pdu, &iter->value_type, &iter->value, &iter->len, &consumed) == FALSE) return FALSE; iter->header_type = header; iter->header = hdr; iter->pos = pdu + consumed - iter->pdu; return TRUE; } unsigned char wsp_header_iter_get_code_page(struct wsp_header_iter *iter) { return iter->code_page; } gboolean wsp_header_iter_at_end(struct wsp_header_iter *iter) { if (iter->pos == iter->max) return TRUE; return FALSE; } gboolean wsp_header_iter_is_multipart(struct wsp_header_iter *iter) { const unsigned char *pdu = iter->pdu + iter->pos; return is_content_type_header(pdu, iter->code_page, iter->flags); } enum wsp_header_type wsp_header_iter_get_hdr_type(struct wsp_header_iter *iter) { return iter->header_type; } const unsigned char *wsp_header_iter_get_pdu(struct wsp_header_iter *iter) { return iter->pdu; } const void *wsp_header_iter_get_hdr(struct wsp_header_iter *iter) { return iter->header; } enum wsp_value_type wsp_header_iter_get_val_type(struct wsp_header_iter *iter) { return iter->value_type; } const void *wsp_header_iter_get_val(struct wsp_header_iter *iter) { return iter->value; } unsigned int wsp_header_iter_get_val_len(struct wsp_header_iter *iter) { return iter->len; } gboolean wsp_multipart_iter_init(struct wsp_multipart_iter *mi, struct wsp_header_iter *hi, const void **out_content_type, unsigned int *out_content_type_len) { const unsigned char *pdu = hi->pdu + hi->pos; const unsigned char *end = hi->pdu + hi->max; unsigned int consumed; unsigned int ct_len; /* Assume content-type header is well known */ if (pdu + 1 > end) return FALSE; pdu++; /* Consume the Content-Type value of Content-Type header */ if (wsp_decode_field(pdu, end - pdu, NULL, NULL, NULL, &consumed) == FALSE) return FALSE; pdu += consumed; ct_len = consumed; /* * Consume the uinvar specifying the number of parts. This is set to * 0 in later specifications and can be safely ignored */ if (wsp_decode_uintvar(pdu, end - pdu, NULL, &consumed) == FALSE) return FALSE; memset(mi, 0, sizeof(*mi)); mi->pdu = hi->pdu + hi->pos; mi->max = hi->max - hi->pos; mi->pos = pdu + consumed - mi->pdu; if (out_content_type) *out_content_type = mi->pdu + 1; if (out_content_type_len) *out_content_type_len = ct_len; return TRUE; } gboolean wsp_multipart_iter_next(struct wsp_multipart_iter *mi) { const unsigned char *pdu = mi->pdu + mi->pos; const unsigned char *end = mi->pdu + mi->max; unsigned int headers_len; unsigned int body_len; unsigned int consumed; if (wsp_decode_uintvar(pdu, end - pdu, &headers_len, &consumed) == FALSE) return FALSE; pdu += consumed; if (wsp_decode_uintvar(pdu, end - pdu, &body_len, &consumed) == FALSE) return FALSE; pdu += consumed; if (pdu + headers_len + body_len > end) return FALSE; /* Consume the Content-Type value */ if (wsp_decode_field(pdu, end - pdu, NULL, NULL, NULL, &consumed) == FALSE) return FALSE; mi->content_type = pdu; mi->content_type_len = consumed; mi->headers = pdu + consumed; mi->headers_len = headers_len - consumed; mi->body = pdu + headers_len; mi->body_len = body_len; mi->pos = pdu - mi->pdu + headers_len + body_len; return TRUE; } const void *wsp_multipart_iter_get_content_type(struct wsp_multipart_iter *mi) { return mi->content_type; } unsigned int wsp_multipart_iter_get_content_type_len( struct wsp_multipart_iter *mi) { return mi->content_type_len; } const void *wsp_multipart_iter_get_hdr(struct wsp_multipart_iter *mi) { return mi->headers; } unsigned int wsp_multipart_iter_get_hdr_len(struct wsp_multipart_iter *mi) { return mi->headers_len; } const void *wsp_multipart_iter_get_body(struct wsp_multipart_iter *mi) { return mi->body; } unsigned int wsp_multipart_iter_get_body_len(struct wsp_multipart_iter *mi) { return mi->body_len; } gboolean wsp_multipart_iter_close(struct wsp_multipart_iter *mi, struct wsp_header_iter *hi) { if (mi->pos != mi->max) return FALSE; hi->pos += mi->pos; return TRUE; } void wsp_parameter_iter_init(struct wsp_parameter_iter *pi, const unsigned char *pdu, unsigned int len) { pi->pdu = pdu; pi->max = len; pi->pos = 0; } gboolean wsp_parameter_iter_next(struct wsp_parameter_iter *pi, struct wsp_parameter *out) { const unsigned char *pdu = pi->pdu + pi->pos; const unsigned char *end = pi->pdu + pi->max; unsigned int token; unsigned int consumed; const char *untyped; const char *value; /* Well known parameter token */ if (wsp_decode_integer(pdu, end - pdu, &token, &consumed) == TRUE) { pdu += consumed; switch (token) { case WSP_PARAMETER_TYPE_LEVEL: case WSP_PARAMETER_TYPE_DIFFERENCES: if (*pdu & 0x80) { unsigned int i = *pdu & 0x7f; pdu += 1; pi->pos = pdu - pi->pdu; if (out) { out->type = token; out->value = WSP_PARAMETER_VALUE_INT; out->integer = i; } return TRUE; } /* Fall through to the string case */ case WSP_PARAMETER_TYPE_NAME_DEFUNCT: case WSP_PARAMETER_TYPE_FILENAME_DEFUNCT: case WSP_PARAMETER_TYPE_START_DEFUNCT: case WSP_PARAMETER_TYPE_START_INFO_DEFUNCT: case WSP_PARAMETER_TYPE_COMMENT_DEFUNCT: case WSP_PARAMETER_TYPE_DOMAIN_DEFUNCT: case WSP_PARAMETER_TYPE_PATH_DEFUNCT: case WSP_PARAMETER_TYPE_NAME: case WSP_PARAMETER_TYPE_FILENAME: case WSP_PARAMETER_TYPE_START: case WSP_PARAMETER_TYPE_START_INFO: case WSP_PARAMETER_TYPE_COMMENT: case WSP_PARAMETER_TYPE_DOMAIN: case WSP_PARAMETER_TYPE_PATH: case WSP_PARAMETER_TYPE_MAC: { const char *text = wsp_decode_text(pdu, end - pdu, &consumed); if (text == NULL) return FALSE; pdu += consumed; pi->pos = pdu - pi->pdu; if (out) { out->type = token; out->value = WSP_PARAMETER_VALUE_TEXT; out->text = text; } return TRUE; } case WSP_PARAMETER_TYPE_TYPE: case WSP_PARAMETER_TYPE_SIZE: case WSP_PARAMETER_TYPE_MAX_AGE: { unsigned int i; if (wsp_decode_integer(pdu, end - pdu, &i, &consumed) == FALSE) return FALSE; pdu += consumed; pi->pos = pdu - pi->pdu; if (out) { out->type = token; out->value = WSP_PARAMETER_VALUE_INT; out->integer = i; } return TRUE; } case WSP_PARAMETER_TYPE_PADDING: case WSP_PARAMETER_TYPE_SEC: { if ((*pdu & 0x80) == 0) return FALSE; pdu += 1; pi->pos = pdu - pi->pdu; if (out) { out->type = token; out->value = WSP_PARAMETER_VALUE_INT; out->integer = *pdu & 0x7f; } return TRUE; } case WSP_PARAMETER_TYPE_CREATION_DATE: case WSP_PARAMETER_TYPE_MODIFICATION_DATE: case WSP_PARAMETER_TYPE_READ_DATE: { unsigned int i; if (wsp_decode_integer(pdu, end - pdu, &i, &consumed) == FALSE) return FALSE; pdu += consumed; pi->pos = pdu - pi->pdu; if (out) { out->type = token; out->value = WSP_PARAMETER_VALUE_DATE; out->integer = i; } return TRUE; } case WSP_PARAMETER_TYPE_SECURE: if (*pdu != 0) return FALSE; pdu += 1; pi->pos = pdu - pi->pdu; if (out) { out->type = token; out->value = WSP_PARAMETER_VALUE_TEXT; out->text = (const char *) pdu - 1; } return TRUE; case WSP_PARAMETER_TYPE_CHARSET: { unsigned int i; const char *charset; if (wsp_decode_integer(pdu, end - pdu, &i, &consumed) == FALSE) return FALSE; charset = get_text_entry(i, charset_assignments); if (charset == NULL) return FALSE; pdu += consumed; pi->pos = pdu - pi->pdu; if (out) { out->type = token; out->value = WSP_PARAMETER_VALUE_TEXT; out->text = charset; } return TRUE; } case WSP_PARAMETER_TYPE_CONTENT_TYPE: { const char *ct; if (*pdu & 0x80) { unsigned int i = *pdu & 0x7f; if (i >= LAST_CONTENT_TYPE) return FALSE; ct = content_types[i]; pdu += 1; } else if ((ct = wsp_decode_text(pdu, end - pdu, &consumed))) pdu += consumed; else return FALSE; pi->pos = pdu - pi->pdu; if (out) { out->type = token; out->value = WSP_PARAMETER_VALUE_TEXT; out->text = ct; } return TRUE; } /* TODO */ case WSP_PARAMETER_TYPE_Q: default: return FALSE; } } untyped = wsp_decode_text(pdu, end - pdu, &consumed); if (untyped == NULL) return FALSE; pdu += consumed; if (wsp_decode_integer(pdu, end - pdu, &token, &consumed) == TRUE) { pdu += consumed; pi->pos = pdu - pi->pdu; out->type = WSP_PARAMETER_TYPE_UNTYPED; out->value = WSP_PARAMETER_VALUE_INT; out->integer = token; out->untyped = untyped; return TRUE; } value = wsp_decode_text(pdu, end - pdu, &consumed); if (value == NULL) return FALSE; pdu += consumed; pi->pos = pdu - pi->pdu; out->type = WSP_PARAMETER_TYPE_UNTYPED; out->value = WSP_PARAMETER_VALUE_TEXT; out->text = value; out->untyped = untyped; return TRUE; } static const char *decode_token(char *buf, gboolean accept_quotes, unsigned int *out_consumed, char *out_terminator) { unsigned int pos = 0; unsigned int start; unsigned int end; char *endp; char terminator = '\0'; /* Skip leading space */ while (buf[pos] == ' ' || buf[pos] == '\t') pos += 1; if (buf[pos] == '\0') return NULL; start = pos; if (buf[pos] == '"') { if (accept_quotes == FALSE) return NULL; pos += 1; start = pos; endp = strchr(buf + pos, '"'); if (endp == NULL) return NULL; pos = endp - buf; end = pos; pos += 1; } else { endp = strpbrk(buf + pos, sep_chars); if (endp == NULL) pos = strlen(buf); else pos = endp - buf; end = pos; } while (buf[pos] == ' ' || buf[pos] == '\t') pos += 1; if (buf[pos] != '\0') { terminator = buf[pos]; pos += 1; } buf[end] = '\0'; if (strpbrk(buf + start, ctl_chars) != NULL) return NULL; *out_consumed = pos; *out_terminator = terminator; return buf + start; } gboolean wsp_text_header_iter_init(struct wsp_text_header_iter *iter, const char *hdr) { unsigned int len = strlen(hdr); char terminator; unsigned int consumed; const char *key; const char *value; if (len > MAX_TEXT_HEADER_SIZE) return FALSE; memcpy(iter->hdr, hdr, len); iter->hdr[len] = '\0'; iter->pos = 0; iter->key = NULL; iter->value = NULL; key = decode_token(iter->hdr, FALSE, &consumed, &terminator); if (key == NULL) return FALSE; if (terminator != ':') return FALSE; len = consumed; value = decode_token(iter->hdr + len, TRUE, &consumed, &terminator); if (value == NULL) return FALSE; if (terminator != '\0' && terminator != ';') return FALSE; len += consumed; iter->key = key; iter->value = value; iter->pos = len; return TRUE; } gboolean wsp_text_header_iter_param_next(struct wsp_text_header_iter *iter) { unsigned int pos = iter->pos; char terminator; unsigned int consumed; const char *key; const char *value; key = decode_token(iter->hdr + pos, FALSE, &consumed, &terminator); if (key == NULL) return FALSE; /* Empty value */ if (terminator == ';' || terminator == '\0') { iter->key = key; iter->value = NULL; iter->pos += consumed; return TRUE; } if (terminator != '=') return FALSE; pos += consumed; value = decode_token(iter->hdr + pos, TRUE, &consumed, &terminator); if (value == NULL) return FALSE; if (terminator != '\0' && terminator != ';') return FALSE; pos += consumed; iter->key = key; iter->value = value; iter->pos = pos; return TRUE; } const char *wsp_text_header_iter_get_key(struct wsp_text_header_iter *iter) { return iter->key; } const char *wsp_text_header_iter_get_value(struct wsp_text_header_iter *iter) { return iter->value; } mmsd-0.1/src/wsputil.h000066400000000000000000000160631216024552400147700ustar00rootroot00000000000000/* * * Multimedia Messaging Service * * Copyright (C) 2010-2011 Intel Corporation. All rights reserved. * * 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 * */ enum wsp_header_iter_flag { WSP_HEADER_ITER_FLAG_REJECT_CP = 0x1, WSP_HEADER_ITER_FLAG_DETECT_MMS_MULTIPART = 0x2, }; enum wsp_header_type { WSP_HEADER_TYPE_WELL_KNOWN, WSP_HEADER_TYPE_APPLICATION }; enum wsp_value_type { WSP_VALUE_TYPE_LONG, WSP_VALUE_TYPE_SHORT, WSP_VALUE_TYPE_TEXT, }; enum wsp_header_token { WSP_HEADER_TOKEN_APP_ID = 0x2F, }; enum wsp_parameter_type { WSP_PARAMETER_TYPE_Q = 0x00, WSP_PARAMETER_TYPE_CHARSET = 0x01, WSP_PARAMETER_TYPE_LEVEL = 0x02, WSP_PARAMETER_TYPE_TYPE = 0x03, WSP_PARAMETER_TYPE_NAME_DEFUNCT = 0x05, WSP_PARAMETER_TYPE_FILENAME_DEFUNCT = 0x06, WSP_PARAMETER_TYPE_DIFFERENCES = 0x07, WSP_PARAMETER_TYPE_PADDING = 0x08, WSP_PARAMETER_TYPE_CONTENT_TYPE = 0x09, WSP_PARAMETER_TYPE_START_DEFUNCT = 0x0A, WSP_PARAMETER_TYPE_START_INFO_DEFUNCT = 0x0B, WSP_PARAMETER_TYPE_COMMENT_DEFUNCT = 0x0C, WSP_PARAMETER_TYPE_DOMAIN_DEFUNCT = 0x0D, WSP_PARAMETER_TYPE_MAX_AGE = 0x0E, WSP_PARAMETER_TYPE_PATH_DEFUNCT = 0x0F, WSP_PARAMETER_TYPE_SECURE = 0x10, WSP_PARAMETER_TYPE_SEC = 0x11, WSP_PARAMETER_TYPE_MAC = 0x12, WSP_PARAMETER_TYPE_CREATION_DATE = 0x13, WSP_PARAMETER_TYPE_MODIFICATION_DATE = 0x14, WSP_PARAMETER_TYPE_READ_DATE = 0x15, WSP_PARAMETER_TYPE_SIZE = 0x16, WSP_PARAMETER_TYPE_NAME = 0x17, WSP_PARAMETER_TYPE_FILENAME = 0x18, WSP_PARAMETER_TYPE_START = 0x19, WSP_PARAMETER_TYPE_START_INFO = 0x1A, WSP_PARAMETER_TYPE_COMMENT = 0x1B, WSP_PARAMETER_TYPE_DOMAIN = 0x1C, WSP_PARAMETER_TYPE_PATH = 0x1D, WSP_PARAMETER_TYPE_UNTYPED = 0xFF, }; enum wsp_parameter_value { WSP_PARAMETER_VALUE_TEXT, WSP_PARAMETER_VALUE_INT, WSP_PARAMETER_VALUE_DATE, WSP_PARAMETER_VALUE_Q, }; struct wsp_parameter { enum wsp_parameter_type type; enum wsp_parameter_value value; const char *untyped; union { const char *text; unsigned int integer; time_t date; float q; }; }; struct wsp_header_iter { const unsigned char *pdu; unsigned int max; unsigned int pos; unsigned int flags; unsigned char code_page; enum wsp_header_type header_type; const void *header; enum wsp_value_type value_type; const void *value; unsigned int len; }; struct wsp_multipart_iter { const unsigned char *pdu; unsigned int max; unsigned int pos; const void *content_type; const void *headers; const void *body; unsigned int content_type_len; unsigned int headers_len; unsigned int body_len; }; struct wsp_parameter_iter { const unsigned char *pdu; unsigned int max; unsigned int pos; }; #define MAX_TEXT_HEADER_SIZE 4096 struct wsp_text_header_iter { char hdr[MAX_TEXT_HEADER_SIZE + 1]; unsigned int pos; const char *key; const char *value; }; gboolean wsp_decode_uintvar(const unsigned char *pdu, unsigned int len, unsigned int *out_len, unsigned int *consumed); gboolean wsp_decode_integer(const unsigned char *pdu, unsigned int len, unsigned int *out_val, unsigned int *consumed); gboolean wsp_decode_field(const unsigned char *pdu, unsigned int max, enum wsp_value_type *out_type, const void **out_value, unsigned int *out_len, unsigned int *consumed); gboolean wsp_get_well_known_content_type(const char *text, unsigned int *out_val); gboolean wsp_get_well_known_charset(const char *text, unsigned int *out_val); const char *wsp_decode_token_text(const unsigned char *pdu, unsigned int len, unsigned int *consumed); const char *wsp_decode_text(const unsigned char *pdu, unsigned int len, unsigned int *consumed); const char *wsp_decode_quoted_string(const unsigned char *pdu, unsigned int len, unsigned int *consumed); gboolean wsp_decode_content_type(const unsigned char *pdu, unsigned int max, const void **out_value, unsigned int *out_read, unsigned int *out_param_len); gboolean wsp_decode_application_id(struct wsp_header_iter *iter, const void **out_value); gboolean wsp_encode_uintvar(unsigned int value, unsigned char *dest, unsigned int dest_size, unsigned int *written); gboolean wsp_encode_value_length(unsigned int len, unsigned char *dest, unsigned int dest_size, unsigned int *written); gboolean wsp_encode_integer(unsigned int value, unsigned char *dest, unsigned int dest_size, unsigned int *written); void wsp_header_iter_init(struct wsp_header_iter *iter, const unsigned char *pdu, unsigned int len, unsigned int flags); gboolean wsp_header_iter_next(struct wsp_header_iter *iter); unsigned char wsp_header_iter_get_code_page(struct wsp_header_iter *iter); gboolean wsp_header_iter_at_end(struct wsp_header_iter *iter); gboolean wsp_header_iter_is_multipart(struct wsp_header_iter *iter); enum wsp_header_type wsp_header_iter_get_hdr_type(struct wsp_header_iter *iter); const unsigned char *wsp_header_iter_get_pdu(struct wsp_header_iter *iter); const void *wsp_header_iter_get_hdr(struct wsp_header_iter *iter); enum wsp_value_type wsp_header_iter_get_val_type(struct wsp_header_iter *iter); const void *wsp_header_iter_get_val(struct wsp_header_iter *iter); unsigned int wsp_header_iter_get_val_len(struct wsp_header_iter *iter); gboolean wsp_multipart_iter_init(struct wsp_multipart_iter *mi, struct wsp_header_iter *hi, const void **out_content_type, unsigned int *out_content_type_len); gboolean wsp_multipart_iter_next(struct wsp_multipart_iter *mi); const void *wsp_multipart_iter_get_content_type(struct wsp_multipart_iter *mi); unsigned int wsp_multipart_iter_get_content_type_len( struct wsp_multipart_iter *mi); const void *wsp_multipart_iter_get_hdr(struct wsp_multipart_iter *mi); unsigned int wsp_multipart_iter_get_hdr_len(struct wsp_multipart_iter *mi); const void *wsp_multipart_iter_get_body(struct wsp_multipart_iter *mi); unsigned int wsp_multipart_iter_get_body_len(struct wsp_multipart_iter *mi); gboolean wsp_multipart_iter_close(struct wsp_multipart_iter *mi, struct wsp_header_iter *hi); void wsp_parameter_iter_init(struct wsp_parameter_iter *pi, const unsigned char *pdu, unsigned int len); gboolean wsp_parameter_iter_next(struct wsp_parameter_iter *pi, struct wsp_parameter *out_param); gboolean wsp_text_header_iter_init(struct wsp_text_header_iter *iter, const char *hdr); gboolean wsp_text_header_iter_param_next(struct wsp_text_header_iter *iter); const char *wsp_text_header_iter_get_key(struct wsp_text_header_iter *iter); const char *wsp_text_header_iter_get_value(struct wsp_text_header_iter *iter); mmsd-0.1/test/000077500000000000000000000000001216024552400132725ustar00rootroot00000000000000mmsd-0.1/test/delete-message000077500000000000000000000004301216024552400161010ustar00rootroot00000000000000#!/usr/bin/python import sys import dbus if (len(sys.argv) < 2): print "Usage: %s " % (sys.argv[0]) sys.exit(1) bus = dbus.SessionBus() message = dbus.Interface(bus.get_object('org.ofono.mms', sys.argv[1]), 'org.ofono.mms.Message') message.Delete() mmsd-0.1/test/get-conversation000077500000000000000000000013211216024552400165040ustar00rootroot00000000000000#!/usr/bin/python import sys import dbus if (len(sys.argv) < 3): print "Usage: %s '' " % (sys.argv[0]) print "Sample: get-conversation '555-1234' 0" sys.exit(1) bus = dbus.SessionBus() manager = dbus.Interface(bus.get_object('org.ofono.mms', '/org/ofono/mms'), 'org.ofono.mms.Manager') services = manager.GetServices() path = services[0][0] service = dbus.Interface(bus.get_object('org.ofono.mms', path), 'org.ofono.mms.Service') messages = service.GetConversation(sys.argv[1], sys.argv[2]) for entry in messages: path = entry[0] properties = entry[1] print "[ %s ]" % (path) for key in properties.keys(): val = str(properties[key]) print " %s = %s" % (key, val) print mmsd-0.1/test/get-messages000077500000000000000000000010371216024552400156050ustar00rootroot00000000000000#!/usr/bin/python import dbus bus = dbus.SessionBus() manager = dbus.Interface(bus.get_object('org.ofono.mms', '/org/ofono/mms'), 'org.ofono.mms.Manager') services = manager.GetServices() path = services[0][0] service = dbus.Interface(bus.get_object('org.ofono.mms', path), 'org.ofono.mms.Service') messages = service.GetMessages() for entry in messages: path = entry[0] properties = entry[1] print "[ %s ]" % (path) for key in properties.keys(): val = str(properties[key]) print " %s = %s" % (key, val) print mmsd-0.1/test/get-services000077500000000000000000000006071216024552400156230ustar00rootroot00000000000000#!/usr/bin/python import dbus bus = dbus.SessionBus() manager = dbus.Interface(bus.get_object('org.ofono.mms', '/org/ofono/mms'), 'org.ofono.mms.Manager') services = manager.GetServices() for entry in services: path = entry[0] properties = entry[1] print "[ %s ]" % (path) for key in properties.keys(): val = str(properties[key]) print " %s = %s" % (key, val) print mmsd-0.1/test/list-services000077500000000000000000000010171216024552400160130ustar00rootroot00000000000000#!/usr/bin/python import dbus bus = dbus.SessionBus() manager = dbus.Interface(bus.get_object('org.ofono.mms', '/org/ofono/mms'), 'org.ofono.mms.Manager') services = manager.GetServices() for path, properties in services: print "[ %s ]" % (path) object = dbus.Interface(bus.get_object('org.ofono.mms', path), 'org.ofono.mms.Service') try: properties = object.GetProperties() except: continue for key in properties.keys(): val = str(properties[key]) print " %s = %s" % (key, val) print mmsd-0.1/test/mark-message-read000077500000000000000000000004321216024552400165040ustar00rootroot00000000000000#!/usr/bin/python import sys import dbus if (len(sys.argv) < 2): print "Usage: %s " % (sys.argv[0]) sys.exit(1) bus = dbus.SessionBus() message = dbus.Interface(bus.get_object('org.ofono.mms', sys.argv[1]), 'org.ofono.mms.Message') message.MarkRead() mmsd-0.1/test/monitor-mms000077500000000000000000000045101216024552400155010ustar00rootroot00000000000000#!/usr/bin/python import gobject import dbus import dbus.mainloop.glib def property_changed(name, value, path, interface): iface = interface[interface.rfind(".") + 1:] print "{%s} [%s] %s = %s" % (iface, path, name, value) def service_added(name, value, member, path, interface): iface = interface[interface.rfind(".") + 1:] print "{%s} [%s] %s %s" % (iface, name, member, value) def service_removed(name, member, path, interface): iface = interface[interface.rfind(".") + 1:] print "{%s} [%s] %s" % (iface, name, member) def message_added(name, value, member, path, interface): iface = interface[interface.rfind(".") + 1:] print "{%s} [%s] %s %s" % (iface, name, member, value) def message_removed(name, value, member, path, interface): iface = interface[interface.rfind(".") + 1:] print "{%s} [%s] %s %s" % (iface, name, member, value) def property_changed(name, value, member, path, interface): iface = interface[interface.rfind(".") + 1:] print "{%s} [%s] %s %s" % (iface, name, member, value) if __name__ == '__main__': dbus.mainloop.glib.DBusGMainLoop(set_as_default=True) bus = dbus.SessionBus() bus.add_signal_receiver(property_changed, bus_name="org.ofono.mms", signal_name = "PropertyChanged", path_keyword="path", interface_keyword="interface") bus.add_signal_receiver(service_added, bus_name="org.ofono.mms", signal_name = "ServiceAdded", member_keyword="member", path_keyword="path", interface_keyword="interface") bus.add_signal_receiver(service_removed, bus_name="org.ofono.mms", signal_name = "ServiceRemoved", member_keyword="member", path_keyword="path", interface_keyword="interface") bus.add_signal_receiver(message_added, bus_name="org.ofono.mms", signal_name = "MessageAdded", member_keyword="member", path_keyword="path", interface_keyword="interface") bus.add_signal_receiver(message_removed, bus_name="org.ofono.mms", signal_name = "MessageRemoved", member_keyword="member", path_keyword="path", interface_keyword="interface") bus.add_signal_receiver(property_changed, bus_name="org.ofono.mms", signal_name = "PropertyChanged", member_keyword="member", path_keyword="path", interface_keyword="interface") mainloop = gobject.MainLoop() mainloop.run() mmsd-0.1/test/send-message000077500000000000000000000031031216024552400155700ustar00rootroot00000000000000#!/usr/bin/python import sys import dbus import csv if (len(sys.argv) < 4): print "Usage: %s"\ " ,..."\ " "\ " <,,>,..."\ % (sys.argv[0]) print "Sample(Related): %s"\ " \"+33611111111,+33622222222\""\ " \"smil.txt\""\ " \"cid-1,text/plain,text.txt\""\ " \"cid-2,image/jpeg,image.jpg\""\ % (sys.argv[0]) print "Sample(Mixed): %s"\ " \"+33611111111,+33622222222\""\ " \"\""\ " \"cid-1,text/plain,text.txt\""\ " \"cid-2,image/jpeg,image.jpg\""\ % (sys.argv[0]) sys.exit(1) bus = dbus.SessionBus() manager = dbus.Interface(bus.get_object('org.ofono.mms', '/org/ofono/mms'), 'org.ofono.mms.Manager') services = manager.GetServices() path = services[0][0] service = dbus.Interface(bus.get_object('org.ofono.mms', path), 'org.ofono.mms.Service') recipients = dbus.Array([],signature=dbus.Signature('s')) reader = csv.reader([sys.argv[1]]) for r in reader: print "Recipient list: %s" % r for i in r: recipients.append(dbus.String(i)) if sys.argv[2] == "": print "Send MMS as Mixed" smil = "" else: print "Send MMS as Related" print "Smil path: %s" % (sys.argv[2]) file = open(sys.argv[2],"r") smil = dbus.String(file.read()) attachments = dbus.Array([],signature=dbus.Signature('(sss)')) for a in sys.argv[3:]: print "Attachment: (%s)" % a reader = csv.reader([a]) for r in reader: attachments.append(dbus.Struct((dbus.String(r[0]), dbus.String(r[1]), dbus.String(r[2]) ), signature=None)) path = service.SendMessage(recipients, smil, attachments) print path mmsd-0.1/test/set-use-delivery-reports000077500000000000000000000010661216024552400201250ustar00rootroot00000000000000#!/usr/bin/python import sys import dbus bus = dbus.SessionBus() manager = dbus.Interface(bus.get_object('org.ofono.mms', '/org/ofono/mms'), 'org.ofono.mms.Manager') services = manager.GetServices() path = services[0][0] service = dbus.Interface(bus.get_object('org.ofono.mms', path), 'org.ofono.mms.Service') if len(sys.argv) > 1: allowed = dbus.Boolean(int(sys.argv[1])) else: allowed = dbus.Boolean(1) print "Setting delivery report use for %s...(UseDeliveryReports=%d)" %\ (path, allowed) service.SetProperty("UseDeliveryReports", allowed) mmsd-0.1/tools/000077500000000000000000000000001216024552400134535ustar00rootroot00000000000000mmsd-0.1/tools/create-hex-array.c000066400000000000000000000033101216024552400167550ustar00rootroot00000000000000/* * * Multimedia Messaging Service * * Copyright (C) 2010-2011 Intel Corporation. All rights reserved. * * 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 static int dump_file(const char *pathname) { struct stat st; unsigned char *map; size_t size, i; int fd; fd = open(pathname, O_RDONLY); if (fd < 0) return -1; if (fstat(fd, &st) < 0) { close(fd); return -1; } size = st.st_size; map = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0); if (!map || map == MAP_FAILED) { close(fd); return -1; } printf("static const unsigned char msg[] = {"); for (i = 0; i < size; i++) { if ((i % 8) == 0) printf("\n\t\t\t\t"); printf("0x%02X, ", map[i]); } printf("\n};\n"); munmap(map, size); close(fd); return 0; } int main(int argc, char *argv[]) { if (argc < 2) { fprintf(stderr, "Missing input\n"); return 1; } if (dump_file(argv[1]) < 0) { fprintf(stderr, "Failed to open file\n"); return 1; } return 0; } mmsd-0.1/tools/decode-mms.c000066400000000000000000000221661216024552400156430ustar00rootroot00000000000000/* * * Multimedia Messaging Service * * Copyright (C) 2010-2011 Intel Corporation. All rights reserved. * * 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 "wsputil.h" static const unsigned char mms_msg1[] = { 0x8C, 0x82, 0x98, 0x4F, 0x67, 0x51, 0x4B, 0x4B, 0x42, 0x00, 0x8D, 0x90, 0x89, 0x08, 0x80, 0x45, 0x72, 0x6F, 0x74, 0x69, 0x6B, 0x00, 0x96, 0x50, 0x69, 0x6E, 0x2D, 0x55, 0x70, 0x73, 0x00, 0x8A, 0x80, 0x8E, 0x02, 0x40, 0x00, 0x88, 0x05, 0x81, 0x03, 0x03, 0xF4, 0x80, 0x83, 0x68, 0x74, 0x74, 0x70, 0x3A, 0x2F, 0x2F, 0x65, 0x70, 0x73, 0x33, 0x2E, 0x64, 0x65, 0x2F, 0x4F, 0x2F, 0x5A, 0x39, 0x49, 0x5A, 0x4F, 0x00 }; #define MMS_HDR_BCC 0x01 #define MMS_HDR_CC 0x02 #define MMS_HDR_CONTENT_LOCATION 0x03 #define MMS_HDR_CONTENT_TYPE 0x04 #define MMS_HDR_DATE 0x05 #define MMS_HDR_DELIVERY_REPORT 0x06 #define MMS_HDR_DELIVERY_TIME 0x07 #define MMS_HDR_EXPIRY 0x08 #define MMS_HDR_FROM 0x09 #define MMS_HDR_MESSAGE_CLASS 0x0a #define MMS_HDR_MESSAGE_ID 0x0b #define MMS_HDR_MESSAGE_TYPE 0x0c #define MMS_HDR_MMS_VERSION 0x0d #define MMS_HDR_MESSAGE_SIZE 0x0e #define MMS_HDR_PRIORITY 0x0f #define MMS_HDR_READ_REPLY 0x10 #define MMS_HDR_REPORT_ALLOWED 0x11 #define MMS_HDR_RESPONSE_STATUS 0x12 #define MMS_HDR_RESPONSE_TEXT 0x13 #define MMS_HDR_SENDER_VISIBILITY 0x14 #define MMS_HDR_STATUS 0x15 #define MMS_HDR_SUBJECT 0x16 #define MMS_HDR_TO 0x17 #define MMS_HDR_TRANSACTION_ID 0x18 static const char *mms_header[] = { NULL, "Bcc", "Cc", "X-Mms-Content-Location", "Content-Type", "Date", "X-Mms-Delivery-Report", "X-Mms-Delivery-Time", "X-Mms-Expiry", "From", "X-Mms-Message-Class", "Message-ID", "X-Mms-Message-Type", "X-Mms-MMS-Version", "X-Mms-Message-Size", "X-Mms-Priority", "X-Mms-Read-Reply", "X-Mms-Report-Allowed", "X-Mms-Response-Status", "X-Mms-Response-Text", "X-Mms-Sender-Visibility", "X-Mms-Status", "Subject", "To", "X-Mms-Transaction-Id", }; static const char *wap_header[] = { "Accept", "Accept-Charset (deprecated)", "Accept-Encoding (deprecated)", "Accept-Language", "Accept-Ranges", "Age", "Allow", "Authorization", "Cache-Control (deprecated)", "Connection", "Content-Base (deprecated)", "Content-Encoding", "Content-Language", "Content-Length", "Content-Location", "Content-MD5", "Content-Range (deprecated)", "Content-Type", "Date", "Etag", "Expires", "From", "Host", "If-Modified-Since", "If-Match", "If-None-Match", "If-Range", "If-Unmodified-Since", "Location", "Last-Modified", "Max-Forwards", "Pragma", "Proxy-Authenticate", "Proxy-Authorization", "Public", "Range", "Referer", "Retry-After", "Server", "Transfer-Encoding", "Upgrade", "User-Agent", "Vary", "Via", "Warning", "WWW-Authenticate", "Content-Disposition (deprecated)", "X-Wap-Application-Id", "X-Wap-Content-URI", "X-Wap-Initiator-URI", "Accept-Application", "Bearer-Indication", "Push-Flag", "Profile", "Profile-Diff", "Profile-Warning (deprecated)", "Expect", "TE", "Trailer", "Accept-Charset", "Accept-Encoding", "Cache-Control (deprecated)", "Content-Range", "X-Wap-Tod", "Content-ID", "Set-Cookie", "Cookie", "Encoding-Version", "Profile-Warning", "Content-Disposition", "X-WAP-Security", "Cache-Control", }; static const char *wap_param[] = { "Q", "Charset", "Level", "Type", "Unused", "Name (deprecated)", "Filename (deprecated)", "Differences", "Padding", "Type (multipart)", "Start (deprecated)", "Start-Info (deprecated)", "Comment (deprecated)", "Domain (deprecated)", "Max-Age", "Path (deprecated)", "Secure", "SEC", "MAC", "Creatione-Date", "Modification-Date", "Read-Date", "Size", "Name", "Filename", "Start", "Start-Info", "Comment", "Domain", "Path", }; static void decode_headers(struct wsp_header_iter *iter, const char *header_lut[], const char *prefix) { while (wsp_header_iter_next(iter)) { const unsigned char *hdr = wsp_header_iter_get_hdr(iter); const unsigned char *val = wsp_header_iter_get_val(iter); enum wsp_value_type type = wsp_header_iter_get_val_type(iter); unsigned int len, i; switch (wsp_header_iter_get_hdr_type(iter)) { case WSP_HEADER_TYPE_WELL_KNOWN: printf("%s%s: ", prefix, header_lut[hdr[0] & 0x7f]); break; case WSP_HEADER_TYPE_APPLICATION: printf("%s%s: ", prefix, (const char *) hdr); break; } len = wsp_header_iter_get_val_len(iter); switch (type) { case WSP_VALUE_TYPE_TEXT: printf("%s", (const char *) val); break; default: for (i = 0; i < len; i++) printf("%02x ", val[i]); printf("(len %d, ", len); if (type == WSP_VALUE_TYPE_SHORT) printf("Short)"); else printf("Long)"); break; } printf("\n"); } } static void decode_parameters(const unsigned char *pdu, unsigned int len, const char *prefix) { struct wsp_parameter_iter iter; struct wsp_parameter param; char buf[128]; printf("%sParameter Size: %d\n", prefix, len); wsp_parameter_iter_init(&iter, pdu, len); while (wsp_parameter_iter_next(&iter, ¶m)) { if (param.type == WSP_PARAMETER_TYPE_UNTYPED) printf("%s%s: ", prefix, param.untyped); else printf("%s%s: ", prefix, wap_param[param.type]); switch (param.value) { case WSP_PARAMETER_VALUE_TEXT: printf("%s\n", param.text); break; case WSP_PARAMETER_VALUE_INT: printf("%u\n", param.integer); break; case WSP_PARAMETER_VALUE_DATE: strftime(buf, 127, "%Y-%m-%dT%H:%M:%S%z", localtime(¶m.date)); buf[127] = '\0'; printf("%s\n", buf); break; case WSP_PARAMETER_VALUE_Q: printf("\n"); } } printf("\n"); } static void decode_message(const unsigned char *data, unsigned int size) { struct wsp_header_iter iter; struct wsp_multipart_iter mi; unsigned int flags = 0; const void *ct; unsigned int ct_len; const void *multipart_mimetype; unsigned int consumed; flags |= WSP_HEADER_ITER_FLAG_REJECT_CP; flags |= WSP_HEADER_ITER_FLAG_DETECT_MMS_MULTIPART; wsp_header_iter_init(&iter, data, size, flags); decode_headers(&iter, mms_header, ""); if (wsp_header_iter_at_end(&iter) == TRUE) goto done; if (wsp_header_iter_is_multipart(&iter) == FALSE) { printf("Not a multipart header, but not at the end" "... something is wrong\n"); return; } if (wsp_multipart_iter_init(&mi, &iter, &ct, &ct_len) == FALSE) { printf("multipart_iter_init failed\n"); return; } if (wsp_decode_content_type(ct, ct_len, &multipart_mimetype, &consumed, NULL) == FALSE) { printf("Unable to decode multipart-mimetype\n"); return; } printf("Content-Type: %s\n", (const char *) multipart_mimetype); decode_parameters(ct + consumed, ct_len - consumed, "\t"); while (wsp_multipart_iter_next(&mi) == TRUE) { struct wsp_header_iter hi; ct = wsp_multipart_iter_get_content_type(&mi); ct_len = wsp_multipart_iter_get_content_type_len(&mi); if (wsp_decode_content_type(ct, ct_len, &multipart_mimetype, &consumed, NULL) == FALSE) { printf("Unable to decode part multipart-mimetype\n"); return; } printf("\tContent-Type: %s\n", (const char *) multipart_mimetype); decode_parameters(ct + consumed, ct_len - consumed, "\t\t"); printf("\tHeader Size: %d\n", wsp_multipart_iter_get_hdr_len(&mi)); wsp_header_iter_init(&hi, wsp_multipart_iter_get_hdr(&mi), wsp_multipart_iter_get_hdr_len(&mi), 0); decode_headers(&hi, wap_header, "\t"); if (wsp_header_iter_at_end(&hi) == FALSE) { printf("Unable to fully decode part headers\n"); return; } printf("\tBody Size: %d\n\n", wsp_multipart_iter_get_body_len(&mi)); } if (wsp_multipart_iter_close(&mi, &iter) == FALSE) { printf("Unable to close multipart iter\n"); return; } if (wsp_header_iter_at_end(&iter) == FALSE) { printf("Didn't consume the entire message\n"); return; } done: printf("Done\n"); } static int open_file(const char *pathname) { struct stat st; unsigned char *map; size_t size; int fd; fd = open(pathname, O_RDONLY); if (fd < 0) return -1; if (fstat(fd, &st) < 0) { close(fd); return -1; } size = st.st_size; map = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0); if (!map || map == MAP_FAILED) { close(fd); return -1; } decode_message(map, size); munmap(map, size); close(fd); return 0; } int main(int argc, char *argv[]) { if (argc < 2) { decode_message(mms_msg1, sizeof(mms_msg1)); return 0; } if (open_file(argv[1]) < 0) { fprintf(stderr, "Failed to open file\n"); return 1; } return 0; } mmsd-0.1/unit/000077500000000000000000000000001216024552400132725ustar00rootroot00000000000000mmsd-0.1/unit/ni-mms-1-3-con-271.mms000066400000000000000000000001711216024552400165710ustar00rootroot00000000000000OgQKKB+44123989100/TYPE=PLMN*abcdefghijklmnopqrstuvwxyz0123456789/-+@Z http://eps3.de/O/Z9IZOmmsd-0.1/unit/ni-mms-1-3-con-272.mms000066400000000000000000000002521216024552400165720ustar00rootroot00000000000000OgQKKB+44123989100/TYPE=PLMNMMS-1.3-con-272Zhttp://abcdefghi/abcdefghi/abcdefghi/abcdefghi/abcdefghi/abcdefghi/abcdefghi/abcdefghi/abcdefghi.mmsmmsd-0.1/unit/rc-mms-1-3-con-210.mms000066400000000000000000000053171216024552400165670ustar00rootroot0000000000000000000000210MMMS-1.3-con-210+33611111111/TYPE=PLMN+33622222222/TYPE=PLMNapplication/smil1application/smil" _%Long_file_name_for_gif_image_60X80_with_non_ASCII_characters_ooo_Length_is_93_characters.gifGIF87a<PTU[閚z)՛-x'RV[_r*΍|||Q (efh,JTx(qG3 0LhV'.֣oopm Ѣ܀زsbab ,<PsH*\XP Ç#&t(E+A0Á>\,Ha)cKq ̨P!A  H@fV,i ΁9Nc`Ƌ?G29vI(H`ɓHun$!H#{"0rVK!&@>0X9%X'S?B@j0Cԫ'FР q$T2( Ơq2/3G0qo,vp\ g&ް; '>.{?]駧j߻2xE$u~?N^;|-_ν U*ϼRJp=@:+Hf Oq'M| Zp@OphOc,΁$'P:h\PqAԠy# HwjP#P:!^ 2ӽ[ahIR?u0x; ../G,@Mq‚7NxAx`h@i,$Hޱэ4HwxbXLNQf}F `>SsA;JP:3r*^n%hX]+Zn 3I)8Aln\eH߱'`$gf]ʘl Ik6Z5ql'@xN.5Ay-|сNd E~4"5Ix.J+aK](Ӱ8ͩNwS\@ P˕ 5GMꔪԢ25BN}*Z*U,=XͪVծv5 ;mmsd-0.1/unit/rc-mms-1-3-con-211.mms000066400000000000000000000010401216024552400165550ustar00rootroot0000000000000000000000211M;Shõrt Téxt - ¥üëäÿ+33622222222/TYPE=PLMN+33666565565/TYPE=PLMNapplication/smil+1application/smil"/tmp/php86myoT )"/tmp/php0nDmnYHello Worldmmsd-0.1/unit/rc-mms-1-3-con-212.mms000066400000000000000000000003061216024552400165620ustar00rootroot0000000000000000000000212M1MMS-1.3-con-212+33622222222/TYPE=PLMN+33666565565/TYPE=PLMN*;"/tmp/phpnjgFBVThe quick brown fox jumped over the lazy dog 1234567890/!()mmsd-0.1/unit/rc-mms-1-3-con-213.mms000066400000000000000000000003711216024552400165650ustar00rootroot0000000000000000000000213MMMS-1.3-con-213+33622222222/TYPE=PLMN+33666565565/TYPE=PLMN'q"/tmp/phpNuduZOFrench ê has a roof over the e. German ü is a u with two dots. Portuguese ã is an a with a ~ (tilde) above.mmsd-0.1/unit/rc-mms-1-3-con-214.mms000066400000000000000000000005441216024552400165700ustar00rootroot0000000000000000000000214MlMMS-1.3-con-214+33622222222/TYPE=PLMN+33666565565/TYPE=PLMN*X"/tmp/phpZ198xNFrench has a roof over the e. German is a u with two dots. Portuguese is an a with a ~ (tilde) above.mmsd-0.1/unit/rc-mms-1-3-con-216.mms000066400000000000000000000262641216024552400166010ustar00rootroot0000000000000000000000216MܹMMS-1.3-con-216+33622222222/TYPE=PLMN+33666565565/TYPE=PLMNapplication/smil+.application/smil"/tmp/phpBWSFz1 %6"/tmp/phpzfDes9JFIFddDucky< XICC_PROFILE HLinomntrRGB XYZ  1acspMSFTIEC sRGB-HP cprtP3desclwtptbkptrXYZgXYZ,bXYZ@dmndTpdmddvuedLview$lumimeas $tech0 rTRC< gTRC< bTRC< textCopyright (c) 1998 Hewlett-Packard CompanydescsRGB IEC61966-2.1sRGB IEC61966-2.1XYZ QXYZ XYZ o8XYZ bXYZ $descIEC http://www.iec.chIEC http://www.iec.chdesc.IEC 61966-2.1 Default RGB colour space - sRGB.IEC 61966-2.1 Default RGB colour space - sRGBdesc,Reference Viewing Condition in IEC61966-2.1,Reference Viewing Condition in IEC61966-2.1view_. \XYZ L VPWmeassig CRT curv #(-27;@EJOTY^chmrw| %+28>ELRY`gnu| &/8AKT]gqz !-8COZfr~ -;HUcq~ +:IXgw'7HYj{+=Oat 2FZn  % : O d y  ' = T j " 9 Q i  * C \ u & @ Z t .Id %A^z &Ca~1Om&Ed#Cc'Ij4Vx&IlAe@e Ek*Qw;c*R{Gp@j>i  A l !!H!u!!!"'"U"""# #8#f###$$M$|$$% %8%h%%%&'&W&&&''I'z''( (?(q(())8)k))**5*h**++6+i++,,9,n,,- -A-v--..L.../$/Z///050l0011J1112*2c223 3F3334+4e4455M555676r667$7`7788P8899B999:6:t::;-;k;;<' >`>>?!?a??@#@d@@A)AjAAB0BrBBC:C}CDDGDDEEUEEF"FgFFG5G{GHHKHHIIcIIJ7J}JK KSKKL*LrLMMJMMN%NnNOOIOOP'PqPQQPQQR1R|RSS_SSTBTTU(UuUVV\VVWDWWX/X}XYYiYZZVZZ[E[[\5\\]']x]^^l^__a_``W``aOaabIbbcCccd@dde=eef=ffg=ggh?hhiCiijHjjkOkklWlmm`mnnknooxop+ppq:qqrKrss]sttptu(uuv>vvwVwxxnxy*yyzFz{{c{|!||}A}~~b~#G k͂0WGrׇ;iΉ3dʋ0cʍ1fΏ6n֑?zM _ɖ4 uL$h՛BdҞ@iءG&vVǥ8nRĩ7u\ЭD-u`ֲK³8%yhYѹJº;.! zpg_XQKFAǿ=ȼ:ɹ8ʷ6˶5̵5͵6ζ7ϸ9к<Ѿ?DINU\dlvۀ܊ݖޢ)߯6DScs 2F[p(@Xr4Pm8Ww)KmAdobed       x!1AQ"aq2B#Rb3$r!1AQaq"B2Ⴂ# ?Y,2Ą --'ɍ& d7H>#DL!B%?-bx)ySDioHލݭ}sq 6֞* V{]b}I{n[oI .)RN1#K~Yz*_̇%>B:y҆*ܠwqY}_˸ ^Kǂ] hj*Tu;,!R6 +rXFєtHۮ ~,};I$Irzꅲu)e0c 5eg12\U^3PLdVb麹;={$>| rb]q EFG4?HF4Sc?]YlvBFV$Xfi-9zZO80 J[e1-h. S~Œ¶;cW-{飲YAn_gf5S72cy'x}|VK5u {3uvpg$Yn&X;:z[_YUUj}ڝo^Ǽ9^m̼h~l ͩz Lv N, _)nJ2}بk[zLcDjp.hηLS ;Q*5iDHs4y*ox+ZœƵ?;ov+oĊks!h6P5|AXam2+@AYP4=VS}5i=%k+j Іg/1![ ^; $(-ơKKy&'4mʾ"Y;>ޏټe˓%׹X66_6#}oݡSb Ahk+!G1FB $^?ʔ :n=k;˓k*5ҁin| oem<6'?k6P)fH[F:rT9Z̛4tD Hޕ>*Wc~2\Ͷg1yJ2ӋDo_SE.t[Վ:+oY~kZM=5 #qA"*HlݜY9_a*7:.;qrN"jiBKPk+/ 8;v` 2akOX2rG/QA2l]H$=\Q X"B_{71RE%Cj UÓ )ldH˥h v4祱5%!M "H FS ^Lj ,u rK~R1ѧIbkZ )Yo-ҶU{%0'ywBsp >cop( |nu쪥O?P>~߻cebG,h}ji[D#Ycf؁ݡBܗWrl&i鲛w;aw}"+lz;N[4DX$' V^=PuoC*#qR):a#,(8 òo~v`t\4}TJ*mx"Neqm}HyUkb}|W<aAR?Qk[Iǽ!j~r==U~C^`h.RS{3mEDWb@ceq& -Ae^.ŎGk3ȅaZж y";轜J ex#TKsK\XEdT?YMI/ϴn1wow{z4lYb`C Wޙܳq2Pc- -Ucɷ$&[XSg3׏?gNsO4(Aվ;C͉=~ K.%*;9%V?,pƛlkV'Yoto-Z2<BIg0 oR*m@O6bOӜǓQ9/ ŖBMK(5龽[lq%onWFs$II>%ru/nT{eS#ylB>j-<+0VK{fiIpnE OЃ|t#|Z+^I,A< X\D$֛k&ƧA;{)0ql5/QjD]HLt&ZDMD*E1WcQ/'"һ׮nj)d-T̃} ACJ~gMnꮀe_t'] 99+ bP$-yԈ}GִL8FWg _W_2+QA?uS5DpuqQR/r$b v5d0RqIcVltZTEisA:- h}#LbʞP +\Z)VZoOI_?:h0JPG3޸KVy< '.S֪),81!y3#)U|tnTf{{;ʢr+*=M_嶛i )?f[nUq٧6_hZǖS )>V3WpGO&-bˊEU  m ثG&n?n%+Jz7[[ljF}*4#$T~ڮqg3Y&ŀ ky<@'^pi-P5[k$n$r-1>U\-U l"Ŭ~ľʹVRSu*W}NAN$:Fg5`A tkUUVͷ,r oZ&XD(TԂj}FCZ uu-*pV4WbVb}HOIb[UI!뙣V*TbixʒڮVe fIytg2 ai tQ4%'ˬ[l =dqeC~ "]YYJz3tP^".S?w. *1ѓhes!I)R||:v/ftÆ'/tq++%ҎK5'בr|V-v@rkDVpʾ1c5+^;ZRc*u,nu[2{N[ʐM"Ո>`}=A;sa^eR=;`vlM3UʙcIG@F s؏?-\&Fn1I0M#A]"!/yf0)Vhv~Z_[o e# ؚ%gj")2Ux>]>7ʹٿ@ Y\m6E珷c OW4i}0]ߵV!uMeحcS$pnEUfZ)7:WlNz {3).{%J-8PIt#0O Wʰ޻)/Ol|礻4 ȋ{ZFwƦ6[?kdž+g.f#qf(ݑymzL]|kH8szŵČŊ&§iRPgj;x0JWpC#s.oޫZ8 /2#?:oTrb|KBci}4+*$S:ɅdQϕ~SRǓ( &4OtjkˬͫBh[)]ur$3bj$;k!4ÖfHNfyE^w'cOXOX3WSY#sdbŠO@~z_6;eĞu2wqCm9;e'{Y-%Pu*]vlqs`f(+s*<Z>LUd(Y\D) ÕE4oӧNcš|xи1oj;|A-ytlw'[T9ckDLJmmsd-0.1/unit/rc-mms-1-3-con-220.mms000066400000000000000000000105541216024552400165670ustar00rootroot0000000000000000000000220M?MMS-1.3-con-220+33622222222/TYPE=PLMN+33666565565/TYPE=PLMNapplication/smil+1application/smil"/tmp/phpLd6N8p (h"/tmp/phpYseMUMGIF87ax蕙uuuRmPV'%$N+DDD\we`'.1 f_3pJr1/rt"Cg{OgXe:]A&N8(z<&-N}sbab ,xH*\ȰÇ#J<(pŋ3jȰ"Ǐ Cqɓ(GLɲK+_LX͛8sɳgMg EX⃎H*]ʴӧ'$]$TA|TX@Қ}Xӷp:cª!zևذ^wU*fVs%4WC^v(Xb[ȨS;nB"V[3ymZOa@ z(_#AHLKvf܈Z 3_ϾG SwvHʻ6@Xz%\cp'_kme'VoiqUh ɭ\q$t >8_)MAheؕVvr^ *e{ 5+Z~WVg3a5P AA̡TeԑHB؀m(waqS %Ef5Uەa`r x|&'#%:`gQIra4*_Q.%Dzj:ZK זur=I 8P;dj\Ԩ,͚(K ,$EEAFiYmFgԃB;p),r)k|Jp %A To Ep8 *'pYLs71 \jCϦܣq]tqIj͗-}+h6tO RD51:+)洮b2~m|'70c'@b)Dh&P.ÁLcdE1_He*{իPrixM1o x*[钃%u6CR3 yTv)f Kd{ݜY9:j0#&#MŲPd>~1gtTR i⺩dn/:RK0t=C8n+$ 䒊tBO~HTDfxLrzxtNW.R.6%HsīUj &l6Yrj <.:_&!2$D#Ȱ UG+O@]j*8 3 [|3${;Vp(ݬ[/tYHVU qdW>Ȑl8ⷷ& gcK]D'r5&wY](4mGD KB82=BwBӎ B;$Տ .W`ʊg'$[R!%{S{_*aMО3< @U1>pAֳtGfuO=%0H*/sw+NoGeZ6WbF"҉M܁%; .a =Ɉ2QU+L(ؙ7iCkѦ?Zr#VU9xѦI<;Ϋ%Uu[b2AJhZVuVkN0 H0lbv ^3ήQڛvnmrNwl͸p[>uj@z޷& !ye>$W|w;4wM厵5nVG3]0q T\@:e8 PԧF‰pn]C/ѓ`gᒀַ~^vdv8tC:q3D)8ˍoA5KxVD'tAF;.4~Wz(W[r3/o{o킠))O왿ftk??s &;WOt{O>8t}~z'yg~1vhTu$gFu wgy~3#0gC.Hh.4,d{3yz} w%#AHw9HG&)+@~I=L؄vozGCx0ZȅI8<_2w=X/7h#|mQq 0 }dggxV}+8I0`P胉ȗD舏G82dXvHj (d40XxHhV2@"(A( @ȇ")҈ĸ}wȈ؊"0xQdHʸ؍E Ȍ" x& Ɨݸ"`z،Ȑ YHz،X؏9%ؘɍ}y#%Qgڨɒ)/؉59F JH9rH+GhwDsO 4و7 [I}L` QX*f )[ Fmc1蘒IDwxiV{ e18XYiٖ1@99il`79"R-0ə鏞+Yy YA-V`9YYiGi FјٝZɗvٕ뵞 oٔ^Ian)ZyJ Z;mmsd-0.1/unit/rc-mms-1-3-con-224.mms000066400000000000000000000102511216024552400165650ustar00rootroot0000000000000000000000224M㸖MMS-1.3-con-224+33622222222/TYPE=PLMN+33666565565/TYPE=PLMNapplication/smil+:application/smil"/tmp/phpfIU5gx 1"/tmp/phpSgZlHzGIF89ax%$3pJe:]f_DDDOgXCg{babuuusw/'+8(<ARme`1r\}׽&-rt '.PV&Nz"NN 1蕙! NETSCAPE2.0! ?,xmH,Ȥrl:Ш(Zجvˬr8q9n<~/]sH2,.( Kqf8>^5;>=8D8=>;k;C;O2+ Ja5>EBH pgO (pSae 9p1 LD\.-p2Ukm{g I67, |MQLM@/#ѲaLa}xAkP{û L냭?iE56" 5vЮ2:x+h6ۣVhx?"r 7,LMs<ӰK%…*ٲRfϬrCK2-P-jV2dx>4n04rby>`GCN׌= N-ݱߓFͷ7c߳bCɏ"=yV2"Yx/V 찖}OiQ ܧAp/@3_FyR'pYT7ZK3*ʇ`9B_"?,yA@nh]׿Xx.$" !Y{8Y*pB+*8"kzԺ$i5yVT2w9\IsK GD።3#Ejw튟%ѯ)ip4`WdKj,@/HdE&,I3*90kԣoibN^S[f6 eVD%gYrm-GNf1y,z@ݞ"<~^ [U*bSʂkp0̜(ENT$BCˡ9N*cK!Դ.*y|rps7=M@a c y7L2˃~M[L" A_N{Z&,! G.j;+E b+wrG<27MBu7s|YRFH`cq}/xwo?7|#{˶:yxxWG/߹# tc:`PVB O謹~_=/?uG~D`B<7߀+UOGmOU{O{| i q H 8 H gyoGx(~W HC &x'G~ׁ,(^p{{Fh -O"vTgᇁ `w.x38׶ĉS1zjx8 `hs䤊X@)HvP%x(8X{h1^ȆØX3PΈfp'hȍJ\(`HhS)¨(xZC 5yX` IK8;n9a09ȈȒhSP:ٓ<ٓ8@ɒB9YFɏIH! ?,+ Fm8E1,6O%r9% Trm뽆c-;$Vpz}kQlg~dK\FA! ?,$ ?ߏFj;_7$>߮98_ :5>LCl#R :V믛m?IQA! ?,+ Jm8q=߮f$:8o1bwᲴfƄZhܡa4+?%]S5NXI8H^`OlUxdoA! ?,2 CߏF$ ;NXlq>%6 {Zz5 ڪ[T.uWhjFocVoA! ?,: IG+ kdrH,҄8Tt>QzMp-WpBz&:E$ p8Q=Zk?oR=~ZZnCA;mmsd-0.1/unit/rc-mms-1-3-con-228.mms000066400000000000000000000055511216024552400166000ustar00rootroot0000000000000000000000228M–MMS-1.3-con-228+33622222222/TYPE=PLMN+33666565565/TYPE=PLMNapplication/smil+1application/smil"/tmp/phpu66A06 (e"/tmp/phpRWzGLA xUUUUUUUUUUUUUUUUUUUmmmmmmmUWUUUUUUU[}m0`m}}x`h`{uЧ&8W u.jeݫZT82`5 }lfdoo[l ?= o} $_ >bu]0`z귿߿k_{_z۷_?{o_??u~zۮ]ܽ+~?[owk\{kwx{n WhۤUJ%*"{%)UUU*|<*UU@ ߫7UWJ?zoꪫUT }߿ڕTTCv-Wu߿UWUUCzހjC }߽oʪRvzUTUUC @ԕ_ nU_TX#ߪRC1uj }#ʥ<o8[UU?UUC< ]^iԪJxWuv+1application/smil"/tmp/phppFdHSM )"/tmp/phpKw4oMIHello Worldmmsd-0.1/unit/rc-mms-1-3-con-281.mms000066400000000000000000000141521216024552400165740ustar00rootroot0000000000000000000000281MMMS-1.3-con-281+33622222222/TYPE=PLMN+33666565565/TYPE=PLMNX-MMS-Unrecognised-Header-FieldYes)"/tmp/phpDhBvLeHello World#"/tmp/phpoez0I8JFIFddDucky< XICC_PROFILE HLinomntrRGB XYZ  1acspMSFTIEC sRGB-HP cprtP3desclwtptbkptrXYZgXYZ,bXYZ@dmndTpdmddvuedLview$lumimeas $tech0 rTRC< gTRC< bTRC< textCopyright (c) 1998 Hewlett-Packard CompanydescsRGB IEC61966-2.1sRGB IEC61966-2.1XYZ QXYZ XYZ o8XYZ bXYZ $descIEC http://www.iec.chIEC http://www.iec.chdesc.IEC 61966-2.1 Default RGB colour space - sRGB.IEC 61966-2.1 Default RGB colour space - sRGBdesc,Reference Viewing Condition in IEC61966-2.1,Reference Viewing Condition in IEC61966-2.1view_. \XYZ L VPWmeassig CRT curv #(-27;@EJOTY^chmrw| %+28>ELRY`gnu| &/8AKT]gqz !-8COZfr~ -;HUcq~ +:IXgw'7HYj{+=Oat 2FZn  % : O d y  ' = T j " 9 Q i  * C \ u & @ Z t .Id %A^z &Ca~1Om&Ed#Cc'Ij4Vx&IlAe@e Ek*Qw;c*R{Gp@j>i  A l !!H!u!!!"'"U"""# #8#f###$$M$|$$% %8%h%%%&'&W&&&''I'z''( (?(q(())8)k))**5*h**++6+i++,,9,n,,- -A-v--..L.../$/Z///050l0011J1112*2c223 3F3334+4e4455M555676r667$7`7788P8899B999:6:t::;-;k;;<' >`>>?!?a??@#@d@@A)AjAAB0BrBBC:C}CDDGDDEEUEEF"FgFFG5G{GHHKHHIIcIIJ7J}JK KSKKL*LrLMMJMMN%NnNOOIOOP'PqPQQPQQR1R|RSS_SSTBTTU(UuUVV\VVWDWWX/X}XYYiYZZVZZ[E[[\5\\]']x]^^l^__a_``W``aOaabIbbcCccd@dde=eef=ffg=ggh?hhiCiijHjjkOkklWlmm`mnnknooxop+ppq:qqrKrss]sttptu(uuv>vvwVwxxnxy*yyzFz{{c{|!||}A}~~b~#G k͂0WGrׇ;iΉ3dʋ0cʍ1fΏ6n֑?zM _ɖ4 uL$h՛BdҞ@iءG&vVǥ8nRĩ7u\ЭD-u`ֲK³8%yhYѹJº;.! zpg_XQKFAǿ=ȼ:ɹ8ʷ6˶5̵5͵6ζ7ϸ9к<Ѿ?DINU\dlvۀ܊ݖޢ)߯6DScs 2F[p(@Xr4Pm8Ww)KmAdobed       <P!1AQa"q2B#bR3!1AQaq2"BR# ?g7yX‘Q&A+5XoΊׅ m Gp`xRO?C)ic$/6cmӘZG6(OQ{=2SO(|-䘖I.*vIAmi˓d}0X gUX3zfT픵G\>B/qcq 5*&T9HI*oe`xo@U<0BDLf2gX1 anZ2})HHCtKD]!֝uK,vPm:ҘTv 󓛕WE {,Q52_Z&EƩW=ʹi{,]v+CtgMSeN=?pD>>iSҺ_hsLuKo>gnX_ܓ%--c2xu;# {|5ջGH%*wE TMZh}@mtgm2ظ6RZ0+/V(yi%WŌR}q97%&Z\{wSDKHOA[M(yF2ctx6!i½u'3p, /\gd5 1MIT~@̯hd_O8\&k{wl#sAr k2 Ϟ S*&FqHT*.$p6 ;H?! k-qh1֤:8I;BNuP}o鷟#;[)岋碤0\E_==)]L,;AEݔ84H*IJ,y_ezQ`+_}ǹ[tK,A6ؘJ;׆ lrWNT'5o9(m̒O-8n]UqjGDf JY|;ⶺ(3>h":A[cqw^9.>9DC,.B ۳Sj:@ve.dѦo9@A`~CE.ɭńbOcf$ꍠrdYCx,绷kPJpn&b0~6t+,|@c9dm z|uUp}RL8grm2*>jyjӅUw6BTyf,?ldKBĖ6 !EI77-MCQʧtv^b$ҢnPIP rh:[D=㹻ko1Asr9`C'd7T|R=/9)fuSec1XI=olɜV"N١Ad2åw0#">gƻA$aO.l:qVHwjsIv<7 D"pV$ \cᘇU2H9K15uG0m1NaJIH 5j9e(FC,a9GDa#inK|$yBP#)@45kf!K=]`_NY,h+])ynY{*3/DuˡC\(=ۗoN{Ÿ=v"sT7RwC]i]`m}[_Sqy?M^ϳُ4)]wh(vdn>ӻZV~޻wSOCv>䞅Dn5k^fޤY 6æmmsd-0.1/unit/rc-mms-1-3-con-282.mms000066400000000000000000000141271216024552400165770ustar00rootroot0000000000000000000000282MÖMMS-1.3-con-282+33622222222/TYPE=PLMN+33666565565/TYPE=PLMNNewMessageClass)"/tmp/phpSYH61uHello World#"/tmp/phprzSGZSJFIFddDucky< XICC_PROFILE HLinomntrRGB XYZ  1acspMSFTIEC sRGB-HP cprtP3desclwtptbkptrXYZgXYZ,bXYZ@dmndTpdmddvuedLview$lumimeas $tech0 rTRC< gTRC< bTRC< textCopyright (c) 1998 Hewlett-Packard CompanydescsRGB IEC61966-2.1sRGB IEC61966-2.1XYZ QXYZ XYZ o8XYZ bXYZ $descIEC http://www.iec.chIEC http://www.iec.chdesc.IEC 61966-2.1 Default RGB colour space - sRGB.IEC 61966-2.1 Default RGB colour space - sRGBdesc,Reference Viewing Condition in IEC61966-2.1,Reference Viewing Condition in IEC61966-2.1view_. \XYZ L VPWmeassig CRT curv #(-27;@EJOTY^chmrw| %+28>ELRY`gnu| &/8AKT]gqz !-8COZfr~ -;HUcq~ +:IXgw'7HYj{+=Oat 2FZn  % : O d y  ' = T j " 9 Q i  * C \ u & @ Z t .Id %A^z &Ca~1Om&Ed#Cc'Ij4Vx&IlAe@e Ek*Qw;c*R{Gp@j>i  A l !!H!u!!!"'"U"""# #8#f###$$M$|$$% %8%h%%%&'&W&&&''I'z''( (?(q(())8)k))**5*h**++6+i++,,9,n,,- -A-v--..L.../$/Z///050l0011J1112*2c223 3F3334+4e4455M555676r667$7`7788P8899B999:6:t::;-;k;;<' >`>>?!?a??@#@d@@A)AjAAB0BrBBC:C}CDDGDDEEUEEF"FgFFG5G{GHHKHHIIcIIJ7J}JK KSKKL*LrLMMJMMN%NnNOOIOOP'PqPQQPQQR1R|RSS_SSTBTTU(UuUVV\VVWDWWX/X}XYYiYZZVZZ[E[[\5\\]']x]^^l^__a_``W``aOaabIbbcCccd@dde=eef=ffg=ggh?hhiCiijHjjkOkklWlmm`mnnknooxop+ppq:qqrKrss]sttptu(uuv>vvwVwxxnxy*yyzFz{{c{|!||}A}~~b~#G k͂0WGrׇ;iΉ3dʋ0cʍ1fΏ6n֑?zM _ɖ4 uL$h՛BdҞ@iءG&vVǥ8nRĩ7u\ЭD-u`ֲK³8%yhYѹJº;.! zpg_XQKFAǿ=ȼ:ɹ8ʷ6˶5̵5͵6ζ7ϸ9к<Ѿ?DINU\dlvۀ܊ݖޢ)߯6DScs 2F[p(@Xr4Pm8Ww)KmAdobed       <P!1AQa"q2B#bR3!1AQaq2"BR# ?g7yX‘Q&A+5XoΊׅ m Gp`xRO?C)ic$/6cmӘZG6(OQ{=2SO(|-䘖I.*vIAmi˓d}0X gUX3zfT픵G\>B/qcq 5*&T9HI*oe`xo@U<0BDLf2gX1 anZ2})HHCtKD]!֝uK,vPm:ҘTv 󓛕WE {,Q52_Z&EƩW=ʹi{,]v+CtgMSeN=?pD>>iSҺ_hsLuKo>gnX_ܓ%--c2xu;# {|5ջGH%*wE TMZh}@mtgm2ظ6RZ0+/V(yi%WŌR}q97%&Z\{wSDKHOA[M(yF2ctx6!i½u'3p, /\gd5 1MIT~@̯hd_O8\&k{wl#sAr k2 Ϟ S*&FqHT*.$p6 ;H?! k-qh1֤:8I;BNuP}o鷟#;[)岋碤0\E_==)]L,;AEݔ84H*IJ,y_ezQ`+_}ǹ[tK,A6ؘJ;׆ lrWNT'5o9(m̒O-8n]UqjGDf JY|;ⶺ(3>h":A[cqw^9.>9DC,.B ۳Sj:@ve.dѦo9@A`~CE.ɭńbOcf$ꍠrdYCx,绷kPJpn&b0~6t+,|@c9dm z|uUp}RL8grm2*>jyjӅUw6BTyf,?ldKBĖ6 !EI77-MCQʧtv^b$ҢnPIP rh:[D=㹻ko1Asr9`C'd7T|R=/9)fuSec1XI=olɜV"N١Ad2åw0#">gƻA$aO.l:qVHwjsIv<7 D"pV$ \cᘇU2H9K15uG0m1NaJIH 5j9e(FC,a9GDa#inK|$yBP#)@45kf!K=]`_NY,h+])ynY{*3/DuˡC\(=ۗoN{Ÿ=v"sT7RwC]i]`m}[_Sqy?M^ϳُ4)]wh(vdn>ӻZV~޻wSOCv>䞅Dn5k^fޤY 6æmmsd-0.1/unit/sr-mms-mixed.mms000066400000000000000000000001321216024552400163260ustar00rootroot00000000000000000000000001+33612345678/TYPE=PLMN"Hello World mmsd-0.1/unit/sr-mms-related-multi-to.mms000066400000000000000000000007341216024552400204200ustar00rootroot00000000000000000000000002+33612345678/TYPE=PLMN+33612345679/TYPE=PLMNapplication/smil1application/smil" "Hello World mmsd-0.1/unit/test-mmsutil.c000066400000000000000000000763121216024552400161160ustar00rootroot00000000000000/* * * Multimedia Messaging Service * * Copyright (C) 2010-2011 Intel Corporation. All rights reserved. * * 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 "mmsutil.h" static const char *message_type_to_string(enum mms_message_type type) { switch (type) { case MMS_MESSAGE_TYPE_SEND_REQ: return "send-req"; case MMS_MESSAGE_TYPE_SEND_CONF: return "send-conf"; case MMS_MESSAGE_TYPE_NOTIFICATION_IND: return "notification-ind"; case MMS_MESSAGE_TYPE_NOTIFYRESP_IND: return "notifyresp-ind"; case MMS_MESSAGE_TYPE_RETRIEVE_CONF: return "retrieve-conf"; case MMS_MESSAGE_TYPE_ACKNOWLEDGE_IND: return "acknowledge-ind"; case MMS_MESSAGE_TYPE_DELIVERY_IND: return "delivery-ind"; } return NULL; } static const char *message_rsp_status_to_string( enum mms_message_rsp_status status) { switch (status) { case MMS_MESSAGE_RSP_STATUS_OK: return "ok"; case MMS_MESSAGE_RSP_STATUS_ERR_UNSUPPORTED_MESSAGE: return "error-unsupported-message"; case MMS_MESSAGE_RSP_STATUS_ERR_TRANS_FAILURE: return "error-transient-failure"; case MMS_MESSAGE_RSP_STATUS_ERR_TRANS_NETWORK_PROBLEM: return "error-transient-network-problem"; case MMS_MESSAGE_RSP_STATUS_ERR_PERM_FAILURE: return "error-permanent-failure"; case MMS_MESSAGE_RSP_STATUS_ERR_PERM_SERVICE_DENIED: return "error-permanent-service-denied"; case MMS_MESSAGE_RSP_STATUS_ERR_PERM_MESSAGE_FORMAT_CORRUPT: return "error-permanent-message-format-corrupt"; case MMS_MESSAGE_RSP_STATUS_ERR_PERM_SENDING_ADDRESS_UNRESOLVED: return "error-permanent-sending-address-unresolved"; case MMS_MESSAGE_RSP_STATUS_ERR_PERM_CONTENT_NOT_ACCEPTED: return "error-permanent-content-not-accepted"; case MMS_MESSAGE_RSP_STATUS_ERR_PERM_LACK_OF_PREPAID: return "error-permanent-lack-of-prepaid"; } return NULL; } static void dump_notification_ind(struct mms_message *msg) { char buf[128]; strftime(buf, 127, "%Y-%m-%dT%H:%M:%S%z", localtime(&msg->ni.expiry)); buf[127] = '\0'; g_print("From: %s\n", msg->ni.from); g_print("Subject: %s\n", msg->ni.subject); g_print("Class: %s\n", msg->ni.cls); g_print("Size: %d\n", msg->ni.size); g_print("Expiry: %s\n", buf); g_print("Location: %s\n", msg->ni.location); } static void dump_attachment(gpointer data, gpointer user_data) { struct mms_attachment *attach = data; g_print("Attachment:\n"); g_print("\tOffset: %zd\n", attach->offset); g_print("\tLength: %zd\n", attach->length); g_print("\tContent-type: %s\n", attach->content_type); g_print("\tContent-id: %s\n", attach->content_id); } static void dump_retrieve_conf(struct mms_message *msg) { char buf[128]; strftime(buf, 127, "%Y-%m-%dT%H:%M:%S%z", localtime(&msg->rc.date)); buf[127] = '\0'; g_print("From: %s\n", msg->rc.from); g_print("To: %s\n", msg->rc.to); g_print("Subject: %s\n", msg->rc.subject); g_print("Class: %s\n", msg->rc.cls); g_print("Priority: %s\n", msg->rc.priority); g_print("Msg-Id: %s\n", msg->rc.msgid); g_print("Date: %s\n", buf); g_slist_foreach(msg->attachments, dump_attachment, NULL); } static void dump_send_conf(struct mms_message *msg) { g_print("Response-Status: %s\n", message_rsp_status_to_string(msg->sc.rsp_status)); g_print("Msg-Id: %s\n", msg->sc.msgid); } static void dump_send_req(struct mms_message *msg) { g_print("To: %s\n", msg->sr.to); g_slist_foreach(msg->attachments, dump_attachment, NULL); } static gboolean check_encoded_msg(const char *filename, const unsigned char *msg_pdu) { struct stat st; unsigned char *pdu; int fd; int i; int ret; fd = open(filename, O_RDONLY); if (fd < 0) { g_printerr("Failed to open %s\n", filename); return FALSE; } if (fstat(fd, &st) < 0) { g_printerr("Failed to stat %s\n", filename); close(fd); return FALSE; } pdu = mmap(NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0); if (!pdu || pdu == MAP_FAILED) { g_printerr("Failed to mmap %s\n", filename); close(fd); return FALSE; } if (g_test_verbose()) { for (i = 0; i < st.st_size; i++) g_print("%02x ", pdu[i]); g_print("\n"); } ret = memcmp(msg_pdu, pdu, st.st_size); munmap(pdu, st.st_size); close(fd); return ret == 0; } /* * MMS M-Notify.Ind PDU 1 * This PDU shows the decoding of a M-Notify.Ind PDU with below content: * Overall message size: 68 * MMS message type: notification-ind * MMS transaction id: OgQKKB * MMS version: 1.0 * From: Erotik * Subject: Pin-Ups * Class: Personal * Size: 16384 * Expiry: 2011-05-19T10:56:340200 * Location: http://eps3.de/O/Z9IZO */ static const unsigned char mms_m_notify_ind_1[] = { 0x8C, 0x82, 0x98, 0x4F, 0x67, 0x51, 0x4B, 0x4B, 0x42, 0x00, 0x8D, 0x90, 0x89, 0x08, 0x80, 0x45, 0x72, 0x6F, 0x74, 0x69, 0x6B, 0x00, 0x96, 0x50, 0x69, 0x6E, 0x2D, 0x55, 0x70, 0x73, 0x00, 0x8A, 0x80, 0x8E, 0x02, 0x40, 0x00, 0x88, 0x05, 0x81, 0x03, 0x03, 0xF4, 0x80, 0x83, 0x68, 0x74, 0x74, 0x70, 0x3A, 0x2F, 0x2F, 0x65, 0x70, 0x73, 0x33, 0x2E, 0x64, 0x65, 0x2F, 0x4F, 0x2F, 0x5A, 0x39, 0x49, 0x5A, 0x4F, 0x00 }; /* * MMS M-Notify.Ind PDU 2 * This PDU shows the decoding of a M-Notify.Ind PDU without "subject" field * and below content: * Overall message size: 93 * MMS message type: notification-ind * MMS transaction id: wLJeT7THu * MMS version: 1.0 * From: 15551230000/TYPE=PLMN * Subject: (null) * Class: Personal * Size: 23069 * Expiry: 2011-05-19T14:32:320200 * Location: http://mmsc11:10021/mmsc/1_1?wLJeT7THu */ static const unsigned char mms_m_notify_ind_2[] = { 0x8C, 0x82, 0x98, 0x77, 0x4C, 0x4A, 0x65, 0x54, 0x37, 0x54, 0x48, 0x75, 0x00, 0x8D, 0x90, 0x89, 0x17, 0x80, 0x31, 0x35, 0x35, 0x35, 0x31, 0x32, 0x33, 0x30, 0x30, 0x30, 0x30, 0x2F, 0x54, 0x59, 0x50, 0x45, 0x3D, 0x50, 0x4C, 0x4D, 0x4E, 0x00, 0x8A, 0x80, 0x8E, 0x02, 0x5A, 0x1D, 0x88, 0x05, 0x81, 0x03, 0x03, 0xF4, 0x80, 0x83, 0x68, 0x74, 0x74, 0x70, 0x3A, 0x2F, 0x2F, 0x6D, 0x6D, 0x73, 0x63, 0x31, 0x31, 0x3A, 0x31, 0x30, 0x30, 0x32, 0x31, 0x2F, 0x6D, 0x6D, 0x73, 0x63, 0x2F, 0x31, 0x5F, 0x31, 0x3F, 0x77, 0x4C, 0x4A, 0x65, 0x54, 0x37, 0x54, 0x48, 0x75, 0x00 }; /* * MMS M-Notify.Ind PDU 3 * MMS-1.3-con-271: Long Subject field. * This PDU shows the decoding of a M-Notify.Ind PDU with a maximum length * "us-ascii" encoded "subject" field value (40 characters) and below content: * Overall message size: 121 * MMS message type: notification-ind * MMS transaction id: OgQKKB * MMS version: 1.0 * From: +44123989100/TYPE=PLMN * Subject: abcdefghijklmnopqrstuvwxyz0123456789/-+@ * Class: Personal * Size: 23069 * Expiry: 2011-05-27T10:39:58+0200 * Location: http://eps3.de/O/Z9IZO */ static const char mms_m_notify_ind_3[] = "./unit/ni-mms-1-3-con-271.mms"; /* * MMS M-Notify.Ind PDU 4 * MMS-1.3-con-272: Long X-Mms-Content-Location field in Notification. * This PDU shows the decoding of a M-Notify.Ind PDU with a maximum length * "X-Mms-Content-Location field value (100 characters) and below content: * Overall message size: 170 * MMS message type: notification-ind * MMS transaction id: OgQKKB * MMS version: 1.0 * From: +44123989100/TYPE=PLMN * Subject: MMS-1.3-con-272 * Class: Personal * Size: 23069 * Expiry: 2011-05-27T10:39:58+0200 * Location: http://abcdefghi/abcdefghi/abcdefghi/abcdefghi/abcdefghi/abcdefghi * /abcdefghi/abcdefghi/abcdefghi.mms */ static const char mms_m_notify_ind_4[] = "./unit/ni-mms-1-3-con-272.mms"; /* * MMS M-Retrieve.Conf PDU 1 * This PDU shows the decoding of a M-Retrieve.Conf PDU with an "us-ascii" * encoded text "subject" field and below content: * Overall message size: 200 * MMS message type: retrieve-conf * MMS transaction id: 1201657238 * MMS version: 1.3 * From: 49891000/TYPE=PLMN * To: (null) * Subject: MMS-1.3-con-212 * Class: (null) * Priority: (null) * Msg-Id: mt-212 * Date: 2008-01-30T02:40:380100 */ static const unsigned char mms_m_retrieve_conf_1[] = { 0x8C, 0x84, 0x98, 0x31, 0x32, 0x30, 0x31, 0x36, 0x35, 0x37, 0x32, 0x33, 0x38, 0x00, 0x8D, 0x93, 0x8B, 0x6D, 0x74, 0x2D, 0x32, 0x31, 0x32, 0x00, 0x85, 0x04, 0x47, 0x9F, 0xD5, 0x96, 0x89, 0x15, 0x80, 0x2B, 0x34, 0x39, 0x38, 0x39, 0x31, 0x30, 0x30, 0x30, 0x2F, 0x54, 0x59, 0x50, 0x45, 0x3D, 0x50, 0x4C, 0x4D, 0x4E, 0x00, 0x96, 0x11, 0x83, 0x4D, 0x4D, 0x53, 0x2D, 0x31, 0x2E, 0x33, 0x2D, 0x63, 0x6F, 0x6E, 0x2D, 0x32, 0x31, 0x32, 0x00, 0x84, 0xA3, 0x01, 0x40, 0x3B, 0x14, 0x83, 0x85, 0x54, 0x65, 0x78, 0x74, 0x5F, 0x75, 0x73, 0x2D, 0x61, 0x73, 0x63, 0x69, 0x69, 0x2E, 0x74, 0x78, 0x74, 0x00, 0x81, 0x83, 0xC0, 0x22, 0x3C, 0x54, 0x65, 0x78, 0x74, 0x5F, 0x75, 0x73, 0x2D, 0x61, 0x73, 0x63, 0x69, 0x69, 0x2E, 0x74, 0x78, 0x74, 0x3E, 0x00, 0x8E, 0x54, 0x65, 0x78, 0x74, 0x5F, 0x75, 0x73, 0x2D, 0x61, 0x73, 0x63, 0x69, 0x69, 0x2E, 0x74, 0x78, 0x74, 0x00, 0x54, 0x68, 0x65, 0x20, 0x71, 0x75, 0x69, 0x63, 0x6B, 0x20, 0x62, 0x72, 0x6F, 0x77, 0x6E, 0x20, 0x66, 0x6F, 0x78, 0x20, 0x6A, 0x75, 0x6D, 0x70, 0x65, 0x64, 0x20, 0x6F, 0x76, 0x65, 0x72, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6C, 0x61, 0x7A, 0x79, 0x20, 0x64, 0x6F, 0x67, 0x20, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x30, 0x2F, 0x21, 0x28, 0x29, }; /* * MMS M-Retrieve.Conf PDU 2 * This PDU shows the decoding of a M-Retrieve.Conf PDU with multiple "To" * fields and below content: * Overall message size: 192 * MMS message type: retrieve-conf * MMS transaction id: 1201657238 * MMS version: 1.0 * From: 1234567890/TYPE=PLMN * To: 1111111111/TYPE=PLMN,2222222222/TYPE=PLMN,3333333333/TYPE=PLMN * Subject: multito * Class: Personal * Priority: Normal * Msg-Id: (null) * Date: 2011-04-04T11:41:500200 */ static const unsigned char mms_m_retrieve_conf_2[] = { 0x8C, 0x84, 0x98, 0x31, 0x32, 0x30, 0x31, 0x36, 0x35, 0x37, 0x32, 0x33, 0x38, 0x00, 0x8D, 0x90, 0x85, 0x04, 0x4D, 0x99, 0x92, 0x5E, 0x96, 0x6D, 0x75, 0x6C, 0x74, 0x69, 0x74, 0x6F, 0x00, 0x89, 0x17, 0x80, 0x2B, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x30, 0x2F, 0x54, 0x59, 0x50, 0x45, 0x3D, 0x50, 0x4C, 0x4D, 0x4E, 0x00, 0x97, 0x2B, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x2F, 0x54, 0x59, 0x50, 0x45, 0x3D, 0x50, 0x4C, 0x4D, 0x4E, 0x00, 0x97, 0x2B, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x2F, 0x54, 0x59, 0x50, 0x45, 0x3D, 0x50, 0x4C, 0x4D, 0x4E, 0x00, 0x97, 0x2B, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x2F, 0x54, 0x59, 0x50, 0x45, 0x3D, 0x50, 0x4C, 0x4D, 0x4E, 0x00, 0x8A, 0x80, 0x8F, 0x81, 0x94, 0x80, 0x86, 0x81, 0x90, 0x81, 0x84, 0xA3, 0x01, 0x26, 0x0E, 0x83, 0xC0, 0x22, 0x3C, 0x47, 0x65, 0x6E, 0x65, 0x72, 0x69, 0x63, 0x5F, 0x54, 0x65, 0x78, 0x74, 0x2E, 0x74, 0x78, 0x74, 0x3E, 0x00, 0x8E, 0x2F, 0x74, 0x6D, 0x70, 0x2F, 0x70, 0x68, 0x70, 0x38, 0x76, 0x6C, 0x66, 0x79, 0x42, 0x00, 0xEF, 0xBB, 0xBF, 0x48, 0x65, 0x6C, 0x6C, 0x6F, 0x20, 0x57, 0x6F, 0x72, 0x6C, 0x64 }; /* * MMS M-Retrieve.Conf PDU 3 * Overall message size: 147 * MMS message type: retrieve-conf * MMS transaction id: (null) * MMS version: 1.0 * From: 1234567890/TYPE=PLMN * To: 6666666666/TYPE=PLMN * Subject: test without transaction ID * Class: Personal * Priority: Normal * Msg-Id: (null) * Date: 2011-04-08T12:27:050200 */ static const unsigned char mms_m_retrieve_conf_3[] = { 0x8C, 0x84, 0x8D, 0x90, 0x85, 0x04, 0x4D, 0x9E, 0xE2, 0xF9, 0x96, 0x74, 0x65, 0x73, 0x74, 0x20, 0x77, 0x69, 0x74, 0x68, 0x6F, 0x75, 0x74, 0x20, 0x74, 0x72, 0x61, 0x6E, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6F, 0x6E, 0x20, 0x49, 0x44, 0x00, 0x89, 0x17, 0x80, 0x2B, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x30, 0x2F, 0x54, 0x59, 0x50, 0x45, 0x3D, 0x50, 0x4C, 0x4D, 0x4E, 0x00, 0x97, 0x2B, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x2F, 0x54, 0x59, 0x50, 0x45, 0x3D, 0x50, 0x4C, 0x4D, 0x4E, 0x00, 0x8A, 0x80, 0x8F, 0x81, 0x94, 0x80, 0x86, 0x81, 0x90, 0x81, 0x84, 0xA3, 0x01, 0x1F, 0x0E, 0x83, 0xC0, 0x22, 0x3C, 0x68, 0x65, 0x6C, 0x6C, 0x6F, 0x2E, 0x74, 0x78, 0x74, 0x3E, 0x00, 0x8E, 0x2F, 0x74, 0x6D, 0x70, 0x2F, 0x70, 0x68, 0x70, 0x32, 0x49, 0x6E, 0x74, 0x53, 0x37, 0x00, 0x48, 0x65, 0x6C, 0x6C, 0x6F, 0x20, 0x57, 0x6F, 0x72, 0x6C, 0x64, 0x20, 0x21, 0x0A }; /* * MMS M-Retrieve.Conf PDU 4 * MMS-1.3-con-210: Long Media Content-Location header field value. * This PDU shows the decoding of a M-Retrieve.Conf PDU where a SMIL part * references an object using a long "Content-Location" field value. * Overall message size: 2767 * MMS message type: retrieve-conf * MMS transaction id: 00000000210 * MMS version: 1.3 * From: +33611111111/TYPE=PLMN * To: +33622222222/TYPE=PLMN * Subject: MMS-1.3-con-210 * Class: (null) * Priority: (null) * Msg-Id: (null) * Date: 2011-05-31T10:42:30+0200 */ static const char mms_m_retrieve_conf_4[] = "./unit/rc-mms-1-3-con-210.mms"; /* * MMS M-Retrieve.Conf PDU 5 * MMS-1.3-con-271: Long Subject field. * This PDU shows the decoding of a M-Retrieve.Conf PDU with a maximum length * "us-ascii" encoded "subject" field value (40 characters) and below content: * Overall message size: 556 * MMS message type: retrieve-conf * MMS transaction id: 00000000271 * MMS version: 1.3 * From: +33622222222/TYPE=PLMN * To: +33666565565/TYPE=PLMN * Subject: abcdefghijklmnopqrstuvwxyz0123456789/-+@ * Class: (null) * Priority: (null) * Msg-Id: (null) * Date: 2011-06-03T11:23:30+0200 */ static const char mms_m_retrieve_conf_5[] = "./unit/rc-mms-1-3-con-271.mms"; /* * MMS M-Retrieve.Conf PDU 6 * MMS-1.3-con-212: Text with US-ASCII encoding. * This PDU shows the decoding of a M-Retrieve.Conf PDU with a text object * with "us-ascii" encoding and below content: * Overall message size: 198 * MMS message type: retrieve-conf * MMS transaction id: 00000000212 * MMS version: 1.3 * From: +33622222222/TYPE=PLMN * To: +33666565565/TYPE=PLMN * Subject: MMS-1.3-con-212 * Class: (null) * Priority: (null) * Msg-Id: (null) * Date: 2011-06-03T11:45:21+0200 */ static const char mms_m_retrieve_conf_6[] = "./unit/rc-mms-1-3-con-212.mms"; /* * MMS M-Retrieve.Conf PDU 7 * MMS-1.3-con-213: Text with UTF-8 encoding. * This PDU shows the decoding of a M-Retrieve.Conf PDU with a text object * with "utf-8" encoding and below content: * Overall message size: 249 * MMS message type: retrieve-conf * MMS transaction id: 00000000213 * MMS version: 1.3 * From: +33622222222/TYPE=PLMN * To: +33666565565/TYPE=PLMN * Subject: MMS-1.3-con-213 * Class: (null) * Priority: (null) * Msg-Id: (null) * Date: 2011-06-03T11:52:45+0200 */ static const char mms_m_retrieve_conf_7[] = "./unit/rc-mms-1-3-con-213.mms"; /* * MMS M-Retrieve.Conf PDU 8 * MMS-1.3-con-214: Text with UTF-16 encoding. * This PDU shows the decoding of a M-Retrieve.Conf PDU with a text object * with "utf-16" encoding and below content: * Overall message size: 356 * MMS message type: retrieve-conf * MMS transaction id: 00000000214 * MMS version: 1.3 * From: +33622222222/TYPE=PLMN * To: +33666565565/TYPE=PLMN * Subject: MMS-1.3-con-214 * Class: (null) * Priority: (null) * Msg-Id: (null) * Date: 2011-06-03T14:41:16+0200 */ static const char mms_m_retrieve_conf_8[] = "./unit/rc-mms-1-3-con-214.mms"; /* * MMS M-Retrieve.Conf PDU 9 * MMS-1.3-con-216: JPG Image size 160x120. * This PDU shows the decoding of a M-Retrieve.Conf PDU with a 160x120 JPG * Image object and below content: * Overall message size: 114440 * MMS message type: retrieve-conf * MMS transaction id: 00000000216 * MMS version: 1.3 * From: +33622222222/TYPE=PLMN * To: +33666565565/TYPE=PLMN * Subject: MMS-1.3-con-216 * Class: (null) * Priority: (null) * Msg-Id: (null) * Date: 2011-06-03T15:08:09+0200 */ static const char mms_m_retrieve_conf_9[] = "./unit/rc-mms-1-3-con-216.mms"; /* * MMS M-Retrieve.Conf PDU 10 * MMS-1.3-con-220: GIF Image size 160x120. * This PDU shows the decoding of a M-Retrieve.Conf PDU with a 160x120 GIF87a * Image object and below content: * Overall message size: 4460 * MMS message type: retrieve-conf * MMS transaction id: 00000000220 * MMS version: 1.3 * From: +33622222222/TYPE=PLMN * To: +33666565565/TYPE=PLMN * Subject: MMS-1.3-con-220 * Class: (null) * Priority: (null) * Msg-Id: (null) * Date: 2011-06-03T15:27:27+0200 */ static const char mms_m_retrieve_conf_10[] = "./unit/rc-mms-1-3-con-220.mms"; /* * MMS M-Retrieve.Conf PDU 11 * MMS-1.3-con-224: Animated GIF Image size 160x120. * This PDU shows the decoding of a M-Retrieve.Conf PDU with a 160x120 animated * GIF87a Image object and below content: * Overall message size: 4265 * MMS message type: retrieve-conf * MMS transaction id: 00000000224 * MMS version: 1.3 * From: +33622222222/TYPE=PLMN * To: +33666565565/TYPE=PLMN * Subject: MMS-1.3-con-224 * Class: (null) * Priority: (null) * Msg-Id: (null) * Date: 2011-06-03T15:38:00+0200 */ static const char mms_m_retrieve_conf_11[] = "./unit/rc-mms-1-3-con-224.mms"; /* * MMS M-Retrieve.Conf PDU 12 * MMS-1.3-con-228: WBMP Image size 160x120. * This PDU shows the decoding of a M-Retrieve.Conf PDU with a 160x120 WBMP * Image object and below content: * Overall message size: 2921 * MMS message type: retrieve-conf * MMS transaction id: 00000000228 * MMS version: 1.3 * From: +33622222222/TYPE=PLMN * To: +33666565565/TYPE=PLMN * Subject: MMS-1.3-con-228 * Class: (null) * Priority: (null) * Msg-Id: (null) * Date: 2011-06-03T15:46:42+020 */ static const char mms_m_retrieve_conf_12[] = "./unit/rc-mms-1-3-con-228.mms"; /* * MMS M-Retrieve.Conf PDU 13 * MMS-1.3-con-211: Subject field with UTF8 encoding. * This PDU shows the decoding of a M-Retrieve.Conf PDU with Subject field * with UTF8 encoding and below content: * Overall message size: 544 * MMS message type: retrieve-conf * MMS transaction id: 00000000211 * MMS version: 1.3 * From: +33622222222/TYPE=PLMN * To: +33666565565/TYPE=PLMN * Subject: Shõrt Téxt - ¥üëäÿ * Class: (null) * Priority: (null) * Msg-Id: (null) * Date: 2011-06-03T16:01:31+0200 */ static const char mms_m_retrieve_conf_13[] = "./unit/rc-mms-1-3-con-211.mms"; /* * MMS M-Retrieve.Conf PDU 14 * MMS-1.3-con-282: Receive recognised fields with unrecognised values. * This PDU shows the decoding of a M-Retrieve.Conf PDU with recognised field * but with an unrecognised value (X-Mms-Message-Class: "NewMessageClass") and * below content: * Overall message size: 6231 * MMS message type: retrieve-conf * MMS transaction id: 00000000282 * MMS version: 1.3 * From: +33622222222/TYPE=PLMN * To: +33666565565/TYPE=PLMN * Subject: MMS-1.3-con-282 * Class: NewMessageClass * Priority: (null) * Msg-Id: (null) * Date: 2011-06-03T16:25:07+0200 */ static const char mms_m_retrieve_conf_14[] = "./unit/rc-mms-1-3-con-282.mms"; /* * MMS M-Retrieve.Conf PDU 15 * MMS-1.3-con-281: Receive unrecognised header field. * This PDU shows the decoding of a M-Retrieve.Conf PDU with an unrecognised * header field (X-MMS-Unrecognised-Header-Field: "Yes") and below content: * Overall message size: 6250 * MMS message type: retrieve-conf * MMS transaction id: 00000000281 * MMS version: 1.3 * From: +33622222222/TYPE=PLMN * To: +33666565565/TYPE=PLMN * Subject: MMS-1.3-con-281 * Class: (null) * Priority: (null) * Msg-Id: (null) * Date: 2011-06-03T16:37:02+0200 */ static const char mms_m_retrieve_conf_15[] = "./unit/rc-mms-1-3-con-281.mms"; /* * MMS M-Send.Req PDU 1 * This PDU shows the decoding of a M-Send.Req PDU with a content-type * value "application/vnd.wap.multipart.mixed" and below content: * Overall message size: 90 * MMS message type: send-req * MMS transaction id: 000000000001 * MMS version: 1.0 * To: +33612345678/TYPE=PLMN * Attachment: * Offset: 75 * Length: 15 * Content-type: text/plain;charset=utf-8 * Content-id: */ static const char mms_m_send_req_1[] = "./unit/sr-mms-mixed.mms"; /* * MMS M-Send.Req PDU 2 * This PDU shows the decoding of a M-Send.Req PDU with a content-type * value "application/vnd.wap.multipart.related" and multiple TO fields. * Overall message size: 476 * MMS message type: send-req * MMS transaction id: 000000000002 * MMS version: 1.0 * To: +33612345678/TYPE=PLMN,+33612345679/TYPE=PLMN * Attachment: * Offset: 129 * Length: 305 * Content-type: application/smil * Content-id: * Attachment: * Offset: 461 * Length: 15 * Content-type: text/plain;charset=utf-8 * Content-id: */ static const char mms_m_send_req_2[] = "./unit/sr-mms-related-multi-to.mms"; /* * MMS M-Send.Conf PDU 1 * This PDU shows the decoding of a M-Send.Conf PDU with an accepted "response * status" and a below content: * Overall message size: 28 * MMS message type: send-conf * MMS transaction id: 31887 * MMS version: 1.0 * Response-Status: ok * Msg-Id: 4dc268d71438a */ static const unsigned char mms_m_send_conf_1[] = { 0x8C, 0x81, 0x98, 0x33, 0x31, 0x38, 0x38, 0x37, 0x00, 0x8D, 0x90, 0x92, 0x80, 0x8B, 0x34, 0x64, 0x63, 0x32, 0x36, 0x38, 0x64, 0x37, 0x31, 0x34, 0x33, 0x38, 0x61, 0x00 }; /* * MMS M-Send.Conf PDU 2 * This PDU shows the decoding of a M-Send.Conf PDU with an rejected "response * status" and a below content: * Overall message size: 13 * MMS message type: send-conf * MMS transaction id: 31888 * MMS version: 1.0 * Response-Status: error-permanent-message-format-corrupt * Msg-Id: (null) */ static const unsigned char mms_m_send_conf_2[] = { 0x8C, 0x81, 0x98, 0x33, 0x31, 0x38, 0x38, 0x38, 0x00, 0x8D, 0x90, 0x92, 0xE2 }; struct mms_test { const char *pathname; const unsigned char *pdu; unsigned int len; }; static const struct mms_test mms_m_notify_ind_test_1 = { .pdu = mms_m_notify_ind_1, .len = sizeof(mms_m_notify_ind_1), }; static const struct mms_test mms_m_notify_ind_test_2 = { .pdu = mms_m_notify_ind_2, .len = sizeof(mms_m_notify_ind_2), }; static const struct mms_test mms_m_notify_ind_test_3 = { .pathname = mms_m_notify_ind_3, }; static const struct mms_test mms_m_notify_ind_test_4 = { .pathname = mms_m_notify_ind_4, }; static const struct mms_test mms_m_retrieve_conf_test_1 = { .pdu = mms_m_retrieve_conf_1, .len = sizeof(mms_m_retrieve_conf_1), }; static const struct mms_test mms_m_retrieve_conf_test_2 = { .pdu = mms_m_retrieve_conf_2, .len = sizeof(mms_m_retrieve_conf_2), }; static const struct mms_test mms_m_retrieve_conf_test_3 = { .pdu = mms_m_retrieve_conf_3, .len = sizeof(mms_m_retrieve_conf_3), }; static const struct mms_test mms_m_retrieve_conf_test_4 = { .pathname = mms_m_retrieve_conf_4, }; static const struct mms_test mms_m_retrieve_conf_test_5 = { .pathname = mms_m_retrieve_conf_5, }; static const struct mms_test mms_m_retrieve_conf_test_6 = { .pathname = mms_m_retrieve_conf_6, }; static const struct mms_test mms_m_retrieve_conf_test_7 = { .pathname = mms_m_retrieve_conf_7, }; static const struct mms_test mms_m_retrieve_conf_test_8 = { .pathname = mms_m_retrieve_conf_8, }; static const struct mms_test mms_m_retrieve_conf_test_9 = { .pathname = mms_m_retrieve_conf_9, }; static const struct mms_test mms_m_retrieve_conf_test_10 = { .pathname = mms_m_retrieve_conf_10, }; static const struct mms_test mms_m_retrieve_conf_test_11 = { .pathname = mms_m_retrieve_conf_11, }; static const struct mms_test mms_m_retrieve_conf_test_12 = { .pathname = mms_m_retrieve_conf_12, }; static const struct mms_test mms_m_retrieve_conf_test_13 = { .pathname = mms_m_retrieve_conf_13, }; static const struct mms_test mms_m_retrieve_conf_test_14 = { .pathname = mms_m_retrieve_conf_14, }; static const struct mms_test mms_m_retrieve_conf_test_15 = { .pathname = mms_m_retrieve_conf_15, }; static const struct mms_test mms_m_send_req_dec_test_1 = { .pathname = mms_m_send_req_1, }; static const struct mms_test mms_m_send_req_dec_test_2 = { .pathname = mms_m_send_req_2, }; static const struct mms_test mms_m_send_conf_test_1 = { .pdu = mms_m_send_conf_1, .len = sizeof(mms_m_send_conf_1), }; static const struct mms_test mms_m_send_conf_test_2 = { .pdu = mms_m_send_conf_2, .len = sizeof(mms_m_send_conf_2), }; static void test_decode_mms(gconstpointer data) { const struct mms_test *test = data; struct mms_message *msg; unsigned int len; gboolean ret; msg = g_try_new0(struct mms_message, 1); if (msg == NULL) { g_printerr("Failed allocate message"); return; } if (test->pathname != NULL) { struct stat st; unsigned char *pdu; int fd; fd = open(test->pathname, O_RDONLY); if (fd < 0) { g_printerr("Failed to open %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; } ret = mms_message_decode(pdu, len, msg); munmap(pdu, len); close(fd); } else { const unsigned char *pdu = test->pdu; len = test->len; ret = mms_message_decode(pdu, len, msg); } g_assert(ret == TRUE); if (g_test_verbose()) { g_print("Overall message size: %d\n", len); g_print("MMS message type: %s\n", message_type_to_string(msg->type)); g_print("MMS transaction id: %s\n", msg->transaction_id); g_print("MMS version: %u.%u\n", (msg->version & 0x70) >> 4, msg->version & 0x0f); switch (msg->type) { case MMS_MESSAGE_TYPE_NOTIFICATION_IND: dump_notification_ind(msg); break; case MMS_MESSAGE_TYPE_RETRIEVE_CONF: dump_retrieve_conf(msg); break; case MMS_MESSAGE_TYPE_SEND_CONF: dump_send_conf(msg); break; case MMS_MESSAGE_TYPE_SEND_REQ: dump_send_req(msg); break; default: break; } } out: mms_message_free(msg); } struct mms_encode_test { struct mms_message msg; const unsigned char pdu[]; }; static const struct mms_encode_test mms_m_notifyresp_ind_test_1 = { .msg = { .type = MMS_MESSAGE_TYPE_NOTIFYRESP_IND, .uuid = NULL, .path = NULL, .transaction_id = "0123456789abcdef", .version = MMS_MESSAGE_VERSION_1_2, .attachments = NULL, {.nri = { .notify_status = MMS_MESSAGE_NOTIFY_STATUS_RETRIEVED, } } }, .pdu = { 0x8C, 0x83, 0x98, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x00, 0x8D, 0x92, 0x95, 0x81 } }; #define CT_MUTLIPART "Content-Type: \"application/vnd.wap.multipart." #define CT_TYPE ";type=\"application/smil\"" #define CT_START ";start=\"\"" #define CT_MULTIPART_RELATED CT_MUTLIPART "related\"" CT_TYPE CT_START #define CT_MULTIPART_MIXED CT_MUTLIPART "mixed\"" static const struct mms_encode_test mms_m_send_req_test_1 = { .msg = { .type = MMS_MESSAGE_TYPE_SEND_REQ, .uuid = NULL, .path = NULL, .transaction_id = "0123456789abcdef_sr1", .version = MMS_MESSAGE_VERSION_1_2, .attachments = NULL, {.sr = { .status = MMS_MESSAGE_STATUS_DRAFT, .to = "0123456789", .dr = TRUE, .content_type = CT_MULTIPART_MIXED, } } }, .pdu = { 0x8C, 0x80, 0x98, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x5F, 0x73, 0x72, 0x31, 0x00, 0x8D, 0x92, 0x89, 0x01, 0x81, 0x97, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x00, 0x86, 0x80, 0x84, 0xA3 } }; static void test_encode_mms(gconstpointer data) { struct mms_encode_test *test_msg = (struct mms_encode_test *) data; char *filepath; gboolean ret; int fd; filepath = g_strdup_printf("%s/.mms/mms_XXXXXX.tmp", g_get_home_dir()); if (filepath == NULL) return; fd = g_mkstemp_full(filepath, O_WRONLY | O_CREAT, S_IWUSR | S_IRUSR); if (fd < 0) { g_free(filepath); return; } if (g_test_verbose()) g_print("tmp filename : %s\n", filepath); ret = mms_message_encode(&test_msg->msg, fd); close(fd); if (ret == TRUE) ret = check_encoded_msg(filepath, test_msg->pdu); unlink(filepath); g_free(filepath); g_assert(ret == TRUE); } int main(int argc, char **argv) { g_test_init(&argc, &argv, NULL); g_test_add_data_func("/mmsutil/Decode MMS M-Notify.Ind PDU 1", &mms_m_notify_ind_test_1, test_decode_mms); g_test_add_data_func("/mmsutil/Decode MMS M-Notify.Ind PDU 2", &mms_m_notify_ind_test_2, test_decode_mms); g_test_add_data_func("/mmsutil/Decode MMS M-Notify.Ind PDU 3", &mms_m_notify_ind_test_3, test_decode_mms); g_test_add_data_func("/mmsutil/Decode MMS M-Notify.Ind PDU 4", &mms_m_notify_ind_test_4, test_decode_mms); g_test_add_data_func("/mmsutil/Decode MMS M-Retrieve.Conf PDU 1", &mms_m_retrieve_conf_test_1, test_decode_mms); g_test_add_data_func("/mmsutil/Decode MMS M-Retrieve.Conf PDU 2", &mms_m_retrieve_conf_test_2, test_decode_mms); g_test_add_data_func("/mmsutil/Decode MMS M-Retrieve.Conf PDU 3", &mms_m_retrieve_conf_test_3, test_decode_mms); g_test_add_data_func("/mmsutil/Decode MMS M-Retrieve.Conf PDU 4", &mms_m_retrieve_conf_test_4, test_decode_mms); g_test_add_data_func("/mmsutil/Decode MMS M-Retrieve.Conf PDU 5", &mms_m_retrieve_conf_test_5, test_decode_mms); g_test_add_data_func("/mmsutil/Decode MMS M-Retrieve.Conf PDU 6", &mms_m_retrieve_conf_test_6, test_decode_mms); g_test_add_data_func("/mmsutil/Decode MMS M-Retrieve.Conf PDU 7", &mms_m_retrieve_conf_test_7, test_decode_mms); g_test_add_data_func("/mmsutil/Decode MMS M-Retrieve.Conf PDU 8", &mms_m_retrieve_conf_test_8, test_decode_mms); g_test_add_data_func("/mmsutil/Decode MMS M-Retrieve.Conf PDU 9", &mms_m_retrieve_conf_test_9, test_decode_mms); g_test_add_data_func("/mmsutil/Decode MMS M-Retrieve.Conf PDU 10", &mms_m_retrieve_conf_test_10, test_decode_mms); g_test_add_data_func("/mmsutil/Decode MMS M-Retrieve.Conf PDU 11", &mms_m_retrieve_conf_test_11, test_decode_mms); g_test_add_data_func("/mmsutil/Decode MMS M-Retrieve.Conf PDU 12", &mms_m_retrieve_conf_test_12, test_decode_mms); g_test_add_data_func("/mmsutil/Decode MMS M-Retrieve.Conf PDU 13", &mms_m_retrieve_conf_test_13, test_decode_mms); g_test_add_data_func("/mmsutil/Decode MMS M-Retrieve.Conf PDU 14", &mms_m_retrieve_conf_test_14, test_decode_mms); g_test_add_data_func("/mmsutil/Decode MMS M-Retrieve.Conf PDU 15", &mms_m_retrieve_conf_test_15, test_decode_mms); g_test_add_data_func("/mmsutil/Decode MMS M-Send.Req PDU 1", &mms_m_send_req_dec_test_1, test_decode_mms); g_test_add_data_func("/mmsutil/Decode MMS M-Send.Req PDU 2", &mms_m_send_req_dec_test_2, test_decode_mms); g_test_add_data_func("/mmsutil/Decode MMS M-Send.Conf PDU 1", &mms_m_send_conf_test_1, test_decode_mms); g_test_add_data_func("/mmsutil/Decode MMS M-Send.Conf PDU 2", &mms_m_send_conf_test_2, test_decode_mms); g_test_add_data_func("/mmsutil/Encode MMS M-NotifyResp.Ind 1", &mms_m_notifyresp_ind_test_1, test_encode_mms); g_test_add_data_func("/mmsutil/Encode MMS M-SendReq 1", &mms_m_send_req_test_1, test_encode_mms); return g_test_run(); } mmsd-0.1/unit/test-wsputil.c000066400000000000000000000254231216024552400161300ustar00rootroot00000000000000/* * * Multimedia Messaging Service * * Copyright (C) 2010-2011 Intel Corporation. All rights reserved. * * 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 "wsputil.h" static void dump_field(enum wsp_value_type type, const void *data, unsigned int len) { switch (type) { case WSP_VALUE_TYPE_LONG: { unsigned int i; const unsigned char *l = data; for (i = 0; i < len; i++) { g_print("%02x ", l[i]); if ((i % 32) == 31) g_print("\n"); } g_print("\n"); break; } case WSP_VALUE_TYPE_SHORT: { const unsigned char *s = data; g_print("%02x\n", s[0] & 0x7f); break; } case WSP_VALUE_TYPE_TEXT: g_print("%s\n", (const char *) data); break; } g_print("Field length: %d\n", len); } static const unsigned char push1[] = { 0x01, 0x06, 0x24, 0x61, 0x70, 0x70, 0x6C, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x2F, 0x76, 0x6E, 0x64, 0x2E, 0x77, 0x61, 0x70, 0x2E, 0x6D, 0x6D, 0x73, 0x2D, 0x6D, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x00, 0xAF, 0x84, 0xB4, 0x86, 0x8C, 0x82, 0x98, 0x4F, 0x67, 0x51, 0x4B, 0x4B, 0x42, 0x00, 0x8D, 0x90, 0x89, 0x08, 0x80, 0x45, 0x72, 0x6F, 0x74, 0x69, 0x6B, 0x00, 0x96, 0x50, 0x69, 0x6E, 0x2D, 0x55, 0x70, 0x73, 0x00, 0x8A, 0x80, 0x8E, 0x02, 0x40, 0x00, 0x88, 0x05, 0x81, 0x03, 0x03, 0xF4, 0x80, 0x83, 0x68, 0x74, 0x74, 0x70, 0x3A, 0x2F, 0x2F, 0x65, 0x70, 0x73, 0x33, 0x2E, 0x64, 0x65, 0x2F, 0x4F, 0x2F, 0x5A, 0x39, 0x49, 0x5A, 0x4F, 0x00 }; static const unsigned char push2[] = { 0x00, 0x06, 0x24, 0x61, 0x70, 0x70, 0x6C, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x2F, 0x76, 0x6E, 0x64, 0x2E, 0x77, 0x61, 0x70, 0x2E, 0x6D, 0x6D, 0x73, 0x2D, 0x6D, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x00, 0xAF, 0x84, 0x8D, 0xDC, 0x8C, 0x82, 0x98, 0x77, 0x4C, 0x4A, 0x65, 0x54, 0x37, 0x54, 0x48, 0x75, 0x00, 0x8D, 0x90, 0x89, 0x17, 0x80, 0x31, 0x35, 0x35, 0x35, 0x31, 0x32, 0x33, 0x30, 0x30, 0x30, 0x30, 0x2F, 0x54, 0x59, 0x50, 0x45, 0x3D, 0x50, 0x4C, 0x4D, 0x4E, 0x00, 0x8A, 0x80, 0x8E, 0x02, 0x5A, 0x1D, 0x88, 0x05, 0x81, 0x03, 0x03, 0xF4, 0x80, 0x83, 0x68, 0x74, 0x74, 0x70, 0x3A, 0x2F, 0x2F, 0x6D, 0x6D, 0x73, 0x63, 0x31, 0x31, 0x3A, 0x31, 0x30, 0x30, 0x32, 0x31, 0x2F, 0x6D, 0x6D, 0x73, 0x63, 0x2F, 0x31, 0x5F, 0x31, 0x3F, 0x77, 0x4C, 0x4A, 0x65, 0x54, 0x37, 0x54, 0x48, 0x75, 0x00 }; static const unsigned char push3[] = { 0x4C, 0x06, 0x03, 0xCB, 0xAF, 0x88, 0x03, 0x0E, 0x6A, 0x00, 0xC5, 0x05, 0x85, 0x06, 0x86, 0x07, 0x87, 0x01, 0x46, 0x47, 0x03, 0x31, 0x2E, 0x30, 0x00, 0x01, 0x01, 0x49, 0x4A, 0x46, 0x48, 0x03, 0x63, 0x69, 0x64, 0x3A, 0x32, 0x30, 0x30, 0x35, 0x40, 0x67, 0x72, 0x61, 0x6E, 0x6A, 0x65, 0x2E, 0x63, 0x6F, 0x6D, 0x00, 0x01, 0x01, 0x4B, 0x4C, 0xC3, 0x10, 0xF1, 0xF7, 0x7C, 0x37, 0x95, 0xD3, 0x39, 0x65, 0x84, 0x1E, 0x4A, 0x27, 0xA6, 0xC2, 0x71, 0xDB, 0x01, 0x01, 0x01, 0x4D, 0x4F, 0x52, 0x53, 0x03, 0x32, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01 }; static const unsigned char push4[] = { 0xB7, 0x06, 0x03, 0xC4, 0xAF, 0x87, 0x00, 0xEF, 0xC4, 0x29, 0x75, 0x46, 0xCA, 0xD6, 0xC5, 0x51, 0x08, 0xF1, 0x60, 0xBD, 0xB8, 0x00, 0x03, 0x38, 0x00, 0x00, 0x00, 0x00, 0x48, 0x08, 0x66, 0x75, 0x6E, 0x61, 0x6D, 0x62, 0x6F, 0x6C }; static const unsigned char push5[] = { 0xC4, 0x06, 0x2F, 0x1F, 0x2D, 0xB6, 0x91, 0x81, 0x92, 0x35, 0x39, 0x44, 0x33, 0x36, 0x44, 0x31, 0x34, 0x45, 0x38, 0x38, 0x34, 0x39, 0x39, 0x41, 0x33, 0x44, 0x35, 0x36, 0x45, 0x42, 0x41, 0x36, 0x44, 0x34, 0x46, 0x39, 0x34, 0x41, 0x36, 0x37, 0x30, 0x34, 0x31, 0x42, 0x44, 0x32, 0x41, 0x39, 0x43, 0x00, 0x03, 0x0B, 0x6A, 0x00, 0x45, 0xC6, 0x00, 0x01, 0x55, 0x01, 0x87, 0x36, 0x06, 0x03, 0x77, 0x32, 0x00, 0x01, 0x87, 0x22, 0x06, 0x03, 0x49, 0x4E, 0x54, 0x45, 0x52, 0x4E, 0x45, 0x54, 0x00, 0x01, 0xC6, 0x59, 0x01, 0x87, 0x3A, 0x06, 0x03, 0x68, 0x74, 0x74, 0x70, 0x3A, 0x2F, 0x2F, 0x75, 0x73, 0x65, 0x72, 0x70, 0x69, 0x6E, 0x31, 0x32, 0x33, 0x34, 0x00, 0x01, 0x87, 0x1C, 0x01, 0x01, 0x01, 0x01, }; struct push_test { const unsigned char *pdu; unsigned int len; }; /* * MMS Push 1 * This PDU shows a MMS push message with below content: * Overall Push Length: 107 * TID: 1 * Type: 6 * Push Content + Header Length: 36 * Content-Type: application/vnd.wap.mms-message * Header: Well known: 0x2F -> X-Wap-Application-Id * Value: 0x04 -> x-wap-application:mms.ua * Field length: 1 * Header: Well known: 0x34 -> Push-Flag * Value: 0x06 * Field length: 1 * Body Length: 68 */ static const struct push_test push_test_1 = { .pdu = push1, .len = sizeof(push1), }; /* * MMS Push 2 * This PDU shows a MMS push message with below content: * Overall Push Length: 132 * TID: 0 * Type: 6 * Push Content + Header Length: 36 * Content-Type: application/vnd.wap.mms-message * Header: Well known: 0x2F -> X-Wap-Application-Id * Value: 0x04 -> x-wap-application:mms.ua * Field length: 1 * Header: Well known: 0x0D -> Content-Length * Value: 0x5C * Field length: 1 * Body Length: 93 */ static const struct push_test push_test_2 = { .pdu = push2, .len = sizeof(push2), }; /* * DRM Push * This PDU shows a DRM push message with below content: * DRM Push * Overall Push Length: 90 * TID: 76 * Type: 6 * Push Content + Header Length: 3 * Content-Type: application/vnd.oma.drm.rights+wbxml * Header: Well known: 0x2F -> X-Wap-Application-Id * Value: 0x08 -> x-wap-application:drm.ua * Field length: 1 * Body Length: 84 */ static const struct push_test push_test_3 = { .pdu = push3, .len = sizeof(push3), }; /* * DM Push * This PDU shows a DM push message with below content: * Overall Push Length: 38 * TID: 183 * Type: 6 * Push Content + Header Length: 3 * Content-Type: application/vnd.syncml.notification * Header: Well known: 0x2F -> X-Wap-Application-Id * Value: 0x07 -> x-wap-application:syncml.dm * Field length: 1 * Body Length: 32 */ static const struct push_test push_test_4 = { .pdu = push4, .len = sizeof(push4), }; /* * CP Push * This PDU shows a CP push message with below content: * Overall Push Length: 115 * TID: 196 * Type: 6 * Push Content + Header Length: 47 * Content-Type: application/vnd.wap.connectivity-wbxml * Body Length: 65 */ static const struct push_test push_test_5 = { .pdu = push5, .len = sizeof(push5), }; static void test_decode_push(gconstpointer data) { const struct push_test *test = data; const unsigned char *pdu = test->pdu; unsigned int len = test->len; unsigned int headerslen; const void *content_data; struct wsp_header_iter iter; gboolean ret; unsigned int nread; unsigned int consumed; unsigned int param_len; g_assert(pdu[1] == 0x06); /* Consume TID and Type */ nread = 2; ret = wsp_decode_uintvar(pdu + nread, len, &headerslen, &consumed); g_assert(ret == TRUE); /* Consume uintvar bytes */ nread += consumed; if (g_test_verbose()) { g_print("Overall Push Length: %d\n", len); g_print("TID: %d\n", pdu[0]); g_print("Type: %d\n", pdu[1]); g_print("Push Content + Header Length: %d\n", headerslen); } ret = wsp_decode_content_type(pdu + nread, headerslen, &content_data, &consumed, ¶m_len); g_assert(ret == TRUE); /* Consume Content Type bytes, including parameters */ consumed += param_len; nread += consumed; if (g_test_verbose()) g_print("Content-Type: %s\n", (const char *) content_data); wsp_header_iter_init(&iter, pdu + nread, headerslen - consumed, 0); while (wsp_header_iter_next(&iter)) { const void *hdr = wsp_header_iter_get_hdr(&iter); const void *val = wsp_header_iter_get_val(&iter); const unsigned char *wk; const void *urn; if (g_test_verbose()) g_print("Header: "); switch (wsp_header_iter_get_hdr_type(&iter)) { case WSP_HEADER_TYPE_WELL_KNOWN: wk = hdr; if (g_test_verbose()) g_print("Well known %02x\n", wk[0] & 0x7f); if ((wk[0] & 0x7f) == WSP_HEADER_TOKEN_APP_ID) { ret = wsp_decode_application_id(&iter, &urn); g_assert(ret == TRUE); if (g_test_verbose()) g_print("app_id=%s\n", (const char *)urn); } break; case WSP_HEADER_TYPE_APPLICATION: if (g_test_verbose()) g_print("Application: %s\n", (const char *) hdr); break; } if (g_test_verbose()) { g_print("Value: "); dump_field(wsp_header_iter_get_val_type(&iter), val, wsp_header_iter_get_val_len(&iter)); } } if (g_test_verbose()) g_print("Body Length: %d\n", len - nread - headerslen + consumed); } struct text_header_iter_test { const char *header; gboolean success; const char *key; const char *value; const char *dict[]; }; static const struct text_header_iter_test text_header_iter_1 = { .header = "Content-Type: \"text/html\"; charset=ISO-8859-4; q; bar", .success = TRUE, .key = "Content-Type", .value = "text/html", .dict = { "charset", "ISO-8859-4", "q", NULL, "bar", NULL, NULL, NULL },}; static void test_wsp_text_header_iter(gconstpointer data) { const struct text_header_iter_test *test = data; struct wsp_text_header_iter iter; gboolean r; int i; r = wsp_text_header_iter_init(&iter, test->header); g_assert(r == test->success); if (r == FALSE) return; g_assert_cmpstr(test->key, ==, wsp_text_header_iter_get_key(&iter)); g_assert_cmpstr(test->value, ==, wsp_text_header_iter_get_value(&iter)); for (i = 0; test->dict[i] != NULL; i++) { r = wsp_text_header_iter_param_next(&iter); g_assert(r == TRUE); g_assert_cmpstr(test->dict[i++], ==, wsp_text_header_iter_get_key(&iter)); g_assert_cmpstr(test->dict[i], ==, wsp_text_header_iter_get_value(&iter)); } } int main(int argc, char **argv) { g_test_init(&argc, &argv, NULL); g_test_add_data_func("/wsputil/Decode MMS Push 1", &push_test_1, test_decode_push); g_test_add_data_func("/wsputil/Decode MMS Push 2", &push_test_2, test_decode_push); g_test_add_data_func("/wsputil/Decode DRM Push", &push_test_3, test_decode_push); g_test_add_data_func("/wsputil/Decode DM Push", &push_test_4, test_decode_push); g_test_add_data_func("/wsputil/Decode CP Push", &push_test_5, test_decode_push); g_test_add_data_func("/wsputil/WSP Text Header Iter Test 1", &text_header_iter_1, test_wsp_text_header_iter); return g_test_run(); }