pax_global_header00006660000000000000000000000064125154126130014512gustar00rootroot0000000000000052 comment=37ad7111424725a5446e82dac516d7cab88149c7 telepathy-ring-2.3.24-37ad7111424725a5446e82dac516d7cab88149c7/000077500000000000000000000000001251541261300217545ustar00rootroot00000000000000telepathy-ring-2.3.24-37ad7111424725a5446e82dac516d7cab88149c7/.gitignore000066400000000000000000000016531251541261300237510ustar00rootroot00000000000000/m4/libtool.m4 /m4/ltoptions.m4 /m4/ltsugar.m4 /m4/ltversion.m4 /m4/lt~obsolete.m4 /install-sh /ltmain.sh /depcomp /missing /libtool /aclocal.m4 /autom4te.cache /config.* /configure /modem/tests/test-modem /modem/signals-marshal.c /modem/signals-marshal.h /debian/stamp-autogen /debian/stamp-autotools-files /ring-extensions/all.xml /ring-extensions/_gen /sms-glib/tests/test-sms-glib /stamp-h1 *.[oa] *.lo *.la *.pyc *.pyo .*.swp .deps .libs Makefile.in Makefile cscope.out tags /telepathy-ring-*.tar.gz /src/telepathy-ring /src/org.freedesktop.Telepathy.ConnectionManager.ring.service /tools/telepathy-glib-env /configure.lineno /modem/_gen /src/test-ring tests/twisted/config.py tests/twisted/ring-twisted-tests.list tests/twisted/run-test.sh tests/twisted/tools/exec-with-log.sh tests/twisted/tools/org.freedesktop.Telepathy.ConnectionManager.ring.service tests/twisted/tools/ring-testing.log tests/twisted/tools/tmp-session-bus.conf telepathy-ring-2.3.24-37ad7111424725a5446e82dac516d7cab88149c7/AUTHORS000066400000000000000000000000561251541261300230250ustar00rootroot00000000000000Pekka.Pessi@nokia.com lassi.syrjala@nokia.com telepathy-ring-2.3.24-37ad7111424725a5446e82dac516d7cab88149c7/COPYING000066400000000000000000000634721251541261300230230ustar00rootroot00000000000000 GNU LESSER GENERAL PUBLIC LICENSE Version 2.1, February 1999 Copyright (C) 1991, 1999 Free Software Foundation, Inc. 51 Franklin Street, 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. [This is the first released version of the Lesser GPL. It also counts as the successor of the GNU Library Public License, version 2, hence the version number 2.1.] Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public Licenses are intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This license, the Lesser General Public License, applies to some specially designated software packages--typically libraries--of the Free Software Foundation and other authors who decide to use it. You can use it too, but we suggest you first think carefully about whether this license or the ordinary General Public License is the better strategy to use in any particular case, based on the explanations below. When we speak of free software, we are referring to freedom of use, 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 and use pieces of it in new free programs; and that you are informed that you can do these things. To protect your rights, we need to make restrictions that forbid distributors to deny you these rights or to ask you to surrender these rights. These restrictions translate to certain responsibilities for you if you distribute copies of the library or if you modify it. For example, if you distribute copies of the library, whether gratis or for a fee, you must give the recipients all the rights that we gave you. You must make sure that they, too, receive or can get the source code. If you link other code with the library, you must provide complete object files to the recipients, so that they can relink them with the library after making changes to the library and recompiling it. And you must show them these terms so they know their rights. We protect your rights with a two-step method: (1) we copyright the library, and (2) we offer you this license, which gives you legal permission to copy, distribute and/or modify the library. To protect each distributor, we want to make it very clear that there is no warranty for the free library. Also, if the library is modified by someone else and passed on, the recipients should know that what they have is not the original version, so that the original author's reputation will not be affected by problems that might be introduced by others. Finally, software patents pose a constant threat to the existence of any free program. We wish to make sure that a company cannot effectively restrict the users of a free program by obtaining a restrictive license from a patent holder. Therefore, we insist that any patent license obtained for a version of the library must be consistent with the full freedom of use specified in this license. Most GNU software, including some libraries, is covered by the ordinary GNU General Public License. This license, the GNU Lesser General Public License, applies to certain designated libraries, and is quite different from the ordinary General Public License. We use this license for certain libraries in order to permit linking those libraries into non-free programs. When a program is linked with a library, whether statically or using a shared library, the combination of the two is legally speaking a combined work, a derivative of the original library. The ordinary General Public License therefore permits such linking only if the entire combination fits its criteria of freedom. The Lesser General Public License permits more lax criteria for linking other code with the library. We call this license the "Lesser" General Public License because it does Less to protect the user's freedom than the ordinary General Public License. It also provides other free software developers Less of an advantage over competing non-free programs. These disadvantages are the reason we use the ordinary General Public License for many libraries. However, the Lesser license provides advantages in certain special circumstances. For example, on rare occasions, there may be a special need to encourage the widest possible use of a certain library, so that it becomes a de-facto standard. To achieve this, non-free programs must be allowed to use the library. A more frequent case is that a free library does the same job as widely used non-free libraries. In this case, there is little to gain by limiting the free library to free software only, so we use the Lesser General Public License. In other cases, permission to use a particular library in non-free programs enables a greater number of people to use a large body of free software. For example, permission to use the GNU C Library in non-free programs enables many more people to use the whole GNU operating system, as well as its variant, the GNU/Linux operating system. Although the Lesser General Public License is Less protective of the users' freedom, it does ensure that the user of a program that is linked with the Library has the freedom and the wherewithal to run that program using a modified version of the Library. The precise terms and conditions for copying, distribution and modification follow. Pay close attention to the difference between a "work based on the library" and a "work that uses the library". The former contains code derived from the library, whereas the latter must be combined with the library in order to run. GNU LESSER GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License Agreement applies to any software library or other program which contains a notice placed by the copyright holder or other authorized party saying it may be distributed under the terms of this Lesser General Public License (also called "this License"). Each licensee is addressed as "you". A "library" means a collection of software functions and/or data prepared so as to be conveniently linked with application programs (which use some of those functions and data) to form executables. The "Library", below, refers to any such software library or work which has been distributed under these terms. A "work based on the Library" means either the Library or any derivative work under copyright law: that is to say, a work containing the Library or a portion of it, either verbatim or with modifications and/or translated straightforwardly into another language. (Hereinafter, translation is included without limitation in the term "modification".) "Source code" for a work means the preferred form of the work for making modifications to it. For a library, 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 library. Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running a program using the Library is not restricted, and output from such a program is covered only if its contents constitute a work based on the Library (independent of the use of the Library in a tool for writing it). Whether that is true depends on what the Library does and what the program that uses the Library does. 1. You may copy and distribute verbatim copies of the Library's complete 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 distribute a copy of this License along with the Library. 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 Library or any portion of it, thus forming a work based on the Library, 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) The modified work must itself be a software library. b) You must cause the files modified to carry prominent notices stating that you changed the files and the date of any change. c) You must cause the whole of the work to be licensed at no charge to all third parties under the terms of this License. d) If a facility in the modified Library refers to a function or a table of data to be supplied by an application program that uses the facility, other than as an argument passed when the facility is invoked, then you must make a good faith effort to ensure that, in the event an application does not supply such function or table, the facility still operates, and performs whatever part of its purpose remains meaningful. (For example, a function in a library to compute square roots has a purpose that is entirely well-defined independent of the application. Therefore, Subsection 2d requires that any application-supplied function or table used by this function must be optional: if the application does not supply it, the square root function must still compute square roots.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Library, 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 Library, 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 Library. In addition, mere aggregation of another work not based on the Library with the Library (or with a work based on the Library) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may opt to apply the terms of the ordinary GNU General Public License instead of this License to a given copy of the Library. To do this, you must alter all the notices that refer to this License, so that they refer to the ordinary GNU General Public License, version 2, instead of to this License. (If a newer version than version 2 of the ordinary GNU General Public License has appeared, then you can specify that version instead if you wish.) Do not make any other change in these notices. Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General Public License applies to all subsequent copies and derivative works made from that copy. This option is useful when you wish to copy part of the code of the Library into a program that is not a library. 4. You may copy and distribute the Library (or a portion or derivative of it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you 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. If distribution of 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 satisfies the requirement to distribute the source code, even though third parties are not compelled to copy the source along with the object code. 5. A program that contains no derivative of any portion of the Library, but is designed to work with the Library by being compiled or linked with it, is called a "work that uses the Library". Such a work, in isolation, is not a derivative work of the Library, and therefore falls outside the scope of this License. However, linking a "work that uses the Library" with the Library creates an executable that is a derivative of the Library (because it contains portions of the Library), rather than a "work that uses the library". The executable is therefore covered by this License. Section 6 states terms for distribution of such executables. When a "work that uses the Library" uses material from a header file that is part of the Library, the object code for the work may be a derivative work of the Library even though the source code is not. Whether this is true is especially significant if the work can be linked without the Library, or if the work is itself a library. The threshold for this to be true is not precisely defined by law. If such an object file uses only numerical parameters, data structure layouts and accessors, and small macros and small inline functions (ten lines or less in length), then the use of the object file is unrestricted, regardless of whether it is legally a derivative work. (Executables containing this object code plus portions of the Library will still fall under Section 6.) Otherwise, if the work is a derivative of the Library, you may distribute the object code for the work under the terms of Section 6. Any executables containing that work also fall under Section 6, whether or not they are linked directly with the Library itself. 6. As an exception to the Sections above, you may also combine or link a "work that uses the Library" with the Library to produce a work containing portions of the Library, and distribute that work under terms of your choice, provided that the terms permit modification of the work for the customer's own use and reverse engineering for debugging such modifications. You must give prominent notice with each copy of the work that the Library is used in it and that the Library and its use are covered by this License. You must supply a copy of this License. If the work during execution displays copyright notices, you must include the copyright notice for the Library among them, as well as a reference directing the user to the copy of this License. Also, you must do one of these things: a) Accompany the work with the complete corresponding machine-readable source code for the Library including whatever changes were used in the work (which must be distributed under Sections 1 and 2 above); and, if the work is an executable linked with the Library, with the complete machine-readable "work that uses the Library", as object code and/or source code, so that the user can modify the Library and then relink to produce a modified executable containing the modified Library. (It is understood that the user who changes the contents of definitions files in the Library will not necessarily be able to recompile the application to use the modified definitions.) b) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (1) uses at run time a copy of the library already present on the user's computer system, rather than copying library functions into the executable, and (2) will operate properly with a modified version of the library, if the user installs one, as long as the modified version is interface-compatible with the version that the work was made with. c) Accompany the work with a written offer, valid for at least three years, to give the same user the materials specified in Subsection 6a, above, for a charge no more than the cost of performing this distribution. d) If distribution of the work is made by offering access to copy from a designated place, offer equivalent access to copy the above specified materials from the same place. e) Verify that the user has already received a copy of these materials or that you have already sent this user a copy. For an executable, the required form of the "work that uses the Library" must include any data and utility programs needed for reproducing the executable from it. However, as a special exception, the materials to be 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. It may happen that this requirement contradicts the license restrictions of other proprietary libraries that do not normally accompany the operating system. Such a contradiction means you cannot use both them and the Library together in an executable that you distribute. 7. You may place library facilities that are a work based on the Library side-by-side in a single library together with other library facilities not covered by this License, and distribute such a combined library, provided that the separate distribution of the work based on the Library and of the other library facilities is otherwise permitted, and provided that you do these two things: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities. This must be distributed under the terms of the Sections above. b) Give prominent notice with the combined library of the fact that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 8. You may not copy, modify, sublicense, link with, or distribute the Library except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, link with, or distribute the Library 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. 9. 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 Library or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Library (or any work based on the Library), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Library or works based on it. 10. Each time you redistribute the Library (or any work based on the Library), the recipient automatically receives a license from the original licensor to copy, distribute, link with or modify the Library 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 with this License. 11. 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 Library at all. For example, if a patent license would not permit royalty-free redistribution of the Library 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 Library. 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. 12. If the distribution and/or use of the Library is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Library 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. 13. The Free Software Foundation may publish revised and/or new versions of the Lesser 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 Library 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 Library does not specify a license version number, you may choose any version ever published by the Free Software Foundation. 14. If you wish to incorporate parts of the Library into other free programs whose distribution conditions are incompatible with these, 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 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY "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 LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. 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 LIBRARY 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 LIBRARY (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 LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), 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 Libraries If you develop a new library, and you want it to be of the greatest possible use to the public, we recommend making it free software that everyone can redistribute and change. You can do so by permitting redistribution under these terms (or, alternatively, under the terms of the ordinary General Public License). To apply these terms, attach the following notices to the library. 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 library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Also add information on how to contact you by electronic and paper mail. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the library, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the library `Frob' (a library for tweaking knobs) written by James Random Hacker. , 1 April 1990 Ty Coon, President of Vice That's all there is to it! telepathy-ring-2.3.24-37ad7111424725a5446e82dac516d7cab88149c7/ChangeLog000066400000000000000000000017211251541261300235270ustar00rootroot00000000000000release 2.2.1 Added always_dispatch flag to mcp account release 2.2.0 Added mission-control plugin release 2.1.3 BMC#12164, 12932: emitting correct message type Added manual page, updated documentation Not using sms-glib Add manager for Conference channels Fix problems closing channels when disconnecting release 2.1.2 Fix emitting Closed and ChannelClosed signals Implement Channel.Interface.SMS Improved conferencing support. BMC#12873: implement Telepathy Hold model properly with oFono release 2.1.1 Initial conferencing support. fd.o #32718: return InvalidHandle if calling to self or anonymous handle fd.o #30954: advertize Message mixin immutable properties fd.o #31726: use TpBaseChannel fd.o #31664: avoid deprecated tp_get_bus release 2.1.0 Initial release for oFono 0.33 and later. Refactored modem interface. Basic calls and sending SMSs work. release 2.0.1 Initial release for oFono 0.26. Basic calls, sending and receiving SMSs work. telepathy-ring-2.3.24-37ad7111424725a5446e82dac516d7cab88149c7/HACKING000066400000000000000000000141361251541261300227500ustar00rootroot00000000000000GENERAL ======= Telepathy-Ring is a GSM connection manager that provides call and messaging services through the standard Telepathy interfaces on the session bus. Telepathy-Ring was originally written atop CSD, the Cellular Services Daemon shipped on the Nokia N900. The current backend (work in progress...) is Ofono . REQUIRED LIBRARIES ================== Telepathy-Ring is based on telepathy-glib and rtcom-telepathy-glib. The latter will, however, be made redundant soon enough when the ring-specific interfaces hit upstream. The unit testing framework of choice is 'check'. There is a Python test script (tests/check-ring.py) based on python-telepathy, python-dbus, and python-gobject. CONNECTION MANAGER AND CONNECTION OBJECT ======================================== The connection manager itself can be found in "src/" subdirectory. Each object is in a module of its own. The connection object is based on TpBaseConnection and implements two 'requestotron'-supporting channel manager objects: RingMediaManager and RingTextManager. It also supports the standard 'Contacts' and 'Capabilities' interfaces. The extension interface 'Emergency' can be used to query the known emergency numbers. The extension interface 'StoredMessages' is used to expunge SMSes spooled on disk. The GSM interface is used to access GSM-specific settings. CONTACT HANDLES =============== The RingConnection object manages the contact handles. The function ring_normalize_name() in ring-connection.c processes contact handles, or normalizes them. It gets called when a Telepathy client calls RequestHandles in order to call or send an SMS, or when a call or an SMS is received from network. The contact handles can be phone numbers (e.g., "+12345556789"), emergency call urns ("urn:service:sos"), or, strings of 11 UTF-8 characters (SMS TP-Originator-Address in alphanumeric format). Note that ring_normalize_name() strips away whitespace and commonly used punctuation from phone numbers (i.e., "+1-(234)-555-6789" becomes "+12345556789"). As it is not usually possible to get the phone number used by the handset ("MS ISDN") the self handle corresponds to a well-known string "". The alphanumeric SMS addresses are used when an SMS is sent from a service like 'My Nokia'. Please note that alphanumeric SMS addresses are SMSC-specific and they can not be used to send SMSes. Also note that any special handle, such as the self handle, must use a representation that can not be normalized. Anything longer than 11 UTF-8 characters suffices, however, if it cannot be normalized as a phone number. MEDIA CHANNELS ============== RingMediaManager manages two kind of media channels, ordinary peer-to-peer calls (RingCallChannel) and conferencing channels (RingConferenceChannel). The StreamedMedia interface is implemented by RingMediaChannel, which is used as a base object for these two. The RingMediaChannel implements also Hold, DTMF and DialStrings interfaces. Hold interface can be used to put call on hold. DTMF interface is used to send DTMF digits, DialStrings interface dial strings with more automated processing. There are numerous additional channel interfaces required in addition to the StreamedMedia. Group interface is used to accept and release calls. It is implemented in quite different way in the RingCallChannel and RingConferenceChannel, RingCallChannel is a peer-to-peer implementation and RingConferenceChannel can have multiple participants. The CallState interface is used to indicate various states, such as call waiting or remote hold. The Emergency interface is used to handle emergency calls and indicate that the given call has been given emergency priority. The Conference, MergeableConference, and Splittable interfaces are used to control conferences. Setting up a conference requires at least two participants. TONE GENERATION =============== The tone generator service is used to generate DTMF feedback tones and error tones in case a call ends unexpectedly. The tone generation is driven by RingMediaChannel class. Currently only RingCallChannels are using the tone generation. The error tones are generated while the call is in 'released' state until the call enters the 'terminated' state. An alerting tone is played if the network is known not to generate the tone (i.e. if there is no user connection, see 3GPP 24.008 sect. 5.2.1.5). TEXT CHANNELS ============= RingTextManager manages textual channels (sending and receiving SMSes). The implementation supports o.f.T.Channel.Interface.Messages interface with text/plain and text/x-vcard (vCard 2.1) media types. The text/x-calendar (vCalendar 1.0) support is there, but it has not been enabled yet. The SMSes are spooled on disk by the cellular backend, and they have to be explicitly expunged by the client (after it has logged them securely). The client should use the Connection.Interface.StoredMessages interface for this. Text channels used to receive class 0 (flash SMSs) are a special case; it is not possible to send on such a channel. Likewise, a channel created for receiving an SMS with alphanumeric address cannot be sent on. MODEM INTERFACE LIBRARY ======================= Modem interface library found from "modem/" subdirectory is used to communicate with the Ofono services over D-Bus. The library shields the application from usual D-Bus-isms and tries to provide an ordinary GObject-esque experience with it. Note that only the minimal set of functionality used by Telepathy-Ring is provided. The services supported are call (for making CS calls), sms (for sending, receiving, and manipulating spooled SMSs) and sim (for accessing some SIM information). TESTING ======= There is a Python script testing basic functionality (tests/check-ring.py) and a shell script for setting up suitable test environment and running the python script (tests/check-ring.sh). FURTHER READING =============== In addition to the Telepathy specifications, the following 3GPP documents are useful when trying to understand how speech calls and short messages work in GSM and UMTS: * 3GPP TS 22.001, 24.008 (call signaling) * 3GPP TS 24.011, 23,038, 23.040, 27.005 (SMS) * 3GPP TS 11.11 (SIM) telepathy-ring-2.3.24-37ad7111424725a5446e82dac516d7cab88149c7/Makefile.am000066400000000000000000000005501251541261300240100ustar00rootroot00000000000000# # Makefile.am for telepathy-ring # # Copyright (C) 2007 Nokia Corporation # Contact: Pekka Pessi # See file COPYING. # ACLOCAL_AMFLAGS = -I m4 SUBDIRS = tests ring-extensions modem src docs tools scripts mc-plugin TAGS: find src modem ring-extensions -name '*.[hc]' | \ xargs etags EXTRA_DIST = \ autogen.sh .PHONY: TAGS telepathy-ring-2.3.24-37ad7111424725a5446e82dac516d7cab88149c7/README000066400000000000000000000034161251541261300226400ustar00rootroot00000000000000GENERAL ======= Telepathy-Ring is a Telepathy connection manager for GSM and similar mobile telephony. Telepathy-Ring is named after Douglas H. Ring, one of the inventors of cellular mobile phone system. Currently, Telephathy-Ring implements Connection Manager and Connection functionality, StreamedMedia channels for voice calls and Text channels for SMS messaging. ENVIRONMENT VARIABLES AND DEBUGGING =================================== The following environment variables are used: - RING_PERSIST - if set, keep running even if no connection is active - RING_REALTIME - if set, try to use realtime priority - RING_MEMLOCK - if set, use mmlockall() - RING_LOGFILE - redirect debugging output (stdout/stderr) to named file - RING_DEBUG - if set, determine what kind of debugging output to print - all - everything - connection - connection-related - media - call and media-related - text - SMS-related - MODEM_DEBUG - determine what kind of debugging output to print from modem libraries - all - print everything - call - print call-related messages - sms - print sms-related messages - sim - print sim-related messages - dbus - print dbus-related messages - SMS_DEBUG - determine what kind of debugging output to print from sms-glib - all - print everything - submit - encoding SMS-SUBMIT - deliver - decoding SMS-DELIVER - status-report - decoding and handling SMS-STATUS-REPORT REQUIRED LIBRARIES ================== Telepathy-Ring is based on telepathy-glib (0.11.11 or newer) RUNNING WITH ELEVATED PRIORITY ============================== Telepathy-Ring can be launched with RING_REALTIME and RING_MEMLOCK environment variables set. If they are not set, the priorities are read from /var/lib/telepathy-ring/realtime and /var/lib/telepathy-ring/memlock instead. telepathy-ring-2.3.24-37ad7111424725a5446e82dac516d7cab88149c7/acinclude.m4000066400000000000000000000036411251541261300241510ustar00rootroot00000000000000dnl --8<----8<----8<----8<----8<----8<----8<----8<----8<----8<----8<----8<-- dnl as-compiler-flag.m4 0.1.0 dnl autostars m4 macro for detection of compiler flags dnl David Schleef dnl $Id: as-compiler-flag.m4,v 1.1 2005/06/18 18:02:46 burgerman Exp $ dnl AS_COMPILER_FLAG(CFLAGS, ACTION-IF-ACCEPTED, [ACTION-IF-NOT-ACCEPTED]) dnl Tries to compile with the given CFLAGS. dnl Runs ACTION-IF-ACCEPTED if the compiler can compile with the flags, dnl and ACTION-IF-NOT-ACCEPTED otherwise. AC_DEFUN([AS_COMPILER_FLAG],[ AC_MSG_CHECKING([to see if compiler understands $1]) save_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS $1" AC_TRY_COMPILE([ ], [], [flag_ok=yes], [flag_ok=no]) CFLAGS="$save_CFLAGS" if test "X$flag_ok" = Xyes ; then $2 true else $3 true fi AC_MSG_RESULT([$flag_ok]) ]) dnl --8<----8<----8<----8<----8<----8<----8<----8<----8<----8<----8<----8<-- dnl AS_AC_EXPAND(VAR, CONFIGURE_VAR) dnl dnl example dnl AS_AC_EXPAND(SYSCONFDIR, $sysconfdir) dnl will set SYSCONFDIR to /usr/local/etc if prefix=/usr/local AC_DEFUN([AS_AC_EXPAND],[ EXP_VAR=[$1] FROM_VAR=[$2] dnl first expand prefix and exec_prefix if necessary prefix_save=$prefix exec_prefix_save=$exec_prefix dnl if no prefix given, then use /usr/local, the default prefix if test "x$prefix" = "xNONE"; then prefix=$ac_default_prefix fi dnl if no exec_prefix given, then use prefix if test "x$exec_prefix" = "xNONE"; then exec_prefix=$prefix fi full_var="$FROM_VAR" dnl loop until it doesn't change anymore while true; do new_full_var="`eval echo $full_var`" if test "x$new_full_var"="x$full_var"; then break; fi full_var=$new_full_var done dnl clean up full_var=$new_full_var AC_SUBST([$1], "$full_var") dnl restore prefix and exec_prefix prefix=$prefix_save exec_prefix=$exec_prefix_save ]) dnl --8<----8<----8<----8<----8<----8<----8<----8<----8<----8<----8<----8<-- telepathy-ring-2.3.24-37ad7111424725a5446e82dac516d7cab88149c7/autogen.sh000077500000000000000000000015001251541261300237510ustar00rootroot00000000000000#!/bin/sh set -e mkdir -p m4 rm -f m4/libtool.m4 m4/ltoptions.m4 m4/ltsugar.m4 m4/ltversion.m4 m4/lt~obsolete.m4 if test -n "$AUTOMAKE"; then : # don't override an explicit user request elif automake-1.11 --version >/dev/null 2>/dev/null && \ aclocal-1.11 --version >/dev/null 2>/dev/null; then # If we have automake-1.11, use it. This is the oldest version (=> least # likely to introduce undeclared dependencies) that will give us # --enable-silent-rules support. AUTOMAKE=automake-1.11 export AUTOMAKE ACLOCAL=aclocal-1.11 export ACLOCAL fi autoreconf -i -f run_configure=true for arg in $*; do case $arg in --no-configure) run_configure=false ;; *) ;; esac done if test $run_configure = true; then ./configure "$@" fi telepathy-ring-2.3.24-37ad7111424725a5446e82dac516d7cab88149c7/configure.ac000066400000000000000000000104531251541261300242450ustar00rootroot00000000000000AC_INIT([telepathy-ring], [2.2.1]) AC_PREREQ([2.59]) AM_INIT_AUTOMAKE([foreign 1.9]) m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES]) AC_CONFIG_MACRO_DIR([m4]) AM_CONFIG_HEADER([config.h]) AC_PROG_CC AC_PROG_CC_STDC dnl Not yet AM_PROG_LIBTOOL dnl decide on error flags AS_COMPILER_FLAG(-Wall, [ ERROR_CFLAGS="-Wall" if test "x$WERROR" = "xyes"; then AS_COMPILER_FLAG(-Werror,ERROR_CFLAGS="$ERROR_CFLAGS -Werror",ERROR_CFLAGS="$ERROR_CFLAGS") fi ]) AC_SUBST(ERROR_CFLAGS) AM_MAINTAINER_MODE AC_ARG_ENABLE(debug, AC_HELP_STRING([--disable-debug],[omit debug code]),, enable_debug=yes) if test x$enable_debug = xyes; then AC_DEFINE([ENABLE_DEBUG], [1], [Enable debug code]) fi AC_ARG_ENABLE(mc-account-plugin, AS_HELP_STRING([--enable-mc-account-plugin=@<:@no/yes/auto@:>@], [build MC account plugin]), , enable_mc_account_plugin=auto) if test "x$enable_mc_account_plugin" != "xno"; then PKG_CHECK_MODULES(MCP, [ mission-control-plugins ], have_mcp="yes", have_mcp="no") AC_MSG_CHECKING([Mission Control plugins dir]) MISSION_CONTROL_PLUGINS_DIR=${libdir}/mission-control-plugins.`pkg-config --variable=MCP_ABI_VERSION mission-control-plugins` AC_MSG_RESULT([$MISSION_CONTROL_PLUGINS_DIR]) AC_SUBST(MISSION_CONTROL_PLUGINS_DIR) else have_mcp=no fi if test "x$enable_mc_account_plugin" = "xyes" -a "x$have_mcp" != "xyes"; then AC_MSG_ERROR([Could not find mission-control plugin dependencies: $MCP_PKG_ERRORS]) fi AC_HEADER_STDC AC_C_INLINE dnl GTK docs dnl GTK_DOC_CHECK AC_DEFINE([_GNU_SOURCE], [1], [Enable GNU extensions]) PKG_CHECK_MODULES([GLIB], [gobject-2.0 >= 2.4 glib-2.0]) GLIB_GENMARSHAL=`$PKG_CONFIG --variable=glib_genmarshal glib-2.0` AC_SUBST(GLIB_GENMARSHAL) PKG_CHECK_MODULES([CHECK], [check >= 0.9.4]) PKG_CHECK_MODULES([DBUS], [dbus-1 >= 0.60, dbus-glib-1 >= 0.88]) PKG_CHECK_MODULES([UUID], [uuid]) AS_AC_EXPAND(DATADIR, ${datadir}) DBUS_SERVICES_DIR="$DATADIR/dbus-1/services" AC_SUBST(DBUS_SERVICES_DIR) AC_DEFINE_UNQUOTED([DBUS_SERVICES_DIR], "$DBUS_SERVICES_DIR", [DBus services directory]) dnl Check for telepathy-glib PKG_CHECK_MODULES(TP, [telepathy-glib >= 0.11.14]) if pkg-config 'telepathy-glib >= 0.13.3' || pkg-config 'telepathy-glib >= 0.12.2' then AC_DEFINE([HAVE_TP_MESSAGE_MIXIN_WITH_DELI], 1, [Do you have working DeliveryReportingSupport]) fi if pkg-config 'telepathy-glib >= 0.13.10' then AC_DEFINE([HAVE_TP_SMS_CHANNEL], 1, [Do you have SMSChannel on Channel.Interface.SMS]) fi AC_CHECK_PROGS([XSLTPROC], [$XSLTPROC xsltproc]) AC_CHECK_PROGS([DBUS_BINDING_TOOL], [$DBUS_BINDING_TOOL dbus-binding-tool]) AC_CHECK_PROGS([PYTHON], [$PYTHON python2.5 python2.4 python2.3 python]) AM_CONDITIONAL([HAVE_TP_EXTENSIONS], [test -n "$XSLTPROC" && test -n "$PYTHON" && test -n "$DBUS_BINDING_TOOL"]) if ! test -z "$XSLTPROC""$PYTHON""$DBUS_BINDING_TOOL"; then AC_DEFINE([HAVE_TP_EXTENSIONS], 1, [Can you generate extensions]) fi AC_CHECK_FUNC([getresuid]) AC_SEARCH_LIBS(pthread_mutex_trylock, pthread,, AC_ERROR([POSIX threads not available])) AC_CHECK_LIB([mlocknice], [mln_lock_data],, AC_MSG_WARN([Library mlocknice not found])) AC_SUBST([testdir],'/opt/tests/telepathy-ring') AC_SUBST([includemodemdir],'${includedir}/modem-glib') dnl check for a version of python that can run the twisted tests AC_MSG_CHECKING([for Python with Twisted]) for TEST_PYTHON in python2.5 python2.6 python; do AS_IF([$TEST_PYTHON -c "from sys import version_info; import dbus, dbus.mainloop.glib; raise SystemExit(version_info < (2, 5, 0, 'final', 0))" >/dev/null 2>&1], [ AS_IF([$TEST_PYTHON -c "import twisted.internet.reactor" >/dev/null 2>&1], [ AM_CONDITIONAL([WANT_TWISTED_TESTS], [true]) break ], [TEST_PYTHON=no]) ]) done AC_MSG_RESULT([$TEST_PYTHON]) AC_SUBST(TEST_PYTHON) AM_CONDITIONAL([WANT_TWISTED_TESTS], test xno != x$TEST_PYTHON) AC_CONFIG_FILES([Makefile]) AC_CONFIG_FILES([modem/Makefile \ modem/tests/Makefile \ src/Makefile tests/Makefile \ tests/twisted/Makefile \ tests/twisted/tools/Makefile \ docs/Makefile \ scripts/Makefile \ tools/Makefile \ ring-extensions/Makefile \ mc-plugin/Makefile]) AC_OUTPUT telepathy-ring-2.3.24-37ad7111424725a5446e82dac516d7cab88149c7/docs/000077500000000000000000000000001251541261300227045ustar00rootroot00000000000000telepathy-ring-2.3.24-37ad7111424725a5446e82dac516d7cab88149c7/docs/DTMF-playback000066400000000000000000000120641251541261300251500ustar00rootroot00000000000000DTMF Tones, Telepathy and GSM/3G Signaling ========================================== Telepathy-Ring provides two interfaces for sending DTMF digits, DTMF interface used to send single DTMF tones (mainly intended for interactive use) and another for predefined dial strings. Telepathy-Ring indicates that the DTMF transmission takes place with SendingDialString and StoppedDialString signals. The individual tones are played by ToneGenerator, Telepathy-Ring controls the playback with StartEventTone() and StopTone() methods. The traffic channel normally used to transmit audio is taken over by signaling messages during the transmission of DTMF tones on GSM/3G cellular network. As the downlink media is also taken over, Telepathy-Ring uses the ToneGenerator daemon to locally generate the tones sent via signaling messages. DTMF Playback in Telepathy-Ring 0.7 =================================== A local playback of sent DTMF digits was driven by the modem events indicating when it starts to send the dialstring and when each DTMF digit is signaled via the radio channel. Telepathy-Ring relayed these events as Telepathy signals to the Call-UI and the media policy daemon. Call-UI Policy ToneGen Tp-Ring Modem | | | | | |---------StartTone--------->| | | | | |----StartDTMF--->| | | | | | | | | | | | | | |<==SendingDTMF===| | |<=SendingDialString=| | | | | | | | | | | | | | | |<==DTMFTone(*)===| | | |<-StartTone-| | | | | | | ¦ ¦ ¦ ¦ ¦ | | | | | |---------StopTone---------->| | | | | |----StopDTMF---->| | | | | | | | | |<==DTMFTone(-)===| | | |<-StopTone--| | | | | | | | | | |<==StoppedDTMF===| | |<=StoppedDialString=| | | | | | | Based on a very rough estimate there is also ~100..200 millisecond delay from the StartTone to the moment when the playback tone is actually played from the device speakers. Similar delay occurs when tone is stopped. The playback continues some time after StopTone method gets invoked. The echo cancellation does not work efficiently enough in all cases and the locally played DTMF tones can be captured and sent as encoded audio, too. In order to avoid the leakage, the media policy daemon follows the SendingDialString and StoppedDialString signal and mutes and unmutes the microphone respectively. DTMF Playback in Telepathy-Ring 1.0 =================================== The playback for the individial DTMF tones has been changed in version 1.0.0, however. The tone generator is driven by the requests from UI, not by the indications from the modem: Call-UI Policy ToneGen Tp-Ring Modem | | | | | |---------StartTone--------->| | | | | |----StartDTMF--->| | |<=SendingDialString=| | | | |<-StartTone-| | | | | | | | | | |<==SendingDTMF===| ¦ ¦ ¦ ¦ ¦ | | | | | |---------StopTone---------->| | | | | |----StopDTMF---->| | | |<-StopTone--| | | | | | | ¦ ¦ ¦ ¦ ¦ | | | | | | | | |<==DTMFTone(*)===| | | | | | | | | | | | | | |<==DTMFTone(-)===| | | | | | | | | |<==StoppedDTMF===| | |<=StoppedDialString=| | This scenario has different potential DTMF leakage problem. The main difference with previous is much tighter timing between SendingDialString signal and the StartTone method. There is delay of roughly ~240 milliseconds between SendingDTMF and DTMFTone signals from the modem, which controlled sending of SendingDialString signal and StartTone method call. However in this case, the initial SendingDialString signal and StartTone request are sent back-to-back without any delay. There might be a duration when the DTMF playback has already started but the microphone has not been muted nor the DTMF signaling transmission has taken over the radio traffic channel. telepathy-ring-2.3.24-37ad7111424725a5446e82dac516d7cab88149c7/docs/Makefile.am000066400000000000000000000003241251541261300247370ustar00rootroot00000000000000EXTRA_DIST = design.txt DTMF-playback # Man page man_MANS = telepathy-ring.8 CLEANFILES = $(man_MANS) EXTRA_DIST += telepathy-ring.8.in %.8: %.8.in Makefile sed -e 's,[@]libexecdir[@],@libexecdir@,' < $< > $@ telepathy-ring-2.3.24-37ad7111424725a5446e82dac516d7cab88149c7/docs/design.txt000066400000000000000000000226071251541261300247250ustar00rootroot00000000000000title: Design notes for Telepathy-Ring version: 2011-02-04 General ------- Telepathy-ring is a Telepathy connection manager for GSM and similar Circuit Switched (CS) mobile telephony. Currently, it implements basic Connection Manager functionality with Contacts and Capabilities extensions, StreamedMedia channels for voice calls, and Text channel for SMS. Testing ------- For testing basic functionality, use the 'tests/check-ring.sh' script, or separate ltap-phone-script or ltap-sim packages. Use of CS Telephone ------------------- Interface between the host and the CS TE (terminal equipment, in other words, actual phone or modem) is implemented using the "modem" libraries and D-Bus services provided by oFono. The modem library interface provides uniform interface towards the phone, be it physically and software-wise implemented in any way. The Telepathy Connection interface represent the links towards the Call, SMS and SIM services provided by TE. The Call and SMS links are implemented using telepathy-glib ChannelManager interface. Short Overview of Telepathy Media Handling with VoIP Calls ---------------------------------------------------------- The Telepathy framework has been originally developed with VoIP calls in mind. With VoIP. the call signaling and media connections use separate transport streams and they can be handled by separate software elements. For example, when a SIP VoIP call is set up, the phone application can start up a separate stream engine process. The stream engine takes care of the actual media transport and processing. The Telepathy.Channel.Type.StreamedMedia interface is used to represent the signaling connection of a VoIP call within the Telepathy connection manager. The channel provides additional media handler interfaces in order set up and negotiate the separate media connections for the VoIP call. When the call is set up, the new Telepathy.Channel instance is dispatched to the ChannelHandler responsible for the StreamedMedia channels. It is usually the actual call UI application, taking care of user interface and media aspects. While the Telepathy framework provides a Stream Engine package for media processing, it does specify that a Telepathy client should use this particular media stream engine with standard interfaces, however. The media stream engine uses the Telepathy.Channel.Interface.MediaSignaling in order to obtain the call-specific Media.SessionHandler interface(s) from the signaling channel. After signaling channel has been informed that the media engine is ready to set up the media streams, it creates Media.StreamHandler interface instances for each media stream within the session and emits them to the media engine. The Media.StreamHandler interface is then used to negotiate and set up the individual media stream(s). The call UI application itself does not necessarily use the above-mentioned media signaling interfaces directly. It can use Channel.Type.StreamedMedia interface towards the call signaling and the Telepathy.StreamEngine interface towards the media stream engine. Unfortunately, that latter interface is not mandatory part of the Telepathy framework. CS Calls and Media Handling in Telepathy ---------------------------------------- The TelepathyChannel object can be used represent a circuit switched call connection. The optional interfaces like Channel.Interface.DTMF or Channel.Interface.Hold can be used as is with the CS call. However, the media handling is more problematic. The media within a circuit-switched call can be handled with different modes, depending on the processing it requires: 1) TE has its own, independent media handling (e.g., TE is a separate phone connected through USB or Bluetooth) 2) TE uses same media devices as host applications, but it takes care of obtaining the media processing resources by itself (e.g, TE is a GSM module which connects directly to the audio hw) - Note: some audio (or video) pre-emption may be needed even in this case 3) TE emits audio frames via the libcs connection to the host for processing (e.g., TE is a GSM module which has no connection to audio hw, but the host software must take care of encoding and decoding audio) The cases 1 and 2 are referred as "independent media" and case 3 as "shared media" below. Independent media mode requires more attention from the telepathy client handling the CS call channels. Either no media stream engine is started, or all the media stream engines should be able to detect the media mode as described above. The shared media mode looks pretty much like the VoIP case above: a separate media stream engine should be started and it negotiates the media transport connection using the media handler interfaces. However, the existing Media.StreamHandler interface is very VoIP-centric. Even the very basic media semantics are quite different when handling a CS call. There are multiple options for handling the CS calls: 1) Add new Channel type (e.g., CircuitMedia) and ChannelHandlers 2) Add CS call support to the standard Telepathy Stream Engine 3) Add separate CS Stream Engine With the latter option options, the existing telepathy clients that use the Telepathy Stream Engine can make CS calls with no or minimal changes. There are also multiple options handling the the media signaling interface between the CS call channel and the media stream: 1) Do not implement Telepathy.Channel.Interface.MediaSignaling with CS call 2) Integrate CS Stream Engine with CS Connection Manager (and use private interfaces between signaling and media) 3) Add new Telepathy.Channel.Interface (e.g., CircuitMediaSignaling) 4) Extend Media.SessionHandler interface 5) Extend Media.StreamHandler interface The third option may require new interfaces not unlike Media.SessionHandler or Media.StreamHandler for handling different media within a multimedia CS call. The media engine design does not necessarily need any interface towards application. The microphone muting and adjusting volume levels is best done with system-level means and policy. The final design decision was to not to provide any Telepathy interface to control CS call media, but instead tightly couple the media engine with the modem. CS Call Handling Assumptions ---------------------------- The discussion below follows these assumptions: - a CS call object has RingMediaChannel DBUS interface which implements - Telepathy.Channel.Type.StreamedMedia (as is) - Telepathy.Channel.Interface.Group (accepting or rejecting calls) - Telepathy.Channel.Interface.DTMF (sending or receiving DTMF tones) - Telepathy.Channel.Interface.Hold (placing call on hold or retrieving it) There is no Telepathy.Channel.Interface.MediaSignaling. Outbound calls -------------- - client requests a Channel.Type.StreamedMedia with target id (dialed telephone number) using the Connection.Requests.CreateChannel() method - RingCallChannel object (rc) is created - RingConnection emits NewChannel with ringchannel - emit MembersChanged: - selfhandle is added to current members - handle for dialed number is added to the list of remote pending members - client requests channel to set up media streams to remote handle with Telepathy.Channel.Type.StreamedMedia.RequestStreams() method - RingCallChannel calls modem_call_request_dial() (org.ofono.VoiceCallManager.Dial()) - emit StreamAdded() signal for audio stream - RingCallChannel receives MODEM_CALL_STATE_DIALING - emits StreamStateChanged with TP_MEDIA_STREAM_STATE_CONNECTING - RingCallChannel receives MODEM_CALL_STATE_ALERTING - emits StreamStateChanged with TP_MEDIA_STREAM_STATE_CONNECTED - emits StreamDirectionChanged with TP_MEDIA_STREAM_DIRECTION_RECEIVE, TP_MEDIA_STREAM_PENDING_REMOTE_SEND cleared and TP_MEDIA_STREAM_PENDING_LOCAL_SEND set - RingCallChannel receives MODEM_CALL_STATE_ACTIVE - emits MembersChanged signal where remote handle (bob) is added as proper member - emits StreamStateChanged with TP_MEDIA_STREAM_STATE_CONNECTED - emits StreamDirectionChanged with both TP_MEDIA_STREAM_PENDING_REMOTE_SEND and TP_MEDIA_STREAM_PENDING_LOCAL_SEND cleared Incoming calls -------------- - RingConnection gets 'incoming' signal from modem/call-service (Calls property in Ofono) - binds remote telephone number to a handle - creates new RingCallChannel (rc) object for handle with remote user and audio stream - emits NewChannel signal - emit MembersChanged signal: - handle (handle) is added to the list of current members - selfhandle is added to local pending members - emits StreamAdded indicating that an audio stream has been added - NewChannel (and MembersChanged?) is dispatched to client - RingCallChannel receives MODEM_CALL_STATE_{INCOMING,WAITING} - emits StreamStateChanged (TP_MEDIA_STREAM_STATE_CONNECTED) - emits StreamDirectionChanged with TP_MEDIA_STREAM_DIRECTION_SEND, TP_MEDIA_STREAM_PENDING_REMOTE_SEND set and TP_MEDIA_STREAM_PENDING_LOCAL_SEND cleared - client accepts the call with Channel.Interface.Group.AddMembers (adding selfhandle to members) - RingCallChannel invokes modem_call_answer() (VoiceCall.Answer method) - RingCallChannel receives MODEM_CALL_STATE_ACTIVE - emits MembersChanged signal where self handle is added as proper member - emits StreamStateChanged with TP_MEDIA_STREAM_STATE_CONNECTED - emits StreamDirectionChanged with both TP_MEDIA_STREAM_PENDING_REMOTE_SEND and TP_MEDIA_STREAM_PENDING_LOCAL_SEND cleared telepathy-ring-2.3.24-37ad7111424725a5446e82dac516d7cab88149c7/docs/style.el000066400000000000000000000014761251541261300243760ustar00rootroot00000000000000; ; telepathy style for Emacs ; (defconst my-telepathy-c-style '("gnu" (indent-tabs-mode . nil) (c-offsets-alist (brace-list-open . tp-brace-list-open) (arglist-intro . 4) (arglist-cont-nonempty . tp-lineup-arglist-cont))) "C Style for telepathy") (defun tp-brace-list-open (langelem) (save-excursion (goto-char (cdr langelem)) (if (looking-at "\\(\\btypedef\\b\\s-+\\)?\\benum\\b") 0 '+))) (defun tp-lineup-arglist-cont (langelem) (let (syntax) (save-excursion (goto-char (cdr langelem)) (setq syntax (c-guess-basic-syntax))) (if (assq 'topmost-intro-cont syntax) ;; Lineup arglist in function definitions (c-lineup-arglist-intro-after-paren langelem) ;; 'topmost-intro is used in declarations 4))) (c-add-style "telepathy" my-telepathy-c-style) telepathy-ring-2.3.24-37ad7111424725a5446e82dac516d7cab88149c7/docs/telepathy-ring.8.in000066400000000000000000000031401251541261300263340ustar00rootroot00000000000000.TH TELEPATHY-RING "4" "February 2011" "Telepathy" "D-Bus services" \" This man page is based on page written by Simon McVittie for \" telepathy-sofiasip. \" Copyright © 2007-2010 Collabora Ltd. \" Copyright © 2011 Nokia Corporation. \" It may be distributed under the same terms as telepathy-ring itself. .SH NAME telepathy-ring \- Telepathy connection manager for GSM .SH SYNOPSIS \fB@libexecdir@/telepathy\-ring\fR .SH DESCRIPTION \fBtelepathy\-ring\fR implements the Telepathy D-Bus specification for the GSM and UMTS, allowing Telepathy clients like .BR empathy (1) to place mobile calls and send or receive SMSs. It uses .BR ofonod (8) for interfacing with modems. .PP \fBtelepathy\-ring\fR is a D-Bus service which runs on the session bus, and should usually be started automatically by D-Bus activation. However, it might be useful to start it manually for debugging. .SH OPTIONS There are no command-line options. .SH ENVIRONMENT .TP \fBRING_DEBUG\fR=\fItype\fR May be set to "all" for full debug output, or various undocumented options (which may change from release to release) to filter the output. .TP \fBMODEM_DEBUG\fR=\fItype\fR May be set to "all" for full debug output from oFono interface, or various undocumented options (which may change from release to release) to filter the output. .TP \fBRING_PERSIST\fR May be set to any value to avoid telepathy-ring's usual automatic exit when there have been no connections for a few seconds. .TP .SH SEE ALSO .IR http://telepathy.freedesktop.org/ , .BR mission-control-5 (1) .BR empathy (1) .BR ofonod (8) telepathy-ring-2.3.24-37ad7111424725a5446e82dac516d7cab88149c7/mc-plugin/000077500000000000000000000000001251541261300236475ustar00rootroot00000000000000telepathy-ring-2.3.24-37ad7111424725a5446e82dac516d7cab88149c7/mc-plugin/Makefile.am000066400000000000000000000006021251541261300257010ustar00rootroot00000000000000AM_CPPFLAGS = $(MCP_CFLAGS) $(ERROR_CFLAGS) pluginsdir = $(MISSION_CONTROL_PLUGINS_DIR) plugins_LTLIBRARIES = mcp-account-manager-ring.la mcp_account_manager_ring_la_SOURCES = \ mission-control-plugin.c \ mcp-account-manager-ring.h \ mcp-account-manager-ring.c mcp_account_manager_ring_la_LIBADD = $(MCP_LIBS) mcp_account_manager_ring_la_LDFLAGS = -shared -module -avoid-version telepathy-ring-2.3.24-37ad7111424725a5446e82dac516d7cab88149c7/mc-plugin/mcp-account-manager-ring.c000066400000000000000000000153171251541261300306000ustar00rootroot00000000000000/* * Copyright (C) 2012 Jolla Ltd. * Contact: John Brooks * * Based on Empathy ubuntu-online-accounts: * Copyright (C) 2012 Collabora Ltd. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . */ #include "config.h" #include "mcp-account-manager-ring.h" #include #define PLUGIN_NAME "ring-account" #define PLUGIN_PRIORITY (MCP_ACCOUNT_STORAGE_PLUGIN_PRIO_DEFAULT - 10) #define PLUGIN_DESCRIPTION "Provide account for telepathy-ring" #define PLUGIN_PROVIDER "im.telepathy.Account.Storage.Ring" static void account_storage_iface_init(McpAccountStorageIface *iface); G_DEFINE_TYPE_WITH_CODE (McpAccountManagerRing, mcp_account_manager_ring, G_TYPE_OBJECT, G_IMPLEMENT_INTERFACE (MCP_TYPE_ACCOUNT_STORAGE, account_storage_iface_init)); struct _McpAccountManagerRingPrivate { gchar *account_name; GHashTable *params; }; static void mcp_account_manager_ring_dispose(GObject *object) { McpAccountManagerRing *self = (McpAccountManagerRing*) object; g_hash_table_unref(self->priv->params); G_OBJECT_CLASS (mcp_account_manager_ring_parent_class)->dispose(object); } static void mcp_account_manager_ring_init(McpAccountManagerRing *self) { g_debug("MC Ring account plugin initialized"); self->priv = G_TYPE_INSTANCE_GET_PRIVATE(self, MCP_TYPE_ACCOUNT_MANAGER_RING, McpAccountManagerRingPrivate); self->priv->account_name = "ring/tel/account0"; self->priv->params = g_hash_table_new(g_str_hash, g_str_equal); g_hash_table_insert(self->priv->params, g_strdup("manager"), g_strdup("ring")); g_hash_table_insert(self->priv->params, g_strdup("protocol"), g_strdup("tel")); g_hash_table_insert(self->priv->params, g_strdup("DisplayName"), g_strdup("Cellular")); g_hash_table_insert(self->priv->params, g_strdup("Enabled"), g_strdup("true")); g_hash_table_insert(self->priv->params, g_strdup("ConnectAutomatically"), g_strdup("true")); g_hash_table_insert(self->priv->params, g_strdup("always_dispatch"), g_strdup("true")); } static void mcp_account_manager_ring_class_init(McpAccountManagerRingClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS(klass); gobject_class->dispose = mcp_account_manager_ring_dispose; g_type_class_add_private(gobject_class, sizeof(McpAccountManagerRingPrivate)); } static GList *account_manager_ring_list(const McpAccountStorage *storage, const McpAccountManager *am) { McpAccountManagerRing *self = (McpAccountManagerRing*) storage; GList *accounts = NULL; g_debug("%s", G_STRFUNC); accounts = g_list_prepend(accounts, g_strdup(self->priv->account_name)); return accounts; } static gboolean account_manager_ring_get(const McpAccountStorage *storage, const McpAccountManager *am, const gchar *account_name, const gchar *key) { McpAccountManagerRing *self = (McpAccountManagerRing*) storage; if (strcmp(account_name, self->priv->account_name)) return FALSE; if (key == NULL) { GHashTableIter iter; gpointer itkey, value; g_hash_table_iter_init(&iter, self->priv->params); while (g_hash_table_iter_next(&iter, &itkey, &value)) { g_debug("%s: %s, %s %s", G_STRFUNC, account_name, (char*)itkey, (char*)value); mcp_account_manager_set_value(am, account_name, itkey, value); } } else { gchar *value = g_hash_table_lookup(self->priv->params, key); g_debug("%s: %s, %s %s", G_STRFUNC, account_name, (char*)key, (char*)value); mcp_account_manager_set_value(am, account_name, key, value); } return TRUE; } static gboolean account_manager_ring_set(const McpAccountStorage *storage, const McpAccountManager *am, const gchar *account_name, const gchar *key, const gchar *val) { return FALSE; } static gchar *account_manager_ring_create(const McpAccountStorage *storage, const McpAccountManager *am, const gchar *cm_name, const gchar *protocol_name, GHashTable *params, GError **error) { g_set_error(error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT, "Ring account manager cannot create accounts"); return NULL; } static gboolean account_manager_ring_delete(const McpAccountStorage *storage, const McpAccountManager *am, const gchar *account_name, const gchar *key) { g_debug("%s: %s, %s", G_STRFUNC, account_name, key); return FALSE; } static gboolean account_manager_ring_commit(const McpAccountStorage *storage, const McpAccountManager *am) { g_debug("%s", G_STRFUNC); return FALSE; } static void account_manager_ring_get_identifier(const McpAccountStorage *storage, const gchar *account_name, GValue *identifier) { McpAccountManagerRing *self = (McpAccountManagerRing*) storage; if (strcmp(account_name, self->priv->account_name)) return; g_debug("%s: %s", G_STRFUNC, account_name); g_value_init(identifier, G_TYPE_UINT); g_value_set_uint(identifier, 0); } static guint account_manager_ring_get_restrictions(const McpAccountStorage *storage, const gchar *account_name) { McpAccountManagerRing *self = (McpAccountManagerRing*) storage; if (strcmp(account_name, self->priv->account_name)) return G_MAXUINT; return TP_STORAGE_RESTRICTION_FLAG_CANNOT_SET_PARAMETERS | TP_STORAGE_RESTRICTION_FLAG_CANNOT_SET_ENABLED | TP_STORAGE_RESTRICTION_FLAG_CANNOT_SET_PRESENCE | TP_STORAGE_RESTRICTION_FLAG_CANNOT_SET_SERVICE; } static void account_storage_iface_init(McpAccountStorageIface *iface) { mcp_account_storage_iface_set_name(iface, PLUGIN_NAME); mcp_account_storage_iface_set_desc(iface, PLUGIN_DESCRIPTION); mcp_account_storage_iface_set_priority(iface, PLUGIN_PRIORITY); mcp_account_storage_iface_set_provider(iface, PLUGIN_PROVIDER); #define IMPLEMENT(x) mcp_account_storage_iface_implement_##x(iface, account_manager_ring_##x) IMPLEMENT (get); IMPLEMENT (list); IMPLEMENT (set); IMPLEMENT (create); IMPLEMENT (delete); IMPLEMENT (commit); IMPLEMENT (get_identifier); IMPLEMENT (get_restrictions); #undef IMPLEMENT } McpAccountManagerRing *mcp_account_manager_ring_new(void) { return g_object_new(MCP_TYPE_ACCOUNT_MANAGER_RING, NULL); } telepathy-ring-2.3.24-37ad7111424725a5446e82dac516d7cab88149c7/mc-plugin/mcp-account-manager-ring.h000066400000000000000000000026331251541261300306020ustar00rootroot00000000000000#ifndef __MCP_ACCOUNT_MANAGER_RING_H__ #define __MCP_ACCOUNT_MANAGER_RING_H__ #include G_BEGIN_DECLS #define MCP_TYPE_ACCOUNT_MANAGER_RING \ (mcp_account_manager_ring_get_type ()) #define MCP_ACCOUNT_MANAGER_RING(o) \ (G_TYPE_CHECK_INSTANCE_CAST ((o), MCP_TYPE_ACCOUNT_MANAGER_RING, \ McpAccountManagerUoa)) #define MCP_ACCOUNT_MANAGER_RING_CLASS(k) \ (G_TYPE_CHECK_CLASS_CAST((k), MCP_TYPE_ACCOUNT_MANAGER_RING, \ McpAccountManagerRingClass)) #define MCP_IS_ACCOUNT_MANAGER_RING(o) \ (G_TYPE_CHECK_INSTANCE_TYPE ((o), MCP_TYPE_ACCOUNT_MANAGER_RING)) #define MCP_IS_ACCOUNT_MANAGER_RING_CLASS(k) \ (G_TYPE_CHECK_CLASS_TYPE ((k), MCP_TYPE_ACCOUNT_MANAGER_RING)) #define MCP_ACCOUNT_MANAGER_RING_GET_CLASS(o) \ (G_TYPE_INSTANCE_GET_CLASS ((o), MCP_TYPE_ACCOUNT_MANAGER_RING, \ McpAccountManagerRingClass)) typedef struct _McpAccountManagerRingPrivate McpAccountManagerRingPrivate; typedef struct { GObject parent; McpAccountManagerRingPrivate *priv; } _McpAccountManagerRing; typedef struct { GObjectClass parent_class; } _McpAccountManagerRingClass; typedef _McpAccountManagerRing McpAccountManagerRing; typedef _McpAccountManagerRingClass McpAccountManagerRingClass; GType mcp_account_manager_ring_get_type (void) G_GNUC_CONST; McpAccountManagerRing *mcp_account_manager_ring_new (void); G_END_DECLS #endif telepathy-ring-2.3.24-37ad7111424725a5446e82dac516d7cab88149c7/mc-plugin/mission-control-plugin.c000066400000000000000000000022631251541261300304510ustar00rootroot00000000000000/* * Copyright (C) 2012 Jolla Ltd. * Contact: John Brooks * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . */ #include #include "mcp-account-manager-ring.h" GObject * mcp_plugin_ref_nth_object (guint n) { static void *plugin_0 = NULL; switch (n) { case 0: if (plugin_0 == NULL) plugin_0 = g_object_new (MCP_TYPE_ACCOUNT_MANAGER_RING, NULL); else g_object_ref (plugin_0); return plugin_0; default: return NULL; } } telepathy-ring-2.3.24-37ad7111424725a5446e82dac516d7cab88149c7/modem/000077500000000000000000000000001251541261300230555ustar00rootroot00000000000000telepathy-ring-2.3.24-37ad7111424725a5446e82dac516d7cab88149c7/modem/Makefile.am000066400000000000000000000036311251541261300251140ustar00rootroot00000000000000# # Makefile.am for modem libraries # # Copyright (C) 2007 Nokia Corporation # Contact: Pekka Pessi # See file COPYING. # SUBDIRS = . tests # ----------------------------------------------------------------------------- # Headers and libraries AM_CFLAGS = $(ERROR_CFLAGS) @GLIB_CFLAGS@ @DBUS_CFLAGS@ @TP_CFLAGS@ \ @UUID_CFLAGS@ INCLUDES = -I$(top_srcdir) -I$(top_srcdir)/modem AM_LDFLAGS = -static LIBADD = @TP_LIBS@ @DBUS_LIBS@ @GLIB_LIBS@ @UUID_LIBS@ # Build targets lib_LTLIBRARIES = \ libmodem-glib.la noinst_LTLIBRARIES = modemdir = ${includemodemdir}/modem modem_HEADERS = request.h errors.h libmodem_glib_la_SOURCES = request.c request-private.h \ ofono.h ofono.c errors.c oface.h oface.c \ service.c service.h modem.c modem.h \ debug.h debug.c nodist_libmodem_glib_la_SOURCES = $(BUILT_SOURCES) libmodem_glib_la_LIBADD = ${LIBADD} modem_HEADERS += call.h tones.h libmodem_glib_la_SOURCES += call-service.c call.c tones.c modem_HEADERS += sms.h sms-message.h sms-history.h libmodem_glib_la_SOURCES += sms-service.c sms-message.c sms-history.c modem_HEADERS += sim.h libmodem_glib_la_SOURCES += sim-service.c modem_HEADERS += radio-settings.h libmodem_glib_la_SOURCES += radio-settings.c # ---------------------------------------------------------------------- EXTRA_DIST = signals-marshal.list BUILT_SOURCES = \ signals-marshal.h \ signals-marshal.c signals-marshal.c: ${srcdir}/signals-marshal.list $(AM_V_GEN)glib-genmarshal --body --prefix=_modem__marshal $< >$@ signals-marshal.h: ${srcdir}/signals-marshal.list $(AM_V_GEN)glib-genmarshal --header --prefix=_modem__marshal $< >$@ CLEANFILES = ${BUILT_SOURCES} # ----------------------------------------------------------------------------- #pkgconfigdir = $(libdir)/pkgconfig #pkgconfig_DATA = modem.pc # ----------------------------------------------------------------------------- # include Makefile-tests.am telepathy-ring-2.3.24-37ad7111424725a5446e82dac516d7cab88149c7/modem/call-service.c000066400000000000000000001214621251541261300256000ustar00rootroot00000000000000/* * modem/call-service.c - Interface towards oFono VoiceCallManager * * Copyright (C) 2008,2010 Nokia Corporation * @author Pekka Pessi * @author Lassi Syrjala * @author Kai Vehmanen * Copyright (C) 2014 Jolla Ltd * * This work is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This work is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this work; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #define MODEM_DEBUG_FLAG MODEM_LOG_CALL #include "modem/debug.h" #include "modem/call.h" #include "modem/ofono.h" #include "modem/errors.h" #include "modem/tones.h" #include "modem/request-private.h" #include #include "signals-marshal.h" #include #include /* ---------------------------------------------------------------------- */ G_DEFINE_TYPE (ModemCallService, modem_call_service, MODEM_TYPE_OFACE); #define VOICECALL_AGENT_PATH "/voicecallagent" #define VOICECALL_AGENT_IFACE "org.ofono.VoiceCallAgent" /* Properties */ enum { PROP_NONE, PROP_EMERGENCY_NUMBERS, PROP_ALERT_TONE_NEEDED, LAST_PROPERTY }; /* Signals */ enum { SIGNAL_INCOMING, SIGNAL_CREATED, SIGNAL_REMOVED, N_SIGNALS }; static guint signals[N_SIGNALS]; struct _ModemCallServicePrivate { /* < object_path, call instance > */ GHashTable *instances; struct { GQueue queue[1]; GQueue created[1]; } dialing; struct { ModemCall *instance; } conference; char **emergency_numbers; gchar *forwarded; ModemCall *active, *hold; /* Do we have in-band connection? */ DBusGProxy *call_agent_proxy; gboolean call_agent_registered; gboolean alert_tone_needed; unsigned signals :1; unsigned :0; }; /* ---------------------------------------------------------------------- */ static void modem_call_service_connect_to_instance (ModemCallService *self, ModemCall *ci); static void modem_call_service_disconnect_instance (ModemCallService *self, ModemCall *ci); static ModemCall *modem_call_service_ensure_instance (ModemCallService *self, char const *object_path, GHashTable *properties); static ModemRequestCallNotify modem_call_request_dial_reply; static ModemRequestCallNotify modem_call_conference_request_reply; static void on_modem_call_state (ModemCall *, ModemCallState, ModemCallService *); static DBusHandlerResult modem_call_agent_dbus_message_handler( DBusConnection *, DBusMessage *, void *); static void modem_call_agent_register(ModemCallService *); static void modem_call_agent_register_reply (DBusGProxy *, DBusGProxyCall *, void *); static void modem_call_agent_unregister(DBusConnection *, void *); static DBusObjectPathVTable modem_call_agent_table = { .unregister_function = modem_call_agent_unregister, .message_function = modem_call_agent_dbus_message_handler, }; /* ---------------------------------------------------------------------- */ #define RETURN_IF_NOT_VALID(self) \ g_return_if_fail (self != NULL && \ modem_oface_is_connected (MODEM_OFACE (self))) #define RETURN_VAL_IF_NOT_VALID(self, val) \ g_return_val_if_fail (self != NULL && \ modem_oface_is_connected (MODEM_OFACE (self)), (val)) #define RETURN_NULL_IF_NOT_VALID(self) RETURN_VAL_IF_NOT_VALID (self, NULL) #define DBUS_PROXY(x) modem_oface_dbus_proxy (MODEM_OFACE (x)) /* ---------------------------------------------------------------------- */ static void modem_call_service_constructed (GObject *object) { if (G_OBJECT_CLASS (modem_call_service_parent_class)->constructed) G_OBJECT_CLASS (modem_call_service_parent_class)->constructed (object); } static void modem_call_service_init (ModemCallService *self) { DEBUG ("enter"); self->priv = G_TYPE_INSTANCE_GET_PRIVATE(self, MODEM_TYPE_CALL_SERVICE, ModemCallServicePrivate); g_queue_init (self->priv->dialing.queue); g_queue_init (self->priv->dialing.created); self->priv->instances = g_hash_table_new_full ( g_str_hash, g_str_equal, NULL, g_object_unref); self->priv->forwarded = NULL; self->priv->call_agent_proxy = NULL; self->priv->call_agent_registered = FALSE; self->priv->alert_tone_needed = FALSE; } static void modem_call_service_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { ModemCallService *self = MODEM_CALL_SERVICE (object); switch (property_id) { case PROP_EMERGENCY_NUMBERS: g_value_set_boxed (value, modem_call_get_emergency_numbers (self)); break; case PROP_ALERT_TONE_NEEDED: g_value_set_boolean(value, self->priv->alert_tone_needed); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void modem_call_service_set_property (GObject *obj, guint property_id, const GValue *value, GParamSpec *pspec) { ModemCallService *self = MODEM_CALL_SERVICE (obj); ModemCallServicePrivate *priv = self->priv; gpointer old; switch (property_id) { case PROP_EMERGENCY_NUMBERS: old = priv->emergency_numbers; priv->emergency_numbers = g_value_dup_boxed (value); g_strfreev (old); break; case PROP_ALERT_TONE_NEEDED: priv->alert_tone_needed = g_value_get_boolean (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, property_id, pspec); break; } } static void modem_call_service_dispose (GObject *object) { DEBUG ("enter"); if (G_OBJECT_CLASS (modem_call_service_parent_class)->dispose) G_OBJECT_CLASS (modem_call_service_parent_class)->dispose (object); } static void modem_call_service_finalize (GObject *object) { DEBUG ("enter"); ModemCallService *self = MODEM_CALL_SERVICE (object); ModemCallServicePrivate *priv = self->priv; if (priv->forwarded) { g_free(priv->forwarded); priv->forwarded = NULL; } g_strfreev (priv->emergency_numbers), priv->emergency_numbers = NULL; g_hash_table_destroy (priv->instances); G_OBJECT_CLASS (modem_call_service_parent_class)->finalize (object); DEBUG ("leave"); } /* ---------------------------------------------------------------------- */ static ModemOfaceManagedReply reply_to_call_manager_get_calls; static void on_manager_call_added (DBusGProxy *proxy, char const *path, GHashTable *properties, gpointer user_data); static void on_manager_call_removed (DBusGProxy *proxy, char const *path, gpointer user_data); static void on_manager_call_forwarded (DBusGProxy *proxy, char const *type, gpointer user_data); static char const * modem_call_service_property_mapper (char const *name) { if (!strcmp (name, "EmergencyNumbers")) return "emergency-numbers"; return NULL; } static void reply_to_call_manager_get_calls (ModemOface *_self, ModemRequest *request, GPtrArray *array, GError const *error, gpointer user_data) { ModemCallService *self = MODEM_CALL_SERVICE (_self); DEBUG ("enter"); if (!error) { guint i; for (i = 0; i < array->len; i++) { GValueArray *va = g_ptr_array_index (array, i); char const *path = g_value_get_boxed (va->values + 0); GHashTable *properties = g_value_get_boxed (va->values + 1); modem_call_service_ensure_instance (self, path, properties); } } modem_oface_check_connected (_self, request, error); } /** Connect to call service */ static void modem_call_service_connect (ModemOface *_self) { DEBUG ("(%p): enter", _self); ModemCallService *self = MODEM_CALL_SERVICE (_self); ModemCallServicePrivate *priv = self->priv; DBusGProxy *proxy = DBUS_PROXY (_self); if (!priv->signals) { priv->signals = TRUE; #define CONNECT(p, handler, name, signature...) \ dbus_g_proxy_add_signal (p, (name), ##signature); \ dbus_g_proxy_connect_signal (p, (name), G_CALLBACK (handler), self, NULL) CONNECT (proxy, on_manager_call_added, "CallAdded", DBUS_TYPE_G_OBJECT_PATH, MODEM_TYPE_DBUS_DICT, G_TYPE_INVALID); CONNECT (proxy, on_manager_call_removed, "CallRemoved", DBUS_TYPE_G_OBJECT_PATH, G_TYPE_INVALID); CONNECT (proxy, on_manager_call_forwarded, "Forwarded", G_TYPE_STRING, G_TYPE_INVALID); #undef CONNECT } modem_oface_connect_properties (_self, TRUE); modem_oface_add_connect_request (_self, modem_oface_request_managed (_self, "GetCalls", reply_to_call_manager_get_calls, NULL)); modem_call_agent_register(self); } /** Disconnect from call service */ static void modem_call_service_disconnect (ModemOface *_self) { DEBUG ("(%p): enter", _self); ModemCallService *self = MODEM_CALL_SERVICE (_self); ModemCallServicePrivate *priv = self->priv; GHashTableIter iter[1]; ModemCall *ci; while (!g_queue_is_empty (priv->dialing.queue)) { ModemRequest *request = g_queue_pop_head (priv->dialing.queue); modem_request_cancel (request); } if (priv->signals) { priv->signals = FALSE; dbus_g_proxy_disconnect_signal (DBUS_PROXY (self), "CallAdded", G_CALLBACK (on_manager_call_added), self); dbus_g_proxy_disconnect_signal (DBUS_PROXY (self), "CallRemoved", G_CALLBACK (on_manager_call_removed), self); dbus_g_proxy_disconnect_signal (DBUS_PROXY (self), "Forwarded", G_CALLBACK (on_manager_call_forwarded), self); } for (g_hash_table_iter_init (iter, priv->instances); g_hash_table_iter_next (iter, NULL, (gpointer)&ci); g_hash_table_iter_init (iter, priv->instances)) { modem_call_service_disconnect_instance (self, ci); } ci = priv->conference.instance; modem_call_service_disconnect_instance (self, ci); modem_oface_disconnect_properties (_self); } /* ---------------------------------------------------------------------- */ static void modem_call_service_class_init (ModemCallServiceClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); ModemOfaceClass *oface_class = MODEM_OFACE_CLASS (klass); DEBUG ("enter"); g_type_class_add_private (klass, sizeof (ModemCallServicePrivate)); object_class->constructed = modem_call_service_constructed; object_class->get_property = modem_call_service_get_property; object_class->set_property = modem_call_service_set_property; object_class->dispose = modem_call_service_dispose; object_class->finalize = modem_call_service_finalize; oface_class->ofono_interface = MODEM_OFACE_CALL_MANAGER; oface_class->property_mapper = modem_call_service_property_mapper; oface_class->connect = modem_call_service_connect; oface_class->disconnect = modem_call_service_disconnect; /* Properties */ g_object_class_install_property (object_class, PROP_EMERGENCY_NUMBERS, g_param_spec_boxed ("emergency-numbers", "Emergency Numbers", "List of emergency numbers obtained from modem", G_TYPE_STRV, G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (object_class, PROP_ALERT_TONE_NEEDED, g_param_spec_boolean ("alert-tone-needed", "Alert Tone needed", "True if the network cannot send in-band alerting tone", FALSE, /* default assumption: user connection is OK */ G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS)); /* Signals to emit */ signals[SIGNAL_INCOMING] = g_signal_new ("incoming", G_OBJECT_CLASS_TYPE (klass), G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED, 0, NULL, NULL, _modem__marshal_VOID__OBJECT_STRING, G_TYPE_NONE, 2, MODEM_TYPE_CALL, G_TYPE_STRING); signals[SIGNAL_CREATED] = g_signal_new ("created", G_OBJECT_CLASS_TYPE (klass), G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED, 0, NULL, NULL, _modem__marshal_VOID__OBJECT_STRING, G_TYPE_NONE, 2, MODEM_TYPE_CALL, G_TYPE_STRING); signals[SIGNAL_REMOVED] = g_signal_new ("removed", G_OBJECT_CLASS_TYPE (klass), G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED, 0, NULL, NULL, _modem__marshal_VOID__OBJECT, G_TYPE_NONE, 1, MODEM_TYPE_CALL); DEBUG ("leave"); } /* ---------------------------------------------------------------------- */ static void modem_call_service_connect_to_instance (ModemCallService *self, ModemCall *instance) { ModemCallServicePrivate *priv = self->priv; gchar const *object_path; if (!instance) return; g_signal_connect (instance, "state", G_CALLBACK (on_modem_call_state), self); modem_oface_connect (MODEM_OFACE (instance)); object_path = modem_call_get_path (instance); g_hash_table_insert (priv->instances, (gpointer) object_path, instance); } static void modem_call_service_disconnect_instance (ModemCallService *self, ModemCall *instance) { ModemCallServicePrivate *priv = self->priv; if (!instance) return; g_hash_table_steal (priv->instances, modem_call_get_path (instance)); g_signal_handlers_disconnect_by_func (instance, on_modem_call_state, self); g_signal_emit (self, signals[SIGNAL_REMOVED], 0, instance); modem_oface_disconnect (MODEM_OFACE (instance)); g_object_unref (instance); } static ModemCall * modem_call_service_ensure_instance (ModemCallService *self, char const *object_path, GHashTable *properties) { ModemCallServicePrivate *priv = self->priv; char *key; GValue *value; GHashTableIter iter[1]; gchar const *remote; ModemCallState state; gboolean incoming = FALSE, originating = FALSE; ModemCall *ci; DEBUG ("path %s", object_path); if (DEBUGGING) { for (g_hash_table_iter_init (iter, properties); g_hash_table_iter_next (iter, (gpointer)&key, (gpointer)&value);) { char *s = g_strdup_value_contents (value); DEBUG ("%s = %s", key, s); g_free (s); } } ci = g_hash_table_lookup (priv->instances, object_path); if (ci) { DEBUG ("call already exists %p", (void *)ci); return ci; } value = g_hash_table_lookup (properties, "LineIdentification"); remote = g_value_get_string (value); value = g_hash_table_lookup (properties, "State"); state = modem_call_state_from_ofono_state (g_value_get_string (value)); switch (state) { case MODEM_CALL_STATE_INCOMING: case MODEM_CALL_STATE_WAITING: incoming = TRUE; originating = FALSE; break; case MODEM_CALL_STATE_DIALING: case MODEM_CALL_STATE_ALERTING: case MODEM_CALL_STATE_ACTIVE: case MODEM_CALL_STATE_HELD: incoming = FALSE; originating = TRUE; break; case MODEM_CALL_STATE_INVALID: case MODEM_CALL_STATE_DISCONNECTED: DEBUG ("call already in invalid state"); return NULL; } ci = g_object_new (MODEM_TYPE_CALL, "object-path", object_path, "call-service", self, "state", state, "terminating", !originating, "originating", originating, NULL); modem_oface_update_properties (MODEM_OFACE (ci), properties); modem_call_service_connect_to_instance (self, ci); if (incoming) { DEBUG ("emit \"incoming\" (\"%s\" (%p), \"%s\")", modem_call_get_name (ci), ci, remote); g_signal_emit (self, signals[SIGNAL_INCOMING], 0, ci, remote); if (priv->forwarded && !strcmp(priv->forwarded, "incoming")) { g_signal_emit_by_name (ci, "forwarded"); } } else if (g_queue_is_empty (priv->dialing.queue)) { DEBUG ("emit \"created\" (\"%s\" (%p), \"%s\")", modem_call_get_name (ci), ci, remote); g_signal_emit (self, signals[SIGNAL_CREATED], 0, ci, remote); if (priv->forwarded && !strcmp(priv->forwarded, "outgoing")) { g_signal_emit_by_name (ci, "forwarded"); } } else { g_queue_push_tail (priv->dialing.created, ci); } if (priv->forwarded) { g_free(priv->forwarded); priv->forwarded = NULL; } return ci; } static ModemCall * modem_call_service_get_dialed (ModemCallService *self, char const *object_path, char const *remote) { ModemCallServicePrivate *priv = self->priv; ModemCall *ci; ci = g_hash_table_lookup (priv->instances, object_path); if (ci) { DEBUG ("call already exists %p", (void *)ci); if (g_queue_find (priv->dialing.created, ci)) g_queue_remove (priv->dialing.created, ci); return ci; } ci = g_object_new (MODEM_TYPE_CALL, "object-path", object_path, "call-service", self, "remote", remote, "state", MODEM_CALL_STATE_DIALING, "ofono-state", "dialing", "terminating", FALSE, "originating", TRUE, NULL); modem_call_service_connect_to_instance (self, ci); return ci; } /* ---------------------------------------------------------------------- */ /* ModemCallService interface */ static void on_manager_call_added (DBusGProxy *proxy, char const *path, GHashTable *properties, gpointer user_data) { ModemCallService *self = MODEM_CALL_SERVICE (user_data); DEBUG ("%s", path); modem_call_service_ensure_instance (self, path, properties); } static void on_manager_call_removed (DBusGProxy *proxy, char const *path, gpointer user_data) { DEBUG ("%s", path); ModemCallService *self = MODEM_CALL_SERVICE (user_data); ModemCallServicePrivate *priv = self->priv; ModemCall *ci = g_hash_table_lookup (priv->instances, path); if (ci) { modem_call_service_disconnect_instance (self, ci); } } static void on_manager_call_forwarded (DBusGProxy *proxy, char const *type, gpointer user_data) { DEBUG ("%s", type); ModemCallService *self = MODEM_CALL_SERVICE (user_data); ModemCall *ci = NULL; GHashTableIter iter[1]; g_hash_table_iter_init (iter, self->priv->instances); while (g_hash_table_iter_next (iter, NULL, (gpointer)&ci)) { ModemCallState state; g_object_get (ci, "state", &state, NULL); if (state == MODEM_CALL_STATE_INCOMING && strcmp(type, "incoming") == 0) { break; } else if (state == MODEM_CALL_STATE_DIALING && strcmp(type, "outgoing") == 0) { break; } } if (ci) { g_signal_emit_by_name (ci, "forwarded"); } else { self->priv->forwarded = g_strdup(type); } } void modem_call_service_resume (ModemCallService *self) { GHashTableIter iter[1]; ModemCall *ci; DEBUG ("enter"); RETURN_IF_NOT_VALID (self); /* XXX/KV: no such signal */ #if 0 g_signal_emit (self, signals[SIGNAL_EMERGENCY_NUMBERS_CHANGED], 0, modem_call_get_emergency_numbers (self)); #endif g_hash_table_iter_init (iter, self->priv->instances); while (g_hash_table_iter_next (iter, NULL, (gpointer)&ci)) { char *remote; gboolean terminating = FALSE; ModemCallState state; g_object_get (ci, "state", &state, "remote", &remote, "terminating", &terminating, NULL); if (state != MODEM_CALL_STATE_DISCONNECTED && state != MODEM_CALL_STATE_INVALID) { /* XXX - atm the value of 'terminating' cannot be trusted. * oFono should probably provide the direction as a property * since we cannot rely on the call state here. */ if (terminating) { modem_message (MODEM_LOG_CALL, "incoming [with state %s] call from \"%s\"", modem_call_get_state_name (state), remote); DEBUG ("emit \"incoming\"(%s (%p), %s)", modem_call_get_name (ci), ci, remote); g_signal_emit (self, signals[SIGNAL_INCOMING], 0, ci, remote); } else { modem_message (MODEM_LOG_CALL, "created [with state %s] call to \"%s\"", modem_call_get_state_name (state), remote); DEBUG ("emit \"created\"(%s (%p), %s)", modem_call_get_name (ci), ci, remote); g_signal_emit (self, signals[SIGNAL_CREATED], 0, ci, remote); } g_signal_emit_by_name (ci, "state", state, 0, 0); } g_free (remote); } } /* ---------------------------------------------------------------------- */ /* Obtain the list of emergency numbers (usually from SIM) */ static char const modem_call_sos[] = "urn:service:sos"; /** Get currently cached list of emergency numbers. */ char const * const * modem_call_get_emergency_numbers (ModemCallService *self) { static char const * const default_numbers[] = { "112", "911", "118", "119", "000", "110", "08", "999", NULL }; if (MODEM_IS_CALL_SERVICE (self) && self->priv->emergency_numbers) { return (char const * const *)self->priv->emergency_numbers; } return default_numbers; } /** Get emergency service corresponding to number. */ char const * modem_call_get_emergency_service (ModemCallService *self, char const *destination) { char const * const *numbers; if (destination == NULL) return NULL; if (modem_call_get_valid_emergency_urn (destination)) return modem_call_get_valid_emergency_urn (destination); numbers = modem_call_get_emergency_numbers (self); for (; *numbers; numbers++) { size_t n = strlen (*numbers); if (!g_str_has_prefix (destination, *numbers)) continue; if (destination[n] && destination[n] != 'p' && destination[n] != 'w') continue; return modem_call_sos; } return NULL; } /** Check if @urn is an emergency service. */ char const * modem_call_get_valid_emergency_urn (char const *urn) { /* urn:service:sos see RFC 5031 * * service-URN = "URN:service:" service * service = top-level *("." sub-service) * top-level = let-dig [ *25let-dig-hyp let-dig ] * sub-service = let-dig [ *let-dig-hyp let-dig ] * let-dig-hyp = let-dig / "-" * let-dig = ALPHA / DIGIT * ALPHA = %x41-5A / %x61-7A ; A-Z / a-z * DIGIT = %x30-39 ; 0-9 */ char const *sos; int i, dot, hyp; int n = (sizeof modem_call_sos) - 1; if (urn == NULL) return NULL; if (g_ascii_strncasecmp (urn, modem_call_sos, n)) return NULL; sos = urn + n; if (sos[0] == '\0') return urn; /* sos service */ if (sos[0] != '.') return NULL; /* Not a sos. subservice */ for (i = 1, dot = 0, hyp = -2; ; i++) { /* A-Z a-z 0-9 - . */ if (sos[i] == '\0') { if (i > hyp + 1 && i > dot + 1) return urn; } if (('a' <= sos[i] && sos[i] <= 'z') || ('0' <= sos[i] && sos[i] <= '9') || ('A' <= sos[i] && sos[i] <= 'Z')) continue; else if (sos[i] == '-') { if (i > dot + 1) { hyp = i; continue; } } else if (sos[i] == '.') { if (i > dot + 1 && i > hyp + 1) { dot = i; continue; } } return modem_call_sos; /* Invalid syntax */ } } /* ---------------------------------------------------------------------- */ static void request_notify_cancel (gpointer data); ModemRequest * modem_call_request_dial (ModemCallService *self, char const *destination, ModemClirOverride clir, ModemCallRequestDialReply callback, gpointer user_data) { char const *clir_str; ModemRequest *request; ModemCallServicePrivate *priv = self->priv; DEBUG ("called"); RETURN_NULL_IF_NOT_VALID (self); g_return_val_if_fail (destination != NULL, NULL); g_return_val_if_fail (callback != NULL, NULL); modem_message (MODEM_LOG_CALL, "trying to create call to \"%s\"", destination); if (clir == MODEM_CLIR_OVERRIDE_DISABLED) clir_str = "disabled"; else if (clir == MODEM_CLIR_OVERRIDE_ENABLED) clir_str = "enabled"; else clir_str = ""; request = modem_request_begin (self, DBUS_PROXY (self), "Dial", modem_call_request_dial_reply, G_CALLBACK (callback), user_data, G_TYPE_STRING, destination, G_TYPE_STRING, clir_str, G_TYPE_INVALID); modem_request_add_cancel_notify (request, request_notify_cancel); modem_request_add_data_full (request, "call-destination", g_strdup (destination), g_free); g_queue_push_tail (priv->dialing.queue, request); return request; } static void request_notify_cancel (gpointer _request) { modem_request_add_qdata (_request, g_quark_from_static_string ("call-canceled"), GUINT_TO_POINTER (1)); } static void modem_call_request_dial_reply (DBusGProxy *proxy, DBusGProxyCall *call, void *_request) { DEBUG ("enter"); ModemRequest *request = _request; ModemCallService *self = MODEM_CALL_SERVICE (modem_request_object (request)); ModemCallServicePrivate *priv = self->priv; ModemCallRequestDialReply *callback = modem_request_callback (request); gpointer user_data = modem_request_user_data (request); char *destination = modem_request_get_data (request, "call-destination"); GError *error = NULL; ModemCall *ci = NULL; char *object_path = NULL; if (dbus_g_proxy_end_call (proxy, call, &error, DBUS_TYPE_G_OBJECT_PATH, &object_path, G_TYPE_INVALID)) { ci = modem_call_service_get_dialed (self, object_path, destination); } else { object_path = NULL; modem_error_fix (&error); } if (ci) { DEBUG ("%s: instance %s (%p)", MODEM_OFACE_CALL_MANAGER ".Dial", object_path, (void *)ci); modem_message (MODEM_LOG_CALL, "call create request to \"%s\" successful", destination); } else { char ebuffer[32]; modem_message (MODEM_LOG_CALL, "call create request to \"%s\" failed: %s.%s: %s", destination, modem_error_domain_prefix (error->domain), modem_error_name (error, ebuffer, sizeof ebuffer), error->message); DEBUG ("%s: " GERROR_MSG_FMT, MODEM_OFACE_CALL_MANAGER ".Dial", GERROR_MSG_CODE (error)); } if (modem_request_get_data (request, "call-canceled")) { if (ci) modem_call_request_release (ci, NULL, NULL); } else { g_assert (ci || error); callback (self, request, ci, error, user_data); } if (g_queue_find (priv->dialing.queue, request)) g_queue_remove (priv->dialing.queue, request); while (g_queue_is_empty (priv->dialing.queue) && !g_queue_is_empty (priv->dialing.created)) { char *remote; ci = g_queue_pop_head (priv->dialing.created); g_object_get (ci, "remote", &remote, NULL); g_signal_emit (self, signals[SIGNAL_CREATED], 0, ci, remote); g_free (remote); } g_free (object_path); g_clear_error (&error); } static void on_modem_call_state (ModemCall *ci, ModemCallState state, ModemCallService *self) { ModemCallServicePrivate *priv; #if nomore gboolean releasing = FALSE; #endif RETURN_IF_NOT_VALID (self); priv = self->priv; switch (state) { case MODEM_CALL_STATE_ACTIVE: if (priv->hold == ci) priv->hold = NULL; if (!modem_call_is_member (ci)) priv->active = ci; break; case MODEM_CALL_STATE_HELD: if (priv->active == ci) priv->active = NULL; if (!modem_call_is_member (ci)) priv->hold = ci; break; case MODEM_CALL_STATE_DISCONNECTED: #if nomore releasing = TRUE; #endif /* FALLTHROUGH */ case MODEM_CALL_STATE_INVALID: if (priv->active == ci) priv->active = NULL; if (priv->hold == ci) priv->hold = NULL; break; default: break; } #if nomore if (releasing) { GError *error = modem_call_new_error (causetype, cause, NULL); gboolean originating; char *remote; char const *what; g_object_get (ci, "originating", &originating, "remote", &remote, NULL); switch (state) { case MODEM_CALL_STATE_MO_RELEASE: what = "mo-released"; break; case MODEM_CALL_STATE_MT_RELEASE: what = "mt-released"; break; default: what = "terminated"; } gboolean mpty = MODEM_IS_CALL_CONFERENCE (ci); modem_message (MODEM_LOG_CALL, "%s %s %s%s%s %s.%s: %s", what, mpty ? "conference" : originating ? "outgoing call to" : "incoming call from", mpty ? "" : "'", mpty ? "call" : remote, mpty ? "" : "'", modem_error_domain_prefix (error->domain), modem_error_name (error, NULL, 0), error->message); g_free (remote); g_error_free (error); } #endif } ModemRequest * modem_call_request_conference (ModemCallService *self, ModemCallServiceReply *callback, gpointer user_data) { RETURN_NULL_IF_NOT_VALID (self); return modem_request (MODEM_CALL_SERVICE (self), DBUS_PROXY (self), "CreateMultiparty", modem_call_conference_request_reply, G_CALLBACK (callback), user_data, G_TYPE_INVALID); } static void modem_call_conference_request_reply (DBusGProxy *proxy, DBusGProxyCall *call, void *_request) { DEBUG ("enter"); GPtrArray *paths; ModemRequest *request = _request; ModemCallService *self = modem_request_object (request); ModemCallServiceReply *callback = modem_request_callback (request); gpointer user_data = modem_request_user_data (request); GError *error = NULL; if (dbus_g_proxy_end_call (proxy, call, &error, MODEM_TYPE_ARRAY_OF_PATHS, &paths, G_TYPE_INVALID)) { guint i; char const *path; ModemCall *ci; for (i = 0; i < paths->len; i++) { path = g_ptr_array_index (paths, i); ci = g_hash_table_lookup (self->priv->instances, path); if (ci != NULL) { g_object_set (ci, "multiparty", TRUE, NULL); } } g_boxed_free (MODEM_TYPE_ARRAY_OF_PATHS, paths); } else { modem_error_fix (&error); } callback (self, request, error, user_data); g_clear_error (&error); } static void modem_call_service_noparams_request_reply (DBusGProxy *proxy, DBusGProxyCall *call, void *_request) { ModemRequest *request = _request; ModemCallService *self = modem_request_object (request); ModemCallServiceReply *callback = modem_request_callback (request); gpointer user_data = modem_request_user_data (request); GError *error = NULL; DEBUG ("enter"); if (dbus_g_proxy_end_call (proxy, call, &error, G_TYPE_INVALID)) ; else modem_error_fix (&error); if (callback) callback (self, request, error, user_data); g_clear_error (&error); } ModemRequest * modem_call_request_hangup_conference (ModemCallService *self, ModemCallServiceReply *callback, gpointer user_data) { RETURN_NULL_IF_NOT_VALID (self); return modem_request (MODEM_CALL_SERVICE (self), DBUS_PROXY (self), "HangupMultiparty", modem_call_service_noparams_request_reply, G_CALLBACK (callback), user_data, G_TYPE_INVALID); } ModemCall * modem_call_service_get_call (ModemCallService *self, char const *object_path) { ModemCallServicePrivate *priv = self->priv; return g_hash_table_lookup (priv->instances, object_path); } /** * modem_call_service_swap_calls * @self ModemCallService object * * Swaps active and held calls. 0 or more active calls become * held, and 0 or more held calls become active. */ ModemRequest * modem_call_service_swap_calls (ModemCallService *self, ModemCallServiceReply callback, gpointer user_data) { RETURN_NULL_IF_NOT_VALID (self); DEBUG ("%s.%s", MODEM_OFACE_CALL_MANAGER, "SwapCalls"); return modem_request (MODEM_CALL_SERVICE (self), DBUS_PROXY (self), "SwapCalls", modem_call_service_noparams_request_reply, G_CALLBACK (callback), user_data, G_TYPE_INVALID); } /** * modem_call_service_get_calls: * @self: ModemCallService object * * Obtains NULL-terminated list of ordinary ModemCall objects. Note that the * reference count of ModemCall objects is not incremented and object * pointers are valid only during the lifetime of the ModemCallService object. * The returned list can be reclaimed with g_free (). * * Returns: NULL-terminated list of pointers to ModemCall objects. */ ModemCall ** modem_call_service_get_calls (ModemCallService *self) { ModemCall *ci; GPtrArray *calls; GHashTableIter iter[1]; calls = g_ptr_array_sized_new (MODEM_MAX_CALLS + 1); g_hash_table_iter_init (iter, self->priv->instances); while (g_hash_table_iter_next (iter, NULL, (gpointer)&ci)) { g_ptr_array_add (calls, ci); } g_ptr_array_add (calls, NULL); return (ModemCall **)g_ptr_array_free (calls, FALSE); } /* ------------------------------------------------------------------------- */ static char const * _modem_call_validate_address (char const *address) { size_t n, m; if (address == NULL) return "no destination"; n = (sizeof modem_call_sos) - 1; if (g_ascii_strncasecmp (address, modem_call_sos, n) == 0) { if (address[n] != '\0' && address[n] != '.') return "invalid service urn"; return NULL; } /* Remove regocnized service prefixes */ if (g_str_has_prefix (address, "*31#")) address += 4; else if (g_str_has_prefix (address, "#31#")) address += 4; if (*address == '+') address++; n = strspn (address, "0123456789abc*#"); if (n == 0) { if (address[n]) return "not a phone number"; else return "too short"; } if (n > 20) return "too long"; if (address[n - 1] == '#') return "invalid service code"; /* Possible dialstring */ m = strspn (address + n, "0123456789abc*#pwPW"); if (address[n + m] != '\0') { if (m == 0) return "invalid address"; else return "invalid dial string"; } if (m == 1) return "invalid dial string"; /* OK */ return NULL; } gboolean modem_call_is_valid_address (char const *address) { return _modem_call_validate_address (address) == NULL; } gboolean modem_call_validate_address (char const *address, GError **error) { char const *message = _modem_call_validate_address (address); if (message) { g_set_error_literal (error, MODEM_CALL_ERRORS, MODEM_CALL_ERROR_INVALID_ADDRESS, message); return FALSE; } return TRUE; } void modem_call_split_address (char const *address, char **return_address, char **return_dialstring, ModemClirOverride *return_clir) { char const *emergency; size_t nan; g_return_if_fail (address != NULL); g_return_if_fail (return_address != NULL); g_return_if_fail (return_dialstring != NULL); g_return_if_fail (return_clir); emergency = modem_call_get_valid_emergency_urn (address); if (emergency) { *return_address = g_strdup (emergency); *return_dialstring = NULL; *return_clir = MODEM_CLIR_OVERRIDE_DEFAULT; return; } if (g_str_has_prefix (address, "*31#")) { address += strlen ("*31#"); /* According to 3GPP 22.030, *31# suppresses CLIR */ *return_clir = MODEM_CLIR_OVERRIDE_DISABLED; } if (g_str_has_prefix (address, "#31#")) { /* According to 3GPP 22.030, #31# invokes CLIR */ address += strlen ("#31#"); *return_clir = MODEM_CLIR_OVERRIDE_ENABLED; } nan = strspn (address, "+0123456789*#ABCabc"); *return_address = g_strndup (address, nan); if (address[nan]) *return_dialstring = g_strdup (address + nan); } /* ---------------------------------------------------------------------- */ /* Voice Call Agent interface */ static DBusHandlerResult modem_call_agent_generic_dbus_message( DBusConnection *conn, DBusMessage *msg) { DBusMessage *reply; reply = dbus_message_new_method_return(msg); if (!reply) return DBUS_HANDLER_RESULT_NEED_MEMORY; dbus_connection_send(conn, reply, NULL); dbus_message_unref(reply); return DBUS_HANDLER_RESULT_HANDLED; } static DBusHandlerResult modem_call_agent_dbus_message_handler( DBusConnection *conn, DBusMessage *msg, void *user_data) { const char *method = dbus_message_get_member(msg); const char *iface = dbus_message_get_interface(msg); if (strcmp(VOICECALL_AGENT_IFACE, iface) != 0) return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; if (strcmp("Release", method) == 0) { /* Nothing to do */ return modem_call_agent_generic_dbus_message(conn, msg); } else if (strcmp("RingbackTone", method) == 0) { ModemCallService *self = user_data; dbus_bool_t playTone = 0; DBusMessageIter iter; dbus_message_iter_init(msg, &iter); if (dbus_message_iter_get_arg_type(&iter) == DBUS_TYPE_BOOLEAN) { dbus_message_iter_get_basic(&iter, &playTone); } else { DEBUG ("Invalid arguments received, ignore"); } if (self->priv->alert_tone_needed != playTone) { DEBUG("'alert needed' changed from %d to %d", self->priv->alert_tone_needed, playTone); } self->priv->alert_tone_needed = playTone; return modem_call_agent_generic_dbus_message(conn, msg); } return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; } /* Register as oFono voicecall agent, in order to receive * requests for playing local tones */ static void modem_call_agent_register(ModemCallService *self) { self->priv->call_agent_proxy = dbus_g_proxy_new_for_name(dbus_g_bus_get (DBUS_BUS_SYSTEM, NULL), VOICECALL_AGENT_IFACE, VOICECALL_AGENT_PATH, VOICECALL_AGENT_IFACE); if (!self->priv->call_agent_proxy) { DEBUG("Error in creating call agent proxy"); return; } ModemRequest *agent_request = modem_request_begin (self, DBUS_PROXY (self), "RegisterVoicecallAgent", modem_call_agent_register_reply, NULL, self, DBUS_TYPE_G_OBJECT_PATH, dbus_g_proxy_get_path ( self->priv->call_agent_proxy), G_TYPE_INVALID); if (!agent_request) { DEBUG("Error in registering call agent"); return; } } static void modem_call_agent_register_reply (DBusGProxy *proxy, DBusGProxyCall *call, void *_request) { ModemRequest *request = _request; gpointer user_data = modem_request_user_data (request); ModemCallService *self = user_data; GError *error = NULL; if (!dbus_g_proxy_end_call (proxy, call, &error, G_TYPE_INVALID)) { modem_error_fix (&error); DEBUG("Error while registering agent: %s",error->message); return; } if (!dbus_connection_register_object_path( dbus_bus_get (DBUS_BUS_SYSTEM, NULL), VOICECALL_AGENT_PATH, &modem_call_agent_table, self)) { DEBUG("Error while registering object"); return; } self->priv->call_agent_registered = TRUE; DEBUG("Call agent registered."); } static void modem_call_agent_unregister(DBusConnection *connection, void *user_data) { ModemCallService *self = user_data; self->priv->alert_tone_needed = FALSE; if (!self->priv->call_agent_proxy) { return; } if (self->priv->call_agent_registered) { dbus_connection_unregister_object_path( dbus_bus_get (DBUS_BUS_SYSTEM, NULL), VOICECALL_AGENT_PATH); self->priv->call_agent_registered = FALSE; } dbus_g_proxy_call_no_reply(DBUS_PROXY (self), "UnregisterVoicecallAgent", DBUS_TYPE_G_OBJECT_PATH, dbus_g_proxy_get_path ( self->priv->call_agent_proxy), G_TYPE_INVALID); g_object_unref(self->priv->call_agent_proxy); self->priv->call_agent_proxy = NULL; DEBUG("Call agent unregistered."); } telepathy-ring-2.3.24-37ad7111424725a5446e82dac516d7cab88149c7/modem/call.c000066400000000000000000001177061251541261300241500ustar00rootroot00000000000000/* * modem/call.c - Interface towards Ofono call instances * * Copyright (C) 2008 Nokia Corporation * @author Pekka Pessi * @author Lassi Syrjala * Copyright (C) 2013 Jolla Ltd * * This work is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This work is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this work; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "config.h" #define MODEM_DEBUG_FLAG MODEM_LOG_CALL #include "modem/debug.h" #include "modem/errors.h" #include "modem/call.h" #include "modem/ofono.h" #include "modem/request-private.h" #include "modem/tones.h" #include #include #include "signals-marshal.h" #include #include #include G_DEFINE_TYPE (ModemCall, modem_call, MODEM_TYPE_OFACE); struct _ModemCallPrivate { ModemCallService *service; gpointer handler; gchar *state_str; gchar *remote; gchar *emergency; gchar *start_time; unsigned char state; unsigned char causetype, cause; unsigned originating:1; unsigned terminating:1; unsigned onhold:1; unsigned multiparty:1; unsigned :0; }; /* Properties */ enum { PROP_NONE, PROP_SERVICE, PROP_OFONO_STATE, PROP_STATE, PROP_CAUSETYPE, PROP_CAUSE, PROP_ORIGINATING, PROP_TERMINATING, PROP_EMERGENCY, PROP_ONHOLD, PROP_MULTIPARTY, PROP_REMOTE, PROP_START_TIME, LAST_PROPERTY }; /* Signals */ enum { SIGNAL_STATE, SIGNAL_WAITING, SIGNAL_EMERGENCY, SIGNAL_TERMINATED, SIGNAL_ON_HOLD, SIGNAL_FORWARDED, SIGNAL_DIALSTRING, SIGNAL_DTMF_TONE, N_SIGNALS }; static guint call_signals[N_SIGNALS]; /* ---------------------------------------------------------------------- */ static void on_notify_ofono_state (ModemCall *, GParamSpec *, gpointer); static void reply_to_instance_request (DBusGProxy *, DBusGProxyCall *, void *); static void on_disconnect_reason (DBusGProxy *, char const *, gpointer); static void on_propertychanged (DBusGProxy *, char const *, GValue *, gpointer); #if nomore /* Ofono does not provide this information */ static void on_on_hold (DBusGProxy *, gboolean onhold, ModemCall*); static void on_forwarded (DBusGProxy *, ModemCall *); static void on_waiting (DBusGProxy *proxy, ModemCall *); static void on_emergency (DBusGProxy *proxy, char const *service, ModemCall *); static void on_sending_dtmf (DBusGProxy *proxy, char const *dialstring, ModemCall *); static void on_stopped_dtmf (DBusGProxy *proxy, ModemCall *); #endif static void reply_to_stop_dtmf (DBusGProxy *, DBusGProxyCall *, void *); /* ---------------------------------------------------------------------- */ #define RETURN_IF_NOT_VALID(self) \ g_return_if_fail (self != NULL && \ modem_oface_is_connected (MODEM_OFACE (self))) #define RETURN_VAL_IF_NOT_VALID(self, val) \ g_return_val_if_fail (self != NULL && \ modem_oface_is_connected (MODEM_OFACE (self)), (val)) #define RETURN_NULL_IF_NOT_VALID(self) RETURN_VAL_IF_NOT_VALID (self, NULL) /* ---------------------------------------------------------------------- */ static void modem_call_init (ModemCall *self) { self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, MODEM_TYPE_CALL, ModemCallPrivate); } static void modem_call_constructed (GObject *object) { if (G_OBJECT_CLASS (modem_call_parent_class)->constructed) G_OBJECT_CLASS (modem_call_parent_class)->constructed (object); DEBUG ("ModemCall for %s on %s", modem_oface_object_path (MODEM_OFACE (object)), MODEM_OFACE_CALL); } static void modem_call_dispose (GObject *object) { DEBUG ("enter"); if (G_OBJECT_CLASS (modem_call_parent_class)->dispose) G_OBJECT_CLASS (modem_call_parent_class)->dispose (object); } static void modem_call_finalize (GObject *object) { ModemCall *self = MODEM_CALL (object); ModemCallPrivate *priv = self->priv; priv->service = NULL; g_free (priv->remote), priv->remote = NULL; g_free (priv->emergency), priv->emergency = NULL; g_free (priv->start_time), priv->start_time = NULL; G_OBJECT_CLASS (modem_call_parent_class)->finalize (object); } static void modem_call_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { ModemCall *self = MODEM_CALL (object); ModemCallPrivate *priv = self->priv; switch (property_id) { case PROP_SERVICE: g_value_set_object (value, priv->service); break; case PROP_OFONO_STATE: g_value_set_string (value, priv->state_str); break; case PROP_STATE: g_value_set_uint (value, priv->state); break; case PROP_CAUSE: g_value_set_uint (value, priv->cause); break; case PROP_CAUSETYPE: g_value_set_uint (value, priv->causetype); break; case PROP_ORIGINATING: g_value_set_boolean (value, priv->originating); break; case PROP_TERMINATING: g_value_set_boolean (value, priv->terminating); break; case PROP_EMERGENCY: g_value_set_string (value, priv->emergency); break; case PROP_ONHOLD: g_value_set_boolean (value, priv->onhold); break; case PROP_MULTIPARTY: g_value_set_boolean (value, priv->multiparty); break; case PROP_REMOTE: g_value_set_string (value, priv->remote); break; case PROP_START_TIME: g_value_set_string (value, priv->start_time); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void modem_call_set_property (GObject *obj, guint property_id, const GValue *value, GParamSpec *pspec) { ModemCall *self = MODEM_CALL (obj); ModemCallPrivate *priv = self->priv; char *tbf; switch (property_id) { case PROP_SERVICE: priv->service = g_value_get_object (value); break; /* XXX/KV: ugly hack until usage of state is harmonized */ case PROP_OFONO_STATE: g_free (priv->state_str); priv->state_str = g_value_dup_string (value); break; case PROP_STATE: priv->state = g_value_get_uint (value); break; case PROP_CAUSE: priv->cause = g_value_get_uint (value); break; case PROP_CAUSETYPE: priv->causetype = g_value_get_uint (value); break; case PROP_ORIGINATING: priv->originating = g_value_get_boolean (value); if (priv->originating) priv->terminating = FALSE; break; case PROP_TERMINATING: priv->terminating = g_value_get_boolean (value); if (priv->terminating) priv->originating = FALSE; break; case PROP_EMERGENCY: tbf = priv->emergency; priv->emergency = g_value_dup_string (value); g_free (tbf); break; case PROP_ONHOLD: priv->onhold = g_value_get_boolean (value); break; case PROP_MULTIPARTY: priv->multiparty = g_value_get_boolean (value); break; case PROP_REMOTE: tbf = priv->remote; priv->remote = g_value_dup_string (value); g_free (tbf); break; case PROP_START_TIME: g_free (priv->start_time); priv->start_time = g_value_dup_string (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, property_id, pspec); break; } } /* ------------------------------------------------------------------------- */ /** Maps properties of org.ofono.VoiceCall to matching ModemCall properties */ char const * modem_call_property_mapper (char const *name) { if (!strcmp (name, "LineIdentification")) return "remote"; if (!strcmp (name, "Multiparty")) return "multiparty"; if (!strcmp (name, "State")) return "ofono-state"; if (!strcmp (name, "StartTime")) return "start-time"; if (!strcmp (name, "Information")) return NULL; if (!strcmp (name, "Icon")) return NULL; return NULL; } static void modem_call_connect (ModemOface *_self) { DEBUG ("(%p): enter", _self); /* CallAdded gives us initial properties */ modem_oface_connect_properties (_self, FALSE); /* Additionally, listen to 'DisconnectReason' signal*/ ModemCall *self = MODEM_CALL (_self); DBusGProxy *proxy = modem_oface_dbus_proxy (_self); dbus_g_proxy_add_signal (proxy, "DisconnectReason", G_TYPE_STRING, G_TYPE_INVALID); dbus_g_proxy_connect_signal (proxy, "DisconnectReason", G_CALLBACK (on_disconnect_reason), self, NULL); dbus_g_proxy_add_signal (proxy, "PropertyChanged", G_TYPE_STRING, G_TYPE_INVALID); dbus_g_proxy_connect_signal (proxy, "PropertyChanged", G_CALLBACK (on_propertychanged), self, NULL); g_signal_connect (_self, "notify::ofono-state", G_CALLBACK(on_notify_ofono_state), _self); } static void modem_call_connected (ModemOface *_self) { DEBUG ("(%p): enter", _self); on_notify_ofono_state (MODEM_CALL (_self), NULL, _self); } static void modem_call_disconnect (ModemOface *_self) { DEBUG ("(%p): enter", _self); g_signal_handlers_disconnect_by_func (_self, G_CALLBACK(on_notify_ofono_state), _self); modem_oface_disconnect_properties (_self); } static void modem_call_class_init (ModemCallClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); ModemOfaceClass *oface_class = MODEM_OFACE_CLASS (klass); g_type_class_add_private (klass, sizeof (ModemCallPrivate)); object_class->constructed = modem_call_constructed; object_class->dispose = modem_call_dispose; object_class->finalize = modem_call_finalize; object_class->get_property = modem_call_get_property; object_class->set_property = modem_call_set_property; oface_class->ofono_interface = MODEM_OFACE_CALL; oface_class->property_mapper = modem_call_property_mapper; oface_class->connect = modem_call_connect; oface_class->connected = modem_call_connected; oface_class->disconnect = modem_call_disconnect; /* Properties */ g_object_class_install_property (object_class, PROP_SERVICE, g_param_spec_object ("call-service", "Call service", "The call service object that owns " "the modem call object", MODEM_TYPE_CALL_SERVICE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (object_class, PROP_OFONO_STATE, g_param_spec_string ("ofono-state", "Call State String", "State name of the call instance", NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (object_class, PROP_STATE, g_param_spec_uint ("state", "Call state", "State of the call instance", MODEM_CALL_STATE_INVALID, MODEM_CALL_STATE_DISCONNECTED, MODEM_CALL_STATE_INVALID, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (object_class, PROP_CAUSE, g_param_spec_uint ("cause", "Call cause", "Cause of the latest state transition", 0, 255, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (object_class, PROP_CAUSETYPE, g_param_spec_uint ("causetype", "Call cause type", "Source of the latest state transition", 0, MODEM_CALL_CAUSE_TYPE_REMOTE, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (object_class, PROP_ORIGINATING, g_param_spec_boolean ("originating", "Originated Call", "Terminal is originating this call", FALSE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (object_class, PROP_TERMINATING, g_param_spec_boolean ("terminating", "Terminating Call", "Terminal is terminating this call", FALSE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (object_class, PROP_EMERGENCY, g_param_spec_string ("emergency", "Emergency Service", "Emergency Service for this call", NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (object_class, PROP_ONHOLD, g_param_spec_boolean ("onhold", "Call is On Hold", "This call has been put on hold by remote party", FALSE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (object_class, PROP_MULTIPARTY, g_param_spec_boolean ("multiparty", "Multiparty Membership", "This instance is a member of a conference call", FALSE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (object_class, PROP_REMOTE, g_param_spec_string ("remote", "Remote Party Address", "Address of remote party associated with this call", NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (object_class, PROP_START_TIME, g_param_spec_string ("start-time", "Start Time", "The time the call started", NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); call_signals[SIGNAL_STATE] = g_signal_new ("state", G_OBJECT_CLASS_TYPE (klass), G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED, 0, NULL, NULL, g_cclosure_marshal_VOID__UINT, G_TYPE_NONE, 1, G_TYPE_UINT); /* XXX: not implemented */ call_signals[SIGNAL_WAITING] = g_signal_new ("waiting", G_OBJECT_CLASS_TYPE (klass), G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED, 0, NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); /* XXX: not implemented */ call_signals[SIGNAL_EMERGENCY] = g_signal_new ("emergency", G_OBJECT_CLASS_TYPE (klass), G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED, 0, NULL, NULL, g_cclosure_marshal_VOID__STRING, G_TYPE_NONE, 1, G_TYPE_STRING); /* XXX: not implemented */ call_signals[SIGNAL_TERMINATED] = g_signal_new ("terminated", G_OBJECT_CLASS_TYPE (klass), G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED, 0, NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); call_signals[SIGNAL_ON_HOLD] = g_signal_new ("on-hold", G_OBJECT_CLASS_TYPE (klass), G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED, 0, NULL, NULL, g_cclosure_marshal_VOID__BOOLEAN, G_TYPE_NONE, 1, G_TYPE_BOOLEAN); /* XXX: not implemented */ call_signals[SIGNAL_FORWARDED] = g_signal_new ("forwarded", G_OBJECT_CLASS_TYPE (klass), G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED, 0, NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); /* XXX: not implemented */ call_signals[SIGNAL_DIALSTRING] = g_signal_new ("dialstring", G_OBJECT_CLASS_TYPE (klass), G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED, 0, NULL, NULL, g_cclosure_marshal_VOID__STRING, G_TYPE_NONE, 1, G_TYPE_STRING); /* XXX: not implemented */ call_signals[SIGNAL_DTMF_TONE] = g_signal_new ("dtmf-tone", G_OBJECT_CLASS_TYPE (klass), G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED, 0, NULL, NULL, g_cclosure_marshal_VOID__INT, G_TYPE_NONE, 1, G_TYPE_INT); } /* ---------------------------------------------------------------------- */ /* ModemCall interface */ char const * modem_call_get_name (ModemCall const *self) { if (self == NULL) return ""; char const *path = modem_oface_object_path (MODEM_OFACE (self)); char const *last = strrchr (path, '/'); if (last) return last + 1; return ""; } char const * modem_call_get_path (ModemCall const *self) { return modem_oface_object_path (MODEM_OFACE (self)); } char const * modem_call_get_state_name (int state) { switch (state) { case MODEM_CALL_STATE_INVALID: return "INVALID"; case MODEM_CALL_STATE_DIALING: return "DIALING"; case MODEM_CALL_STATE_ALERTING: return "ALERTING"; case MODEM_CALL_STATE_INCOMING: return "INCOMING"; case MODEM_CALL_STATE_WAITING: return "WAITING"; case MODEM_CALL_STATE_ACTIVE: return "ACTIVE"; case MODEM_CALL_STATE_HELD: return "HELD"; case MODEM_CALL_STATE_DISCONNECTED: return "DISCONNECTED"; default: return "UNKNOWN"; } } ModemCallState modem_call_get_state (ModemCall const *self) { return MODEM_IS_CALL (self) ? self->priv->state : MODEM_CALL_STATE_INVALID; } gboolean modem_call_has_path (ModemCall const *self, char const *object_path) { gchar const *my_path = modem_oface_object_path (MODEM_OFACE (self)); return object_path && my_path && strcmp (my_path, object_path) == 0; } gboolean modem_call_try_set_handler (ModemCall *self, gpointer handler) { if (!MODEM_IS_CALL (self)) return FALSE; ModemCallPrivate *priv = self->priv; if (handler) { if (priv->handler != NULL && priv->handler != handler) { return FALSE; } } priv->handler = handler; return TRUE; } void modem_call_set_handler (ModemCall *self, gpointer handler) { gboolean success = modem_call_try_set_handler (self, handler); g_assert (success); } gpointer modem_call_get_handler (ModemCall *self) { return MODEM_IS_CALL (self) ? self->priv->handler : NULL; } gboolean modem_call_is_member (ModemCall const *self) { return MODEM_IS_CALL (self) && self->priv->multiparty; } gboolean modem_call_is_originating (ModemCall const *self) { return MODEM_IS_CALL (self) && self->priv->originating; } gboolean modem_call_is_terminating (ModemCall const *self) { return MODEM_IS_CALL (self) && self->priv->terminating; } gboolean modem_call_is_active (ModemCall const *self) { return MODEM_IS_CALL (self) && self->priv->state == MODEM_CALL_STATE_ACTIVE; } gboolean modem_call_is_held (ModemCall const *self) { return self && self->priv->state == MODEM_CALL_STATE_HELD; } ModemCallState modem_call_state_from_ofono_state (const char *state) { if (G_UNLIKELY (!state)) return MODEM_CALL_STATE_INVALID; else if (!strcmp (state, "active")) return MODEM_CALL_STATE_ACTIVE; else if (!strcmp (state, "held")) return MODEM_CALL_STATE_HELD; else if (!strcmp (state, "dialing")) return MODEM_CALL_STATE_DIALING; else if (!strcmp (state, "alerting")) return MODEM_CALL_STATE_ALERTING; else if (!strcmp (state, "incoming")) return MODEM_CALL_STATE_INCOMING; else if (!strcmp (state, "waiting")) return MODEM_CALL_STATE_WAITING; else if (!strcmp (state, "disconnected")) return MODEM_CALL_STATE_DISCONNECTED; return MODEM_CALL_STATE_INVALID; } ModemCallState modem_call_cause_type_from_ofono_disconnect_reason (const char *reason) { if (G_UNLIKELY (!reason)) return MODEM_CALL_CAUSE_TYPE_UNKNOWN; else if (!strcmp (reason, "network")) return MODEM_CALL_CAUSE_TYPE_NETWORK; else if (!strcmp (reason, "local")) return MODEM_CALL_CAUSE_TYPE_LOCAL; else if (!strcmp (reason, "remote")) return MODEM_CALL_CAUSE_TYPE_REMOTE; return MODEM_CALL_CAUSE_TYPE_UNKNOWN; } static void on_notify_ofono_state (ModemCall *self, GParamSpec *pspec, gpointer user_data) { ModemCallPrivate *priv = self->priv; ModemCallState state; DEBUG ("enter with \"%s\"", priv->state_str); state = modem_call_state_from_ofono_state (priv->state_str); if (state == priv->state) return; g_object_set (self, "state", state, NULL); g_signal_emit (self, call_signals[SIGNAL_STATE], 0, state); } #if nomore static void on_call_state (DBusGProxy *proxy, guint state, guint causetype, guint cause, ModemCall *self) { ModemCallPrivate *priv = self->priv; g_assert (proxy); g_assert (self); g_assert (priv->proxy == proxy); DEBUG ("CallState (%s (%u), %u, %u) from %s%s", modem_call_get_state_name (state), state, causetype, cause, dbus_g_proxy_get_path (proxy), priv->handler ? "" : " (no channel)"); g_object_set (self, "state", state, causetype > MODEM_CALL_CAUSE_TYPE_NETWORK /* Unknown causetype, ignore */ ? NULL : "causetype", causetype, "cause", cause, NULL); switch (state) { case MODEM_CALL_STATE_INCOMING: g_object_set (self, "terminating", TRUE, NULL); break; case MODEM_CALL_STATE_DIALING: g_object_set (self, "originating", TRUE, NULL); break; case MODEM_CALL_STATE_INVALID: g_object_set (self, "remote", NULL, "emergency", NULL, "originating", FALSE, "terminating", FALSE, "onhold", FALSE, "multiparty", FALSE, NULL); break; } if (priv->handler == NULL) { switch (state) { case MODEM_CALL_STATE_DIALING: case MODEM_CALL_STATE_ALERTING: case MODEM_CALL_STATE_INCOMING: case MODEM_CALL_STATE_WAITING: case MODEM_CALL_STATE_ACTIVE: case MODEM_CALL_STATE_HELD: { char const *remote = priv->remote ? priv->remote : ""; if (priv->terminating) g_signal_emit_by_name (priv->service, "incoming", self, remote); else if (priv->originating) g_signal_emit_by_name (priv->service, "created", self, remote); } break; default: break; } } g_signal_emit (self, call_signals[SIGNAL_STATE], 0, state, causetype, cause); if (state == MODEM_CALL_STATE_TERMINATED) { g_object_set (self, "remote", NULL, "emergency", NULL, "originating", FALSE, "terminating", FALSE, "onhold", FALSE, "multiparty", FALSE, NULL); } } #endif ModemRequest * modem_call_request_answer (ModemCall *self, ModemCallReply callback, gpointer user_data) { DEBUG ("enter"); RETURN_NULL_IF_NOT_VALID (self); if (self->priv->state != MODEM_CALL_STATE_WAITING) { DEBUG ("%s.%s (%s)", MODEM_OFACE_CALL, "Answer", modem_call_get_path (self)); return modem_request (MODEM_CALL (self), modem_oface_dbus_proxy (MODEM_OFACE (self)), "Answer", reply_to_instance_request, G_CALLBACK (callback), user_data, G_TYPE_INVALID); } else { DEBUG ("%s.%s (%s)", MODEM_OFACE_CALL_MANAGER, "HoldAndAnswer", modem_call_get_path (self)); return modem_request (MODEM_CALL (self), modem_oface_dbus_proxy (MODEM_OFACE (self->priv->service)), "HoldAndAnswer", reply_to_instance_request, G_CALLBACK (callback), user_data, G_TYPE_INVALID); } } ModemRequest * modem_call_request_release (ModemCall *self, ModemCallReply callback, gpointer user_data) { DEBUG ("%s.%s (%s)", MODEM_OFACE_CALL, "Hangup", modem_call_get_path (self)); RETURN_NULL_IF_NOT_VALID (self); return modem_request (MODEM_CALL (self), modem_oface_dbus_proxy (MODEM_OFACE (self)), "Hangup", reply_to_instance_request, G_CALLBACK (callback), user_data, G_TYPE_INVALID); } static void reply_to_private_chat_request (DBusGProxy *proxy, DBusGProxyCall *call, void *_request) { DEBUG ("enter"); GPtrArray *paths; ModemRequest *request = _request; ModemCall *self = modem_request_object (request); ModemCallReply *callback = modem_request_callback (request); gpointer user_data = modem_request_user_data (request); GError *error = NULL; if (dbus_g_proxy_end_call (proxy, call, &error, MODEM_TYPE_ARRAY_OF_PATHS, &paths, G_TYPE_INVALID)) { guint i; char const *path; for (i = 0; i < paths->len; i++) { path = g_ptr_array_index (paths, i); DEBUG("Calls in conf after split %u/%u: %s\n", i + 1, paths->len, path); } /* XXX: should this list be passed to the client as well? */ } else { modem_error_fix (&error); } callback (self, request, error, user_data); g_clear_error (&error); } ModemRequest * modem_call_request_split (ModemCall *self, ModemCallReply callback, gpointer user_data) { DEBUG ("%s.%s (%s)", MODEM_OFACE_CALL_MANAGER, "PrivateChat", modem_call_get_path (self)); RETURN_NULL_IF_NOT_VALID (self); return modem_request (MODEM_CALL (self), modem_oface_dbus_proxy (MODEM_OFACE (self->priv->service)), "PrivateChat", reply_to_private_chat_request, G_CALLBACK (callback), user_data, DBUS_TYPE_G_OBJECT_PATH, modem_call_get_path (self), G_TYPE_INVALID); } ModemRequest * modem_call_request_hold (ModemCall *self, int hold, ModemCallReply callback, gpointer user_data) { ModemCallPrivate *priv = self->priv; DEBUG (""); RETURN_NULL_IF_NOT_VALID (self); if (!((priv->state == MODEM_CALL_STATE_HELD && !hold) || (priv->state == MODEM_CALL_STATE_ACTIVE && hold))) { DEBUG ("invalid hold request %d in %s state\n", hold, priv->state_str); return NULL; } DEBUG ("%s.%s", MODEM_OFACE_CALL_MANAGER, "SwapCalls"); return modem_request (MODEM_CALL (self), modem_oface_dbus_proxy (MODEM_OFACE (self->priv->service)), "SwapCalls", reply_to_instance_request, G_CALLBACK (callback), user_data, G_TYPE_INVALID); } static void reply_to_instance_request (DBusGProxy *proxy, DBusGProxyCall *call, void *_request) { DEBUG ("enter"); ModemRequest *request = _request; ModemCall *self = modem_request_object (request); ModemCallReply *callback = modem_request_callback (request); gpointer user_data = modem_request_user_data (request); GError *error = NULL; if (dbus_g_proxy_end_call (proxy, call, &error, G_TYPE_INVALID)) ; else modem_error_fix (&error); if (callback) callback (self, request, error, user_data); g_clear_error (&error); } gboolean modem_call_can_join (ModemCall const *self) { if (!self) return FALSE; return (self->priv->state == MODEM_CALL_STATE_ACTIVE || self->priv->state == MODEM_CALL_STATE_HELD); } ModemRequest * modem_call_send_dtmf (ModemCall *self, char const *dialstring, ModemCallReply *callback, gpointer user_data) { int i; char modemstring[256]; RETURN_NULL_IF_NOT_VALID (self); g_return_val_if_fail (dialstring != NULL, NULL); for (i = 0; dialstring[i]; i++) { if (i == 255) return NULL; /* Too long */ switch (dialstring[i]) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': case '#': case '*': modemstring[i] = dialstring[i]; break; case 'p': case 'P': modemstring[i] = 'p'; break; case 'w': case 'W': modemstring[i] = 'w'; break; case 'a': case 'A': modemstring[i] = 'a'; break; case 'b': case 'B': modemstring[i] = 'b'; break; case 'c': case 'C': modemstring[i] = 'c'; break; case 'd': case 'D': modemstring[i] = 'd'; break; default: return NULL; } } modemstring[i] = '\0'; return modem_request (self, modem_oface_dbus_proxy (MODEM_OFACE (self->priv->service)), "SendTones", reply_to_instance_request, G_CALLBACK (callback), user_data, G_TYPE_STRING, modemstring, G_TYPE_INVALID); } /* XXX: Ofono at the moment supports only fixed-duration tones */ ModemRequest * modem_call_start_dtmf (ModemCall *self, char const tone, ModemCallReply *callback, gpointer user_data) { char tones[2] = { tone }; RETURN_NULL_IF_NOT_VALID (self); return modem_request (MODEM_CALL (self), modem_oface_dbus_proxy (MODEM_OFACE (self->priv->service)), "SendTones", reply_to_instance_request, G_CALLBACK (callback), user_data, G_TYPE_STRING, tones, G_TYPE_INVALID); } ModemRequest * modem_call_stop_dtmf (ModemCall *self, ModemCallReply *callback, gpointer user_data) { RETURN_NULL_IF_NOT_VALID (self); return modem_request (MODEM_CALL (self), modem_oface_dbus_proxy (MODEM_OFACE (self->priv->service)), "StopTones", reply_to_stop_dtmf, G_CALLBACK (callback), user_data, G_TYPE_INVALID); } static void reply_to_stop_dtmf (DBusGProxy *proxy, DBusGProxyCall *call, void *_request) { DEBUG ("enter"); ModemRequest *request = _request; ModemCall *self = modem_request_object (request); ModemCallReply *callback = modem_request_callback (request); gpointer user_data = modem_request_user_data (request); char *stopped; GError *error = NULL; if (dbus_g_proxy_end_call (proxy, call, &error, G_TYPE_STRING, &stopped, G_TYPE_INVALID)) { g_free (stopped); } else { modem_error_fix (&error); DEBUG ("got " GERROR_MSG_FMT, GERROR_MSG_CODE (error)); } if (callback) callback (self, request, error, user_data); g_clear_error (&error); } GError * modem_call_new_error (guint causetype, guint cause, char const *prefixed) { GQuark domain; char const *msg; char *error = " Error"; char printed[64]; int code = cause; if (cause == 0) { domain = MODEM_CALL_ERRORS; code = MODEM_CALL_ERROR_NO_ERROR; msg = "None"; error = ""; } else if (causetype == MODEM_CALL_CAUSE_TYPE_NETWORK) { domain = MODEM_CALL_NET_ERRORS; code = cause; #define _(x) MODEM_CALL_NET_ERROR_ ## x switch (cause) { case _(UNASSIGNED_NUMBER): msg = "Unassigned Number"; break; case _(NO_ROUTE): msg = "No Route To Destination"; break; case _(CH_UNACCEPTABLE): msg = "Channel Unacceptable"; break; case _(OPER_BARRING): msg = "Operator Determined Barring"; break; case _(NORMAL): msg = "Normal Call Clearing"; error = ""; break; case _(USER_BUSY): msg = "User Busy"; error = ""; break; case _(NO_USER_RESPONSE): msg = "No User Response"; break; case _(ALERT_NO_ANSWER): msg = "Alert No Answer"; break; case _(CALL_REJECTED): msg = "Call Rejected"; break; case _(NUMBER_CHANGED): msg = "Number Changed"; break; case _(NON_SELECT_CLEAR): msg = "Non-Selected Clearing"; break; case _(DEST_OUT_OF_ORDER): msg = "Destination Out Of Order"; break; case _(INVALID_NUMBER): msg = "Invalid Number"; break; case _(FACILITY_REJECTED): msg = "Facility Rejected"; break; case _(RESP_TO_STATUS): msg = "Response To Status"; error = ""; break; case _(NORMAL_UNSPECIFIED): msg = "Unspecified Normal"; error = ""; break; case _(NO_CHANNEL): msg = "No Channel Available"; break; case _(NETW_OUT_OF_ORDER): msg = "Network Out Of Order"; break; case _(TEMPORARY_FAILURE): msg = "Temporary Failure"; error =""; break; case _(CONGESTION): msg = "Congestion"; break; case _(ACCESS_INFO_DISC): msg = "Access Information Discarded"; break; case _(CHANNEL_NA): msg = "Channel Not Available"; break; case _(RESOURCES_NA): msg = "Resources Not Available"; break; case _(QOS_NA): msg = "QoS Not Available"; break; case _(FACILITY_UNSUBS): msg = "Requested Facility Not Subscribed"; break; case _(COMING_BARRED_CUG): msg = "Incoming Calls Barred Within CUG"; break; case _(BC_UNAUTHORIZED): msg = "Bearer Capability Unauthorized"; break; case _(BC_NA): msg = "Bearer Capability Not Available"; break; case _(SERVICE_NA): msg = "Service Not Available"; break; case _(BEARER_NOT_IMPL): msg = "Bearer Not Implemented"; break; case _(ACM_MAX): msg = "ACM Max"; break; case _(FACILITY_NOT_IMPL): msg = "Facility Not Implemented"; break; case _(ONLY_RDI_BC): msg = "Only Restricted DI Bearer Capability"; break; case _(SERVICE_NOT_IMPL): msg = "Service Not Implemented"; break; case _(INVALID_TI): msg = "Invalid Transaction Identifier"; break; case _(NOT_IN_CUG): msg = "Not In CUG"; break; case _(INCOMPATIBLE_DEST): msg = "Incompatible Destination"; break; case _(INV_TRANS_NET_SEL): msg = "Invalid Transit Net Selected"; break; case _(SEMANTICAL_ERR): msg = "Semantical"; break; case _(INVALID_MANDATORY): msg = "Invalid Mandatory Information"; break; case _(MSG_TYPE_INEXIST): msg = "Message Type Non-Existent"; break; case _(MSG_TYPE_INCOMPAT): msg = "Message Type Incompatible"; break; case _(IE_NON_EXISTENT): msg = "Information Element Non-Existent"; break; case _(COND_IE_ERROR): msg = "Conditional Information Element"; break; case _(MSG_INCOMPATIBLE): msg = "Incompatible Message"; break; case _(TIMER_EXPIRY): msg = "Timer Expiry"; break; case _(PROTOCOL_ERROR): msg = "Protocol"; break; case _(INTERWORKING): msg = "Error Cause Not Known Because of Interworking", error = ""; break; default: code = MODEM_CALL_NET_ERROR_GENERIC; snprintf (printed, sizeof printed, "Error %u with type %u", cause, causetype); msg = printed, error = ""; } #undef _ } else if (causetype == MODEM_CALL_CAUSE_TYPE_LOCAL || causetype == MODEM_CALL_CAUSE_TYPE_REMOTE) { domain = MODEM_CALL_ERRORS; #define _(x) MODEM_CALL_ERROR_ ## x switch (cause) { case _(NO_CALL): msg = "No Call"; break; case _(RELEASE_BY_USER): msg = "Release By User"; error = ""; break; case _(BUSY_USER_REQUEST): msg = "Busy User Request"; break; case _(ERROR_REQUEST): msg = "Request"; break; case _(CALL_ACTIVE): msg = "Call Active"; break; case _(NO_CALL_ACTIVE): msg = "No Call Active"; break; case _(INVALID_CALL_MODE): msg = "Invalid Call Mode"; break; case _(TOO_LONG_ADDRESS): msg = "Too Long Address"; break; case _(INVALID_ADDRESS): msg = "Invalid Address"; break; case _(EMERGENCY): msg = "Emergency"; break; case _(NO_SERVICE): msg = "No Service"; break; case _(NO_COVERAGE): msg = "No Coverage"; break; case _(CODE_REQUIRED): msg = "Code Required"; break; case _(NOT_ALLOWED): msg = "Not Allowed"; break; case _(DTMF_ERROR): msg = "DTMF Error"; break; case _(CHANNEL_LOSS): msg = "Channel Loss"; break; case _(FDN_NOT_OK): msg = "FDN Not Ok"; break; case _(BLACKLIST_BLOCKED): msg = "Blacklist Blocked"; break; case _(BLACKLIST_DELAYED): msg = "Blacklist Delayed"; break; case _(EMERGENCY_FAILURE): msg = "Emergency Failure"; break; case _(NO_SIM): msg = "No SIM"; break; case _(DTMF_SEND_ONGOING): msg = "DTMF Send Ongoing"; break; case _(CS_INACTIVE): msg = "CS Inactive"; break; case _(NOT_READY): msg = "Not Ready"; break; case _(INCOMPATIBLE_DEST): msg = "Incompatible Dest"; break; default: code = MODEM_CALL_ERROR_GENERIC; snprintf (printed, sizeof printed, "Error %u with type %u", cause, causetype); msg = printed, error = ""; } #undef _ } else { domain = MODEM_CALL_ERRORS; code = MODEM_CALL_ERROR_GENERIC; snprintf (printed, sizeof printed, "Error %u with type %u", cause, causetype); msg = printed, error = ""; } if (prefixed) { return g_error_new (domain, code, "%s: %s%s", prefixed, msg, error); } else { return g_error_new (domain, code, "%s%s", msg, error); } } /* TODO: these need to be revised once Ofono provides sufficient information. */ int modem_call_event_tone (guint state, guint causetype, guint cause) { switch (state) { case MODEM_CALL_STATE_DIALING: case MODEM_CALL_STATE_WAITING: case MODEM_CALL_STATE_INCOMING: case MODEM_CALL_STATE_ACTIVE: return TONES_STOP; case MODEM_CALL_STATE_ALERTING: return TONES_EVENT_RINGING; case MODEM_CALL_STATE_DISCONNECTED: if (causetype == MODEM_CALL_CAUSE_TYPE_NETWORK) { /* 3GPP TS 22.001 F.4 */ switch (cause) { case MODEM_CALL_NET_ERROR_NORMAL: case MODEM_CALL_NET_ERROR_NORMAL_UNSPECIFIED: return TONES_EVENT_DROPPED; case MODEM_CALL_NET_ERROR_USER_BUSY: case MODEM_CALL_NET_ERROR_CALL_REJECTED: return TONES_EVENT_BUSY; case MODEM_CALL_NET_ERROR_RESP_TO_STATUS: return TONES_NONE; case MODEM_CALL_NET_ERROR_NO_CHANNEL: case MODEM_CALL_NET_ERROR_TEMPORARY_FAILURE: case MODEM_CALL_NET_ERROR_CONGESTION: case MODEM_CALL_NET_ERROR_CHANNEL_NA: case MODEM_CALL_NET_ERROR_QOS_NA: case MODEM_CALL_NET_ERROR_BC_NA: return TONES_EVENT_CONGESTION; default: return TONES_EVENT_SPECIAL_INFORMATION; } } else { switch (cause) { case MODEM_CALL_ERROR_RELEASE_BY_USER: if (causetype == MODEM_CALL_CAUSE_TYPE_LOCAL) return TONES_NONE; else return TONES_EVENT_DROPPED; case MODEM_CALL_ERROR_BLACKLIST_BLOCKED: case MODEM_CALL_ERROR_BLACKLIST_DELAYED: return TONES_EVENT_BUSY; case MODEM_CALL_ERROR_CHANNEL_LOSS: case MODEM_CALL_ERROR_NO_SERVICE: case MODEM_CALL_ERROR_NO_COVERAGE: return TONES_EVENT_CONGESTION; case MODEM_CALL_ERROR_BUSY_USER_REQUEST: if (causetype == MODEM_CALL_CAUSE_TYPE_LOCAL) return TONES_NONE; else return TONES_EVENT_SPECIAL_INFORMATION; default: return TONES_EVENT_SPECIAL_INFORMATION; } } break; } return TONES_NONE; } int modem_call_error_tone (GError *error) { if (error == NULL) return TONES_NONE; if (error->domain == MODEM_CALL_NET_ERRORS) return modem_call_event_tone (MODEM_CALL_STATE_DISCONNECTED, MODEM_CALL_CAUSE_TYPE_NETWORK, error->code); if (error->domain == MODEM_CALL_ERRORS) return modem_call_event_tone (MODEM_CALL_STATE_DISCONNECTED, MODEM_CALL_CAUSE_TYPE_REMOTE, error->code); return TONES_EVENT_SPECIAL_INFORMATION; } static void on_disconnect_reason (DBusGProxy *proxy, char const *reason, gpointer user_data) { ModemCall *self = MODEM_CALL(user_data); ModemCallCauseType causetype = modem_call_cause_type_from_ofono_disconnect_reason(reason); guint cause = MODEM_CALL_ERROR_RELEASE_BY_USER; if (causetype == MODEM_CALL_CAUSE_TYPE_NETWORK) { /* * TODO: Get the exact disconnect cause from modem * (not possible with current oFono API) */ cause = MODEM_CALL_NET_ERROR_NETW_OUT_OF_ORDER; } /* Save the disconnect reason; a state signal will follow shortly */ g_object_set (self, "cause", cause, NULL); g_object_set (self, "causetype", causetype, NULL); } static void on_propertychanged (DBusGProxy *proxy, char const *property, GValue *value, gpointer user_data) { ModemCall *self = MODEM_CALL(user_data); if (!strcmp (property, "RemoteHeld")) { g_signal_emit (self, call_signals[SIGNAL_ON_HOLD], 0, g_value_get_boolean (value)); } } telepathy-ring-2.3.24-37ad7111424725a5446e82dac516d7cab88149c7/modem/call.h000066400000000000000000000165651251541261300241560ustar00rootroot00000000000000/* * modem/call.h - Client for Ofono VoiceCalls * * Copyright (C) 2007 Nokia Corporation * @author Pekka Pessi * @author Lassi Syrjala * * This work is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This work is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this work; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _MODEM_CALL_H_ #define _MODEM_CALL_H_ #include #include #include G_BEGIN_DECLS typedef struct _ModemCallService ModemCallService; typedef struct _ModemCallServiceClass ModemCallServiceClass; typedef struct _ModemCallServicePrivate ModemCallServicePrivate; struct _ModemCallServiceClass { ModemOfaceClass parent_class; }; struct _ModemCallService { ModemOface parent; ModemCallServicePrivate *priv; }; GType modem_call_service_get_type (void); /* TYPE MACROS */ #define MODEM_TYPE_CALL_SERVICE \ (modem_call_service_get_type ()) #define MODEM_CALL_SERVICE(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ MODEM_TYPE_CALL_SERVICE, ModemCallService)) #define MODEM_CALL_SERVICE_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST ((klass), \ MODEM_TYPE_CALL_SERVICE, ModemCallServiceClass)) #define MODEM_IS_CALL_SERVICE(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \ MODEM_TYPE_CALL_SERVICE)) #define MODEM_IS_CALL_SERVICE_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE ((klass), MODEM_TYPE_CALL_SERVICE)) #define MODEM_CALL_SERVICE_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), \ MODEM_TYPE_CALL_SERVICE, ModemCallServiceClass)) typedef struct _ModemCall ModemCall; typedef struct _ModemCallClass ModemCallClass; typedef struct _ModemCallPrivate ModemCallPrivate; typedef enum _ModemCallState ModemCallState; typedef enum _ModemCallCauseType ModemCallCauseType; typedef enum _ModemClirOverride ModemClirOverride; struct _ModemCallClass { ModemOfaceClass parent_class; }; struct _ModemCall { ModemOface parent; ModemCallPrivate *priv; }; GType modem_call_get_type (void); /* TYPE MACROS */ #define MODEM_TYPE_CALL \ (modem_call_get_type ()) #define MODEM_CALL(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), MODEM_TYPE_CALL, ModemCall)) #define MODEM_CALL_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST ((klass), MODEM_TYPE_CALL, ModemCallClass)) #define MODEM_IS_CALL(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MODEM_TYPE_CALL)) #define MODEM_IS_CALL_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE ((klass), MODEM_TYPE_CALL)) #define MODEM_CALL_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), MODEM_TYPE_CALL, ModemCallClass)) /* ------------------------------------------------------------------------- */ #define MODEM_OFACE_CALL_MANAGER "org.ofono.VoiceCallManager" #define MODEM_OFACE_CALL "org.ofono.VoiceCall" /** * Call properties */ /** ModemCall state */ enum _ModemCallState { MODEM_CALL_STATE_INVALID = 0, MODEM_CALL_STATE_DIALING, MODEM_CALL_STATE_ALERTING, MODEM_CALL_STATE_INCOMING, MODEM_CALL_STATE_WAITING, MODEM_CALL_STATE_ACTIVE, MODEM_CALL_STATE_HELD, MODEM_CALL_STATE_DISCONNECTED }; enum _ModemCallCauseType { MODEM_CALL_CAUSE_TYPE_UNKNOWN, MODEM_CALL_CAUSE_TYPE_NETWORK, MODEM_CALL_CAUSE_TYPE_LOCAL, MODEM_CALL_CAUSE_TYPE_REMOTE }; /** Dial flags */ enum _ModemClirOverride { MODEM_CLIR_OVERRIDE_DEFAULT = 0, MODEM_CLIR_OVERRIDE_ENABLED, MODEM_CLIR_OVERRIDE_DISABLED }; enum { MODEM_MAX_CALLS = 7 }; char const *modem_call_service_property_name_by_ofono_name (char const *); char const *modem_call_get_state_name (int state); GError *modem_call_new_error (guint causetype, guint cause, char const *prefixed); void modem_call_service_resume (ModemCallService *); /* Validate addresses */ gboolean modem_call_is_valid_address (char const *address); gboolean modem_call_validate_address (char const *address, GError **error); void modem_call_split_address (char const *address, char **return_address, char **return_dialstring, ModemClirOverride *return_clir); int modem_call_event_tone (guint state, guint causetype, guint cause); int modem_call_error_tone (GError *); char const * const *modem_call_get_emergency_numbers (ModemCallService *self); char const *modem_call_get_valid_emergency_urn (char const *urn); char const *modem_call_get_emergency_service (ModemCallService*, char const*); typedef void ModemCallServiceReply (ModemCallService *, ModemRequest *, GError *error, gpointer user_data); ModemCall *modem_call_service_get_call (ModemCallService *, char const *); ModemCall **modem_call_service_get_calls (ModemCallService *); typedef void ModemCallRequestDialReply (ModemCallService *, ModemRequest *, ModemCall *, GError *error, gpointer user_data); ModemRequest *modem_call_request_dial (ModemCallService *self, char const *destination, ModemClirOverride clir, ModemCallRequestDialReply *callback, gpointer user_data); ModemRequest *modem_call_request_conference (ModemCallService *, ModemCallServiceReply *callback, gpointer user_data); ModemRequest *modem_call_request_hangup_conference (ModemCallService *, ModemCallServiceReply *callback, gpointer user_data); ModemRequest *modem_call_service_swap_calls (ModemCallService *self, ModemCallServiceReply callback, gpointer user_data); char const *modem_call_get_name (ModemCall const *); char const *modem_call_get_path (ModemCall const *); gboolean modem_call_has_path (ModemCall const *, char const *object_path); ModemCallState modem_call_get_state (ModemCall const *); ModemCallState modem_call_state_from_ofono_state (char const *state); gboolean modem_call_try_set_handler (ModemCall *, gpointer); void modem_call_set_handler (ModemCall *, gpointer); gpointer modem_call_get_handler (ModemCall *); gboolean modem_call_is_member (ModemCall const *); gboolean modem_call_is_originating (ModemCall const *); gboolean modem_call_is_terminating (ModemCall const *); gboolean modem_call_is_active (ModemCall const *); gboolean modem_call_is_held (ModemCall const *); typedef void ModemCallReply (ModemCall *, ModemRequest *, GError *, gpointer user_data); ModemRequest *modem_call_request_answer (ModemCall*, ModemCallReply *, gpointer user_data); ModemRequest *modem_call_request_release (ModemCall*, ModemCallReply *, gpointer user_data); ModemRequest *modem_call_send_dtmf (ModemCall *self, char const *dialstring, ModemCallReply *callback, gpointer user_data); ModemRequest *modem_call_start_dtmf (ModemCall *self, char tone, ModemCallReply *callback, gpointer user_data); ModemRequest *modem_call_stop_dtmf (ModemCall *self, ModemCallReply *callback, gpointer user_data); gboolean modem_call_can_join (ModemCall const *); ModemRequest *modem_call_request_hold (ModemCall *, int hold, ModemCallReply *, gpointer user_data); ModemRequest *modem_call_request_split (ModemCall *, ModemCallReply *, gpointer user_data); G_END_DECLS #endif /* #ifndef _MODEM_CALL_H_ */ telepathy-ring-2.3.24-37ad7111424725a5446e82dac516d7cab88149c7/modem/debug.c000066400000000000000000000061521251541261300243130ustar00rootroot00000000000000/* * modem/debug.c - Debugging facilities * * Copyright (C) 2008 Nokia Corporation * @author Pekka Pessi * * This work is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This work is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this work; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "config.h" #include #include #include "modem/debug.h" static ModemLogFlags modem_debug_flags = 0; static const GDebugKey modem_debug_keys[] = { { "dbus", MODEM_LOG_DBUS }, { "modem", MODEM_LOG_MODEM }, { "call", MODEM_LOG_CALL }, { "sms", MODEM_LOG_SMS }, { "sim", MODEM_LOG_SIM }, { "audio", MODEM_LOG_AUDIO }, { "radio", MODEM_LOG_RADIO }, { "settings", MODEM_LOG_SETTINGS }, { "gprs", MODEM_LOG_GPRS }, { "cdma", MODEM_LOG_CDMA }, }; void modem_debug_set_flags_from_env(void) { const char *flags_string; int flags = 0; flags_string = g_getenv("MODEM_DEBUG"); if (flags_string) { flags |= g_parse_debug_string(flags_string, modem_debug_keys, G_N_ELEMENTS(modem_debug_keys)); } flags_string = g_getenv("CALL_DEBUG"); if (flags_string) { flags |= g_parse_debug_string(flags_string, modem_debug_keys, G_N_ELEMENTS(modem_debug_keys)); } if (flags) modem_debug_set_flags(flags); } void modem_debug_set_flags(int new_flags) { modem_debug_flags |= new_flags; } gboolean modem_debug_flag_is_set(int flag) { return (modem_debug_flags & flag) != 0; } char const * modem_debug_domain(int flag) { if (flag & MODEM_LOG_CDMA) return "modem-cdma"; if (flag & MODEM_LOG_CALL) return "modem-call"; if (flag & MODEM_LOG_SMS) return "modem-sms"; if (flag & MODEM_LOG_SIM) return "modem-sim"; if (flag & MODEM_LOG_AUDIO) return "modem-audio"; if (flag & MODEM_LOG_RADIO) return "modem-radio"; if (flag & MODEM_LOG_SETTINGS) return "modem-settings"; if (flag & MODEM_LOG_GPRS) return "modem-gprs"; return "modem"; } void modem_debug(int flag, const char *format, ...) { if (flag & modem_debug_flags) { va_list args; va_start(args, format); g_logv(modem_debug_domain(flag), G_LOG_LEVEL_DEBUG, format, args); va_end(args); } } void modem_message(int flag, const char *format, ...) { va_list args; va_start(args, format); g_logv(modem_debug_domain(flag), G_LOG_LEVEL_MESSAGE, format, args); va_end(args); } void modem_critical(int flag, const char *format, ...) { va_list args; va_start(args, format); g_logv(modem_debug_domain(flag), G_LOG_LEVEL_WARNING, format, args); va_end(args); } telepathy-ring-2.3.24-37ad7111424725a5446e82dac516d7cab88149c7/modem/debug.h000066400000000000000000000044771251541261300243300ustar00rootroot00000000000000/* * modem/debug.h - Debugging facilities * * Copyright (C) 2008 Nokia Corporation * @author Pekka Pessi * * This work is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This work is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this work; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _MODEM_DEBUG_ #define _MODEM_DEBUG_ #include "config.h" #include G_BEGIN_DECLS typedef enum { MODEM_LOG_DBUS = 1 << 0, MODEM_LOG_MODEM = 1 << 1, MODEM_LOG_CALL = 1 << 2, MODEM_LOG_AUDIO = 1 << 3, MODEM_LOG_SMS = 1 << 4, MODEM_LOG_SIM = 1 << 5, MODEM_LOG_RADIO = 1 << 6, MODEM_LOG_SETTINGS = 1 << 7, MODEM_LOG_GPRS = 1 << 8, MODEM_LOG_CDMA = 1 << 9, } ModemLogFlags; gboolean modem_debug_flag_is_set(int flag); void modem_debug_set_flags(int flag); void modem_debug_set_flags_from_env(void); void modem_debug(int flag, const char *format, ...) G_GNUC_PRINTF (2, 3); void modem_message(int flag, const char *format, ...) G_GNUC_PRINTF (2, 3); void modem_critical(int flag, const char *format, ...) G_GNUC_PRINTF (2, 3); void modem_dump_properties(GHashTable *properties); G_END_DECLS #ifdef ENABLE_DEBUG #define DEBUG(format, ...) \ modem_debug(MODEM_DEBUG_FLAG, "%s: " format, G_STRFUNC, ##__VA_ARGS__) #define DEBUG_DBUS(format, ...) \ modem_debug(MODEM_LOG_DBUS, "%s: " format, G_STRFUNC, ##__VA_ARGS__) #define DEBUGGING modem_debug_flag_is_set(MODEM_DEBUG_FLAG) #else /* ENABLE_DEBUG */ #define DEBUG(format, ...) #define DEBUGGING (0) #endif /* ENABLE_DEBUG */ #define GERROR_MSG_FMT "%s (%d@%s)" #define GERROR_MSG_CODE(e) \ ((e) ? (e)->message : "no error"), \ ((e) ? (e)->code : 0), \ ((e) ? g_quark_to_string((e)->domain) : "") #endif /* _MODEM_DEBUG_ */ telepathy-ring-2.3.24-37ad7111424725a5446e82dac516d7cab88149c7/modem/errors.c000066400000000000000000000514141251541261300245420ustar00rootroot00000000000000/* * modem/error.c - Ofono errors * * Copyright (C) 2008-2010 Nokia Corporation * @author Pekka Pessi * @author Lassi Syrjala * * This work is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This work is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this work; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "config.h" #define MODEM_DEBUG_FLAG MODEM_LOG_DBUS #include #include #include #include #include #include GQuark modem_ofono_errors_quark (void) { static gsize quark = 0; if (g_once_init_enter (&quark)) { GQuark domain = g_quark_from_static_string (MODEM_OFONO_ERROR_PREFIX); g_once_init_leave (&quark, domain); } return (GQuark)quark; } /** Get GType for errors */ GType modem_ofono_error_get_type (void) { static const GEnumValue values[] = { #define _(x, t) { MODEM_OFONO_ERROR_ ## x, "MODEM_OFONO_ERROR_" #x, #t } _(FAILED, Failed), _(INVALID_ARGUMENTS, InvalidArguments), _(INVALID_FORMAT, InvalidFormat), _(NOT_IMPLEMENTED, NotImplemented), _(NOT_SUPPORTED, NotSupported), _(IN_PROGRESS, InProgress), _(NOT_FOUND, NotFound), _(NOT_ACTIVE, NotActive), _(TIMED_OUT, Timedout), /* XXX: TimedOut? */ _(SIM_NOT_READY, SimNotReady), _(IN_USE, InUse), _(NOT_ATTACHED, NotAttached), _(ATTACH_IN_PROGRESS, AttachInProgress), #undef _ { 0, NULL, NULL }, }; static gsize etype = 0; if (g_once_init_enter (&etype)) { GType type = g_enum_register_static ("ModemOfonoError", values); g_once_init_leave (&etype, type); } return (GType)etype; } GQuark modem_call_errors_quark (void) { static gsize quark = 0; if (g_once_init_enter (&quark)) { GQuark domain = g_quark_from_static_string (MODEM_CALL_ERROR_PREFIX); g_once_init_leave (&quark, domain); } return (GQuark)quark; } /** Get GType for errors */ GType modem_call_error_get_type (void) { static const GEnumValue values[] = { #define _(x, t) { MODEM_CALL_ERROR_ ## x, "MODEM_CALL_ERROR_" #x, #t } _(GENERIC, Generic), _(NO_ERROR, None), _(NO_CALL, NoCall), _(RELEASE_BY_USER, ReleaseByUser), _(BUSY_USER_REQUEST, BusyUserRequest), _(ERROR_REQUEST, RequestError), _(CALL_ACTIVE, CallActive), _(NO_CALL_ACTIVE, NoCallActive), _(INVALID_CALL_MODE, InvalidCallMode), _(TOO_LONG_ADDRESS, TooLongAddress), _(INVALID_ADDRESS, InvalidAddress), _(EMERGENCY, Emergency), _(NO_SERVICE, NoService), _(NO_COVERAGE, NoCoverage), _(CODE_REQUIRED, CodeRequired), _(NOT_ALLOWED, NotAllowed), _(DTMF_ERROR, DTMFError), _(CHANNEL_LOSS, ChannelLoss), _(FDN_NOT_OK, FDNNotOk), _(BLACKLIST_BLOCKED, BlacklistBlocked), _(BLACKLIST_DELAYED, BlacklistDelayed), _(EMERGENCY_FAILURE, EmergencyFailure), _(NO_SIM, NoSIM), _(DTMF_SEND_ONGOING, DTMFSendOngoing), _(CS_INACTIVE, CSInactive), _(NOT_READY, NotReady), _(INCOMPATIBLE_DEST, IncompatibleDest), #undef _ { 0, NULL, NULL }, }; static gsize etype = 0; if (g_once_init_enter (&etype)) { GType type = g_enum_register_static ("ModemCallError", values); g_once_init_leave (&etype, type); } return (GType)etype; } GQuark modem_call_net_errors_quark (void) { static gsize quark = 0; if (g_once_init_enter (&quark)) { GQuark domain = g_quark_from_static_string (MODEM_CALL_NET_ERROR_PREFIX); g_once_init_leave (&quark, domain); } return (GQuark)quark; } /** Get GType for network errors */ GType modem_call_net_error_get_type (void) { static const GEnumValue values[] = { #define _(x, t) { MODEM_CALL_NET_ERROR_ ## x, "MODEM_CALL_NET_ERROR_" #x, #t } _(UNASSIGNED_NUMBER, UnassignedNumber), _(NO_ROUTE, NoRouteToDestination), _(CH_UNACCEPTABLE, ChannelUnacceptable), _(OPER_BARRING, OperatorDeterminedBarring), _(NORMAL, NormalCallClearing), _(USER_BUSY, UserBusy), _(NO_USER_RESPONSE, NoUserResponse), _(ALERT_NO_ANSWER, AlertNoAnswer), _(CALL_REJECTED, CallRejected), _(NUMBER_CHANGED, NumberChanged), _(NON_SELECT_CLEAR, NonSelectedClearing), _(DEST_OUT_OF_ORDER, DestinationOutOfOrder), _(INVALID_NUMBER, InvalidNumber), _(FACILITY_REJECTED, FacilityRejected), _(RESP_TO_STATUS, ResponseToStatus), _(NORMAL_UNSPECIFIED, UnspecifiedNormal), _(NO_CHANNEL, NoChannelAvailable), _(NETW_OUT_OF_ORDER, NetworkOutOfOrder), _(TEMPORARY_FAILURE, TemporaryFailure), _(CONGESTION, Congestion), _(ACCESS_INFO_DISC, AccessInformationDiscarded), _(CHANNEL_NA, ChannelNotAvailable), _(RESOURCES_NA, ResourcesNotAvailable), _(QOS_NA, QoSNotAvailable), _(FACILITY_UNSUBS, RequestedFacilityNotSubscribed), _(COMING_BARRED_CUG, IncomingCallsBarredWithinCUG), _(BC_UNAUTHORIZED, BearerCapabilityUnauthorized), _(BC_NA, BearerCapabilityNotAvailable), _(SERVICE_NA, ServiceNotAvailable), _(BEARER_NOT_IMPL, BearerNotImplemented), _(ACM_MAX, ACMMax), _(FACILITY_NOT_IMPL, FacilityNotImplemented), _(ONLY_RDI_BC, OnlyRestrictedDIBearerCapability), _(SERVICE_NOT_IMPL, ServiceNotImplemented), _(INVALID_TI, InvalidTransactionIdentifier), _(NOT_IN_CUG, NotInCUG), _(INCOMPATIBLE_DEST, IncompatibleDestination), _(INV_TRANS_NET_SEL, InvalidTransitNetSelected), _(SEMANTICAL_ERR, SemanticalError), _(INVALID_MANDATORY, InvalidMandatoryInformation), _(MSG_TYPE_INEXIST, MessageTypeNonExistent), _(MSG_TYPE_INCOMPAT, MessageTypeIncompatible), _(IE_NON_EXISTENT, InformationElementNonExistent), _(COND_IE_ERROR, ConditionalInformationElementError), _(MSG_INCOMPATIBLE, IncompatibleMessage), _(TIMER_EXPIRY, TimerExpiry), _(PROTOCOL_ERROR, ProtocolError), _(INTERWORKING, Generic), #undef _ { 0, NULL, NULL }, }; static gsize etype = 0; if (g_once_init_enter (&etype)) { GType type = g_enum_register_static ("ModemCallNetError", values); g_once_init_leave (&etype, type); } return (GType)etype; } /* ---------------------------------------------------------------------- */ GQuark modem_sms_net_errors_quark (void) { static gsize quark = 0; if (g_once_init_enter (&quark)) { GQuark domain = g_quark_from_static_string (MODEM_SMS_NET_ERROR_PREFIX); g_once_init_leave (&quark, domain); } return (GQuark)quark; } /** Get GType for GSM errors */ GType modem_sms_net_error_get_type (void) { static const GEnumValue values[] = { #define _(x, t) { MODEM_SMS_NET_ERROR_ ## x, "MODEM_SMS_NET_ERROR_" #x, #t } _(SUCCESS, Success), _(UNASSIGNED_NUMBER, UnassignedNumber), _(OPER_DETERMINED_BARR, OperatorDeterminedBarring), _(CALL_BARRED, CallBarred), _(RESERVED, Reserved), _(MSG_TRANSFER_REJ, MessageTransferRejected), _(MEMORY_CAPACITY_EXC, MemoryCapacityExceeded), _(DEST_OUT_OF_ORDER, DestinationOutOfOrder), _(UNDEFINED_SUBSCRIBER, UndefinedSubscriber), _(FACILITY_REJECTED, FacilityRejected), _(UNKNOWN_SUBSCRIBER, UnknownSubscriber), _(NETWORK_OUT_OF_ORDER, NetworkOutOfOrder), _(TEMPORARY_FAILURE, TemporaryFailure), _(CONGESTION, Congestion), _(RESOURCE_UNAVAILABLE, ResourceUnavailable), _(REQ_FACILITY_NOT_SUB, RequiredFacilityNotSubscribed), _(REQ_FACILITY_NOT_IMP, RequiredFacilityNotImplemented), _(INVALID_REFERENCE, InvalidReferenceValue), _(INVALID_MSG, InvalidMessage), _(INVALID_MAND_IE, InvalidMandatoryInfoElement), _(INVALID_MSG_TYPE, InvalidMessageType), _(INCOMPATIBLE_MSG_TYPE, IncompatibleMessageType), _(INVALID_IE_TYPE, InvalidInfoElementType), _(PROTOCOL_ERROR, ProtocolError), _(INTERWORKING, InterworkingError), _(LOW_LAYER_NO_CAUSE, LowLayerUnknown), _(IMSI_UNKNOWN_HLR, IMSIUnknownInHLR), _(ILLEGAL_MS, IllegalMS), _(IMSI_UNKNOWN_VLR, IMSIUnknownInVLR), _(IMEI_NOT_ACCEPTED, IMEINotAccepted), _(ILLEGAL_ME, IllegalME), _(PLMN_NOT_ALLOWED, PLMNNotAllowed), _(LA_NOT_ALLOWED, LocationAreaNotAllowed), _(ROAM_NOT_ALLOWED_LA, RoamingNotAllowedInThisLocationArea), _(NO_SUITABLE_CELLS_LA, NoSuitableCellsInThisLocationArea), _(NETWORK_FAILURE, NetworkFailure), _(MAC_FAILURE, MacFailure), _(SYNC_FAILURE, SyncFailure), _(LOW_LAYER_CONGESTION, LowLayerCongestion), _(AUTH_UNACCEPTABLE, AuthUnacceptable), _(SERV_OPT_NOT_SUPPORTED, ServiceOptionNotSupported), _(SERV_OPT_NOT_SUBSCRIBED, ServiceOptionNotSubscribed), _(SERV_OPT_TEMP_OUT_OF_ORDER, ServiceOptionTemporarilyOutOfOrder), _(CALL_CANNOT_BE_IDENTIFIED, CallCannotBeIdentified), _(LOW_LAYER_INVALID_MSG, LowLayerInvalidMessage), _(LOW_LAYER_INVALID_MAND_IE, LowLayerInvalidMandatoryInfoElement), _(LOW_LAYER_INVALID_MSG_TYPE, LowLayerInvalidMessageType), _(LOW_LAYER_INCOMPATIBLE_MSG_TYPE, LowLayerIncompatibleMessageType), _(LOW_LAYER_INVALID_IE_TYPE, LowLayerInvalidIEType), _(LOW_LAYER_INVALID_IE, LowLayerInvalidIE), _(LOW_LAYER_INCOMPATIBLE_MSG, LowLayerIncompatibleMessage), _(CS_BARRED, CSBarred), _(LOW_LAYER_PROTOCOL_ERROR, LowLayerProtocolError), #undef _ { 0, NULL, NULL }, }; static gsize etype = 0; if (g_once_init_enter (&etype)) { GType type = g_enum_register_static ("ModemSmsNetError", values); g_once_init_leave (&etype, type); } return (GType)etype; } /* ---------------------------------------------------------------------- */ GQuark modem_sms_errors_quark (void) { static gsize quark = 0; if (g_once_init_enter (&quark)) { GQuark domain = g_quark_from_static_string (MODEM_SMS_ERROR_PREFIX); g_once_init_leave (&quark, domain); } return (GQuark)quark; } /** GType for sms errors */ GType modem_sms_error_get_type (void) { static const GEnumValue values[] = { #define _(x, t) { MODEM_SMS_ERROR_ ## x, "MODEM_SMS_ERROR_" #x, #t } _(ROUTING_RELEASED, RoutingReleased), _(INVALID_PARAMETER, InvalidParameter), _(DEVICE_FAILURE, DeviceFailure), _(PP_RESERVED, PointToPointReserved), _(ROUTE_NOT_AVAILABLE, RouteNotAvailable), _(ROUTE_NOT_ALLOWED, RouteNotAllowed), _(SERVICE_RESERVED, ServiceReserved), _(INVALID_LOCATION, InvalidLocation), _(NO_NETW_RESPONSE, NoNetworkResponse), _(DEST_ADDR_FDN_RESTRICTED, DestinationAddressFDNRestricted), _(SMSC_ADDR_FDN_RESTRICTED, SMSCAddressFDNRestricted), _(RESEND_ALREADY_DONE, ResendAlreadyDone), _(SMSC_ADDR_NOT_AVAILABLE, SMSCAddressNotAvailable), _(ROUTING_FAILED, RoutingFailed), _(CS_INACTIVE, CSInactive), _(SENDING_ONGOING, SendingOngoing), _(SERVER_NOT_READY, ServerNotReady), _(NO_TRANSACTION, NoTransaction), _(INVALID_SUBSCRIPTION_NR, InvalidSubscriptionNumber), _(RECEPTION_FAILED, ReceptionFailed), _(RC_REJECTED, RCRejected), _(ALL_SUBSCRIPTIONS_ALLOCATED, AllSubscriptionsAllocated), _(SUBJECT_COUNT_OVERFLOW, SubjectCountOverflow), _(DCS_COUNT_OVERFLOW, DCSCountOverflow), #undef _ { 0, NULL, NULL }, }; static gsize etype = 0; if (g_once_init_enter (&etype)) { GType type = g_enum_register_static ("ModemSmsError", values); g_once_init_leave (&etype, type); } return (GType)etype; } /* ---------------------------------------------------------------------- */ typedef struct _ModemErrorMapping ModemErrorMapping; struct { GStaticRWLock lock; struct _ModemErrorMapping { ModemErrorMapping *next; GQuark domain; GType type; char const *prefix; gsize prefixlen; } *list; } modem_registered_errors = { G_STATIC_RW_LOCK_INIT, NULL, }; static ModemErrorMapping ** modem_error_append_mapping (ModemErrorMapping **list, GQuark domain, char const *prefix, GType type) { *list = g_new0 (ModemErrorMapping, 1); (*list)->domain = domain; (*list)->type = type; (*list)->prefix = prefix; (*list)->prefixlen = strlen (prefix); g_type_class_unref (g_type_class_ref (type)); dbus_g_error_domain_register (domain, prefix, type); return &(*list)->next; } static ModemErrorMapping ** modem_registered_errors_writer_lock (void) { ModemErrorMapping **list; g_static_rw_lock_writer_lock (&modem_registered_errors.lock); list = &modem_registered_errors.list; if (*list == NULL) { #define _(n) list = modem_error_append_mapping (list, \ MODEM_## n ##_ERRORS, \ MODEM_## n ##_ERROR_PREFIX, \ MODEM_TYPE_## n ##_ERROR) _(OFONO); _(CALL); _(CALL_NET); _(SMS); _(SMS_NET); #undef _ } return &modem_registered_errors.list; } static void modem_registered_errors_writer_unlock (void) { g_static_rw_lock_writer_unlock (&modem_registered_errors.lock); } static ModemErrorMapping const * modem_registered_errors_reader_lock (void) { if (modem_registered_errors.list == NULL) { modem_registered_errors_writer_lock (); modem_registered_errors_writer_unlock (); } g_static_rw_lock_reader_lock (&modem_registered_errors.lock); return modem_registered_errors.list; } static void modem_registered_errors_reader_unlock (void) { g_static_rw_lock_reader_unlock (&modem_registered_errors.lock); } void modem_error_register_mapping (GQuark domain, char const *prefix, GType type) { DEBUG ("enter"); ModemErrorMapping **list = modem_registered_errors_writer_lock (); for (; *list; list = &(*list)->next) if ((*list)->domain == domain) { modem_registered_errors_writer_unlock (); return; } modem_error_append_mapping (list, domain, prefix, type); modem_registered_errors_writer_unlock (); } char const * modem_error_domain_prefix (GQuark error_domain) { ModemErrorMapping const *map; map = modem_registered_errors_reader_lock (); for (; map; map = map->next) if (map->domain == error_domain) break; modem_registered_errors_reader_unlock (); if (map) return map->prefix; else return ""; } char const * modem_error_name (GError const *error, void *buffer, guint len) { GType type = G_TYPE_INVALID; if (error) { ModemErrorMapping const *map; map = modem_registered_errors_reader_lock (); for (; map; map = map->next) { if (map->domain == error->domain) { type = map->type; break; } } modem_registered_errors_reader_unlock (); } if (type) { GEnumClass *gec = g_type_class_peek (type); GEnumValue *ev = g_enum_get_value (gec, error->code); if (ev) return ev->value_nick; } if (error) g_snprintf (buffer, len, "Code%u", error->code); else g_snprintf (buffer, len, "NullError"); return buffer; } static char * dbus_gerror_fqn (GError const *error) { char const *fqn; switch (error->code) { case DBUS_GERROR_FAILED: fqn = DBUS_ERROR_FAILED; break; case DBUS_GERROR_NO_MEMORY: fqn = DBUS_ERROR_NO_MEMORY; break; case DBUS_GERROR_SERVICE_UNKNOWN: fqn = DBUS_ERROR_SERVICE_UNKNOWN; break; case DBUS_GERROR_NAME_HAS_NO_OWNER: fqn = DBUS_ERROR_NAME_HAS_NO_OWNER; break; case DBUS_GERROR_NO_REPLY: fqn = DBUS_ERROR_NO_REPLY; break; case DBUS_GERROR_IO_ERROR: fqn = DBUS_ERROR_IO_ERROR; break; case DBUS_GERROR_BAD_ADDRESS: fqn = DBUS_ERROR_BAD_ADDRESS; break; case DBUS_GERROR_NOT_SUPPORTED: fqn = DBUS_ERROR_NOT_SUPPORTED; break; case DBUS_GERROR_LIMITS_EXCEEDED: fqn = DBUS_ERROR_LIMITS_EXCEEDED; break; case DBUS_GERROR_ACCESS_DENIED: fqn = DBUS_ERROR_ACCESS_DENIED; break; case DBUS_GERROR_AUTH_FAILED: fqn = DBUS_ERROR_AUTH_FAILED; break; case DBUS_GERROR_NO_SERVER: fqn = DBUS_ERROR_NO_SERVER; break; case DBUS_GERROR_TIMEOUT: fqn = DBUS_ERROR_TIMEOUT; break; case DBUS_GERROR_NO_NETWORK: fqn = DBUS_ERROR_NO_NETWORK; break; case DBUS_GERROR_ADDRESS_IN_USE: fqn = DBUS_ERROR_ADDRESS_IN_USE; break; case DBUS_GERROR_DISCONNECTED: fqn = DBUS_ERROR_DISCONNECTED; break; case DBUS_GERROR_INVALID_ARGS: fqn = DBUS_ERROR_INVALID_ARGS; break; case DBUS_GERROR_FILE_NOT_FOUND: fqn = DBUS_ERROR_FILE_NOT_FOUND; break; case DBUS_GERROR_FILE_EXISTS: fqn = DBUS_ERROR_FILE_EXISTS; break; case DBUS_GERROR_UNKNOWN_METHOD: fqn = DBUS_ERROR_UNKNOWN_METHOD; break; case DBUS_GERROR_TIMED_OUT: fqn = DBUS_ERROR_TIMED_OUT; break; case DBUS_GERROR_MATCH_RULE_NOT_FOUND: fqn = DBUS_ERROR_MATCH_RULE_NOT_FOUND; break; case DBUS_GERROR_MATCH_RULE_INVALID: fqn = DBUS_ERROR_MATCH_RULE_INVALID; break; case DBUS_GERROR_SPAWN_EXEC_FAILED: fqn = DBUS_ERROR_SPAWN_EXEC_FAILED; break; case DBUS_GERROR_SPAWN_FORK_FAILED: fqn = DBUS_ERROR_SPAWN_FORK_FAILED; break; case DBUS_GERROR_SPAWN_CHILD_EXITED: fqn = DBUS_ERROR_SPAWN_CHILD_EXITED; break; case DBUS_GERROR_SPAWN_CHILD_SIGNALED: fqn = DBUS_ERROR_SPAWN_CHILD_SIGNALED; break; case DBUS_GERROR_SPAWN_FAILED: fqn = DBUS_ERROR_SPAWN_FAILED; break; case DBUS_GERROR_UNIX_PROCESS_ID_UNKNOWN: fqn = DBUS_ERROR_UNIX_PROCESS_ID_UNKNOWN; break; case DBUS_GERROR_INVALID_SIGNATURE: fqn = DBUS_ERROR_INVALID_SIGNATURE; break; case DBUS_GERROR_INVALID_FILE_CONTENT: fqn = DBUS_ERROR_INVALID_FILE_CONTENT; break; case DBUS_GERROR_SELINUX_SECURITY_CONTEXT_UNKNOWN: fqn = DBUS_ERROR_SELINUX_SECURITY_CONTEXT_UNKNOWN; break; case DBUS_GERROR_REMOTE_EXCEPTION: fqn = dbus_g_error_get_name ((GError *)error); break; default: return NULL; } return g_strdup (fqn); } char * modem_error_fqn (GError const *error) { char const *domain, *name; g_return_val_if_fail (error, NULL); if (error->domain == DBUS_GERROR) return dbus_gerror_fqn (error); domain = modem_error_domain_prefix (error->domain); if (domain == NULL) return NULL; name = modem_error_name (error, NULL, 0); if (name == NULL) return NULL; return g_strdup_printf ("%s.%s", domain, name); } void modem_error_fix (GError **error) { if (*error == NULL || (*error)->domain != DBUS_GERROR || (*error)->code != DBUS_GERROR_REMOTE_EXCEPTION) return; ModemErrorMapping const *map; GEnumClass *gec; GEnumValue *ev; GError *fixed; char const *fqe = (*error)->message + strlen ((*error)->message) + 1; map = modem_registered_errors_reader_lock (); for (; map; map = map->next) if (strncmp (fqe, map->prefix, map->prefixlen) == 0 && fqe[map->prefixlen] == '.' && strchr (fqe + map->prefixlen + 1, '.') == 0) break; modem_registered_errors_reader_unlock (); if (!map) { DEBUG ("no match for %s", fqe); return; } gec = g_type_class_peek (map->type); ev = g_enum_get_value_by_nick (gec, fqe + map->prefixlen + 1); if (ev == NULL) { DEBUG ("no code point for %s", fqe); return; } fixed = g_error_new_literal (map->domain, ev->value, (*error)->message); g_clear_error (error); *error = fixed; } telepathy-ring-2.3.24-37ad7111424725a5446e82dac516d7cab88149c7/modem/errors.h000066400000000000000000000334411251541261300245470ustar00rootroot00000000000000/* * modem/error.h - Ofono errors * * Copyright (C) 2008-2010 Nokia Corporation * @author Pekka Pessi * @author Lassi Syrjala * * This work is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This work is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this work; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _MODEM_ERRORS_ #define _MODEM_ERRORS_ #include G_BEGIN_DECLS /* ---------------------------------------------------------------------- */ /* This is what oFono provides now */ /** The error domain for oFono */ #define MODEM_OFONO_ERRORS modem_ofono_errors_quark () GQuark modem_ofono_errors_quark (void); /** The error type for oFono */ #define MODEM_TYPE_OFONO_ERROR modem_ofono_error_get_type () GType modem_ofono_error_get_type (void); /** The error prefix for oFono */ #define MODEM_OFONO_ERROR_PREFIX "org.ofono.Error" typedef enum _ModemoFonoError { MODEM_OFONO_ERROR_FAILED = 0, MODEM_OFONO_ERROR_INVALID_ARGUMENTS, MODEM_OFONO_ERROR_INVALID_FORMAT, MODEM_OFONO_ERROR_NOT_IMPLEMENTED, MODEM_OFONO_ERROR_NOT_SUPPORTED, MODEM_OFONO_ERROR_IN_PROGRESS, MODEM_OFONO_ERROR_NOT_FOUND, MODEM_OFONO_ERROR_NOT_ACTIVE, MODEM_OFONO_ERROR_TIMED_OUT, MODEM_OFONO_ERROR_SIM_NOT_READY, MODEM_OFONO_ERROR_IN_USE, MODEM_OFONO_ERROR_NOT_ATTACHED, MODEM_OFONO_ERROR_ATTACH_IN_PROGRESS } ModemoFonoError; /* ---------------------------------------------------------------------- */ /* And this is what we'd like oFono to provide one day */ /** The error domain for the call service. */ #define MODEM_CALL_ERRORS modem_call_errors_quark () GQuark modem_call_errors_quark (void); /** The error type for the call service. */ #define MODEM_TYPE_CALL_ERROR modem_call_error_get_type () GType modem_call_error_get_type (void); #define MODEM_CALL_ERROR_PREFIX "org.ofono.Bogus.Call" typedef enum _ModemCallError { MODEM_CALL_ERROR_NO_ERROR = 0, MODEM_CALL_ERROR_NO_CALL, MODEM_CALL_ERROR_RELEASE_BY_USER, MODEM_CALL_ERROR_BUSY_USER_REQUEST, MODEM_CALL_ERROR_ERROR_REQUEST, MODEM_CALL_ERROR_CALL_ACTIVE, MODEM_CALL_ERROR_NO_CALL_ACTIVE, MODEM_CALL_ERROR_INVALID_CALL_MODE, MODEM_CALL_ERROR_TOO_LONG_ADDRESS, MODEM_CALL_ERROR_INVALID_ADDRESS, MODEM_CALL_ERROR_EMERGENCY, MODEM_CALL_ERROR_NO_SERVICE, MODEM_CALL_ERROR_NO_COVERAGE, MODEM_CALL_ERROR_CODE_REQUIRED, MODEM_CALL_ERROR_NOT_ALLOWED, MODEM_CALL_ERROR_DTMF_ERROR, MODEM_CALL_ERROR_CHANNEL_LOSS, MODEM_CALL_ERROR_FDN_NOT_OK, MODEM_CALL_ERROR_BLACKLIST_BLOCKED, MODEM_CALL_ERROR_BLACKLIST_DELAYED, MODEM_CALL_ERROR_EMERGENCY_FAILURE, MODEM_CALL_ERROR_NO_SIM, MODEM_CALL_ERROR_DTMF_SEND_ONGOING, MODEM_CALL_ERROR_CS_INACTIVE, MODEM_CALL_ERROR_NOT_READY, MODEM_CALL_ERROR_INCOMPATIBLE_DEST, MODEM_CALL_ERROR_GENERIC } ModemCallError; /* ---------------------------------------------------------------------- */ GQuark modem_call_net_errors_quark (void); GType modem_call_net_error_get_type (void); /** The error domain for errors from GSM network. * * See 3GPP 24.008 and Q.931 for detailed explanation. */ #define MODEM_CALL_NET_ERRORS modem_call_net_errors_quark () /** The error type for errors from GSM network */ #define MODEM_TYPE_CALL_NET_ERROR modem_call_net_error_get_type () #define MODEM_CALL_NET_ERROR_PREFIX "org.ofono.Bogus.Call.Network" typedef enum _ModemNetError { MODEM_CALL_NET_ERROR_UNASSIGNED_NUMBER = 0x01, /* UnassignedNumber */ MODEM_CALL_NET_ERROR_NO_ROUTE = 0x03, /* NoRouteToDestination */ MODEM_CALL_NET_ERROR_CH_UNACCEPTABLE = 0x06, /* ChannelUnacceptable */ MODEM_CALL_NET_ERROR_OPER_BARRING = 0x08, /* OperatorDeterminedBarring */ MODEM_CALL_NET_ERROR_NORMAL = 0x10, /* NormalCallClearing */ MODEM_CALL_NET_ERROR_USER_BUSY = 0x11, /* UserBusy */ MODEM_CALL_NET_ERROR_NO_USER_RESPONSE = 0x12, /* NoUserResponse */ MODEM_CALL_NET_ERROR_ALERT_NO_ANSWER = 0x13, /* AlertNoAnswer */ MODEM_CALL_NET_ERROR_CALL_REJECTED = 0x15, /* CallRejected */ MODEM_CALL_NET_ERROR_NUMBER_CHANGED = 0x16, /* NumberChanged */ MODEM_CALL_NET_ERROR_NON_SELECT_CLEAR = 0x1A, /* NonSelectedClearing */ MODEM_CALL_NET_ERROR_DEST_OUT_OF_ORDER = 0x1B, /* DestinationOutOfOrder */ MODEM_CALL_NET_ERROR_INVALID_NUMBER = 0x1C, /* InvalidNumber */ MODEM_CALL_NET_ERROR_FACILITY_REJECTED = 0x1D, /* FacilityRejected */ MODEM_CALL_NET_ERROR_RESP_TO_STATUS = 0x1E, /* ResponseToStatus */ MODEM_CALL_NET_ERROR_NORMAL_UNSPECIFIED = 0x1F, /* UnspecifiedNormal */ MODEM_CALL_NET_ERROR_NO_CHANNEL = 0x22, /* NoChannelAvailable */ MODEM_CALL_NET_ERROR_NETW_OUT_OF_ORDER = 0x26, /* NetworkOutOfOrder */ MODEM_CALL_NET_ERROR_TEMPORARY_FAILURE = 0x29, /* TemporaryFailure */ MODEM_CALL_NET_ERROR_CONGESTION = 0x2A, /* Congestion */ MODEM_CALL_NET_ERROR_ACCESS_INFO_DISC = 0x2B, /* AccessInformationDiscarded */ MODEM_CALL_NET_ERROR_CHANNEL_NA = 0x2C, /* ChannelNotAvailable */ MODEM_CALL_NET_ERROR_RESOURCES_NA = 0x2F, /* ResourcesNotAvailable */ MODEM_CALL_NET_ERROR_QOS_NA = 0x31, /* QoSNotAvailable */ MODEM_CALL_NET_ERROR_FACILITY_UNSUBS = 0x32, /* RequestedFacilityNotSubscribed */ MODEM_CALL_NET_ERROR_COMING_BARRED_CUG = 0x37, /* IncomingCallsBarredWithinCUG */ MODEM_CALL_NET_ERROR_BC_UNAUTHORIZED = 0x39, /* BearerCapabilityUnauthorized */ MODEM_CALL_NET_ERROR_BC_NA = 0x3A, /* BearerCapabilityNotAvailable */ MODEM_CALL_NET_ERROR_SERVICE_NA = 0x3F, /* ServiceNotAvailable */ MODEM_CALL_NET_ERROR_BEARER_NOT_IMPL = 0x41, /* BearerNotImplemented */ MODEM_CALL_NET_ERROR_ACM_MAX = 0x44, /* ACMMax */ MODEM_CALL_NET_ERROR_FACILITY_NOT_IMPL = 0x45, /* FacilityNotImplemented */ MODEM_CALL_NET_ERROR_ONLY_RDI_BC = 0x46, /* OnlyRestrictedDIBearerCapability */ MODEM_CALL_NET_ERROR_SERVICE_NOT_IMPL = 0x4F, /* ServiceNotImplemented */ MODEM_CALL_NET_ERROR_INVALID_TI = 0x51, /* InvalidTransactionIdentifier */ MODEM_CALL_NET_ERROR_NOT_IN_CUG = 0x57, /* NotInCUG */ MODEM_CALL_NET_ERROR_INCOMPATIBLE_DEST = 0x58, /* IncompatibleDestination */ MODEM_CALL_NET_ERROR_INV_TRANS_NET_SEL = 0x5B, /* InvalidTransitNetSelected */ MODEM_CALL_NET_ERROR_SEMANTICAL_ERR = 0x5F, /* SemanticalError */ MODEM_CALL_NET_ERROR_INVALID_MANDATORY = 0x60, /* InvalidMandatoryInformation */ MODEM_CALL_NET_ERROR_MSG_TYPE_INEXIST = 0x61, /* MessageTypeNonExistent */ MODEM_CALL_NET_ERROR_MSG_TYPE_INCOMPAT = 0x62, /* MessageTypeIncompatible */ MODEM_CALL_NET_ERROR_IE_NON_EXISTENT = 0x63, /* InformationElementNonExistent */ MODEM_CALL_NET_ERROR_COND_IE_ERROR = 0x64, /* ConditionalInformationElementError */ MODEM_CALL_NET_ERROR_MSG_INCOMPATIBLE = 0x65, /* IncompatibleMessage */ MODEM_CALL_NET_ERROR_TIMER_EXPIRY = 0x66, /* TimerExpiry */ MODEM_CALL_NET_ERROR_PROTOCOL_ERROR = 0x6F, /* ProtocolError */ MODEM_CALL_NET_ERROR_INTERWORKING = 0x7F, /* Generic */ MODEM_CALL_NET_ERROR_GENERIC = MODEM_CALL_NET_ERROR_INTERWORKING } ModemNetError; /* ---------------------------------------------------------------------- */ /** The error domain for the GSM cause. */ #define MODEM_SMS_NET_ERRORS modem_sms_net_errors_quark () GQuark modem_sms_net_errors_quark (void); /** The error type for the SMS GSM cause. */ #define MODEM_TYPE_SMS_NET_ERROR modem_sms_net_error_get_type () GType modem_sms_net_error_get_type (void); #define MODEM_SMS_NET_ERROR_PREFIX "org.ofono.Bogus.SMS.Network" enum { /* Generic ISDN errors */ MODEM_SMS_NET_ERROR_SUCCESS = 0x00, MODEM_SMS_NET_ERROR_UNASSIGNED_NUMBER = 0x01, MODEM_SMS_NET_ERROR_OPER_DETERMINED_BARR = 0x08, MODEM_SMS_NET_ERROR_CALL_BARRED = 0x0A, MODEM_SMS_NET_ERROR_RESERVED = 0x0B, MODEM_SMS_NET_ERROR_MSG_TRANSFER_REJ = 0x15, MODEM_SMS_NET_ERROR_MEMORY_CAPACITY_EXC = 0x16, MODEM_SMS_NET_ERROR_DEST_OUT_OF_ORDER = 0x1B, MODEM_SMS_NET_ERROR_UNDEFINED_SUBSCRIBER = 0x1C, MODEM_SMS_NET_ERROR_FACILITY_REJECTED = 0x1D, MODEM_SMS_NET_ERROR_UNKNOWN_SUBSCRIBER = 0x1E, MODEM_SMS_NET_ERROR_NETWORK_OUT_OF_ORDER = 0x26, MODEM_SMS_NET_ERROR_TEMPORARY_FAILURE = 0x29, MODEM_SMS_NET_ERROR_CONGESTION = 0x2A, MODEM_SMS_NET_ERROR_RESOURCE_UNAVAILABLE = 0x2F, MODEM_SMS_NET_ERROR_REQ_FACILITY_NOT_SUB = 0x32, MODEM_SMS_NET_ERROR_REQ_FACILITY_NOT_IMP = 0x45, MODEM_SMS_NET_ERROR_INVALID_REFERENCE = 0x51, MODEM_SMS_NET_ERROR_INVALID_MSG = 0x5F, MODEM_SMS_NET_ERROR_INVALID_MAND_IE = 0x60, MODEM_SMS_NET_ERROR_INVALID_MSG_TYPE = 0x61, MODEM_SMS_NET_ERROR_INCOMPATIBLE_MSG_TYPE = 0x62, MODEM_SMS_NET_ERROR_INVALID_IE_TYPE = 0x63, MODEM_SMS_NET_ERROR_PROTOCOL_ERROR = 0x6F, MODEM_SMS_NET_ERROR_INTERWORKING = 0x7F, /* GSM-specifc errors (see 24.008 annexes G, H) */ MODEM_SMS_NET_ERROR_LOW_LAYER_NO_CAUSE = 0x80, MODEM_SMS_NET_ERROR_IMSI_UNKNOWN_HLR = 0x82, MODEM_SMS_NET_ERROR_ILLEGAL_MS = 0x83, MODEM_SMS_NET_ERROR_IMSI_UNKNOWN_VLR = 0x84, MODEM_SMS_NET_ERROR_IMEI_NOT_ACCEPTED = 0x85, MODEM_SMS_NET_ERROR_ILLEGAL_ME = 0x86, MODEM_SMS_NET_ERROR_PLMN_NOT_ALLOWED = 0x8B, MODEM_SMS_NET_ERROR_LA_NOT_ALLOWED = 0x8C, MODEM_SMS_NET_ERROR_ROAM_NOT_ALLOWED_LA = 0x8D, MODEM_SMS_NET_ERROR_NO_SUITABLE_CELLS_LA = 0x8F, MODEM_SMS_NET_ERROR_NETWORK_FAILURE = 0x91, MODEM_SMS_NET_ERROR_MAC_FAILURE = 0x94, MODEM_SMS_NET_ERROR_SYNC_FAILURE = 0x95, MODEM_SMS_NET_ERROR_LOW_LAYER_CONGESTION = 0x96, MODEM_SMS_NET_ERROR_AUTH_UNACCEPTABLE = 0x97, MODEM_SMS_NET_ERROR_SERV_OPT_NOT_SUPPORTED = 0xA0, MODEM_SMS_NET_ERROR_SERV_OPT_NOT_SUBSCRIBED = 0xA1, MODEM_SMS_NET_ERROR_SERV_OPT_TEMP_OUT_OF_ORDER = 0xA2, MODEM_SMS_NET_ERROR_CALL_CANNOT_BE_IDENTIFIED = 0xA6, MODEM_SMS_NET_ERROR_LOW_LAYER_INVALID_MSG = 0xDF, MODEM_SMS_NET_ERROR_LOW_LAYER_INVALID_MAND_IE = 0xE0, MODEM_SMS_NET_ERROR_LOW_LAYER_INVALID_MSG_TYPE = 0xE1, MODEM_SMS_NET_ERROR_LOW_LAYER_INCOMPATIBLE_MSG_TYPE = 0xE2, MODEM_SMS_NET_ERROR_LOW_LAYER_INVALID_IE_TYPE = 0xE3, MODEM_SMS_NET_ERROR_LOW_LAYER_INVALID_IE = 0xE4, MODEM_SMS_NET_ERROR_LOW_LAYER_INCOMPATIBLE_MSG = 0xE5, MODEM_SMS_NET_ERROR_CS_BARRED = 0xE8, MODEM_SMS_NET_ERROR_LOW_LAYER_PROTOCOL_ERROR = 0xEF, }; /* ---------------------------------------------------------------------- */ /** The error domain for the SMS errors. */ #define MODEM_SMS_ERRORS modem_sms_errors_quark () GQuark modem_sms_errors_quark (void); /** The error type for the SMS errors. */ #define MODEM_TYPE_SMS_ERROR modem_sms_error_get_type () GType modem_sms_error_get_type (void); #define MODEM_SMS_ERROR_PREFIX "org.ofono.Bogus.SMS" enum { MODEM_SMS_ERROR_ROUTING_RELEASED = 0x01, MODEM_SMS_ERROR_INVALID_PARAMETER = 0x02, MODEM_SMS_ERROR_DEVICE_FAILURE = 0x03, MODEM_SMS_ERROR_PP_RESERVED = 0x04, MODEM_SMS_ERROR_ROUTE_NOT_AVAILABLE = 0x05, MODEM_SMS_ERROR_ROUTE_NOT_ALLOWED = 0x06, MODEM_SMS_ERROR_SERVICE_RESERVED = 0x07, MODEM_SMS_ERROR_INVALID_LOCATION = 0x08, MODEM_SMS_ERROR_NO_NETW_RESPONSE = 0x0B, MODEM_SMS_ERROR_DEST_ADDR_FDN_RESTRICTED = 0x0C, MODEM_SMS_ERROR_SMSC_ADDR_FDN_RESTRICTED = 0x0D, MODEM_SMS_ERROR_RESEND_ALREADY_DONE = 0x0E, MODEM_SMS_ERROR_SMSC_ADDR_NOT_AVAILABLE = 0x0F, MODEM_SMS_ERROR_ROUTING_FAILED = 0x10, MODEM_SMS_ERROR_CS_INACTIVE = 0x11, MODEM_SMS_ERROR_SENDING_ONGOING = 0x15, MODEM_SMS_ERROR_SERVER_NOT_READY = 0x16, MODEM_SMS_ERROR_NO_TRANSACTION = 0x17, MODEM_SMS_ERROR_INVALID_SUBSCRIPTION_NR = 0x19, MODEM_SMS_ERROR_RECEPTION_FAILED = 0x1A, MODEM_SMS_ERROR_RC_REJECTED = 0x1B, MODEM_SMS_ERROR_ALL_SUBSCRIPTIONS_ALLOCATED = 0x1C, MODEM_SMS_ERROR_SUBJECT_COUNT_OVERFLOW = 0x1D, MODEM_SMS_ERROR_DCS_COUNT_OVERFLOW = 0x1E, }; /* ---------------------------------------------------------------------- */ void modem_error_register_mapping (GQuark domain, char const *prefix, GType); char const *modem_error_domain_prefix (GQuark); char const *modem_error_name (GError const *error, void *buffer, guint len); void modem_error_fix (GError **error); char *modem_error_fqn (GError const *error); G_END_DECLS #endif telepathy-ring-2.3.24-37ad7111424725a5446e82dac516d7cab88149c7/modem/modem.c000066400000000000000000000463711251541261300243350ustar00rootroot00000000000000/* * modem/modem.c - oFono modem * * Copyright (C) 2009, 2010 Nokia Corporation * * @author Pekka Pessi * @author Kai Vehmanen * * This work is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This work is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this work; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "config.h" #define MODEM_DEBUG_FLAG MODEM_LOG_MODEM #include "debug.h" #include "modem/modem.h" #include "modem/request-private.h" #include "modem/oface.h" #include "modem/ofono.h" #include "modem/call.h" #include "modem/sim.h" #include "modem/sms.h" #include "modem/errors.h" #include "modem/oface.h" #include #include #include #include #include "signals-marshal.h" #include #include #include #include #include #include #include /* ------------------------------------------------------------------------ */ G_DEFINE_TYPE (Modem, modem, MODEM_TYPE_OFACE); enum { PROP_NONE, PROP_POWERED, PROP_ONLINE, PROP_NAME, PROP_MANUFACTURER, PROP_MODEL, PROP_REVISION, PROP_IMEI, PROP_FEATURES, PROP_INTERFACES, N_PROPS }; enum { SIGNAL_INTERFACE_ADDED, SIGNAL_INTERFACE_REMOVED, SIGNAL_IMSI_ADDED, N_SIGNALS }; static guint signals[N_SIGNALS]; /* private data */ struct _ModemPrivate { /* Properties */ gboolean powered; gboolean online; gchar *name; gchar *manufacturer; gchar *model; gchar *revision; gchar *imei; gchar **features; gchar **interfaces; GHashTable *ifhash; GHashTable *connecting; GHashTable *ofaces; }; /* ------------------------------------------------------------------------ */ /* Local functions */ static void on_notify_interfaces (Modem *, GParamSpec *, Modem *); static void modem_update_interfaces (Modem *); static void on_sim_notify_imsi (ModemSIMService *, GParamSpec *, Modem *); static void on_oface_connected (ModemOface *, gboolean, Modem *); /* ------------------------------------------------------------------------ */ static void modem_init (Modem *self) { DEBUG ("enter"); self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, MODEM_TYPE_MODEM, ModemPrivate); self->priv->ifhash = g_hash_table_new (g_str_hash, g_str_equal); self->priv->connecting = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref); self->priv->ofaces = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref); } static void modem_constructed (GObject *object) { if (G_OBJECT_CLASS (modem_parent_class)->constructed) G_OBJECT_CLASS (modem_parent_class)->constructed (object); } static void modem_dispose (GObject *object) { if (G_OBJECT_CLASS (modem_parent_class)->dispose) G_OBJECT_CLASS (modem_parent_class)->dispose (object); } static void modem_finalize (GObject *object) { Modem *self = MODEM_MODEM (object); ModemPrivate *priv = self->priv; DEBUG ("enter"); /* Free any data held directly by the object here */ g_free (priv->name); g_free (priv->manufacturer); g_free (priv->model); g_free (priv->revision); g_free (priv->imei); g_strfreev (priv->features); g_strfreev (priv->interfaces); g_hash_table_destroy (priv->ifhash); g_hash_table_destroy (priv->ofaces); g_hash_table_destroy (priv->connecting); G_OBJECT_CLASS (modem_parent_class)->finalize (object); } static void modem_set_property (GObject *obj, guint property_id, const GValue *value, GParamSpec *pspec) { Modem *self = MODEM_MODEM (obj); ModemPrivate *priv = self->priv; switch (property_id) { case PROP_POWERED: priv->powered = g_value_get_boolean (value); break; case PROP_ONLINE: priv->online = g_value_get_boolean (value); break; case PROP_NAME: g_free (priv->name); priv->name = g_value_dup_string (value); break; case PROP_MANUFACTURER: g_free (priv->manufacturer); priv->manufacturer = g_value_dup_string (value); break; case PROP_MODEL: g_free (priv->model); priv->model = g_value_dup_string (value); break; case PROP_REVISION: g_free (priv->revision); priv->revision = g_value_dup_string (value); break; case PROP_IMEI: g_free (priv->imei); priv->imei = g_value_dup_string (value); break; case PROP_FEATURES: g_strfreev (priv->features); priv->features = g_value_dup_boxed (value); break; case PROP_INTERFACES: g_strfreev (priv->interfaces); priv->interfaces = g_value_dup_boxed (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, property_id, pspec); break; } } static void modem_get_property (GObject *obj, guint property_id, GValue *value, GParamSpec *pspec) { Modem *self = MODEM_MODEM (obj); ModemPrivate *priv = self->priv; switch (property_id) { case PROP_POWERED: g_value_set_boolean (value, priv->powered); break; case PROP_ONLINE: g_value_set_boolean (value, priv->online); break; case PROP_NAME: g_value_set_string (value, priv->name); break; case PROP_MANUFACTURER: g_value_set_string (value, priv->manufacturer); break; case PROP_MODEL: g_value_set_string (value, priv->model); break; case PROP_REVISION: g_value_set_string (value, priv->revision); break; case PROP_IMEI: g_value_set_string (value, priv->imei); break; case PROP_FEATURES: g_value_set_boxed (value, priv->features); break; case PROP_INTERFACES: g_value_set_boxed (value, priv->interfaces); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, property_id, pspec); break; } } /* ------------------------------------------------------------------------- */ /** Maps properties of org.ofono.Modem to matching Modem properties */ char const * modem_property_mapper (char const *name) { if (!strcmp (name, "Powered")) return "powered"; if (!strcmp (name, "Online")) return "online"; if (!strcmp (name, "Name")) return "name"; if (!strcmp (name, "Manufacturer")) return "manufacturer"; if (!strcmp (name, "Model")) return "model"; if (!strcmp (name, "Revision")) return "revision"; if (!strcmp (name, "Serial")) return "imei"; if (!strcmp (name, "Features")) return "features"; if (!strcmp (name, "Interfaces")) return "interfaces"; return NULL; } static void modem_connect (ModemOface *_self) { DEBUG ("(%p): enter", _self); modem_oface_connect_properties (_self, FALSE); g_signal_connect (_self, "notify::interfaces", G_CALLBACK(on_notify_interfaces), _self); modem_update_interfaces ( MODEM_MODEM(_self) ); } static void modem_disconnect (ModemOface *_self) { DEBUG ("(%p): enter", _self); Modem *self = MODEM_MODEM (_self); ModemPrivate *priv = self->priv; GHashTableIter iter[1]; char *interface; ModemOface *oface; g_signal_handlers_disconnect_by_func (_self, G_CALLBACK(on_notify_interfaces), _self); for (g_hash_table_iter_init (iter, priv->connecting); g_hash_table_iter_next (iter, (gpointer)&interface, (gpointer)&oface);) { g_signal_handlers_disconnect_by_func (oface, on_oface_connected, self); } for (g_hash_table_iter_init (iter, priv->ofaces); g_hash_table_iter_next (iter, (gpointer)&interface, (gpointer)&oface);) { g_signal_handlers_disconnect_by_func (oface, on_oface_connected, self); if (MODEM_IS_SIM_SERVICE (oface)) g_signal_handlers_disconnect_by_func (oface, on_sim_notify_imsi, self); } } static void modem_class_init (ModemClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); ModemOfaceClass *oface_class = MODEM_OFACE_CLASS (klass); DEBUG ("enter"); object_class->constructed = modem_constructed; object_class->dispose = modem_dispose; object_class->finalize = modem_finalize; object_class->get_property = modem_get_property; object_class->set_property = modem_set_property; oface_class->ofono_interface = MODEM_OFACE_MODEM; oface_class->property_mapper = modem_property_mapper; oface_class->connect = modem_connect; oface_class->disconnect = modem_disconnect; g_type_class_add_private (klass, sizeof (ModemPrivate)); signals[SIGNAL_INTERFACE_ADDED] = g_signal_new ("interface-added", G_OBJECT_CLASS_TYPE (klass), G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED, 0, NULL, NULL, g_cclosure_marshal_VOID__OBJECT, G_TYPE_NONE, 1, G_TYPE_OBJECT); signals[SIGNAL_INTERFACE_REMOVED] = g_signal_new ("interface-removed", G_OBJECT_CLASS_TYPE (klass), G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED, 0, NULL, NULL, g_cclosure_marshal_VOID__OBJECT, G_TYPE_NONE, 1, G_TYPE_OBJECT); signals[SIGNAL_IMSI_ADDED] = g_signal_new ("imsi-added", G_OBJECT_CLASS_TYPE (klass), G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED, 0, NULL, NULL, g_cclosure_marshal_VOID__STRING, G_TYPE_NONE, 1, G_TYPE_STRING); g_object_class_install_property (object_class, PROP_POWERED, g_param_spec_boolean ("powered", "Powered", "The power state of the modem device", FALSE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (object_class, PROP_ONLINE, g_param_spec_boolean ("online", "Online", "The radio state of the modem. Online is false in flight mode.", FALSE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (object_class, PROP_NAME, g_param_spec_string ("name", "Name", "Friendly name of the modem device.", "", G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (object_class, PROP_MANUFACTURER, g_param_spec_string ("manufacturer", "Manufacturer", "The manufacturer of the modem device.", "", G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (object_class, PROP_MODEL, g_param_spec_string ("model", "Model", "The model of the modem device.", "", G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (object_class, PROP_REVISION, g_param_spec_string ("revision", "Revision", "The revision of the modem device.", "", G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (object_class, PROP_IMEI, g_param_spec_string ("imei", "Serial", "The IMEI (serial number) of the modem device.", "", G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (object_class, PROP_FEATURES, g_param_spec_boxed ("features", "Features", "List of currently enabled features with simple " "string abbreviations like 'sms', 'sim' etc.", G_TYPE_STRV, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); /* XXX/KV: should this be removed as this is oFono-specific */ g_object_class_install_property (object_class, PROP_INTERFACES, g_param_spec_boxed ("interfaces", "Interfaces", "Set of interfaces currently supported by the modem. " "The set depends on the state of the device " "(registration status, SIM inserted status, " "network capabilities, device capabilities, etc.)", G_TYPE_STRV, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); } /* -------------------------------------------------------------------------- */ static void on_sim_notify_imsi (ModemSIMService *sim, GParamSpec *dummy, Modem *self) { gchar *imsi = NULL; DEBUG ("enter"); g_object_get (sim, "imsi", &imsi, NULL); if (imsi == NULL) return; if (strlen (imsi)) { DEBUG ("emitting imsi-added \"%s\" for %s", imsi, modem_oface_object_path (MODEM_OFACE (sim))); g_signal_emit (self, signals[SIGNAL_IMSI_ADDED], 0, imsi); } g_free (imsi); } static void on_oface_connected (ModemOface *oface, gboolean connected, Modem *self) { ModemPrivate *priv = self->priv; char *interface; DEBUG ("enter"); g_object_get (oface, "interface", &interface, NULL); DEBUG ("%s %s interface = %s, oface = %p", connected ? "connected" : "disconnected", modem_oface_object_path (oface), interface, oface); if (!connected) { g_signal_handlers_disconnect_by_func (oface, on_oface_connected, self); if (MODEM_IS_SIM_SERVICE (oface)) g_signal_handlers_disconnect_by_func (oface, on_sim_notify_imsi, self); if (g_hash_table_lookup (priv->connecting, interface)) { g_hash_table_remove (priv->connecting, interface); } else if (g_hash_table_lookup (priv->ofaces, interface)) { DEBUG("emitting interface-removed for %s", interface); g_signal_emit (self, signals[SIGNAL_INTERFACE_REMOVED], 0, oface); g_hash_table_remove (priv->ofaces, interface); } g_free (interface); return; } g_hash_table_insert (priv->ofaces, interface, g_object_ref (oface)); g_hash_table_remove (priv->connecting, interface); DEBUG ("emitting interface-added for %s", interface); g_signal_emit (self, signals[SIGNAL_INTERFACE_ADDED], 0, oface); if (MODEM_IS_SIM_SERVICE (oface)) { g_signal_connect (oface, "notify::imsi", G_CALLBACK(on_sim_notify_imsi), self); on_sim_notify_imsi (MODEM_SIM_SERVICE (oface), NULL, self); } } static void modem_update_interfaces (Modem *self) { ModemPrivate *priv = self->priv; ModemOface *oface; ModemOface *already; GHashTableIter iter[1]; char const *object_path; GHashTable *prev_ifhash; char *interface; guint i; DEBUG ("enter"); prev_ifhash = priv->ifhash; priv->ifhash = g_hash_table_new (g_str_hash, g_str_equal); for (i = 0; priv->interfaces[i]; i++) { interface = g_strdup (priv->interfaces[i]); g_hash_table_insert (priv->ifhash, interface, GUINT_TO_POINTER(1)); } object_path = modem_oface_object_path (MODEM_OFACE (self)); for (i = 0; priv->interfaces[i]; i++) { interface = priv->interfaces[i]; if (g_hash_table_lookup (priv->ofaces, interface)) continue; if (g_hash_table_lookup (prev_ifhash, interface)) continue; oface = modem_oface_new (interface, object_path); if (oface == NULL) { DEBUG("Modem %s ignoring interface %s", object_path, interface); continue; } DEBUG("Modem %s adding interface %s", object_path, interface); already = g_hash_table_lookup (priv->connecting, interface); if (already) { /* interface was added, removed and added before it got connected */ g_signal_handlers_disconnect_by_func (already, on_oface_connected, self); } g_hash_table_insert (priv->connecting, g_strdup (interface), oface); g_signal_connect (oface, "connected", G_CALLBACK (on_oface_connected), self); modem_oface_connect (oface); } g_hash_table_unref (prev_ifhash); DEBUG("All modem %s interfaces added", object_path); redo_connecting: for (g_hash_table_iter_init (iter, priv->connecting); g_hash_table_iter_next (iter, (gpointer)&interface, (gpointer)&oface);) { if (g_hash_table_lookup (priv->ifhash, interface) == NULL) { modem_oface_disconnect (oface); goto redo_connecting; } } redo_ofaces: for (g_hash_table_iter_init (iter, priv->ofaces); g_hash_table_iter_next (iter, (gpointer)&interface, (gpointer)&oface);) { if (g_hash_table_lookup (priv->ifhash, interface) == NULL) { modem_oface_disconnect (oface); goto redo_ofaces; } } } static void on_notify_interfaces (Modem *self, GParamSpec *dummy1, Modem *dummy2) { modem_update_interfaces (self); } /* -------------------------------------------------------------------------- */ /* modem interface */ char const * modem_get_modem_path (Modem const *self) { return modem_oface_object_path (MODEM_OFACE (self)); } gboolean modem_is_powered (Modem const *self) { g_return_val_if_fail (MODEM_IS_MODEM (self), FALSE); return self->priv->powered; } gboolean modem_is_online (Modem const *self) { g_return_val_if_fail (MODEM_IS_MODEM (self), FALSE); return self->priv->online; } ModemOface * modem_get_interface (Modem const *self, char const *interface) { g_return_val_if_fail (MODEM_IS_MODEM (self), FALSE); return g_hash_table_lookup (self->priv->ofaces, interface); } gboolean modem_has_interface (Modem const *self, char const *interface) { g_return_val_if_fail (MODEM_IS_MODEM (self), FALSE); if (!self->priv->ifhash) return FALSE; return g_hash_table_lookup (self->priv->ifhash, interface) != NULL; } ModemOface ** modem_list_interfaces (Modem const *self) { ModemPrivate *priv; GPtrArray *array; char *key; ModemOface *oface; GHashTableIter iter[1]; g_return_val_if_fail (MODEM_IS_MODEM (self), g_new (ModemOface *, 1)); priv = self->priv; array = g_ptr_array_sized_new (g_hash_table_size (priv->ofaces) + 1); for (g_hash_table_iter_init (iter, priv->ofaces); g_hash_table_iter_next (iter, (gpointer)&key, (gpointer)&oface);) { g_ptr_array_add(array, oface); } g_ptr_array_add (array, NULL); return (ModemOface **)g_ptr_array_free (array, FALSE); } gboolean modem_supports_sim (Modem const *self) { return modem_get_interface (self, MODEM_OFACE_SIM) != NULL; } gboolean modem_supports_call (Modem const *self) { return modem_get_interface (self, MODEM_OFACE_CALL_MANAGER) != NULL; } gboolean modem_supports_sms (Modem const *self) { return modem_get_interface (self, MODEM_OFACE_SMS) != NULL; } gboolean modem_has_imsi (Modem const *self, gchar const *imsi) { ModemSIMService *sim; gchar *sim_imsi; gboolean match; g_return_val_if_fail (MODEM_IS_MODEM (self), FALSE); g_return_val_if_fail (imsi != NULL, FALSE); sim = (ModemSIMService *)modem_get_interface (self, MODEM_OFACE_SIM); if (sim == NULL) return FALSE; g_object_get(sim, "imsi", &sim_imsi, NULL); match = strcmp(sim_imsi, imsi) == 0; g_free(sim_imsi); return match; } gboolean modem_has_imei (Modem const *self, gchar const *imei) { g_return_val_if_fail (MODEM_IS_MODEM (self), FALSE); g_return_val_if_fail (imei != NULL, FALSE); return g_strcmp0(self->priv->imei, imei) == 0; } telepathy-ring-2.3.24-37ad7111424725a5446e82dac516d7cab88149c7/modem/modem.h000066400000000000000000000054401251541261300243320ustar00rootroot00000000000000/* * modem/modem.h - Interface towards oFono modem instance * * Copyright (C) 2009,2010 Nokia Corporation * @author Pekka Pessi * * This work is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This work is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this work; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _MODEM_MODEM_H_ #define _MODEM_MODEM_H_ #include #include #include G_BEGIN_DECLS typedef struct _Modem Modem; typedef struct _ModemClass ModemClass; typedef struct _ModemPrivate ModemPrivate; struct _ModemClass { ModemOfaceClass parent_class; }; struct _Modem { ModemOface parent; ModemPrivate *priv; }; GType modem_get_type (void); /* TYPE MACROS */ #define MODEM_TYPE_MODEM (modem_get_type ()) #define MODEM_MODEM(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), MODEM_TYPE_MODEM, Modem)) #define MODEM_CLASS_MODEM(klass) \ (G_TYPE_CHECK_CLASS_CAST ((klass), MODEM_TYPE_MODEM, ModemClass)) #define MODEM_IS_MODEM(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MODEM_TYPE_MODEM)) #define MODEM_IS_MODEM_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE ((klass), MODEM_TYPE_MODEM)) #define MODEM_GET_MODEM_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), MODEM_TYPE_MODEM, ModemClass)) /* ---------------------------------------------------------------------- */ #define MODEM_OFACE_MODEM "org.ofono.Modem" char const *modem_get_modem_path (Modem const *self); gboolean modem_is_powered (Modem const *self); gboolean modem_is_online (Modem const *self); gboolean modem_has_interface (Modem const *self, char const *interface); ModemOface *modem_get_interface (Modem const *self, char const *interface); ModemOface **modem_list_interfaces (Modem const *self); gboolean modem_supports_sim (Modem const *self); gboolean modem_supports_call (Modem const *self); gboolean modem_supports_sms (Modem const *self); gboolean modem_has_imsi (Modem const *self, gchar const *imsi); gboolean modem_has_imei (Modem const *self, gchar const *imei); G_END_DECLS #endif /* #ifndef _MODEM_MODEM_H_*/ telepathy-ring-2.3.24-37ad7111424725a5446e82dac516d7cab88149c7/modem/oface.c000066400000000000000000000451671251541261300243130ustar00rootroot00000000000000/* * modem/oface.c - Abstract oFono D-Bus interface class * * Handle GetProperties, SetProperty and PropertyChanged signal * * Copyright (C) 2010 Nokia Corporation * @author Pekka Pessi * * This work is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This work is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this work; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "config.h" #define MODEM_DEBUG_FLAG MODEM_LOG_MODEM #include "debug.h" #include "modem/oface.h" #include "modem/request-private.h" #include "modem/errors.h" #include "modem/ofono.h" #include #include "signals-marshal.h" #include #include #include #include #include #include #include /* ------------------------------------------------------------------------ */ G_DEFINE_TYPE (ModemOface, modem_oface, G_TYPE_OBJECT); /* Properties */ enum { PROP_NONE, PROP_INTERFACE, PROP_OBJECT_PATH, LAST_PROPERTY }; /* Signals */ enum { SIGNAL_CONNECTED, N_SIGNALS }; static guint signals[N_SIGNALS]; /* private data */ struct _ModemOfacePrivate { DBusGProxy *proxy; struct { GQueue queue[1]; GError *error; } connecting; unsigned dispose_has_run:1, connected:1, signals:1, disconnected:1, :0; }; /* ------------------------------------------------------------------------ */ /* Local functions */ static void modem_oface_set_object_path (ModemOface *self, char const *path); static void on_property_changed (DBusGProxy *, char const *, GValue const *, gpointer); /* ------------------------------------------------------------------------ */ static void modem_oface_init (ModemOface *self) { DEBUG ("enter"); self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, MODEM_TYPE_OFACE, ModemOfacePrivate); g_queue_init (self->priv->connecting.queue); } static void modem_oface_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { ModemOface *self = MODEM_OFACE (object); ModemOfacePrivate *priv = self->priv; switch (property_id) { case PROP_INTERFACE: g_value_set_string (value, dbus_g_proxy_get_interface (priv->proxy)); break; case PROP_OBJECT_PATH: g_value_set_string (value, dbus_g_proxy_get_path (priv->proxy)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void modem_oface_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { ModemOface *self = MODEM_OFACE (object); switch (property_id) { case PROP_OBJECT_PATH: modem_oface_set_object_path (self, g_value_get_string (value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void modem_oface_constructed (GObject *object) { if (G_OBJECT_CLASS (modem_oface_parent_class)->constructed) G_OBJECT_CLASS (modem_oface_parent_class)->constructed (object); if (MODEM_OFACE (object)->priv->proxy == NULL) g_warning("object created without dbus-proxy"); } static void modem_oface_dispose (GObject *object) { ModemOface *self = MODEM_OFACE (object); ModemOfacePrivate *priv = self->priv; DEBUG ("enter"); if (priv->dispose_has_run) return; priv->dispose_has_run = TRUE; if (!priv->disconnected) modem_oface_disconnect (self); g_clear_error (&priv->connecting.error); g_object_run_dispose (G_OBJECT (priv->proxy)); if (G_OBJECT_CLASS (modem_oface_parent_class)->dispose) G_OBJECT_CLASS (modem_oface_parent_class)->dispose (object); } static void modem_oface_finalize (GObject *object) { ModemOface *self = MODEM_OFACE (object); DEBUG ("enter"); /* Free any data held directly by the object here */ g_object_unref (self->priv->proxy); g_clear_error (&self->priv->connecting.error); G_OBJECT_CLASS (modem_oface_parent_class)->finalize (object); } static void modem_oface_class_init (ModemOfaceClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); DEBUG ("enter"); object_class->get_property = modem_oface_get_property; object_class->set_property = modem_oface_set_property; object_class->dispose = modem_oface_dispose; object_class->constructed = modem_oface_constructed; object_class->finalize = modem_oface_finalize; g_type_class_add_private (klass, sizeof (ModemOfacePrivate)); /* Properties */ g_object_class_install_property (object_class, PROP_INTERFACE, g_param_spec_string ("interface", "Interface name", "D-Bus interface name", ".", /* default value */ G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (object_class, PROP_OBJECT_PATH, g_param_spec_string ("object-path", "Modem object path", "D-Bus object path used to identify the modem", "/", /* default value */ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); /* Signals to emit */ signals[SIGNAL_CONNECTED] = g_signal_new ("connected", G_OBJECT_CLASS_TYPE (klass), G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED, 0, NULL, NULL, g_cclosure_marshal_VOID__BOOLEAN, G_TYPE_NONE, 1, G_TYPE_BOOLEAN); } /* ------------------------------------------------------------------------- */ /* ModemOface factory */ static DBusGConnection * modem_oface_get_bus (void) { static DBusGConnection *bus = NULL; if (G_UNLIKELY (bus == NULL)) bus = dbus_g_bus_get (DBUS_BUS_SYSTEM, NULL); return bus; } static void modem_oface_set_object_path (ModemOface *self, char const *object_path) { DBusGConnection *bus = modem_oface_get_bus (); char const *interface = MODEM_OFACE_GET_CLASS (self)->ofono_interface; self->priv->proxy = dbus_g_proxy_new_for_name (bus, OFONO_BUS_NAME, object_path, interface); } static GHashTable *modem_oface_types; char const * modem_oface_get_interface_name_by_type (GType type) { ModemOfaceClass *klass; char const *interface = NULL; g_return_val_if_fail (G_TYPE_IS_OBJECT (type), NULL); klass = g_type_class_peek_static (type); if (klass) { g_return_val_if_fail (MODEM_IS_OFACE_CLASS (klass), NULL); interface = klass->ofono_interface; } else { klass = g_type_class_ref (type); if (MODEM_IS_OFACE_CLASS (klass)) interface = klass->ofono_interface; else (void) MODEM_OFACE_CLASS (klass); g_type_class_unref (klass); } return interface; } GType modem_oface_get_type_by_interface_name (char const *interface) { gpointer type = g_hash_table_lookup (modem_oface_types, interface); if (type != NULL) return (GType) type; return G_TYPE_INVALID; } void modem_oface_register_type (GType type) { static gsize once = 0; gpointer interface; if (g_once_init_enter (&once)) { modem_oface_types = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, NULL); g_once_init_leave (&once, 1); } interface = (gpointer) modem_oface_get_interface_name_by_type (type); g_return_if_fail (interface); g_hash_table_insert (modem_oface_types, interface, (gpointer)type); } ModemOface * modem_oface_new (char const *interface, char const *object_path) { GType type = modem_oface_get_type_by_interface_name (interface); if (type != G_TYPE_INVALID) { return g_object_new (type, "object-path", object_path, NULL); } else { return NULL; } } /* ------------------------------------------------------------------------- */ /* Connecting API */ static gboolean modem_oface_set_connected (ModemOface *, gboolean connected); gboolean modem_oface_connect (ModemOface *self) { g_return_val_if_fail (MODEM_IS_OFACE (self), FALSE); g_return_val_if_fail (self->priv->connected == FALSE, FALSE); g_return_val_if_fail (self->priv->disconnected == FALSE, FALSE); g_return_val_if_fail (self->priv->dispose_has_run == FALSE, FALSE); ModemOfacePrivate *priv = self->priv; void (*implement_connect) (ModemOface *); DEBUG ("enter"); g_clear_error (&priv->connecting.error); implement_connect = MODEM_OFACE_GET_CLASS (self)->connect; if (implement_connect) { implement_connect (self); } if (g_queue_is_empty (priv->connecting.queue)) { modem_oface_set_connected (self, priv->connecting.error == NULL); } DEBUG ("leave%s", priv->connected ? " already connected" : ! g_queue_is_empty (priv->connecting.queue) ? " while connecting" : ""); return priv->connected || ! g_queue_is_empty (priv->connecting.queue); } static gboolean modem_oface_set_connected (ModemOface *self, gboolean connected) { g_return_val_if_fail (MODEM_IS_OFACE (self), FALSE); g_return_val_if_fail (self->priv->disconnected == FALSE, FALSE); g_return_val_if_fail (self->priv->dispose_has_run == FALSE, FALSE); ModemOfacePrivate *priv = self->priv; if (priv->connected || priv->disconnected || priv->dispose_has_run) return FALSE; priv->connected = connected != FALSE; if (connected && MODEM_OFACE_GET_CLASS (self)->connected) { MODEM_OFACE_GET_CLASS (self)->connected (self); } g_signal_emit (self, signals[SIGNAL_CONNECTED], 0, connected != FALSE); return TRUE; } void modem_oface_add_connect_request (ModemOface *self, ModemRequest *request) { DEBUG("enter"); g_queue_push_tail (self->priv->connecting.queue, request); } void modem_oface_check_connected (ModemOface *self, ModemRequest *request, GError const *error) { ModemOfacePrivate *priv = self->priv; if (!g_queue_find (priv->connecting.queue, request)) return; g_queue_remove (priv->connecting.queue, request); if (error) modem_oface_set_connecting_error (self, error); if (g_queue_is_empty (priv->connecting.queue)) { modem_oface_set_connected (self, priv->connecting.error == NULL); } } void modem_oface_set_connecting_error (ModemOface *self, GError const *error) { ModemOfacePrivate *priv = self->priv; if (!priv->connecting.error || priv->connecting.error->domain == DBUS_GERROR) { g_clear_error (&priv->connecting.error); g_set_error (&priv->connecting.error, error->domain, error->code, "%s", error->message); } } /** Disconnect from call service */ void modem_oface_disconnect (ModemOface *self) { DEBUG ("(%p): enter%s", self, self->priv->disconnected ? " (already done)" : ""); ModemOfacePrivate *priv = self->priv; void (*implement_disconnect)(ModemOface *); unsigned was_connected = priv->connected; if (priv->disconnected) return; priv->connected = FALSE; priv->disconnected = TRUE; implement_disconnect = MODEM_OFACE_GET_CLASS (self)->disconnect; if (implement_disconnect) { implement_disconnect (self); } while (!g_queue_is_empty (priv->connecting.queue)) { modem_request_cancel (g_queue_pop_head (priv->connecting.queue)); } if (was_connected) g_signal_emit (self, signals[SIGNAL_CONNECTED], 0, FALSE); } static void reply_to_connect_properties (ModemOface *self, ModemRequest *request, GHashTable *properties, GError const *error, gpointer dummy) { if (properties) { modem_oface_update_properties (self, properties); } /* XXX/PP: GetProperties might return error randomly */ /* XXX/PP: we should retry on error */ modem_oface_check_connected (self, request, error); } void modem_oface_connect_properties (ModemOface *self, gboolean get_all) { ModemOfacePrivate *priv = self->priv; if (!priv->signals) { priv->signals = TRUE; dbus_g_proxy_add_signal (priv->proxy, "PropertyChanged", G_TYPE_STRING, G_TYPE_VALUE, G_TYPE_INVALID); dbus_g_proxy_connect_signal (priv->proxy, "PropertyChanged", G_CALLBACK (on_property_changed), self, NULL); } if (!get_all) return; modem_oface_add_connect_request (self, modem_oface_request_properties (self, reply_to_connect_properties, NULL)); } void modem_oface_disconnect_properties (ModemOface *self) { ModemOfacePrivate *priv = self->priv; if (priv->signals) { priv->signals = FALSE; dbus_g_proxy_disconnect_signal (priv->proxy, "PropertyChanged", G_CALLBACK (on_property_changed), self); } } gboolean modem_oface_is_connected (ModemOface const *self) { return MODEM_IS_OFACE (self) && self->priv->connected; } gboolean modem_oface_is_connecting (ModemOface const *self) { return MODEM_IS_OFACE (self) && !g_queue_is_empty (self->priv->connecting.queue); } static void reply_to_get_properties (DBusGProxy *proxy, DBusGProxyCall *call, gpointer _request) { GHashTable *properties = NULL; ModemRequest *request = _request; gpointer object = modem_request_object (request); ModemOfacePropertiesReply *callback = modem_request_callback (request); gpointer user_data = modem_request_user_data (request); GError *error = NULL; if (!dbus_g_proxy_end_call (proxy, call, &error, MODEM_TYPE_DBUS_DICT, &properties, G_TYPE_INVALID)) { modem_error_fix (&error); } if (callback) callback (object, request, properties, error, user_data); if (error) g_error_free (error); if (properties) g_hash_table_unref (properties); } ModemRequest * modem_oface_request_properties (ModemOface *self, ModemOfacePropertiesReply *callback, gpointer user_data) { DEBUG("enter"); g_return_val_if_fail (MODEM_IS_OFACE(self), NULL); return modem_request_begin (self, self->priv->proxy, "GetProperties", reply_to_get_properties, G_CALLBACK (callback), user_data, G_TYPE_INVALID); } static void on_property_changed (DBusGProxy *proxy, char const *property, GValue const *value, gpointer _self) { ModemOface *self = MODEM_OFACE (_self); char const *(*property_mapper)(char const *ofono_property); char const *gname; property_mapper = MODEM_OFACE_GET_CLASS (self)->property_mapper; if (!property_mapper) return; gname = property_mapper (property); if (DEBUGGING) { char *s = g_strdup_value_contents (value); DEBUG("%s = %s (as %s)", property, s, gname); g_free(s); } if (!gname) return; g_object_set_property (G_OBJECT (self), gname, value); } void modem_oface_update_properties (ModemOface *self, GHashTable *properties) { GHashTableIter iter[1]; char *name; GValue *value; DEBUG ("enter"); for (g_hash_table_iter_init (iter, properties); g_hash_table_iter_next (iter, (gpointer)&name, (gpointer)&value);) { on_property_changed (NULL, name, value, self); } } static void reply_to_set_property (DBusGProxy *proxy, DBusGProxyCall *call, void *_request) { ModemRequest *request = _request; ModemOface *self = modem_request_object (request); ModemOfaceVoidReply *callback = modem_request_callback (request); gpointer user_data = modem_request_user_data (request); GError *error = NULL; dbus_g_proxy_end_call (proxy, call, &error, G_TYPE_INVALID); if (callback) callback (self, request, error, user_data); g_clear_error (&error); } ModemRequest * modem_oface_set_property_req (ModemOface *self, char const *property, GValue *value, ModemOfaceVoidReply *callback, gpointer user_data) { ModemOfacePrivate *priv = self->priv; if (DEBUGGING) { char *s = g_strdup_value_contents (value); DEBUG ("%s.%s (%s, %s)", dbus_g_proxy_get_interface (priv->proxy), "SetProperty", property, s); g_free (s); } return modem_request_begin (self, priv->proxy, "SetProperty", reply_to_set_property, G_CALLBACK (callback), user_data, G_TYPE_STRING, property, G_TYPE_VALUE, value, G_TYPE_INVALID); } /* ------------------------------------------------------------------------- */ /* Managed */ static void reply_to_get_managed (DBusGProxy *proxy, DBusGProxyCall *call, gpointer _request) { GPtrArray *array = NULL; ModemRequest *request = _request; gpointer object = modem_request_object (request); ModemOfaceManagedReply *callback = modem_request_callback (request); gpointer user_data = modem_request_user_data (request); GError *error = NULL; if (!dbus_g_proxy_end_call (proxy, call, &error, MODEM_TYPE_DBUS_MANAGED_ARRAY, &array, G_TYPE_INVALID)) modem_error_fix (&error); if (callback) callback (object, request, array, error, user_data); if (error) g_error_free (error); if (array) g_ptr_array_free (array, TRUE); } ModemRequest * modem_oface_request_managed (ModemOface *oface, char const *method, ModemOfaceManagedReply *callback, gpointer userdata) { ModemOfacePrivate *priv = oface->priv; return modem_request_begin (oface, priv->proxy, method, reply_to_get_managed, G_CALLBACK (callback), userdata, G_TYPE_INVALID); } /** Returns pointer to DBusGProxy. * * If you want to have a new reference, use g_object_get_property (). */ DBusGProxy * modem_oface_dbus_proxy (ModemOface *self) { return MODEM_OFACE (self)->priv->proxy; } /** Returns object path for the oface object. * * If you want to have a newly copied string, use g_object_get_property (). */ char const * modem_oface_object_path (ModemOface *self) { return dbus_g_proxy_get_path (MODEM_OFACE (self)->priv->proxy); } char const * modem_oface_interface (ModemOface *self) { return dbus_g_proxy_get_interface (MODEM_OFACE (self)->priv->proxy); } telepathy-ring-2.3.24-37ad7111424725a5446e82dac516d7cab88149c7/modem/oface.h000066400000000000000000000103351251541261300243050ustar00rootroot00000000000000/* * modem/oface.h - Parent class for modem services * * Copyright (C) 2008,2010 Nokia Corporation * @author Pekka Pessi * * This work is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This work is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this work; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _MODEM_OFACE_H_ #define _MODEM_OFACE_H_ #include #include #include G_BEGIN_DECLS typedef struct _ModemOface ModemOface; typedef struct _ModemOfaceClass ModemOfaceClass; typedef struct _ModemOfacePrivate ModemOfacePrivate; struct _ModemOfaceClass { GObjectClass parent_class; char const *ofono_interface; /** Called when start connecting */ void (*connect)(ModemOface *); /** Called when got connected */ void (*connected)(ModemOface *); /** Called when disconnecting */ void (*disconnect)(ModemOface *); char const *(*property_mapper)(char const *ofono_property); }; struct _ModemOface { GObject parent; ModemOfacePrivate *priv; }; GType modem_oface_get_type (void); /* TYPE MACROS */ #define MODEM_TYPE_OFACE (modem_oface_get_type ()) #define MODEM_OFACE(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), MODEM_TYPE_OFACE, ModemOface)) #define MODEM_OFACE_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST ((klass), MODEM_TYPE_OFACE, ModemOfaceClass)) #define MODEM_IS_OFACE(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MODEM_TYPE_OFACE)) #define MODEM_IS_OFACE_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE ((klass), MODEM_TYPE_OFACE)) #define MODEM_OFACE_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), MODEM_TYPE_OFACE, ModemOfaceClass)) /* ---------------------------------------------------------------------- */ typedef void ModemOfacePropertiesReply (ModemOface *, ModemRequest *, GHashTable *properties, GError const *error, gpointer user_data); typedef void ModemOfaceManagedReply (ModemOface *, ModemRequest *, GPtrArray *managed, GError const *error, gpointer user_data); typedef void ModemOfaceVoidReply (ModemOface *, ModemRequest *request, GError const *error, gpointer user_data); /* ---------------------------------------------------------------------- */ void modem_oface_register_type (GType type); char const *modem_oface_get_interface_name_by_type (GType type); GType modem_oface_get_type_by_interface_name (char const *interface); ModemOface *modem_oface_new (char const *interface, char const *object_path); gboolean modem_oface_connect (ModemOface *); void modem_oface_add_connect_request (ModemOface *, ModemRequest *); void modem_oface_check_connected (ModemOface *, ModemRequest *, GError const *); void modem_oface_set_connecting_error (ModemOface *, GError const *); void modem_oface_connect_properties (ModemOface *, gboolean get_all); void modem_oface_disconnect_properties (ModemOface *); gboolean modem_oface_is_connecting (ModemOface const *self); gboolean modem_oface_is_connected (ModemOface const *self); void modem_oface_disconnect (ModemOface *self); ModemRequest *modem_oface_set_property_req (ModemOface *, char const *property, GValue *value, ModemOfaceVoidReply *callback, gpointer user_data); ModemRequest *modem_oface_request_properties (ModemOface *, ModemOfacePropertiesReply *callback, gpointer user_data); void modem_oface_update_properties (ModemOface *, GHashTable *properties); ModemRequest *modem_oface_request_managed (ModemOface *oface, char const *method, ModemOfaceManagedReply *callback, gpointer userdata); DBusGProxy *modem_oface_dbus_proxy (ModemOface *); char const *modem_oface_object_path (ModemOface *self); char const *modem_oface_interface (ModemOface *self); G_END_DECLS #endif /* #ifndef _MODEM_OFACE_H_*/ telepathy-ring-2.3.24-37ad7111424725a5446e82dac516d7cab88149c7/modem/ofono.c000066400000000000000000000046361251541261300243520ustar00rootroot00000000000000/* * modem/ofono.c - Ofono * * Copyright (C) 2010 Nokia Corporation * @author Lassi Syrjala * * This work is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This work is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this work; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "config.h" #define MODEM_DEBUG_FLAG MODEM_LOG_MODEM #include "modem/debug.h" #include "modem/ofono.h" #include "modem/request-private.h" #include "modem/errors.h" #include "modem/service.h" /* ---------------------------------------------------------------------- */ GType modem_type_dbus_dict (void) { static gsize type = 0; if (g_once_init_enter (&type)) { GType t = dbus_g_type_get_map ("GHashTable", G_TYPE_STRING, G_TYPE_VALUE); g_once_init_leave (&type, t); } return type; } GType modem_type_dbus_ao (void) { static gsize type = 0; if (g_once_init_enter (&type)) { GType t = dbus_g_type_get_collection ("GPtrArray", DBUS_TYPE_G_OBJECT_PATH); g_once_init_leave (&type, t); } return type; } GType modem_type_dbus_managed_array (void) { static gsize type = 0; /* a(oa{sv}) */ if (g_once_init_enter (&type)) { GType stype = dbus_g_type_get_struct ("GValueArray", DBUS_TYPE_G_OBJECT_PATH, MODEM_TYPE_DBUS_DICT, G_TYPE_INVALID); GType t = dbus_g_type_get_collection ("GPtrArray", stype); g_once_init_leave (&type, t); } return type; } void modem_ofono_debug_managed (char const *name, char const *object_path, GHashTable *properties) { char *key; GValue *value; GHashTableIter iter[1]; DEBUG ("%s (\"%s\")", name, object_path); for (g_hash_table_iter_init (iter, properties); g_hash_table_iter_next (iter, (gpointer)&key, (gpointer)&value);) { char *s = g_strdup_value_contents (value); DEBUG ("%s = %s", key, s); g_free (s); } } telepathy-ring-2.3.24-37ad7111424725a5446e82dac516d7cab88149c7/modem/ofono.h000066400000000000000000000034661251541261300243570ustar00rootroot00000000000000/* * modem/ofono.h - Ofono * * Copyright (C) 2010 Nokia Corporation * @author Lassi Syrjala * * This work is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This work is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this work; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _MODEM_OFONO_H_ #define _MODEM_OFONO_H_ #include "modem/request.h" #include #include G_BEGIN_DECLS /* ---------------------------------------------------------------------- */ /* D-Bus name of the Ofono service */ #define OFONO_BUS_NAME "org.ofono" /* Quarks for mandatory modem interfaces */ /* D-Bus type a{sv} for Ofono properties */ #define MODEM_TYPE_DBUS_DICT modem_type_dbus_dict () #define MODEM_TYPE_ARRAY_OF_PATHS modem_type_dbus_ao () /* D-Bus type a{oa{sv}} for oFono managed object list */ #define MODEM_TYPE_DBUS_MANAGED_ARRAY modem_type_dbus_managed_array () /* ---------------------------------------------------------------------- */ GType modem_type_dbus_dict (void); GType modem_type_dbus_ao (void); GType modem_type_dbus_managed_array (void); void modem_ofono_debug_managed (char const *name, char const *object_path, GHashTable *properties); G_END_DECLS #endif /* #ifndef _MODEM_OFONO_H_ */ telepathy-ring-2.3.24-37ad7111424725a5446e82dac516d7cab88149c7/modem/radio-settings.c000066400000000000000000000212541251541261300261610ustar00rootroot00000000000000/* * modem/radio-settings.c - ModemRadioSettings class * * Copyright (C) 2011 Nokia Corporation * @author Pekka Pessi * * This work is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This work is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this work; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "config.h" #define MODEM_DEBUG_FLAG MODEM_LOG_RADIO #include "debug.h" #include "modem/radio-settings.h" #include "modem/request-private.h" #include "modem/errors.h" #include "modem/ofono.h" #include #include "signals-marshal.h" #include #include #include #include #include #include #include /* ------------------------------------------------------------------------ */ G_DEFINE_TYPE (ModemRadioSettings, modem_radio_settings, MODEM_TYPE_OFACE); /* Properties */ enum { PROP_NONE, PROP_TECH_PREF, PROP_GSM_BAND, PROP_UMTS_BAND, PROP_FAST_DORMANCY, LAST_PROPERTY }; /* private data */ struct _ModemRadioSettingsPrivate { char *tech_pref; char *gsm_band; char *umts_band; gboolean fast_dormancy; unsigned dispose_has_run:1, connected:1, signals:1, disconnected:1; unsigned connection_error:1; unsigned :0; }; /* ------------------------------------------------------------------------ */ static void modem_radio_settings_init (ModemRadioSettings *self) { DEBUG ("enter"); self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, MODEM_TYPE_RADIO_SETTINGS, ModemRadioSettingsPrivate); } static void modem_radio_settings_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { ModemRadioSettings *self = MODEM_RADIO_SETTINGS (object); ModemRadioSettingsPrivate *priv = self->priv; switch (property_id) { case PROP_TECH_PREF: g_value_set_string (value, priv->tech_pref); break; case PROP_GSM_BAND: g_value_set_string (value, priv->gsm_band); break; case PROP_UMTS_BAND: g_value_set_string (value, priv->umts_band); break; case PROP_FAST_DORMANCY: g_value_set_boolean (value, priv->fast_dormancy); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void modem_radio_settings_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { ModemRadioSettings *self = MODEM_RADIO_SETTINGS (object); ModemRadioSettingsPrivate *priv = self->priv; switch (property_id) { case PROP_TECH_PREF: g_free (priv->tech_pref); priv->tech_pref = g_value_dup_string (value); break; case PROP_GSM_BAND: g_free (priv->gsm_band); priv->gsm_band = g_value_dup_string (value); break; case PROP_UMTS_BAND: g_free (priv->umts_band); priv->umts_band = g_value_dup_string (value); break; case PROP_FAST_DORMANCY: priv->fast_dormancy = g_value_get_boolean (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void modem_radio_settings_constructed (GObject *object) { if (G_OBJECT_CLASS (modem_radio_settings_parent_class)->constructed) G_OBJECT_CLASS (modem_radio_settings_parent_class)->constructed (object); } static void modem_radio_settings_dispose (GObject *object) { if (G_OBJECT_CLASS (modem_radio_settings_parent_class)->dispose) G_OBJECT_CLASS (modem_radio_settings_parent_class)->dispose (object); } static void modem_radio_settings_finalize (GObject *object) { ModemRadioSettings *self = MODEM_RADIO_SETTINGS (object); ModemRadioSettingsPrivate *priv = self->priv; DEBUG ("enter"); /* Free any data held directly by the object here */ g_free (priv->tech_pref); g_free (priv->gsm_band); g_free (priv->umts_band); G_OBJECT_CLASS (modem_radio_settings_parent_class)->finalize (object); } /* ------------------------------------------------------------------------- */ /* ModemOface interface */ static char const * modem_radio_settings_property_mapper (char const *name) { if (!strcmp (name, "TechnologyPreference")) return "technology-preference"; if (!strcmp(name, "GsmBand")) return "gsm-band"; if (!strcmp (name, "UmtsBand")) return "umts-band"; if (!strcmp (name, "FastDormancy")) return "fast-dormancy"; return NULL; } static void modem_radio_settings_connect (ModemOface *_self) { DEBUG ("(%p): enter", _self); modem_oface_connect_properties (_self, TRUE); } static void modem_radio_settings_connected (ModemOface *_self) { DEBUG ("(%p): enter", _self); } static void modem_radio_settings_disconnect (ModemOface *_self) { DEBUG ("(%p): enter", _self); modem_oface_disconnect_properties (_self); } static void modem_radio_settings_class_init (ModemRadioSettingsClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); ModemOfaceClass *oface_class = MODEM_OFACE_CLASS (klass); DEBUG ("enter"); object_class->get_property = modem_radio_settings_get_property; object_class->set_property = modem_radio_settings_set_property; object_class->constructed = modem_radio_settings_constructed; object_class->dispose = modem_radio_settings_dispose; object_class->finalize = modem_radio_settings_finalize; oface_class->ofono_interface = MODEM_OFACE_RADIO_SETTINGS; oface_class->property_mapper = modem_radio_settings_property_mapper; oface_class->connect = modem_radio_settings_connect; oface_class->connected = modem_radio_settings_connected; oface_class->disconnect = modem_radio_settings_disconnect; /* Properties */ g_object_class_install_property (object_class, PROP_TECH_PREF, g_param_spec_string ("technology-preference", "TechnologyPreference", "The current radio access selection mode, also known " "as network preference.", "", /* default value */ G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (object_class, PROP_GSM_BAND, g_param_spec_string ("gsm-band", "GsmBand", "Frequency band in which the modem is allowed to " "operate when using \"gsm\" mode. Setting this property " "has an imediate effect on modem only if " "TechnologyPreference is set to \"gsm\" or \"any\". " "Otherwise the value is kept and applied whenever modem " "uses this mode.", "", /* default value */ G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (object_class, PROP_UMTS_BAND, g_param_spec_string ("umts-band", "UmtsBand", "Frequency band in which the modem is allowed to " "operate when using \"umts\" mode. Setting this property " "has an imediate effect on modem only if " "TechnologyPreference is set to \"umts\" or \"any\". " "Otherwise the value is kept and applied whenever modem " "uses this mode.", "", /* default value */ G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (object_class, PROP_FAST_DORMANCY, g_param_spec_boolean ("fast-dormancy", "FastDormancy", "This property will enable or disable the fast " "dormancy feature in the modem. Fast dormancy " "refers to a modem feature that allows the " "modem to quickly release radio resources after " "a burst of data transfer has ended. Normally, " "radio resources are released by the network " "after a timeout configured by the network. " "Fast dormancy allows the modem to release the " "radio resources more quickly.", FALSE, /* default value */ G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS)); g_type_class_add_private (klass, sizeof (ModemRadioSettingsPrivate)); modem_error_domain_prefix (0); /* Init errors */ } telepathy-ring-2.3.24-37ad7111424725a5446e82dac516d7cab88149c7/modem/radio-settings.h000066400000000000000000000047451251541261300261740ustar00rootroot00000000000000/* * modem/radio-settings.h - oFono RadiSettings interface * * Copyright (C) 2011 Nokia Corporation * @author Pekka Pessi * * This work is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This work is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this work; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _MODEM_RADIO_SETTINGS_H_ #define _MODEM_RADIO_SETTINGS_H_ #include #include #include G_BEGIN_DECLS #define MODEM_OFACE_RADIO_SETTINGS "org.ofono.RadioSettings" typedef struct _ModemRadioSettings ModemRadioSettings; typedef struct _ModemRadioSettingsClass ModemRadioSettingsClass; typedef struct _ModemRadioSettingsPrivate ModemRadioSettingsPrivate; struct _ModemRadioSettingsClass { ModemOfaceClass parent_class; }; struct _ModemRadioSettings { ModemOface parent; ModemRadioSettingsPrivate *priv; }; GType modem_radio_settings_get_type (void); /* TYPE MACROS */ #define MODEM_TYPE_RADIO_SETTINGS \ (modem_radio_settings_get_type ()) #define MODEM_RADIO_SETTINGS(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ MODEM_TYPE_RADIO_SETTINGS, ModemRadioSettings)) #define MODEM_RADIO_SETTINGS_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST ((klass), \ MODEM_TYPE_RADIO_SETTINGS, ModemRadioSettingsClass)) #define MODEM_IS_RADIO_SETTINGS(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MODEM_TYPE_RADIO_SETTINGS)) #define MODEM_IS_RADIO_SETTINGS_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE ((klass), MODEM_TYPE_RADIO_SETTINGS)) #define MODEM_RADIO_SETTINGS_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), \ MODEM_TYPE_RADIO_SETTINGS, ModemRadioSettingsClass)) /* ---------------------------------------------------------------------- */ G_END_DECLS #endif /* #ifndef _MODEM_RADIO_SETTINGS_H_*/ telepathy-ring-2.3.24-37ad7111424725a5446e82dac516d7cab88149c7/modem/request-private.h000066400000000000000000000074631251541261300264000ustar00rootroot00000000000000/* * modem/request-private.h - Private ModemRequest * * Copyright (C) 2008 Nokia Corporation * @author Pekka Pessi * * This work is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This work is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this work; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _MODEM_REQUEST_PRIVATE_H_ #define _MODEM_REQUEST_PRIVATE_H_ #include #include G_BEGIN_DECLS typedef struct _ModemRequestPrivate ModemRequestPrivate; typedef void ModemRequestCallNotify (DBusGProxy *, DBusGProxyCall *, gpointer); ModemRequest *_modem_request_new (gpointer object, DBusGProxy *proxy, GCallback callback, gpointer user_data); void _modem_request_add_proxy_call (ModemRequest *request, DBusGProxyCall *call); void _modem_request_destroy_notify (gpointer _request); /** Make a DBus method call with reply */ #define modem_request_begin(object, proxy, method, method_callback, \ callback, user, gtype, ...) \ ({ ModemRequest *_temp = NULL; DBusGProxyCall *_pcall; \ _temp = _modem_request_new ((object), (proxy), (callback), (user)); \ _pcall = dbus_g_proxy_begin_call ((proxy), (method), \ (method_callback), _temp, _modem_request_destroy_notify, \ (gtype), ## __VA_ARGS__); \ _modem_request_add_proxy_call (_temp, _pcall); \ _temp; }) /** Make a DBus method call with reply and timeout */ #define modem_request_with_timeout(object, proxy, method, \ method_callback, callback, user_data, timeout, gtype, ...) \ ({ ModemRequest *_temp = NULL; DBusGProxyCall *_pcall; \ _temp = _modem_request_new ((object), (proxy), \ (callback), (user_data)); \ _pcall = dbus_g_proxy_begin_call_with_timeout ((proxy), (method), \ (method_callback), _temp, _modem_request_destroy_notify, \ (timeout), (gtype), ## __VA_ARGS__); \ _modem_request_add_proxy_call (_temp, _pcall); \ _temp; }) /** Make a DBus-request. * * If @a callback is NULL, do not wait for reply. */ #define modem_request(object, proxy, method, method_callback, \ callback, user, gtype, ...) \ ((callback) \ ? modem_request_begin ((object), (proxy), (method), \ (method_callback), \ (callback), (user), (gtype), ## __VA_ARGS__) \ : (dbus_g_proxy_call_no_reply ((proxy), (method), \ (gtype), ## __VA_ARGS__), \ NULL)) #define modem_request_object(request) \ (g_ptr_array_index ((GPtrArray*)(request), 0)) #define modem_request_callback(request) \ (g_ptr_array_index ((GPtrArray*)(request), 1)) #define modem_request_user_data(request) \ (g_ptr_array_index ((GPtrArray*)(request), 2)) G_END_DECLS #endif /* #ifndef _MODEM_REQUEST_PRIVATE_H_ */ telepathy-ring-2.3.24-37ad7111424725a5446e82dac516d7cab88149c7/modem/request.c000066400000000000000000000203301251541261300247070ustar00rootroot00000000000000/* * modem/request.c - Extensible closure for asyncronous DBus calls * * Copyright (C) 2008-2010 Nokia Corporation * @author Pekka Pessi * * This work is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This work is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this work; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "config.h" #include "modem/request-private.h" #include /* This is a specially laid out GPtrArray */ struct _ModemRequest { ModemRequestPrivate *priv; guint len; }; typedef struct _ModemRequestNotify ModemRequestNotify; struct _ModemRequestPrivate { GObject *object; GCallback callback; gpointer user_data; DBusGProxy *proxy; DBusGProxyCall *call; struct _ModemRequestNotify { gpointer quark; GDestroyNotify destroy; gpointer data; } notify[]; }; enum { N_SIZED = offsetof (ModemRequestPrivate, notify[4]) / sizeof (gpointer), N_SIZE = offsetof (ModemRequestPrivate, notify[0]) / sizeof (gpointer), N_NOTIFY = sizeof (struct _ModemRequestNotify) / sizeof (gpointer) }; #define DUMMYQ modem_request_dummy_quark () GQuark modem_request_dummy_quark (void) { static GQuark quark; if (G_UNLIKELY (!quark)) quark = g_quark_from_static_string ("modem_request_dummy_quark"); return quark; } #define MODEM_REQUEST_CANCEL_QUARK modem_request_cancel_quark () GQuark modem_request_cancel_quark (void) { static GQuark quark; if (G_UNLIKELY (!quark)) quark = g_quark_from_static_string ("modem_request_cancel"); return quark; } ModemRequest * _modem_request_new (gpointer object, DBusGProxy *proxy, GCallback callback, gpointer user_data) { GPtrArray *container = g_ptr_array_sized_new (N_SIZED); g_ptr_array_set_size (container, N_SIZE); ModemRequest *request = (gpointer)container; ModemRequestPrivate *priv = request->priv; if (object) priv->object = g_object_ref (object); priv->proxy = DBUS_G_PROXY (g_object_ref (proxy)); priv->callback = callback; priv->user_data = user_data; return request; } void _modem_request_add_proxy_call (ModemRequest *request, DBusGProxyCall *call) { request->priv->call = call; } void modem_request_add_qdata_full (ModemRequest *request, GQuark quark, gpointer user_data, GDestroyNotify destroy) { GPtrArray *container = (GPtrArray *)request; gpointer qpointer = GUINT_TO_POINTER (quark); guint i; g_assert (quark != DUMMYQ); for (i = N_SIZE; i < container->len; i += N_NOTIFY) { ModemRequestNotify *notify = (gpointer)(container->pdata + i); if (notify->quark == qpointer) { ModemRequestNotify save = *notify; notify->destroy = destroy; notify->data = user_data; if (save.destroy) save.destroy (save.data); return; } } g_ptr_array_add (container, qpointer); g_ptr_array_add (container, destroy); g_ptr_array_add (container, user_data); } void modem_request_add_qdata (ModemRequest *request, GQuark quark, gpointer data) { modem_request_add_qdata_full (request, quark, data, NULL); } void modem_request_add_qdatas (ModemRequest *request, GQuark quark, gpointer user_data, GDestroyNotify destroy, ...) { va_list ap; va_start (ap, destroy); for (;;) { modem_request_add_qdata_full (request, quark, user_data, destroy); quark = va_arg (ap, GQuark); if (!quark) break; user_data = va_arg (ap, gpointer); destroy = va_arg (ap, GDestroyNotify); } va_end (ap); } void modem_request_add_notifys (ModemRequest *request, GDestroyNotify destroy, gpointer user_data, ...) { GPtrArray *container = (GPtrArray *)request; gpointer qpointer = GUINT_TO_POINTER (DUMMYQ); va_list ap; va_start (ap, user_data); for (;;) { g_ptr_array_add (container, qpointer); g_ptr_array_add (container, destroy); g_ptr_array_add (container, user_data); destroy = va_arg (ap, GDestroyNotify); if (!destroy) break; user_data = va_arg (ap, gpointer); } va_end (ap); } gpointer modem_request_steal_qdata (ModemRequest *request, GQuark quark) { GPtrArray *container = (GPtrArray *)request; gpointer qpointer = GUINT_TO_POINTER (quark); guint i; g_assert (quark != DUMMYQ); for (i = N_SIZE; i < container->len; i += N_NOTIFY) { ModemRequestNotify *notify = (gpointer)(container->pdata + i); if (notify->quark == qpointer) { gpointer data = notify->data; g_ptr_array_remove_range (container, i, N_NOTIFY); return data; } } return NULL; } gpointer modem_request_get_qdata (ModemRequest *request, GQuark quark) { GPtrArray *container = (GPtrArray *)request; gpointer qpointer = GUINT_TO_POINTER (quark); guint i; for (i = N_SIZE; i < container->len; i += N_NOTIFY) { ModemRequestNotify *notify = (gpointer)(container->pdata + i); if (notify->quark == qpointer) return notify->data; } return NULL; } void modem_request_add_data (ModemRequest *request, char const *key, gpointer data) { modem_request_add_data_full (request, key, data, NULL); } void modem_request_add_data_full (ModemRequest *request, char const *key, gpointer data, GDestroyNotify destroy) { modem_request_add_qdata_full (request, g_quark_from_string (key), data, destroy); } gpointer modem_request_get_data (ModemRequest *request, char const *key) { GQuark quark = g_quark_try_string (key); if (!quark) return NULL; return modem_request_get_qdata (request, quark); } gpointer modem_request_steal_data (ModemRequest *request, char const *key) { GQuark quark = g_quark_try_string (key); if (!quark) return NULL; return modem_request_steal_qdata (request, quark); } void _modem_request_destroy_notify (gpointer _request) { ModemRequest *request = _request; ModemRequestPrivate *priv = request->priv; GObject *object = priv->object; DBusGProxy *proxy = priv->proxy; priv->object = NULL; priv->proxy = NULL; priv->call = NULL; GPtrArray *container = (GPtrArray *)request; guint i; for (i = N_SIZE; i < container->len; i += N_NOTIFY) { ModemRequestNotify *notify = (gpointer)(container->pdata + i); GDestroyNotify destroy = notify->destroy; gpointer data = notify->data; memset (notify, 0, sizeof notify); if (destroy) destroy (data); } if (proxy) g_object_unref ((GObject*)proxy); if (object) g_object_unref (object); g_ptr_array_free (_request, TRUE); } /** Cancel request */ void modem_request_cancel (ModemRequest *request) { ModemRequestPrivate *priv = request->priv; GDestroyNotify cancel; cancel = modem_request_steal_qdata (request, MODEM_REQUEST_CANCEL_QUARK); if (cancel) { cancel (request); return; } if (priv->call) dbus_g_proxy_cancel_call (priv->proxy, priv->call); else _modem_request_destroy_notify (request); } void modem_request_add_cancel_notify (ModemRequest *request, GDestroyNotify notify) { modem_request_add_qdata (request, MODEM_REQUEST_CANCEL_QUARK, (gpointer)notify); } telepathy-ring-2.3.24-37ad7111424725a5446e82dac516d7cab88149c7/modem/request.h000066400000000000000000000042501251541261300247170ustar00rootroot00000000000000/* * modem/request.h - Extensible closure for asyncronous DBus calls * * Copyright (C) 2008 Nokia Corporation * @author Pekka Pessi * * This work is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This work is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this work; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _MODEM_REQUEST_H_ #define _MODEM_REQUEST_H_ #include G_BEGIN_DECLS typedef struct _ModemRequest ModemRequest; void modem_request_cancel (ModemRequest *request); void modem_request_add_cancel_notify (ModemRequest *request, GDestroyNotify notify); void modem_request_add_notifys (ModemRequest *request, GDestroyNotify notify, gpointer data, ...) G_GNUC_NULL_TERMINATED; void modem_request_add_qdata (ModemRequest *, GQuark, gpointer data); void modem_request_add_qdata_full (ModemRequest *request, GQuark quark, gpointer data, GDestroyNotify destroy); void modem_request_add_qdatas (ModemRequest *request, GQuark quark, gpointer data, GDestroyNotify notify, ...) G_GNUC_NULL_TERMINATED; gpointer modem_request_get_qdata (ModemRequest *request, GQuark quark); gpointer modem_request_steal_qdata (ModemRequest *request, GQuark quark); void modem_request_add_data (ModemRequest *, char const *key, gpointer data); void modem_request_add_data_full (ModemRequest *request, char const *key, gpointer data, GDestroyNotify destroy); gpointer modem_request_get_data (ModemRequest *request, char const *key); gpointer modem_request_steal_data (ModemRequest *request, char const *key); G_END_DECLS #endif /* #ifndef _MODEM_REQUEST_H_ */ telepathy-ring-2.3.24-37ad7111424725a5446e82dac516d7cab88149c7/modem/service.c000066400000000000000000000403311251541261300246620ustar00rootroot00000000000000/* * modem/service.c - oFono modem service * * Copyright (C) 2009 Nokia Corporation * @author Pekka Pessi * * This work is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This work is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this work; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "config.h" #define MODEM_DEBUG_FLAG MODEM_LOG_MODEM #include "debug.h" #include "modem/oface.h" #include "modem/service.h" #include "modem/request-private.h" #include "modem/ofono.h" #include "modem/errors.h" #include "modem/modem.h" #include "modem/sim.h" #include "modem/call.h" #include "modem/sms.h" #include #include #include #include #include #include "signals-marshal.h" #include #include #include #include #include #include #include /* ------------------------------------------------------------------------ */ G_DEFINE_TYPE(ModemService, modem_service, MODEM_TYPE_OFACE); enum { SIGNAL_MODEM_ADDED, SIGNAL_MODEM_POWERED, SIGNAL_MODEM_REMOVED, SIGNAL_IMEI_ADDED, SIGNAL_IMSI_ADDED, N_SIGNALS }; static guint signals[N_SIGNALS] = {0}; struct _ModemServicePrivate { GHashTable *modems; unsigned signals:1, subscribed:1; unsigned :0; }; /* ------------------------------------------------------------------------ */ /* Local functions */ static void reply_to_get_modems (ModemOface *_self, ModemRequest *request, GPtrArray *modems, GError const *error, gpointer user_data); static void on_modem_added (DBusGProxy *, char const *, GHashTable *, gpointer); static void on_modem_notify_imei (Modem *, GParamSpec *, ModemService *); static void on_modem_notify_powered (Modem *, GParamSpec *, ModemService *); static void on_modem_imsi_added (Modem *, char const *imsi, ModemService *); static void on_modem_removed (DBusGProxy *, char const *, gpointer); /* ------------------------------------------------------------------------ */ static void modem_service_init(ModemService *self) { DEBUG("enter"); self->priv = G_TYPE_INSTANCE_GET_PRIVATE(self, MODEM_TYPE_SERVICE, ModemServicePrivate); self->priv->modems = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref); } static void modem_service_constructed(GObject *object) { if (G_OBJECT_CLASS(modem_service_parent_class)->constructed) G_OBJECT_CLASS(modem_service_parent_class)->constructed(object); } static void modem_service_dispose(GObject *object) { if (G_OBJECT_CLASS(modem_service_parent_class)->dispose) G_OBJECT_CLASS(modem_service_parent_class)->dispose(object); } static void modem_service_finalize(GObject *object) { ModemService *self = MODEM_SERVICE(object); ModemServicePrivate *priv = self->priv; DEBUG("enter"); /* Free any data held directly by the object here */ g_hash_table_unref (priv->modems); G_OBJECT_CLASS(modem_service_parent_class)->finalize(object); } /* ------------------------------------------------------------------------- */ /* ModemOface implementation */ static void reply_to_get_modems (ModemOface *_self, ModemRequest *request, GPtrArray *modem_list, GError const *error, gpointer user_data) { DEBUG ("enter"); if (modem_list) { guint i; for (i = 0; i < modem_list->len; i++) { GValueArray *va = g_ptr_array_index (modem_list, i); char const *path = g_value_get_boxed (va->values + 0); GHashTable *properties = g_value_get_boxed (va->values + 1); on_modem_added (NULL, path, properties, _self); } } modem_oface_check_connected (_self, request, error); } void modem_service_connect (ModemOface *_self) { ModemService *self = MODEM_SERVICE (_self); ModemServicePrivate *priv = self->priv; DEBUG("enter"); if (!priv->signals) { DBusGProxy *proxy = modem_oface_dbus_proxy (_self); priv->signals = TRUE; #define CONNECT(p, handler, name, signature...) \ dbus_g_proxy_add_signal (p, (name), ##signature); \ dbus_g_proxy_connect_signal (p, (name), G_CALLBACK (handler), self, NULL) CONNECT (proxy, on_modem_added, "ModemAdded", DBUS_TYPE_G_OBJECT_PATH, MODEM_TYPE_DBUS_DICT, G_TYPE_INVALID); CONNECT (proxy, on_modem_removed, "ModemRemoved", DBUS_TYPE_G_OBJECT_PATH, G_TYPE_INVALID); #undef CONNECT } if (!priv->subscribed) { DBusConnection *bus = dbus_bus_get(DBUS_BUS_SYSTEM, NULL); /* Pre-subscribe to all interesting signals */ if (bus) { priv->subscribed = 1; dbus_bus_add_match (bus, "type='signal'," "sender='org.ofono'," "interface='org.ofono.Modem'," "member='PropertyChanged'", NULL); dbus_bus_add_match (bus, "type='signal'," "sender='org.ofono'," "interface='org.ofono.SimManager'," "member='PropertyChanged'", NULL); dbus_bus_add_match (bus, "type='signal'," "sender='org.ofono'," "interface='org.ofono.MessageManager'," "member='PropertyChanged'", NULL); dbus_bus_add_match (bus, "type='signal'," "sender='org.ofono'," "interface='org.ofono.MessageManager'," "member='MessageAdded'", NULL); dbus_bus_add_match (bus, "type='signal'," "sender='org.ofono'," "interface='org.ofono.MessageManager'," "member='MessageRemoved'", NULL); dbus_bus_add_match (bus, "type='signal'," "sender='org.ofono'," "interface='org.ofono.MessageManager'," "member='StatusReport'", NULL); dbus_bus_add_match (bus, "type='signal'," "sender='org.ofono'," "interface='org.ofono.VoiceCallManager'," "member='PropertyChanged'", NULL); dbus_bus_add_match (bus, "type='signal'," "sender='org.ofono'," "interface='org.ofono.VoiceCallManager'," "member='CallAdded'", NULL); dbus_bus_add_match (bus, "type='signal'," "sender='org.ofono'," "interface='org.ofono.VoiceCallManager'," "member='CallRemoved'", NULL); } } modem_oface_add_connect_request (_self, modem_oface_request_managed (_self, "GetModems", reply_to_get_modems, NULL)); } void modem_service_disconnect (ModemOface *_self) { ModemService *self = MODEM_SERVICE (_self); ModemServicePrivate *priv = self->priv; DEBUG("enter"); if (priv->signals) { DBusGProxy *proxy = modem_oface_dbus_proxy (_self); priv->signals = FALSE; dbus_g_proxy_disconnect_signal (proxy, "ModemAdded", G_CALLBACK (on_modem_added), self); dbus_g_proxy_disconnect_signal (proxy, "ModemRemoved", G_CALLBACK (on_modem_removed), self); } DEBUG("leave"); } static void modem_service_class_init(ModemServiceClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS(klass); ModemOfaceClass *oface_class = MODEM_OFACE_CLASS (klass); DEBUG("enter"); object_class->constructed = modem_service_constructed; object_class->dispose = modem_service_dispose; object_class->finalize = modem_service_finalize; oface_class->ofono_interface = MODEM_OFACE_MANAGER; oface_class->connect = modem_service_connect; oface_class->disconnect = modem_service_disconnect; signals[SIGNAL_MODEM_ADDED] = g_signal_new ("modem-added", G_OBJECT_CLASS_TYPE (klass), G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED, 0, NULL, NULL, g_cclosure_marshal_VOID__OBJECT, G_TYPE_NONE, 1, MODEM_TYPE_MODEM); signals[SIGNAL_MODEM_POWERED] = g_signal_new ("modem-powered", G_OBJECT_CLASS_TYPE (klass), G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED, 0, NULL, NULL, g_cclosure_marshal_VOID__OBJECT, G_TYPE_NONE, 1, MODEM_TYPE_MODEM); signals[SIGNAL_MODEM_REMOVED] = g_signal_new ("modem-removed", G_OBJECT_CLASS_TYPE (klass), G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED, 0, NULL, NULL, g_cclosure_marshal_VOID__OBJECT, G_TYPE_NONE, 1, MODEM_TYPE_MODEM); signals[SIGNAL_IMEI_ADDED] = g_signal_new ("imei-added", G_OBJECT_CLASS_TYPE (klass), G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED, 0, NULL, NULL, _modem__marshal_VOID__OBJECT_STRING, G_TYPE_NONE, 2, MODEM_TYPE_MODEM, G_TYPE_STRING); signals[SIGNAL_IMSI_ADDED] = g_signal_new ("imsi-added", G_OBJECT_CLASS_TYPE (klass), G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED, 0, NULL, NULL, _modem__marshal_VOID__OBJECT_STRING, G_TYPE_NONE, 2, MODEM_TYPE_MODEM, G_TYPE_STRING); g_type_class_add_private(klass, sizeof (ModemServicePrivate)); dbus_g_object_register_marshaller (_modem__marshal_VOID__STRING_BOXED, G_TYPE_NONE, G_TYPE_STRING, G_TYPE_VALUE, G_TYPE_INVALID); dbus_g_object_register_marshaller (_modem__marshal_VOID__BOXED_BOXED, G_TYPE_NONE, DBUS_TYPE_G_OBJECT_PATH, MODEM_TYPE_DBUS_DICT, G_TYPE_INVALID); dbus_g_object_register_marshaller (_modem__marshal_VOID__BOXED, G_TYPE_NONE, DBUS_TYPE_G_OBJECT_PATH, G_TYPE_INVALID); modem_error_domain_prefix (0); /* Init errors */ } /* ------------------------------------------------------------------------ */ /* modem_service interface */ /* -------------------------------------------------------------------------- */ ModemService *modem_service (void) { static ModemService *service; if (!service) { modem_oface_register_type (MODEM_TYPE_SERVICE); modem_oface_register_type (MODEM_TYPE_MODEM); service = MODEM_SERVICE (modem_oface_new (MODEM_OFACE_MANAGER, "/")); } return service; } void modem_service_refresh (ModemService *self) { modem_oface_connect (MODEM_OFACE (self)); } Modem * modem_service_find_by_path (ModemService *self, char const *object_path) { g_return_val_if_fail (MODEM_IS_SERVICE (self), NULL); if (object_path) return g_hash_table_lookup (self->priv->modems, object_path); else return NULL; } Modem * modem_service_find_best (ModemService *self) { char *key; Modem *modem; GHashTableIter iter[1]; g_return_val_if_fail (MODEM_IS_SERVICE (self), NULL); for (g_hash_table_iter_init (iter, self->priv->modems); g_hash_table_iter_next (iter, (gpointer)&key, (gpointer)&modem);) { if (modem_is_online (modem)) return modem; } for (g_hash_table_iter_init (iter, self->priv->modems); g_hash_table_iter_next (iter, (gpointer)&key, (gpointer)&modem);) { if (modem_is_powered (modem)) return modem; } return NULL; } Modem * modem_service_find_by_imsi (ModemService *self, char const *imsi) { char *key; Modem *modem; GHashTableIter iter[1]; g_return_val_if_fail (MODEM_IS_SERVICE (self), NULL); for (g_hash_table_iter_init (iter, self->priv->modems); g_hash_table_iter_next (iter, (gpointer)&key, (gpointer)&modem);) { if (modem_has_imsi(modem, imsi)) return modem; } return NULL; } Modem * modem_service_find_by_imei (ModemService *self, char const *imei) { char *key; Modem *modem = NULL; GHashTableIter iter[1]; g_return_val_if_fail (MODEM_IS_SERVICE (self), NULL); for (g_hash_table_iter_init (iter, self->priv->modems); g_hash_table_iter_next (iter, (gpointer)&key, (gpointer)&modem);) { if (modem_has_imei (modem, imei)) return modem; } return NULL; } Modem ** modem_service_get_modems (ModemService *self) { ModemServicePrivate *priv; GPtrArray *array; char *key; Modem *modem; GHashTableIter iter[1]; g_return_val_if_fail (MODEM_IS_SERVICE (self), NULL); priv = self->priv; array = g_ptr_array_sized_new (g_hash_table_size (priv->modems) + 1); for (g_hash_table_iter_init (iter, priv->modems); g_hash_table_iter_next (iter, (gpointer)&key, (gpointer)&modem);) { g_ptr_array_add(array, modem); } g_ptr_array_add (array, NULL); return (Modem **)g_ptr_array_free (array, FALSE); } static void on_modem_added (DBusGProxy *_dummy, char const *object_path, GHashTable *properties, gpointer userdata) { ModemService *self = userdata; ModemServicePrivate *priv = self->priv; Modem *modem; if (DEBUGGING) modem_ofono_debug_managed ("ModemAdded", object_path, properties); modem = g_hash_table_lookup (priv->modems, object_path); if (modem) { DEBUG ("Modem %s already has object %p", object_path, (void *)modem); return; } modem = MODEM_MODEM (modem_oface_new (MODEM_OFACE_MODEM, object_path)); if (modem == NULL) { DEBUG ("Cannot create modem object %s", object_path); return; } g_hash_table_insert (priv->modems, g_strdup (object_path), modem); modem_oface_update_properties (MODEM_OFACE (modem), properties); g_signal_connect (modem, "notify::imei", G_CALLBACK (on_modem_notify_imei), self); g_signal_connect (modem, "notify::powered", G_CALLBACK (on_modem_notify_powered), self); g_signal_connect (modem, "imsi-added", G_CALLBACK (on_modem_imsi_added), self); modem_oface_connect (MODEM_OFACE (modem)); g_assert (modem_oface_is_connected (MODEM_OFACE (modem))); g_signal_emit (self, signals[SIGNAL_MODEM_ADDED], 0, modem); on_modem_notify_powered (modem, NULL, self); on_modem_notify_imei (modem, NULL, self); } static void on_modem_notify_imei (Modem *modem, GParamSpec *dummy, ModemService *self) { gchar *imei = NULL; g_object_get(modem, "imei", &imei, NULL); if (imei && strcmp (imei, "")) { DEBUG ("emitting \"%s\" with modem=%p (%s) imei=%s", "imei-added", modem, modem_oface_object_path (MODEM_OFACE (modem)), imei); g_signal_emit (self, signals[SIGNAL_IMEI_ADDED], 0, modem, imei); } g_free (imei); } static void on_modem_notify_powered (Modem *modem, GParamSpec *dummy, ModemService *self) { gboolean powered; g_object_get(modem, "powered", &powered, NULL); if (powered) { DEBUG ("emitting \"%s\" with modem=%p (%s)", "modem-powered", modem, modem_oface_object_path (MODEM_OFACE (modem))); g_signal_emit (self, signals[SIGNAL_MODEM_POWERED], 0, modem); } } static void on_modem_imsi_added (Modem *modem, char const *imsi, ModemService *self) { DEBUG ("emitting \"%s\" with modem=%p (%s) imsi=%s", "imsi-added", modem, modem_oface_object_path (MODEM_OFACE (modem)), imsi); g_signal_emit (self, signals[SIGNAL_IMSI_ADDED], 0, modem, imsi); } static void on_modem_removed (DBusGProxy *proxy, char const *object_path, gpointer userdata) { ModemService *self = userdata; ModemServicePrivate *priv = self->priv; Modem *modem; DEBUG ("ModemRemoved(%s)", object_path); modem = g_hash_table_lookup (priv->modems, object_path); if (!modem) return; modem_oface_disconnect (MODEM_OFACE (modem)); g_signal_emit (self, signals[SIGNAL_MODEM_REMOVED], 0, modem); g_signal_handlers_disconnect_by_func (modem, G_CALLBACK (on_modem_imsi_added), self); g_signal_handlers_disconnect_by_func (modem, G_CALLBACK (on_modem_notify_imei), self); g_signal_handlers_disconnect_by_func (modem, G_CALLBACK (on_modem_notify_powered), self); g_hash_table_remove (priv->modems, object_path); } telepathy-ring-2.3.24-37ad7111424725a5446e82dac516d7cab88149c7/modem/service.h000066400000000000000000000053721251541261300246750ustar00rootroot00000000000000/* * modem/service.h - Interface towards Ofono * * Copyright (C) 2009 Nokia Corporation * @author Pekka Pessi * * This work is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This work is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this work; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _MODEM_SERVICE_H_ #define _MODEM_SERVICE_H_ #include #include #include G_BEGIN_DECLS typedef struct _ModemService ModemService; typedef struct _ModemServiceClass ModemServiceClass; typedef struct _ModemServicePrivate ModemServicePrivate; struct _ModemServiceClass { ModemOfaceClass parent_class; }; struct _ModemService { ModemOface parent; ModemServicePrivate *priv; }; GType modem_service_get_type(void); /* TYPE MACROS */ #define MODEM_TYPE_SERVICE \ (modem_service_get_type()) #define MODEM_SERVICE(obj) \ (G_TYPE_CHECK_INSTANCE_CAST((obj), MODEM_TYPE_SERVICE, ModemService)) #define MODEM_SERVICE_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST((klass), MODEM_TYPE_SERVICE, ModemServiceClass)) #define MODEM_IS_SERVICE(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE((obj), MODEM_TYPE_SERVICE)) #define MODEM_IS_SERVICE_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE((klass), MODEM_TYPE_SERVICE)) #define MODEM_SERVICE_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), MODEM_TYPE_SERVICE, ModemServiceClass)) /* * Signals: * modem-added (modem) * modem-removed (modem) */ /* ---------------------------------------------------------------------- */ #define MODEM_OFACE_MANAGER "org.ofono.Manager" ModemService *modem_service(void); void modem_service_refresh (ModemService *self); Modem *modem_service_find_by_imsi (ModemService *self, char const *imsi); Modem *modem_service_find_by_imei (ModemService *self, char const *imei); Modem *modem_service_find_by_path (ModemService *self, char const *path); Modem *modem_service_find_best (ModemService *self); Modem **modem_service_get_modems(ModemService *self); G_END_DECLS #endif /* #ifndef _MODEM_SERVICE_H_*/ telepathy-ring-2.3.24-37ad7111424725a5446e82dac516d7cab88149c7/modem/signals-marshal.list000066400000000000000000000002631251541261300270400ustar00rootroot00000000000000VOID:INT,STRING,STRING VOID:STRING,BOXED VOID:STRING,STRING VOID:STRING,STRING,STRING VOID:OBJECT VOID:OBJECT,STRING VOID:BOXED,BOXED VOID:BOXED # SMS VOID:STRING,STRING,POINTER telepathy-ring-2.3.24-37ad7111424725a5446e82dac516d7cab88149c7/modem/sim-service.c000066400000000000000000000177071251541261300254630ustar00rootroot00000000000000/* * modem/sim-service.c - ModemSIMService class * * Copyright (C) 2008-2010 Nokia Corporation * @author Pekka Pessi * @author Lassi Syrjala * * This work is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This work is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this work; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "config.h" #define MODEM_DEBUG_FLAG MODEM_LOG_SIM #include "debug.h" #include "modem/sim.h" #include "modem/request-private.h" #include "modem/errors.h" #include "modem/ofono.h" #include #include "signals-marshal.h" #include #include #include #include #include #include #include /* ------------------------------------------------------------------------ */ G_DEFINE_TYPE (ModemSIMService, modem_sim_service, MODEM_TYPE_OFACE); /* Signals we send */ enum { SIGNAL_CONNECTED, SIGNAL_STATUS, N_SIGNALS }; static guint signals[N_SIGNALS] = {0}; /* Properties */ enum { PROP_NONE, PROP_STATUS, PROP_IMSI, LAST_PROPERTY }; /* private data */ struct _ModemSIMServicePrivate { guint state; char *imsi; GQueue queue[1]; unsigned dispose_has_run:1, connected:1, signals:1, disconnected:1; unsigned connection_error:1; unsigned :0; }; /* ------------------------------------------------------------------------ */ /* Local functions */ /* ------------------------------------------------------------------------ */ static void modem_sim_service_init (ModemSIMService *self) { DEBUG ("enter"); self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, MODEM_TYPE_SIM_SERVICE, ModemSIMServicePrivate); } static void modem_sim_service_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { ModemSIMService *self = MODEM_SIM_SERVICE (object); ModemSIMServicePrivate *priv = self->priv; switch (property_id) { case PROP_STATUS: g_value_set_uint (value, priv->state); break; case PROP_IMSI: g_value_set_string (value, priv->imsi); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void modem_sim_service_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { ModemSIMService *self = MODEM_SIM_SERVICE (object); ModemSIMServicePrivate *priv = self->priv; gpointer old; switch (property_id) { case PROP_STATUS: priv->state = g_value_get_uint (value); break; case PROP_IMSI: old = priv->imsi; priv->imsi = g_value_dup_string (value); g_free (old); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void modem_sim_service_constructed (GObject *object) { if (G_OBJECT_CLASS (modem_sim_service_parent_class)->constructed) G_OBJECT_CLASS (modem_sim_service_parent_class)->constructed (object); if (modem_oface_dbus_proxy (MODEM_OFACE (object)) == NULL) g_warning("object created without dbus-proxy set"); } static void modem_sim_service_dispose (GObject *object) { ModemSIMService *self = MODEM_SIM_SERVICE (object); ModemSIMServicePrivate *priv = self->priv; if (priv->dispose_has_run) return; priv->dispose_has_run = TRUE; priv->disconnected = TRUE; priv->connected = FALSE; priv->signals = FALSE; g_object_set (self, "state", MODEM_SIM_STATE_UNKNOWN, NULL); while (!g_queue_is_empty (priv->queue)) modem_request_cancel (g_queue_pop_head (priv->queue)); if (G_OBJECT_CLASS (modem_sim_service_parent_class)->dispose) G_OBJECT_CLASS (modem_sim_service_parent_class)->dispose (object); } static void modem_sim_service_finalize (GObject *object) { ModemSIMService *self = MODEM_SIM_SERVICE (object); ModemSIMServicePrivate *priv = self->priv; DEBUG ("enter"); /* Free any data held directly by the object here */ g_free (priv->imsi); G_OBJECT_CLASS (modem_sim_service_parent_class)->finalize (object); } /* ------------------------------------------------------------------------- */ /* ModemOface interface */ static char const * modem_sim_service_property_mapper (char const *name) { if (!strcmp (name, "SubscriberIdentity")) return "imsi"; if (!strcmp(name, "Present")) return NULL; if (!strcmp (name, "CardIdentifier")) return NULL; if (!strcmp (name, "MobileCountryCode")) return NULL; if (!strcmp (name, "MobileNetworkCode")) return NULL; if (!strcmp (name, "SubscriberNumbers")) return NULL; if (!strcmp (name, "ServiceNumbers")) return NULL; if (!strcmp (name, "PinRequired")) return NULL; if (!strcmp (name, "LockedPins")) return NULL; if (!strcmp (name, "FixedDialing")) return NULL; if (!strcmp (name, "BarredDialing")) return NULL; return NULL; } static void modem_sim_service_connect (ModemOface *_self) { DEBUG ("(%p): enter", _self); modem_oface_connect_properties (_self, TRUE); } static void modem_sim_service_connected (ModemOface *_self) { DEBUG ("(%p): enter", _self); } static void modem_sim_service_disconnect (ModemOface *_self) { DEBUG ("(%p): enter", _self); modem_oface_disconnect_properties (_self); } static void modem_sim_service_class_init (ModemSIMServiceClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); ModemOfaceClass *oface_class = MODEM_OFACE_CLASS (klass); DEBUG ("enter"); object_class->get_property = modem_sim_service_get_property; object_class->set_property = modem_sim_service_set_property; object_class->constructed = modem_sim_service_constructed; object_class->dispose = modem_sim_service_dispose; object_class->finalize = modem_sim_service_finalize; oface_class->ofono_interface = MODEM_OFACE_SIM; oface_class->property_mapper = modem_sim_service_property_mapper; oface_class->connect = modem_sim_service_connect; oface_class->connected = modem_sim_service_connected; oface_class->disconnect = modem_sim_service_disconnect; /* Properties */ g_object_class_install_property (object_class, PROP_STATUS, g_param_spec_uint ("state", "SIM Status", "Current state of Subscriber identity module.", MODEM_SIM_STATE_UNKNOWN, LAST_MODEM_SIM_STATE - 1, MODEM_SIM_STATE_UNKNOWN, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (object_class, PROP_IMSI, g_param_spec_string ("imsi", "IMSI", "Internation Mobile Subscriber Identity", "", /* default value */ G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS)); signals[SIGNAL_STATUS] = g_signal_new ("state", G_OBJECT_CLASS_TYPE (klass), G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED, 0, NULL, NULL, g_cclosure_marshal_VOID__UINT, G_TYPE_NONE, 1, G_TYPE_UINT); g_type_class_add_private (klass, sizeof (ModemSIMServicePrivate)); modem_error_domain_prefix (0); /* Init errors */ } ModemSIMState modem_sim_get_state (ModemSIMService const *self) { return MODEM_IS_SIM_SERVICE (self) ? self->priv->state : 0; } char const * modem_sim_get_imsi (ModemSIMService const *self) { return MODEM_IS_SIM_SERVICE (self) ? self->priv->imsi : NULL; } telepathy-ring-2.3.24-37ad7111424725a5446e82dac516d7cab88149c7/modem/sim.h000066400000000000000000000062551251541261300240260ustar00rootroot00000000000000/* * modem/sim.h - Client for Modem SIM service * * Copyright (C) 2008 Nokia Corporation * @author Pekka Pessi * * This work is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This work is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this work; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _MODEM_SIM_SERVICE_H_ #define _MODEM_SIM_SERVICE_H_ #include #include #include G_BEGIN_DECLS typedef struct _ModemSIMService ModemSIMService; typedef struct _ModemSIMServiceClass ModemSIMServiceClass; typedef struct _ModemSIMServicePrivate ModemSIMServicePrivate; typedef enum _ModemSIMState ModemSIMState; struct _ModemSIMServiceClass { ModemOfaceClass parent_class; }; struct _ModemSIMService { ModemOface parent; ModemSIMServicePrivate *priv; }; GType modem_sim_service_get_type (void); /* TYPE MACROS */ #define MODEM_TYPE_SIM_SERVICE \ (modem_sim_service_get_type ()) #define MODEM_SIM_SERVICE(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ MODEM_TYPE_SIM_SERVICE, ModemSIMService)) #define MODEM_SIM_SERVICE_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST ((klass), \ MODEM_TYPE_SIM_SERVICE, ModemSIMServiceClass)) #define MODEM_IS_SIM_SERVICE(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MODEM_TYPE_SIM_SERVICE)) #define MODEM_IS_SIM_SERVICE_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE ((klass), MODEM_TYPE_SIM_SERVICE)) #define MODEM_SIM_SERVICE_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), \ MODEM_TYPE_SIM_SERVICE, ModemSIMServiceClass)) /* ---------------------------------------------------------------------- */ #define MODEM_OFACE_SIM "org.ofono.SimManager" enum _ModemSIMState { MODEM_SIM_STATE_UNKNOWN = 0, MODEM_SIM_STATE_OK, MODEM_SIM_STATE_NO_SIM, MODEM_SIM_STATE_REMOVED, MODEM_SIM_STATE_PERMANENTLY_BLOCKED, MODEM_SIM_STATE_NOT_READY, MODEM_SIM_STATE_PIN_REQUIRED, MODEM_SIM_STATE_PUK_REQUIRED, MODEM_SIM_STATE_SIMLOCK_REJECTED, MODEM_SIM_STATE_REJECTED, LAST_MODEM_SIM_STATE }; typedef void ModemSIMStringReply (ModemSIMService *self, ModemRequest *request, char *reply, GError *error, gpointer user_data); typedef void ModemSIMUnsignedReply (ModemSIMService *self, ModemRequest *request, guint reply, GError *error, gpointer user_data); char const *modem_sim_get_imsi (ModemSIMService const *self); ModemSIMState modem_sim_get_state (ModemSIMService const *self); G_END_DECLS #endif /* #ifndef _MODEM_SIM_SERVICE_H_*/ telepathy-ring-2.3.24-37ad7111424725a5446e82dac516d7cab88149c7/modem/sms-history.c000066400000000000000000000100131251541261300255150ustar00rootroot00000000000000/* * modem/sms-history.c - oFono SMS History interface * * Copyright (C) 2013 Jolla Ltd * * This work is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This work is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this work; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "config.h" #define MODEM_DEBUG_FLAG MODEM_LOG_SMS #include "debug.h" #include "modem/sms-history.h" #include "modem/sms.h" #include "modem/ofono.h" #include "modem/modem.h" #include "modem/service.h" #include #include "signals-marshal.h" /* ------------------------------------------------------------------------ */ G_DEFINE_TYPE (ModemSmsHistory, modem_sms_history, MODEM_TYPE_OFACE); /* ------------------------------------------------------------------------ */ static void modem_sms_history_init (ModemSmsHistory *self) { DEBUG ("enter"); } static void modem_sms_history_constructed (GObject *object) { if (G_OBJECT_CLASS (modem_sms_history_parent_class)->constructed) G_OBJECT_CLASS (modem_sms_history_parent_class)->constructed (object); } static void modem_sms_history_dispose (GObject *object) { if (G_OBJECT_CLASS (modem_sms_history_parent_class)->dispose) G_OBJECT_CLASS (modem_sms_history_parent_class)->dispose (object); } static void modem_sms_history_finalize (GObject *object) { DEBUG ("enter"); } static void on_sms_history_status_report (DBusGProxy *proxy, char const *token, GHashTable *dict, gpointer user_data) { ModemSmsHistory *self = MODEM_SMS_HISTORY(user_data); ModemService *modemService = modem_service(); if (!modemService) return; Modem *modem = modem_service_find_by_path (modemService, dbus_g_proxy_get_path(proxy)); if (!modem) return; ModemSMSService *smsService = MODEM_SMS_SERVICE(modem_get_interface (modem, MODEM_OFACE_SMS)); if (!smsService) return; on_manager_message_status_report(proxy, token, dict, smsService); } /* ------------------------------------------------------------------------- */ /* ModemOface interface */ static void modem_sms_history_connect (ModemOface *_self) { DEBUG ("(%p): enter", _self); ModemSmsHistory *self = MODEM_SMS_HISTORY (_self); DBusGProxy *proxy = modem_oface_dbus_proxy (_self); dbus_g_proxy_add_signal (proxy, "StatusReport", G_TYPE_STRING, MODEM_TYPE_DBUS_DICT, G_TYPE_INVALID); dbus_g_proxy_connect_signal (proxy, "StatusReport", G_CALLBACK (on_sms_history_status_report), self, NULL); } static void modem_sms_history_connected (ModemOface *_self) { DEBUG ("(%p): enter", _self); } static void modem_sms_history_disconnect (ModemOface *_self) { DEBUG ("(%p): enter", _self); DBusGProxy *proxy = modem_oface_dbus_proxy (_self); ModemSmsHistory *self = MODEM_SMS_HISTORY (_self); dbus_g_proxy_disconnect_signal (proxy, "StatusReport", G_CALLBACK (on_sms_history_status_report), self); } static void modem_sms_history_class_init (ModemSmsHistoryClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); ModemOfaceClass *oface_class = MODEM_OFACE_CLASS (klass); DEBUG ("enter"); object_class->constructed = modem_sms_history_constructed; object_class->dispose = modem_sms_history_dispose; object_class->finalize = modem_sms_history_finalize; oface_class->ofono_interface = MODEM_OFACE_SMS_HISTORY; oface_class->connect = modem_sms_history_connect; oface_class->connected = modem_sms_history_connected; oface_class->disconnect = modem_sms_history_disconnect; modem_error_domain_prefix (0); /* Init errors */ } telepathy-ring-2.3.24-37ad7111424725a5446e82dac516d7cab88149c7/modem/sms-history.h000066400000000000000000000043231251541261300255310ustar00rootroot00000000000000/* * modem/sms-history.h - oFono SMS History interface * * Copyright (C) 2013 Jolla Ltd * * This work is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This work is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this work; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _MODEM_SMS_HISTORY_H_ #define _MODEM_SMS_HISTORY_H_ #include #include G_BEGIN_DECLS #define MODEM_OFACE_SMS_HISTORY "org.ofono.SmsHistory" typedef struct _ModemSmsHistory ModemSmsHistory; typedef struct _ModemSmsHistoryClass ModemSmsHistoryClass; struct _ModemSmsHistoryClass { ModemOfaceClass parent_class; }; struct _ModemSmsHistory { ModemOface parent; }; GType modem_sms_history_get_type (void); /* TYPE MACROS */ #define MODEM_TYPE_SMS_HISTORY \ (modem_sms_history_get_type ()) #define MODEM_SMS_HISTORY(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ MODEM_TYPE_SMS_HISTORY, ModemSmsHistory)) #define MODEM_SMS_HISTORY_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST ((klass), \ MODEM_TYPE_SMS_HISTORY, ModemSmsHistoryClass)) #define MODEM_IS_SMS_HISTORY(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MODEM_TYPE_SMS_HISTORY)) #define MODEM_IS_SMS_HISTORY_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE ((klass), MODEM_TYPE_SMS_HISTORY)) #define MODEM_SMS_HISTORY_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), \ MODEM_TYPE_SMS_HISTORY, ModemSmsHistoryClass)) /* ---------------------------------------------------------------------- */ G_END_DECLS #endif /* #ifndef _MODEM_SMS_HISTORY_H_*/ telepathy-ring-2.3.24-37ad7111424725a5446e82dac516d7cab88149c7/modem/sms-message.c000066400000000000000000000162661251541261300254600ustar00rootroot00000000000000/* * modem/sms-message.c - ModemSMSMessage class * * Copyright (C) 2013 Jolla Ltd * * This work is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This work is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this work; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "config.h" #define MODEM_DEBUG_FLAG MODEM_LOG_SMS #include "debug.h" #include "modem/sms-message.h" #include "modem/sms.h" #include "modem/request-private.h" #include "modem/errors.h" #include "modem/ofono.h" #include #include #include #include "signals-marshal.h" #include #include #include #include #include #define MODEM_OFACE_SMS_MESSAGE "org.ofono.Message" /* ---------------------------------------------------------------------- */ G_DEFINE_TYPE (ModemSMSMessage, modem_sms_message, G_TYPE_OBJECT); /* Forward declarations */ static void on_message_property_changed (DBusGProxy *, char const *, GValue *, gpointer); static void modem_sms_message_init (ModemSMSMessage *self) { self->destination = NULL; self->message_token = NULL; self->message_proxy = NULL; self->message_service = NULL; self->status_report_requested = FALSE; } /* ------------------------------------------------------------------------- */ enum { PROP_0, PROP_DESTINATION, PROP_TOKEN, PROP_PROXY, PROP_SERVICE, PROP_SRR, N_PROPERTIES }; static GParamSpec *obj_properties[N_PROPERTIES] = { NULL, }; static void modem_sms_message_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { ModemSMSMessage *self = MODEM_SMS_MESSAGE (object); switch (property_id) { case PROP_DESTINATION: g_value_set_string (value, self->destination); break; case PROP_TOKEN: g_value_set_string (value, self->message_token); break; case PROP_PROXY: g_value_set_pointer (value, self->message_proxy); break; case PROP_SERVICE: g_value_set_pointer (value, self->message_service); break; case PROP_SRR: g_value_set_boolean (value, self->status_report_requested); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void modem_sms_message_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { ModemSMSMessage *self = MODEM_SMS_MESSAGE (object); gpointer old; switch (property_id) { case PROP_DESTINATION: old = self->destination; self->destination = g_value_dup_string (value); g_free (old); break; case PROP_TOKEN: old = self->message_token; self->message_token = g_value_dup_string (value); g_free (old); break; case PROP_PROXY: self->message_proxy = g_value_get_pointer (value); break; case PROP_SERVICE: self->message_service = g_value_get_pointer (value); break; case PROP_SRR: self->status_report_requested = g_value_get_boolean (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void modem_sms_message_finalize (GObject *object) { ModemSMSMessage *self = MODEM_SMS_MESSAGE (object); g_free (self->destination); g_free (self->message_token); g_object_unref (self->message_proxy); G_OBJECT_CLASS (modem_sms_message_parent_class)->finalize (object); } static void modem_sms_message_constructed (GObject *object) { ModemSMSMessage *self = MODEM_SMS_MESSAGE (object); if (!self->destination || !self->message_token || !self->message_service) { DEBUG("This message object needs destination, token and service"); return; } /* Listen to this org.ofono.Message object's state changes */ DBusGConnection *bus = dbus_g_bus_get (DBUS_BUS_SYSTEM, NULL); if (!bus) return; self->message_proxy = dbus_g_proxy_new_for_name (bus, OFONO_BUS_NAME, self->message_token, MODEM_OFACE_SMS_MESSAGE); if (!self->message_proxy) return; dbus_g_proxy_add_signal (self->message_proxy, "PropertyChanged", G_TYPE_STRING, G_TYPE_VALUE, G_TYPE_INVALID); dbus_g_proxy_connect_signal (self->message_proxy, "PropertyChanged", G_CALLBACK (on_message_property_changed), self, NULL); } static void modem_sms_message_class_init (ModemSMSMessageClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); gobject_class->get_property = modem_sms_message_get_property; gobject_class->set_property = modem_sms_message_set_property; gobject_class->constructed = modem_sms_message_constructed; gobject_class->finalize = modem_sms_message_finalize; obj_properties[PROP_DESTINATION] = g_param_spec_string ("destination", "Destination", "MO SMS Destination", NULL, /* default value */ G_PARAM_READWRITE | G_PARAM_CONSTRUCT ); obj_properties[PROP_TOKEN] = g_param_spec_string ("message_token", "Token", "MO SMS Message Token", NULL, /* default value */ G_PARAM_READWRITE | G_PARAM_CONSTRUCT ); obj_properties[PROP_PROXY] = g_param_spec_pointer ("message_proxy", "D-Bus proxy", "MO SMS D-Bus proxy", G_PARAM_READWRITE | G_PARAM_CONSTRUCT ); obj_properties[PROP_SERVICE] = g_param_spec_pointer ("message_service", "SMS Service", "SMS Service", G_PARAM_READWRITE | G_PARAM_CONSTRUCT ); obj_properties[PROP_SRR] = g_param_spec_boolean ("status_report_requested", "SRR", "MO SMS Status Report Requested", FALSE, /* default value */ G_PARAM_READWRITE | G_PARAM_CONSTRUCT ); g_object_class_install_properties (gobject_class, N_PROPERTIES, obj_properties); } static void on_message_property_changed (DBusGProxy *proxy, char const *property, GValue *value, gpointer user_data) { ModemSMSMessage *self = MODEM_SMS_MESSAGE (user_data); if (strcmp (property, "State") == 0){ const char *state = g_value_get_string (value); if (strcmp (state, "sent") == 0){ modem_sms_emit_outgoing(self->message_service, self->destination, dbus_g_proxy_get_path(proxy)); } if (strcmp (state, "failed") == 0){ GError error = { TP_ERROR, TP_ERROR_NOT_AVAILABLE, "Unspecified SMS error" }; modem_sms_emit_error(self->message_service, self->destination, dbus_g_proxy_get_path(proxy), error); } } } telepathy-ring-2.3.24-37ad7111424725a5446e82dac516d7cab88149c7/modem/sms-message.h000066400000000000000000000032101251541261300254460ustar00rootroot00000000000000/* * modem/sms-message.h - ModemSMSMessage class * * Copyright (C) 2013 Jolla Ltd * * This work is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This work is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this work; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _MODEM_SMS_MESSAGE_H_ #define _MODEM_SMS_MESSAGE_H_ #include #include #include G_BEGIN_DECLS typedef struct _ModemSMSMessage ModemSMSMessage; typedef struct _ModemSMSMessageClass ModemSMSMessageClass; struct _ModemSMSMessageClass { GObjectClass parent_class; }; struct _ModemSMSMessage { GObject parent; /* Properties */ char *destination; char *message_token; ModemSMSService *message_service; gboolean status_report_requested; /* Internal */ DBusGProxy *message_proxy; }; GType modem_sms_message_get_type (void); /* TYPE MACROS */ #define MODEM_TYPE_SMS_MESSAGE (modem_sms_message_get_type ()) #define MODEM_SMS_MESSAGE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj),\ MODEM_TYPE_SMS_MESSAGE, ModemSMSMessage)) G_END_DECLS #endif /* #ifndef _MODEM_SMS_MESSAGE_H_*/ telepathy-ring-2.3.24-37ad7111424725a5446e82dac516d7cab88149c7/modem/sms-service.c000066400000000000000000000733131251541261300254700ustar00rootroot00000000000000/* * modem/sms-service.c - ModemSMSService class * * Copyright (C) 2008-2010 Nokia Corporation * @author Pekka Pessi * @author Lassi Syrjala * Copyright (C) 2013-2014 Jolla Ltd * * This work is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This work is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this work; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "config.h" #define MODEM_DEBUG_FLAG MODEM_LOG_SMS #include "debug.h" #include "modem/sms.h" #include "modem/sms-message.h" #include "modem/sms-history.h" #include "modem/request-private.h" #include "modem/errors.h" #if nomore #include "sms-glib/deliver.h" #include "sms-glib/status-report.h" #endif #include "modem/ofono.h" #include #include #include "signals-marshal.h" #include #include #include #include #include /* ---------------------------------------------------------------------- */ G_DEFINE_TYPE (ModemSMSService, modem_sms_service, MODEM_TYPE_OFACE) /* Signals we emit */ enum { SIGNAL_INCOMING_MESSAGE, SIGNAL_IMMEDIATE_MESSAGE, SIGNAL_OUTGOING_COMPLETE, SIGNAL_OUTGOING_ERROR, SIGNAL_STATUS_REPORT, #if nomore SIGNAL_DELIVER, #endif N_SIGNALS }; static guint signals[N_SIGNALS]; /* Properties */ enum { PROP_NONE, PROP_CONTENT_TYPES, PROP_SMSC, PROP_VALIDITY_PERIOD, PROP_REDUCED_CHARSET, PROP_USE_DELIVERY_REPORTS, LAST_PROPERTY }; /* private data */ struct _ModemSMSServicePrivate { time_t connected; /* Timestamp when got connected */ char *smsc; guint validity_period; #if nomore char **content_types; GHashTable *received; #endif /* A list of pending MO SMS waiting for a send status and - if requested - a status report */ GHashTable *pending_outgoing; unsigned reduced_charset:1; unsigned signals:1, :0; gboolean use_delivery_reports; }; /* ------------------------------------------------------------------------ */ #if nomore static void modem_sms_incoming_deliver (ModemSMSService *self, SMSGDeliver *deliver); #endif static void on_incoming_message (DBusGProxy *, char const *, GHashTable *, gpointer); static void on_immediate_message (DBusGProxy *, char const *, GHashTable *, gpointer); static void on_manager_message_removed (DBusGProxy *, char const *, gpointer); static void add_pending_message (char const *, GHashTable *, gpointer); /* ------------------------------------------------------------------------ */ /* GObject interface */ static void modem_sms_service_init (ModemSMSService *self) { DEBUG ("enter"); self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, MODEM_TYPE_SMS_SERVICE, ModemSMSServicePrivate); #if nomore self->priv->received = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, /* Message object stored in hash owns the key */ g_object_unref); #endif self->priv->pending_outgoing = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, /* Message object stored in hash owns the key */ g_object_unref); } static void modem_sms_service_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { ModemSMSService *self = MODEM_SMS_SERVICE (object); ModemSMSServicePrivate *priv = self->priv; switch (property_id) { #if nomore case PROP_CONTENT_TYPES: g_value_set_boxed (value, priv->content_types); break; #endif case PROP_SMSC: g_value_set_string (value, priv->smsc); break; case PROP_VALIDITY_PERIOD: g_value_set_uint (value, priv->validity_period); break; case PROP_REDUCED_CHARSET: g_value_set_boolean (value, priv->reduced_charset); break; case PROP_USE_DELIVERY_REPORTS: g_value_set_boolean (value, priv->use_delivery_reports); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void modem_sms_service_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { ModemSMSService *self = MODEM_SMS_SERVICE (object); ModemSMSServicePrivate *priv = self->priv; gpointer old; switch (property_id) { #if nomore case PROP_CONTENT_TYPES: old = priv->content_types; priv->content_types = g_value_dup_boxed (value); if (old) g_boxed_free (G_TYPE_STRV, old); break; #endif case PROP_SMSC: old = priv->smsc; priv->smsc = g_value_dup_string (value); g_free (old); break; case PROP_VALIDITY_PERIOD: priv->validity_period = g_value_get_uint (value); break; case PROP_REDUCED_CHARSET: priv->reduced_charset = g_value_get_boolean (value); break; case PROP_USE_DELIVERY_REPORTS: priv->use_delivery_reports = g_value_get_boolean (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void modem_sms_service_constructed (GObject *object) { if (G_OBJECT_CLASS (modem_sms_service_parent_class)->constructed) G_OBJECT_CLASS (modem_sms_service_parent_class)->constructed (object); if (modem_oface_dbus_proxy (MODEM_OFACE (object)) == NULL) g_warning("object created without dbus-proxy set"); } static void modem_sms_service_dispose (GObject *object) { if (G_OBJECT_CLASS (modem_sms_service_parent_class)->dispose) G_OBJECT_CLASS (modem_sms_service_parent_class)->dispose (object); } static void modem_sms_service_finalize (GObject *object) { ModemSMSService *self = MODEM_SMS_SERVICE (object); ModemSMSServicePrivate *priv = self->priv; DEBUG ("enter"); /* Free any data held directly by the object here */ g_free (priv->smsc); #if nomore if (priv->content_types) g_boxed_free (G_TYPE_STRV, priv->content_types); if (priv->received) g_hash_table_destroy (priv->received); #endif g_hash_table_destroy (priv->pending_outgoing); G_OBJECT_CLASS (modem_sms_service_parent_class)->finalize (object); } /* ------------------------------------------------------------------------- */ /* oface */ static void reply_to_sms_manager_get_messages (ModemOface *_self, ModemRequest *request, GPtrArray *array, GError const *error, gpointer user_data); static char const * modem_sms_service_property_mapper (char const *name) { if (!strcmp (name, "UseDeliveryReports")) return "use-delivery-reports"; if (!strcmp (name, "ServiceCenterAddress")) return "service-centre"; if (!strcmp (name, "Bearer")) return NULL; return NULL; } static void modem_sms_service_connect (ModemOface *_self) { DEBUG ("(%p): enter", _self); ModemSMSService *self = MODEM_SMS_SERVICE (_self); ModemSMSServicePrivate *priv = self->priv; DBusGProxy *proxy = modem_oface_dbus_proxy (_self); if (!priv->signals) { priv->signals = TRUE; #define CONNECT(handler, name, signature...) \ dbus_g_proxy_add_signal (proxy, (name), ##signature); \ dbus_g_proxy_connect_signal (proxy, (name), G_CALLBACK (handler), self, NULL) CONNECT (on_immediate_message, "ImmediateMessage", G_TYPE_STRING, MODEM_TYPE_DBUS_DICT, G_TYPE_INVALID); CONNECT (on_incoming_message, "IncomingMessage", G_TYPE_STRING, MODEM_TYPE_DBUS_DICT, G_TYPE_INVALID); CONNECT (on_manager_message_removed, "MessageRemoved", DBUS_TYPE_G_OBJECT_PATH, G_TYPE_INVALID); CONNECT (on_manager_message_status_report, "StatusReport", G_TYPE_STRING, MODEM_TYPE_DBUS_DICT, G_TYPE_INVALID); } modem_oface_connect_properties (_self, TRUE); /* read pending MO SMS from oFono's TX queue */ modem_oface_add_connect_request (_self, modem_oface_request_managed (_self, "GetMessages", reply_to_sms_manager_get_messages, NULL)); } static void reply_to_sms_manager_get_messages (ModemOface *_self, ModemRequest *request, GPtrArray *array, GError const *error, gpointer user_data) { ModemSMSService *self = MODEM_SMS_SERVICE (_self); DEBUG ("(%p): enter", _self); if (!error) { guint i; for (i = 0; i < array->len; i++) { GValueArray *va = g_ptr_array_index (array, i); char const *path = g_value_get_boxed (va->values + 0); GHashTable *properties = g_value_get_boxed (va->values + 1); add_pending_message (path, properties, self); } } modem_oface_check_connected (_self, request, error); } static void modem_sms_service_connected (ModemOface *_self) { DEBUG ("(%p): enter", _self); ModemSMSService *self = MODEM_SMS_SERVICE (_self); ModemSMSServicePrivate *priv = self->priv; time_t now = time (NULL); priv->connected = now; #if nomore GHashTableIter i[1]; gpointer key, value; for (g_hash_table_iter_init (i, priv->received); g_hash_table_iter_next (i, &key, &value);) { g_object_set (value, "time-delivered", (guint64)now, NULL); } #endif } static void modem_sms_service_disconnect (ModemOface *_self) { DEBUG ("(%p): enter", _self); ModemSMSService *self = MODEM_SMS_SERVICE (_self); ModemSMSServicePrivate *priv = self->priv; DBusGProxy *proxy = modem_oface_dbus_proxy (_self); if (priv->signals) { priv->signals = FALSE; dbus_g_proxy_disconnect_signal (proxy, "ImmediateMessage", G_CALLBACK (on_immediate_message), self); dbus_g_proxy_disconnect_signal (proxy, "IncomingMessage", G_CALLBACK (on_incoming_message), self); dbus_g_proxy_disconnect_signal (proxy, "MessageRemoved", G_CALLBACK (on_manager_message_removed), self); dbus_g_proxy_disconnect_signal (proxy, "StatusReport", G_CALLBACK (on_manager_message_status_report), self); } #if nomore g_hash_table_remove_all (priv->received); #endif modem_oface_disconnect_properties (_self); } /* ------------------------------------------------------------------------- */ static void modem_sms_service_class_init (ModemSMSServiceClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); ModemOfaceClass *oface_class = MODEM_OFACE_CLASS (klass); DEBUG ("enter"); object_class->get_property = modem_sms_service_get_property; object_class->set_property = modem_sms_service_set_property; object_class->constructed = modem_sms_service_constructed; object_class->dispose = modem_sms_service_dispose; object_class->finalize = modem_sms_service_finalize; oface_class->ofono_interface = MODEM_OFACE_SMS; oface_class->property_mapper = modem_sms_service_property_mapper; oface_class->connect = modem_sms_service_connect; oface_class->connected = modem_sms_service_connected; oface_class->disconnect = modem_sms_service_disconnect; #if nomore g_object_class_install_property (object_class, PROP_CONTENT_TYPES, g_param_spec_boxed ("content-types", "Content types used", "List of MIME content types used by application.", G_TYPE_STRV, G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS)); #endif g_object_class_install_property (object_class, PROP_SMSC, g_param_spec_string ("service-centre", "SMS Service Centre", "ISDN Address for SMS Service Centre", "", /* default value */ G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (object_class, PROP_VALIDITY_PERIOD, g_param_spec_uint ("validity-period", "SMS Validity Period", "Period while SMS service centre " "keep trying to deliver SMS.", /* anything above 0 gets rounded up to 5 minutes */ 0, /* 0 means no validity period */ 63 * 7 * 24 * 60 * 60, /* max - 63 weeks */ 0, /* no validity period - it is up to service centre */ G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (object_class, PROP_REDUCED_CHARSET, g_param_spec_boolean ("reduced-charset", "SMS reduced character set support", "Whether SMS should be encoded with " "a reduced character set", FALSE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (object_class, PROP_USE_DELIVERY_REPORTS, g_param_spec_boolean ("use-delivery-reports", "Use Delivery Reports", "Use Delivery Reports", FALSE, /* default value */ G_PARAM_READWRITE | G_PARAM_CONSTRUCT )); signals[SIGNAL_IMMEDIATE_MESSAGE] = g_signal_new ("immediate-message", G_OBJECT_CLASS_TYPE (klass), G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED, 0, NULL, NULL, _modem__marshal_VOID__STRING_BOXED, G_TYPE_NONE, 2, G_TYPE_STRING, G_TYPE_HASH_TABLE); signals[SIGNAL_INCOMING_MESSAGE] = g_signal_new ("incoming-message", G_OBJECT_CLASS_TYPE (klass), G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED, 0, NULL, NULL, _modem__marshal_VOID__STRING_BOXED, G_TYPE_NONE, 2, G_TYPE_STRING, G_TYPE_HASH_TABLE); signals[SIGNAL_OUTGOING_COMPLETE] = g_signal_new ("outgoing-complete", G_OBJECT_CLASS_TYPE (klass), G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED, 0, NULL, NULL, _modem__marshal_VOID__STRING_STRING, G_TYPE_NONE, 2, G_TYPE_STRING, G_TYPE_STRING); signals[SIGNAL_OUTGOING_ERROR] = g_signal_new ("outgoing-error", G_OBJECT_CLASS_TYPE (klass), G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED, 0, NULL, NULL, _modem__marshal_VOID__STRING_STRING_POINTER, G_TYPE_NONE, 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_POINTER); signals[SIGNAL_STATUS_REPORT] = g_signal_new ("state-report", G_OBJECT_CLASS_TYPE (klass), G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED, 0, NULL, NULL, NULL, G_TYPE_NONE, 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_BOOLEAN); #if nomore signals[SIGNAL_DELIVER] = g_signal_new ("deliver", G_OBJECT_CLASS_TYPE (klass), G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED, 0, NULL, NULL, g_cclosure_marshal_VOID__OBJECT, G_TYPE_NONE, 1, G_TYPE_OBJECT); #endif g_type_class_add_private (klass, sizeof (ModemSMSServicePrivate)); modem_oface_register_type(MODEM_TYPE_SMS_HISTORY); } /* ---------------------------------------------------------------------- */ /* Signal connection helpers */ #if nomore gulong modem_sms_connect_to_connected (ModemSMSService *self, ModemSMSConnectedHandler *handler, gpointer data) { return g_signal_connect (self, "connected", G_CALLBACK (handler), data); } gulong modem_sms_connect_to_deliver (ModemSMSService *self, ModemSMSDeliverHandler *handler, gpointer data) { return g_signal_connect (self, "deliver", G_CALLBACK (handler), data); } #endif gulong modem_sms_connect_to_incoming_message (ModemSMSService *self, ModemSMSMessageHandler *handler, gpointer data) { return g_signal_connect (self, "incoming-message", G_CALLBACK (handler), data); } gulong modem_sms_connect_to_immediate_message (ModemSMSService *self, ModemSMSMessageHandler *handler, gpointer data) { return g_signal_connect (self, "immediate-message", G_CALLBACK (handler), data); } gulong modem_sms_connect_to_outgoing_complete (ModemSMSService *self, ModemSMSMessageHandler *handler, gpointer data) { return g_signal_connect (self, "outgoing-complete", G_CALLBACK (handler), data); } gulong modem_sms_connect_to_outgoing_error (ModemSMSService *self, ModemSMSMessageHandler *handler, gpointer data) { return g_signal_connect (self, "outgoing-error", G_CALLBACK (handler), data); } gulong modem_sms_connect_to_status_report (ModemSMSService *self, ModemSMSMessageHandler *handler, gpointer data) { return g_signal_connect (self, "state-report", G_CALLBACK (handler), data); } /* ------------------------------------------------------------------------- */ /* modem_sms_service interface */ guint64 modem_sms_service_time_connected (ModemSMSService const *self) { if (MODEM_IS_SMS_SERVICE (self)) return (guint64)self->priv->connected; else return 0; } gint64 modem_sms_parse_time (gchar const *s) { GTimeVal val; if (g_time_val_from_iso8601(s, &val)) return val.tv_sec; else return 0; } /* ------------------------------------------------------------------------- */ static void on_manager_message_removed (DBusGProxy *proxy, char const *token, gpointer user_data) { ModemSMSService *self = MODEM_SMS_SERVICE (user_data); DEBUG ("%s", token); gpointer obj = g_hash_table_lookup (self->priv->pending_outgoing, (gpointer)token); if (!obj) { DEBUG("No pending message with token %s, ignore", token); return; } ModemSMSMessage *message = MODEM_SMS_MESSAGE(obj); GValue srr = G_VALUE_INIT; g_value_init (&srr, G_TYPE_BOOLEAN); g_object_get_property(obj, "status_report_requested", &srr); if (!g_value_get_boolean(&srr)) { /* No status report requested, remove message now from pending list */ g_hash_table_remove (self->priv->pending_outgoing, (gpointer)token); g_object_unref (message); } } void on_manager_message_status_report (DBusGProxy *proxy, char const *token, GHashTable *dict, gpointer user_data) { ModemSMSService *self = MODEM_SMS_SERVICE (user_data); gpointer value; int delivered = 0; value = g_hash_table_lookup (dict, "Delivered"); if (value) { delivered = g_value_get_boolean(value); } DEBUG ("Received status report for message %s, delivery success: %d", token, delivered); gpointer obj = g_hash_table_lookup (self->priv->pending_outgoing, (gpointer)token); if (!obj) { DEBUG("No pending message with token %s, ignore", token); return; } ModemSMSMessage *message = MODEM_SMS_MESSAGE(obj); GValue srr = G_VALUE_INIT; g_value_init (&srr, G_TYPE_BOOLEAN); g_object_get_property(obj, "status_report_requested", &srr); if (!g_value_get_boolean(&srr)) { DEBUG("Status report not requested for message with token %s, ignore", token); return; } GValue destination = G_VALUE_INIT; const char* dest_string; g_value_init (&destination, G_TYPE_STRING); g_object_get_property(obj, "destination", &destination); dest_string = g_value_get_string(&destination); g_signal_emit (self, signals[SIGNAL_STATUS_REPORT], 0, dest_string, token, delivered); g_hash_table_remove (self->priv->pending_outgoing, (gpointer)token); g_object_unref (message); } static void dump_message_dict (GHashTable *dict) { char *key; GValue *value; GHashTableIter iter[1]; g_hash_table_iter_init (iter, dict); while (g_hash_table_iter_next (iter, (gpointer)&key, (gpointer)&value)) { char *s = g_strdup_value_contents (value); DEBUG ("%s = %s", key, s); g_free (s); } } static void add_pending_message (char const *message_path, GHashTable *properties, gpointer user_data) { ModemSMSService *self = MODEM_SMS_SERVICE (user_data); DEBUG ("%s", message_path); /* * Some fields are not known when reading from oFono's TX queue. Use a * destination address that is valid but unlikely to match an existing * one, and don't wait for status reports. */ gpointer message_object = g_object_new( MODEM_TYPE_SMS_MESSAGE, "destination", "00000000000000000000", "message_token", message_path, "message_service", self, "status_report_requested", FALSE, NULL ); g_hash_table_insert (self->priv->pending_outgoing, (gpointer)message_path, g_object_ref (message_object)); } #if nomore static void on_incoming_message (DBusGProxy *proxy, char const *message, GHashTable *dict, gpointer _self) { char const *smsc = "1234567"; char const *type = "text/plain"; char const *originator = ""; char *token; GError *error = NULL; GValue *value; SMSGDeliver *d; ModemSMSService *self = MODEM_SMS_SERVICE (_self); DEBUG ("message = \"%s\"", message); dump_message_dict (dict); value = g_hash_table_lookup (dict, "Sender"); if (value) originator = g_value_get_string (value); token = modem_sms_generate_token (); d = sms_g_deliver_incoming (message, token, originator, smsc, type, NULL, &error); if (!d) { modem_message (MODEM_LOG_SMS, "deserializing SMS-DELIVER \"%s\" failed: " GERROR_MSG_FMT, token, GERROR_MSG_CODE (error)); g_clear_error (&error); } else { modem_sms_incoming_deliver (self, d); g_object_unref (d); } g_free (token); } #endif /* ------------------------------------------------------------------------- */ gboolean modem_sms_service_is_error_serious (GError *error) { if (error == NULL) return FALSE; return TRUE; } /* ---------------------------------------------------------------------- */ /* Properties */ ModemRequest * modem_sms_set_sc_address (ModemSMSService *self, char const *address, ModemSMSServiceReply *callback, gpointer user_data) { GValue value[1]; g_value_init (memset (value, 0, sizeof value), G_TYPE_STRING); g_value_set_string (value, address); return modem_oface_set_property_req (MODEM_OFACE (self), "ServiceCenterAddress", value, (void *)callback, user_data); } ModemRequest * modem_sms_set_srr (ModemSMSService *self, gboolean srr, ModemSMSServiceReply *callback, gpointer user_data) { GValue value[1]; g_value_init (memset (value, 0, sizeof value), G_TYPE_BOOLEAN); g_value_set_boolean (value, srr); return modem_oface_set_property_req (MODEM_OFACE (self), "UseDeliveryReports", value, (void *)callback, user_data); } /* ---------------------------------------------------------------------- */ /* Message deliver */ #if nomore static void modem_sms_incoming_deliver (ModemSMSService *self, SMSGDeliver *deliver) { ModemSMSServicePrivate *priv = self->priv; gchar const *content_type = sms_g_deliver_get_content_type (deliver); gchar const *token = sms_g_deliver_get_message_token (deliver); if (priv->content_types) { int i; for (i = 0; priv->content_types[i]; i++) { if (g_ascii_strcasecmp (content_type, priv->content_types[i]) == 0) break; } if (priv->content_types[i] == NULL) { DEBUG ("SMS-DELIVER containing %s is ignored", content_type); return; } } if (g_hash_table_lookup (priv->received, (gpointer)token)) { DEBUG ("SMS-DELIVER %s already read, ignoring", token); return; } g_hash_table_insert (priv->received, (gpointer)token, g_object_ref (deliver)); DEBUG ("SMS-DELIVER connected:%ld", priv->connected); if (priv->connected) g_signal_emit (self, signals[SIGNAL_DELIVER], 0, deliver); } static void modem_sms_incoming_status_report (ModemSMSService *self, SMSGStatusReport *sr) { ModemSMSServicePrivate *priv = self->priv; gchar const *token = sms_g_status_report_get_message_token (sr); if (g_hash_table_lookup (priv->received, (gpointer)token)) { DEBUG ("SMS-STATUS REPORT %s already read, ignoring", token); return; } g_hash_table_insert (priv->received, (gpointer)token, g_object_ref (sr)); if (priv->connected) g_signal_emit (self, signals[SIGNAL_STATUS_REPORT], 0, sr); } #endif static void on_immediate_message (DBusGProxy *proxy, char const *message, GHashTable *dict, gpointer _self) { ModemSMSService *self = MODEM_SMS_SERVICE (_self); ModemSMSServicePrivate *priv = self->priv; if (!priv->connected) return; DEBUG ("immediate = \"%50s\"%s", message, strlen (message) > 50 ? "..." : ""); dump_message_dict (dict); g_signal_emit (self, signals[SIGNAL_IMMEDIATE_MESSAGE], 0, message, dict); } static void on_incoming_message (DBusGProxy *proxy, char const *message, GHashTable *dict, gpointer _self) { ModemSMSService *self = MODEM_SMS_SERVICE (_self); ModemSMSServicePrivate *priv = self->priv; if (!priv->connected) return; DEBUG ("incoming = \"%50s\"%s", message, strlen (message) > 50 ? "..." : ""); dump_message_dict (dict); g_signal_emit (self, signals[SIGNAL_INCOMING_MESSAGE], 0, message, dict); } /* ---------------------------------------------------------------------- */ /* Sending */ static void reply_to_send_message (DBusGProxy *proxy, DBusGProxyCall *call, void *_request) { ModemRequest *request = _request; ModemSMSService *self = modem_request_object (request); ModemSMSServiceSendReply *callback = modem_request_callback (request); gpointer user_data = modem_request_user_data (request); char const *message_path = NULL; GError *error = NULL; if (dbus_g_proxy_end_call (proxy, call, &error, DBUS_TYPE_G_OBJECT_PATH, &message_path, G_TYPE_INVALID)) { char const *destination; destination = modem_request_get_data (request, "destination"); GValue srr = G_VALUE_INIT; g_value_init (&srr, G_TYPE_BOOLEAN); g_object_get_property(self, "use-delivery-reports", &srr); gboolean srr_bool = g_value_get_boolean(&srr); DEBUG("Status report requested for this message: %d", srr_bool); gpointer message_object = g_object_new( MODEM_TYPE_SMS_MESSAGE, "destination", destination, "message_token", message_path, "message_service", self, "status_report_requested", srr_bool, NULL ); g_hash_table_insert (self->priv->pending_outgoing, (gpointer)message_path, g_object_ref (message_object)); } callback (self, request, message_path, error, user_data); g_clear_error (&error); } ModemRequest * modem_sms_request_send (ModemSMSService *self, char const *to, char const *message, ModemSMSServiceSendReply *reply, gpointer user_data) { ModemRequest *request; DEBUG (MODEM_OFACE_SMS ".SendMessage (%s,%s)", to, message); request = modem_request (self, modem_oface_dbus_proxy (MODEM_OFACE (self)), "SendMessage", reply_to_send_message, G_CALLBACK (reply), user_data, G_TYPE_STRING, to, G_TYPE_STRING, message, G_TYPE_INVALID); if (request) modem_request_add_data_full (request, "destination", g_strdup (to), g_free); return request; } static gchar const * _modem_sms_is_valid_address (gchar const *address) { size_t len; if (address == NULL) return "NULL"; if (address[0] == '+') { address++; } len = strspn (address, "0123456789"); if (address[len]) return "invalid character"; if (len == 0) return "too short"; if (len > 20) return "too long"; return NULL; } /** Return TRUE if @a address is a valid SMS address. * * A valid SMS address is a phone number with at most 20 digits either in * national or in international format (starting with +). * * @param address - ISDN address of address * * @retval TRUE - address is a valid SMS address * @retval FALSE - address is NULL, does not contain valid phone number, or it * is too long. */ gboolean modem_sms_is_valid_address (gchar const *address) { return !_modem_sms_is_valid_address (address); } /** Validate a SMS address @a address. * * A valid SMS address is a phone number with at most 20 digits either * in national or in international format (starting with +). * * @param address - ISDN address of address * @param error - return value for GError describing the validation error * * @retval TRUE - address is a valid SMS address * @retval FALSE - address is NULL, does not contain valid phone number, or it * is too long. */ gboolean modem_sms_validate_address (gchar const *address, GError **error) { gchar const *reason = _modem_sms_is_valid_address (address); if (reason) g_set_error (error, MODEM_SMS_ERRORS, MODEM_SMS_ERROR_INVALID_PARAMETER, "Invalid SMS address \"%s\": %s", address, reason); return !reason; } void modem_sms_emit_outgoing(ModemSMSService *self, char *address, char *path){ g_signal_emit (self, signals[SIGNAL_OUTGOING_COMPLETE], 0, address, path); } void modem_sms_emit_error(ModemSMSService *self, char *address, char *path, GError error){ g_signal_emit (self, signals[SIGNAL_OUTGOING_ERROR], 0, address, path, &error); } telepathy-ring-2.3.24-37ad7111424725a5446e82dac516d7cab88149c7/modem/sms.h000066400000000000000000000112211251541261300240250ustar00rootroot00000000000000/* * modem/sms.h - Client for Modem SMS Service * * Copyright (C) 2008 Nokia Corporation * @author Pekka Pessi * * This work is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This work is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this work; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _MODEM_SMS_SERVICE_H_ #define _MODEM_SMS_SERVICE_H_ #include #if nomore #include #include #include #endif #include #include G_BEGIN_DECLS typedef struct _ModemSMSService ModemSMSService; typedef struct _ModemSMSServiceClass ModemSMSServiceClass; typedef struct _ModemSMSServicePrivate ModemSMSServicePrivate; struct _ModemSMSServiceClass { ModemOfaceClass parent_class; }; struct _ModemSMSService { ModemOface parent; ModemSMSServicePrivate *priv; }; GType modem_sms_service_get_type (void); /* TYPE MACROS */ #define MODEM_TYPE_SMS_SERVICE \ (modem_sms_service_get_type ()) #define MODEM_SMS_SERVICE(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), MODEM_TYPE_SMS_SERVICE, ModemSMSService)) #define MODEM_SMS_SERVICE_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST ((klass), MODEM_TYPE_SMS_SERVICE, ModemSMSServiceClass)) #define MODEM_IS_SMS_SERVICE(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MODEM_TYPE_SMS_SERVICE)) #define MODEM_IS_SMS_SERVICE_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE ((klass), MODEM_TYPE_SMS_SERVICE)) #define MODEM_SMS_SERVICE_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), MODEM_TYPE_SMS_SERVICE, ModemSMSServiceClass)) /* ---------------------------------------------------------------------- */ #define MODEM_OFACE_SMS "org.ofono.MessageManager" #if nomore typedef void ModemSMSConnectedHandler (ModemSMSService *, gpointer); typedef void ModemSMSDeliverHandler (ModemSMSService *, SMSGDeliver *, gpointer); #endif typedef void ModemSMSMessageHandler (ModemSMSService *self, gchar const *message, GHashTable *info, gpointer user_data); typedef void ModemSMSServiceReply (ModemSMSService *self, ModemRequest *request, GError const *error, gpointer user_data); typedef void ModemSMSServiceSendReply (ModemSMSService *self, ModemRequest *request, char const *message_id, GError const *error, gpointer user_data); /* ---------------------------------------------------------------------- */ #if nomore char const *modem_sms_service_property_name_by_ofono_name (char const *); gulong modem_sms_connect_to_connected (ModemSMSService *self, ModemSMSConnectedHandler *user_function, gpointer user_data); gulong modem_sms_connect_to_deliver (ModemSMSService *self, ModemSMSDeliverHandler *user_function, gpointer user_data); #endif gulong modem_sms_connect_to_incoming_message (ModemSMSService *self, ModemSMSMessageHandler *handler, gpointer data); gulong modem_sms_connect_to_immediate_message (ModemSMSService *self, ModemSMSMessageHandler *handler, gpointer data); guint64 modem_sms_service_time_connected (ModemSMSService const *self); gint64 modem_sms_parse_time (gchar const *); void modem_sms_emit_outgoing(ModemSMSService *self, char *address, char *path); void modem_sms_emit_error(ModemSMSService *self, char *address, char *path, GError error); void on_manager_message_status_report (DBusGProxy *, char const *, GHashTable *, gpointer); /* ---------------------------------------------------------------------- */ ModemRequest *modem_sms_set_sc_address (ModemSMSService *self, char const *address, ModemSMSServiceReply *reply, gpointer user_data); ModemRequest *modem_sms_set_srr (ModemSMSService *self, gboolean srr, ModemSMSServiceReply *reply, gpointer user_data); ModemRequest *modem_sms_request_send (ModemSMSService *self, char const *to, char const *message, ModemSMSServiceSendReply *reply, gpointer user_data); /* ---------------------------------------------------------------------- */ gboolean modem_sms_is_valid_address (gchar const *address); gboolean modem_sms_validate_address (gchar const *address, GError **error); G_END_DECLS #endif /* #ifndef _MODEM_SMS_SERVICE_H_*/ telepathy-ring-2.3.24-37ad7111424725a5446e82dac516d7cab88149c7/modem/tests/000077500000000000000000000000001251541261300242175ustar00rootroot00000000000000telepathy-ring-2.3.24-37ad7111424725a5446e82dac516d7cab88149c7/modem/tests/Makefile.am000066400000000000000000000010751251541261300262560ustar00rootroot00000000000000AM_CFLAGS = $(ERROR_CFLAGS) \ @TP_CFLAGS@ @GLIB_CFLAGS@ @DBUS_CFLAGS@ @CHECK_CFLAGS@ INCLUDES = -I$(top_srcdir) -I$(top_srcdir)/tests -I$(top_srcdir)/modem -I.. TESTS = test-modem test_PROGRAMS = $(TESTS) test_modem_SOURCES = \ test-modem.c test-modem.h \ test-modem-call-service.c \ test-modem-call.c \ test-modem-tones.c \ test-sim.c \ test-modem-request.c \ base.h base.c derived.h derived.c # test-modem-sms.c test_modem_LDADD = \ ../libmodem-glib.la \ ../../tests/libtestcommon.la \ @TP_LIBS@ @DBUS_LIBS@ @GLIB_LIBS@ \ @CHECK_LIBS@ -lpthread telepathy-ring-2.3.24-37ad7111424725a5446e82dac516d7cab88149c7/modem/tests/base.c000066400000000000000000000131271251541261300253010ustar00rootroot00000000000000/* * base.c - * * Copyright (C) 2007-2010 Nokia Corporation * @author Pekka Pessi * * This work is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This work is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this work; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "config.h" #include "base.h" void checktag(char const *); #define ENTER(format, ...) \ do { checktag(G_STRFUNC); DEBUG(format, ##__VA_ARGS__); } while(0) #if CHECK_DEBUG_BASE #define DEBUG(format, ...) \ g_log("ring-tests", G_LOG_LEVEL_DEBUG, \ "%s: " format, G_STRFUNC, ##__VA_ARGS__) #else #define DEBUG(format, ...) ((void)0) #endif G_DEFINE_TYPE(Base, base, G_TYPE_OBJECT); /* Properties */ enum { PROP_NONE, PROP_READWRITE, PROP_CONSTRUCT, PROP_CONSTRUCT_ONLY, }; /* private data */ struct _BasePrivate { unsigned readwrite:1, construct:1, construct_only:1, dispose_has_run:1, :0; }; static GObject * base_constructor(GType type, guint n_props, GObjectConstructParam *props) { GObject *object; ENTER("enter (%s, %u@%p)", g_type_name(type), n_props, props); object = G_OBJECT_CLASS(base_parent_class) ->constructor(type, n_props, props); DEBUG("return %p", object); return object; } static void base_init(Base *self) { ENTER("enter (%p)", self); self->priv = G_TYPE_INSTANCE_GET_PRIVATE( self, TYPE_BASE, BasePrivate); } static void base_get_property(GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { ENTER("enter (%p, %d)", object, property_id); Base *self = BASE(object); BasePrivate *priv = self->priv; switch(property_id) { case PROP_READWRITE: g_value_set_boolean(value, priv->readwrite); break; case PROP_CONSTRUCT: g_value_set_boolean(value, priv->construct); break; case PROP_CONSTRUCT_ONLY: g_value_set_boolean(value, priv->construct_only); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); break; } DEBUG("return value=%s", g_strdup_value_contents(value)); } static void base_set_property(GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { ENTER("enter (%p, %d, %s, %p)", object, property_id, g_strdup_value_contents(value), pspec); Base *self = BASE(object); BasePrivate *priv = self->priv; switch(property_id) { case PROP_READWRITE: priv->readwrite = g_value_get_boolean(value); break; case PROP_CONSTRUCT: priv->construct = g_value_get_boolean(value); break; case PROP_CONSTRUCT_ONLY: priv->construct_only = g_value_get_boolean(value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); } DEBUG("return (%p)", object); } static void base_constructed(GObject *object) { ENTER("(%p): enter", object); if (G_OBJECT_CLASS(base_parent_class)->constructed) G_OBJECT_CLASS(base_parent_class)->constructed(object); } static void base_dispose(GObject *object) { Base *self = BASE(object); BasePrivate *priv = self->priv; ENTER("enter (%p): %s", object, priv->dispose_has_run ? "already" : "disposing"); if (priv->dispose_has_run) return; priv->dispose_has_run = TRUE; if (G_OBJECT_CLASS(base_parent_class)->dispose) G_OBJECT_CLASS(base_parent_class)->dispose(object); DEBUG("(%p): return from disposing", object); } static void base_finalize(GObject *object) { Base *self = BASE(object); BasePrivate *priv = self->priv; ENTER("enter (%p)", object); (void)priv; G_OBJECT_CLASS(base_parent_class)->finalize(object); DEBUG("(%p): return", object); } static void base_class_init(BaseClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS(klass); ENTER("enter"); g_type_class_add_private(klass, sizeof (BasePrivate)); object_class->constructor = base_constructor; object_class->get_property = base_get_property; object_class->set_property = base_set_property; object_class->constructed = base_constructed; object_class->dispose = base_dispose; object_class->finalize = base_finalize; /* No Signals */ /* Properties */ g_object_class_install_property( object_class, PROP_READWRITE, g_param_spec_boolean("base-readwrite", "Base readwrite property", "Readwrite property in base object", TRUE, /* default value */ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property( object_class, PROP_CONSTRUCT, g_param_spec_boolean("base-construct", "Base construct property", "Construct property in base object", TRUE, /* default value */ G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS)); g_object_class_install_property( object_class, PROP_CONSTRUCT_ONLY, g_param_spec_boolean("base-construct-only", "Base construct-only property", "Construct-only property in base object", TRUE, /* default value */ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); DEBUG("return"); } telepathy-ring-2.3.24-37ad7111424725a5446e82dac516d7cab88149c7/modem/tests/base.h000066400000000000000000000035121251541261300253030ustar00rootroot00000000000000/* * base.h - * * Copyright (C) 2007-2010 Nokia Corporation * @author Pekka Pessi * * This work is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This work is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this work; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef BASE_H #define BASE_H #include G_BEGIN_DECLS typedef struct _Base Base; typedef struct _BaseClass BaseClass; typedef struct _BasePrivate BasePrivate; struct _BaseClass { GObjectClass parent_class; }; struct _Base { GObject parent; BasePrivate *priv; }; GType base_get_type(void); /* TYPE MACROS */ #define TYPE_BASE \ (base_get_type()) #define BASE(obj) \ (G_TYPE_CHECK_INSTANCE_CAST((obj), TYPE_BASE, Base)) #define BASE_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST((klass), TYPE_BASE, BaseClass)) #define IS_BASE(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE((obj), TYPE_BASE)) #define IS_BASE_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE((klass), TYPE_BASE)) #define BASE_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), TYPE_BASE, BaseClass)) G_END_DECLS #endif /* #ifndef BASE_H */ telepathy-ring-2.3.24-37ad7111424725a5446e82dac516d7cab88149c7/modem/tests/derived.c000066400000000000000000000133571251541261300260160ustar00rootroot00000000000000/* * derived.c - * * Copyright (C) 2007-2010 Nokia Corporation * @author Pekka Pessi * * This work is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This work is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this work; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "config.h" #include "derived.h" void checktag(char const *); #define ENTER(format, ...) \ do { checktag(G_STRFUNC); DEBUG(format, ##__VA_ARGS__); } while(0) #if CHECK_DEBUG_DERIVED #define DEBUG(format, ...) \ g_log("ring-tests", G_LOG_LEVEL_DEBUG, \ "%s: " format, G_STRFUNC, ##__VA_ARGS__) #else #define DEBUG(format, ...) ((void)0) #endif G_DEFINE_TYPE(Derived, derived, TYPE_BASE); /* Properties */ enum { PROP_NONE, PROP_READWRITE, PROP_CONSTRUCT, PROP_CONSTRUCT_ONLY, }; /* private data */ struct _DerivedPrivate { unsigned readwrite:1, construct:1, construct_only:1, dispose_has_run:1, :0; }; static GObject * derived_constructor(GType type, guint n_props, GObjectConstructParam *props) { GObject *object; ENTER("enter (%s, %u@%p)", g_type_name(type), n_props, props); object = G_OBJECT_CLASS(derived_parent_class) ->constructor(type, n_props, props); DEBUG("return %p", object); return object; } static void derived_init(Derived *self) { ENTER("enter (%p)", self); self->priv = G_TYPE_INSTANCE_GET_PRIVATE( self, TYPE_DERIVED, DerivedPrivate); } static void derived_get_property(GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { ENTER("enter (%p, %d)", object, property_id); Derived *self = DERIVED(object); DerivedPrivate *priv = self->priv; switch(property_id) { case PROP_READWRITE: g_value_set_boolean(value, priv->readwrite); break; case PROP_CONSTRUCT: g_value_set_boolean(value, priv->construct); break; case PROP_CONSTRUCT_ONLY: g_value_set_boolean(value, priv->construct_only); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); break; } DEBUG("return value=%s", g_strdup_value_contents(value)); } static void derived_set_property(GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { ENTER("enter (%p, %d, %s, %p)", object, property_id, g_strdup_value_contents(value), pspec); Derived *self = DERIVED(object); DerivedPrivate *priv = self->priv; switch(property_id) { case PROP_READWRITE: priv->readwrite = g_value_get_boolean(value); break; case PROP_CONSTRUCT: priv->construct = g_value_get_boolean(value); break; case PROP_CONSTRUCT_ONLY: priv->construct_only = g_value_get_boolean(value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); } DEBUG("return (%p)", object); } static void derived_constructed(GObject *object) { ENTER("(%p): enter", object); if (G_OBJECT_CLASS(derived_parent_class)->constructed) G_OBJECT_CLASS(derived_parent_class)->constructed(object); } static void derived_dispose(GObject *object) { Derived *self = DERIVED(object); DerivedPrivate *priv = self->priv; ENTER("enter (%p): %s", object, priv->dispose_has_run ? "already" : "disposing"); if (priv->dispose_has_run) return; priv->dispose_has_run = TRUE; if (G_OBJECT_CLASS(derived_parent_class)->dispose) G_OBJECT_CLASS(derived_parent_class)->dispose(object); DEBUG("(%p): return from disposing", object); } static void derived_finalize(GObject *object) { Derived *self = DERIVED(object); DerivedPrivate *priv = self->priv; ENTER("enter (%p)", object); (void)priv; G_OBJECT_CLASS(derived_parent_class)->finalize(object); DEBUG("(%p): return", object); } static void derived_class_init(DerivedClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS(klass); ENTER("enter"); g_type_class_add_private(klass, sizeof (DerivedPrivate)); object_class->constructor = derived_constructor; object_class->get_property = derived_get_property; object_class->set_property = derived_set_property; object_class->constructed = derived_constructed; object_class->dispose = derived_dispose; object_class->finalize = derived_finalize; /* No Signals */ /* Properties */ g_object_class_install_property( object_class, PROP_READWRITE, g_param_spec_boolean("derived-readwrite", "Derived readwrite property", "Readwrite property in derived object", TRUE, /* default value */ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property( object_class, PROP_CONSTRUCT, g_param_spec_boolean("derived-construct", "Derived construct property", "Construct property in derived object", TRUE, /* default value */ G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS)); g_object_class_install_property( object_class, PROP_CONSTRUCT_ONLY, g_param_spec_boolean("derived-construct-only", "Derived construct-only property", "Construct-only property in derived object", TRUE, /* default value */ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); DEBUG("return"); } telepathy-ring-2.3.24-37ad7111424725a5446e82dac516d7cab88149c7/modem/tests/derived.h000066400000000000000000000036741251541261300260240ustar00rootroot00000000000000/* * derived.h - * * Copyright (C) 2007-2010 Nokia Corporation * @author Pekka Pessi * * This work is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This work is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this work; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef DERIVED_H #define DERIVED_H #include #include "base.h" G_BEGIN_DECLS typedef struct _Derived Derived; typedef struct _DerivedClass DerivedClass; typedef struct _DerivedPrivate DerivedPrivate; struct _DerivedClass { BaseClass parent_class; }; struct _Derived { Base parent; DerivedPrivate *priv; }; GType derived_get_type(void); /* TYPE MACROS */ #define TYPE_DERIVED \ (derived_get_type()) #define DERIVED(obj) \ (G_TYPE_CHECK_INSTANCE_CAST((obj), TYPE_DERIVED, Derived)) #define DERIVED_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST((klass), TYPE_DERIVED, DerivedClass)) #define IS_DERIVED(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE((obj), TYPE_DERIVED)) #define IS_DERIVED_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE((klass), TYPE_DERIVED)) #define DERIVED_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), TYPE_DERIVED, DerivedClass)) G_END_DECLS #endif /* #ifndef DERIVED_H */ telepathy-ring-2.3.24-37ad7111424725a5446e82dac516d7cab88149c7/modem/tests/test-modem-call-service.c000066400000000000000000000301031251541261300310050ustar00rootroot00000000000000/* * test-modem-call-service.c - Tests cases for the call service * * Copyright (C) 2008-2010 Nokia Corporation * @author Pekka Pessi * * This work is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This work is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this work; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "config.h" #include #include #include "test-modem.h" #include #include #include #include /* Valid digits are 0123456789abc*# */ START_TEST(test_modem_call_validate_address) { GError *error = NULL; fail_if(modem_call_is_valid_address(NULL)); fail_if(modem_call_is_valid_address("")); fail_if(modem_call_validate_address("", &error)); fail_if(error == NULL); g_error_free(error); fail_if(modem_call_validate_address("", NULL)); fail_if(modem_call_validate_address(" ", NULL)); fail_if(modem_call_validate_address("w#123", NULL)); fail_if(!modem_call_is_valid_address("1")); fail_if(!modem_call_is_valid_address("+1")); /* Internation prefix */ fail_if(modem_call_is_valid_address("+")); fail_if(modem_call_is_valid_address("+1+")); /* Almost too long */ fail_if(!modem_call_is_valid_address("+35899123456789012345")); fail_if(!modem_call_is_valid_address("*31#+35899123456789012345")); fail_if(!modem_call_is_valid_address("#31#+35899123456789012345")); /* Too long */ fail_if(modem_call_is_valid_address("+358991234567890123456")); /* Dialstrings */ fail_if(!modem_call_is_valid_address("+358718008000w123#*abc")); fail_if(!modem_call_is_valid_address("+358718008000abc#*w123#*abc")); fail_if(modem_call_is_valid_address("+358718008000abc#*w123#*abcd")); fail_if(!modem_call_is_valid_address("+358718008000p123")); fail_if(modem_call_is_valid_address("+358718008000p")); /* Prefixes only */ fail_if(modem_call_is_valid_address("*31#+")); /* Invalid prefixes */ fail_if(modem_call_is_valid_address("*31##31#+1")); /* Alphanumeric */ fail_if(modem_call_is_valid_address("tilulilu")); fail_if(!modem_call_is_valid_address("abc#*cba")); /* SOS URNs */ fail_if(!modem_call_is_valid_address("urn:service:sos")); fail_if(!modem_call_is_valid_address("URN:seRVICE:SoS")); fail_if(modem_call_is_valid_address("urn:service:sossoo")); fail_if(!modem_call_is_valid_address("urn:service:sos.soo")); } END_TEST static TCase * tcase_for_modem_call_address_validator(void) { TCase *tc = tcase_create("Test for modem call address validation"); tcase_add_checked_fixture(tc, g_type_init, NULL); tcase_add_test(tc, test_modem_call_validate_address); tcase_set_timeout(tc, 5); return tc; } #if XXX /* Speaking Clock in NTN */ char const *destination = /*"+3584544"*/ "99901"; static GMainLoop *mainloop = NULL; static void setup(void) { if (getenv("TEST_MODEM_CALL_DESTINATION")) destination = getenv("TEST_MODEM_CALL_DESTINATION"); g_type_init(); (void)dbus_g_bus_get(DBUS_BUS_SYSTEM, NULL); mainloop = g_main_loop_new (NULL, FALSE); } static void teardown(void) { g_main_loop_unref(mainloop), mainloop = NULL; } static GError *reply_error = NULL; static ModemCall *created; static void reply_to_dial_request(ModemCallService *call_service, ModemRequest *request, ModemCall *ci, GError *error, gpointer user_data) { if (error) reply_error = g_error_copy(error); else g_clear_error(&reply_error); created = ci; g_main_loop_quit(mainloop); } static void reply_to_call_service_request(ModemCallService *call_service, ModemRequest *request, GError *error, gpointer user_data) { if (error) reply_error = g_error_copy(error); else g_clear_error(&reply_error); g_main_loop_quit(mainloop); } static void reply_to_call_request(ModemCall *call, ModemRequest *request, GError *error, gpointer user_data) { if (error) reply_error = g_error_copy(error); else g_clear_error(&reply_error); g_main_loop_quit(mainloop); } static int got_call_connected; static void on_call_connected(ModemCallService *call_service, gpointer user_data) { got_call_connected++; g_main_loop_quit(mainloop); } #if nomore static void on_emergency_numbers(ModemCallService *call_service, char **strv, gpointer user_data) { char ***return_numbers = user_data; *return_numbers = g_strdupv(strv); g_main_loop_quit(mainloop); } #endif static int got_call_state; ModemCall *state; static void on_call_state(ModemCallService *call_service, gpointer user_data) { got_call_state++; g_main_loop_quit(mainloop); } START_TEST(modem_call_api) { GError *error; ModemCallService *call_service; ModemCall **calls; ModemRequest *request; char **strv; char *emergency_before, *emergency_after; #if nomore char **updated; char *add[] = { "012", "987", NULL }; #endif guint i; fail_if(modem_call_service_is_connected(NULL)); fail_if(modem_call_service_is_connecting(NULL)); call_service = g_object_new(MODEM_TYPE_CALL_SERVICE, NULL); fail_unless(call_service != NULL); error = NULL; fail_if(modem_call_service_is_connected(call_service)); fail_if(modem_call_service_is_connecting(call_service)); strv = (gpointer)modem_call_get_emergency_numbers(call_service); fail_unless(strv != NULL); emergency_before = g_strjoinv(" ", strv); fail_unless(strcmp("112 911 118 119 000 110 08 999", emergency_before) == 0); g_signal_connect(call_service, "connected", G_CALLBACK(on_call_connected), &got_call_connected); fail_unless(modem_call_service_connect(call_service, "/")); fail_unless(modem_call_service_is_connecting(call_service)); g_main_loop_run(mainloop); fail_unless(modem_call_service_is_connected(call_service)); strv = (gpointer)modem_call_get_emergency_numbers(call_service); fail_unless(strv != NULL); emergency_after = g_strjoinv(" ", strv); fail_unless(strcmp(emergency_before, emergency_after) != 0); g_free(emergency_before), g_free(emergency_after); g_clear_error(&reply_error); #if nomore modem_call_remove_emergency_numbers(call_service, reply_to_call_service_request, &reply_error); g_main_loop_run(mainloop); g_clear_error(&reply_error); strv = (gpointer)modem_call_get_emergency_numbers(call_service); fail_unless(strv != NULL); emergency_before = g_strjoinv(" ", strv); updated = NULL; g_signal_connect(call_service, "emergency-numbers-changed", G_CALLBACK(on_emergency_numbers), &updated); modem_call_add_emergency_numbers(call_service, (gpointer)add, NULL, NULL); g_main_loop_run(mainloop); fail_unless(updated != NULL); emergency_after = g_strjoinv(" ", updated); fail_unless(strcmp(emergency_before, emergency_after)); g_free(emergency_before); emergency_before = emergency_after; g_strfreev(updated), updated = NULL; modem_call_remove_emergency_numbers(call_service, NULL, NULL); g_main_loop_run(mainloop); fail_unless(updated != NULL); emergency_after = g_strjoinv(" ", updated); fail_unless(strcmp(emergency_before, emergency_after)); g_free(emergency_before), g_free(emergency_after); g_strfreev(updated), updated = NULL; #endif calls = modem_call_service_get_calls(call_service); fail_unless(calls != NULL); for (i = 0; calls[i]; i++) { g_signal_connect(calls[i], "state", G_CALLBACK(on_call_state), NULL); } for (i = 0; calls[i]; i++) { if (modem_call_get_state(calls[i])) { request = modem_call_request_release(calls[i], reply_to_call_request, &reply_error); g_main_loop_run(mainloop); fail_unless(reply_error == NULL); g_main_loop_run(mainloop); } } for (i = 0; calls[i]; i++) { if (modem_call_get_state(calls[i])) { g_main_loop_run(mainloop); } } g_free(calls), calls = NULL; fail_unless(modem_call_service_connect(call_service, "/")); fail_unless(!modem_call_service_is_connecting(call_service)); request = modem_call_request_conference( call_service, reply_to_call_service_request, &reply_error); g_main_loop_run(mainloop); fail_unless(reply_error != NULL); g_clear_error(&reply_error); request = modem_call_request_dial( call_service, "tilulilu", 0, reply_to_dial_request, &reply_error); g_main_loop_run(mainloop); fail_unless(reply_error != NULL); g_clear_error(&reply_error); request = modem_call_request_dial( call_service, "tilulilu", 0, reply_to_dial_request, &reply_error); modem_request_cancel(request); request = modem_call_request_dial( call_service, destination, 0, reply_to_dial_request, &reply_error); g_main_loop_run(mainloop); if (!reply_error) { fail_unless(created != NULL); while (modem_call_get_state(created) < MODEM_CALL_STATE_ACTIVE) g_main_loop_run(mainloop); request = modem_call_request_release(created, reply_to_call_request, &reply_error); g_main_loop_run(mainloop); fail_unless(reply_error == NULL); while (modem_call_get_state(created) != 0) g_main_loop_run(mainloop); created = NULL; } got_call_connected = 0; modem_call_service_disconnect(call_service); fail_unless(got_call_connected); g_object_unref(call_service); } END_TEST START_TEST(modem_call_api2) { ModemCallService *call_service; call_service = g_object_new(MODEM_TYPE_CALL_SERVICE, NULL); fail_unless(call_service != NULL); fail_unless(modem_call_service_connect(call_service, "/")); modem_call_service_disconnect(call_service); fail_if(modem_call_service_is_connected(call_service)); fail_if(modem_call_service_is_connecting(call_service)); g_object_unref(call_service); call_service = g_object_new(MODEM_TYPE_CALL_SERVICE, NULL); fail_unless(call_service != NULL); fail_unless(modem_call_service_connect(call_service, "/")); g_object_run_dispose(G_OBJECT(call_service)); fail_if(modem_call_service_is_connected(call_service)); fail_if(modem_call_service_is_connecting(call_service)); g_object_unref(call_service); call_service = g_object_new(MODEM_TYPE_CALL_SERVICE, NULL); fail_unless(call_service != NULL); g_object_run_dispose(G_OBJECT(call_service)); fail_if(modem_call_service_connect(call_service, "/")); fail_if(modem_call_service_is_connected(call_service)); fail_if(modem_call_service_is_connecting(call_service)); g_object_unref(call_service); } END_TEST #include "modem/call-service.c" START_TEST(modem_call_internal) { ModemCallService *call_service; call_service = g_object_new(MODEM_TYPE_CALL_SERVICE, NULL); fail_unless(call_service != NULL); fail_if(modem_call_service_is_connected(call_service)); fail_if(modem_call_service_is_connecting(call_service)); g_signal_connect(call_service, "connected", G_CALLBACK(on_call_connected), &got_call_connected); fail_unless(modem_call_service_connect(call_service, "/")); fail_unless(modem_call_service_is_connecting(call_service)); g_main_loop_run(mainloop); fail_unless(modem_call_service_is_connected(call_service)); got_call_connected = 0; modem_call_service_disconnect(call_service); fail_unless(got_call_connected != 0); g_object_unref(call_service); } END_TEST #endif static TCase * tcase_for_modem_call_service(void) { TCase *tc = tcase_create("Test for ModemCallService"); #if XXX tcase_add_checked_fixture(tc, setup, teardown); tcase_add_test(tc, modem_call_api); tcase_add_test(tc, modem_call_api2); tcase_add_test(tc, modem_call_internal); #endif tcase_set_timeout(tc, 60); return tc; } /* ====================================================================== */ struct test_cases modem_call_service_tcases[] = { DECLARE_TEST_CASE(tcase_for_modem_call_address_validator), DECLARE_TEST_CASE_OFF_BY_DEFAULT(tcase_for_modem_call_service), LAST_TEST_CASE }; telepathy-ring-2.3.24-37ad7111424725a5446e82dac516d7cab88149c7/modem/tests/test-modem-call.c000066400000000000000000000404341251541261300273570ustar00rootroot00000000000000/* * test-modem-call.c - Tests cases for call instances * * Copyright (C) 2008-2010 Nokia Corporation * @author Pekka Pessi * * This work is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This work is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this work; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "config.h" #include #include #include #include #include #include "test-modem.h" #include #include #include static void setup(void) { g_type_init(); (void)dbus_g_bus_get(DBUS_BUS_SYSTEM, NULL); modem_service(); } static void teardown(void) { } START_TEST(modem_call_properties) { ModemCall *ci = g_object_new(MODEM_TYPE_CALL, "object-path", "/path", "call-service", NULL, NULL); ModemCallService *client = (gpointer)-1; char *remote = (gpointer)-1; char *emergency = (gpointer)-1; unsigned state = (unsigned)-1; gboolean originating = (gboolean)-1, terminating = (gboolean)-1; gboolean onhold = (gboolean)-1, member = (gboolean)-1; g_object_get(ci, "call-service", &client, "remote", &remote, "state", &state, "originating", &originating, "terminating", &terminating, "emergency", &emergency, "onhold", &onhold, "multiparty", &member, NULL); fail_if(client == (gpointer)-1); fail_if(remote == (gpointer)-1); fail_if(state == (unsigned)-1); fail_if(originating == (gboolean)-1); fail_if(terminating == (gboolean)-1); fail_if(emergency == (gpointer)-1); fail_if(onhold == (gboolean)-1); fail_if(member == (gboolean)-1); fail_unless(client == NULL); fail_unless(remote == NULL); fail_unless(state == MODEM_CALL_STATE_INVALID); fail_unless(originating == FALSE); fail_unless(terminating == FALSE); fail_unless(emergency == NULL); fail_unless(onhold == FALSE); fail_unless(member == FALSE); g_free(remote); #if XXX g_object_set(ci, "remote", "99001", "state", MODEM_CALL_STATE_ACTIVE, "originating", 1, "terminating", 0, "emergency", "urn:service:sos", "onhold", 1, "multiparty", 1, NULL); g_object_get(ci, "remote", &remote, "state", &state, "originating", &originating, "terminating", &terminating, "emergency", &emergency, "onhold", &onhold, "multiparty", &member, NULL); fail_unless(remote && strcmp(remote, "99001") == 0); fail_unless(state == MODEM_CALL_STATE_ACTIVE); fail_unless(originating == TRUE); fail_unless(terminating == FALSE); fail_unless(emergency && strcmp(emergency, "urn:service:sos") == 0); fail_unless(onhold == TRUE); fail_unless(member == TRUE); g_free(remote); g_free(emergency); #endif } END_TEST static TCase * modem_call_tcase(void) { TCase *tc = tcase_create("Test for ModemCall"); tcase_add_checked_fixture(tc, setup, teardown); tcase_add_test(tc, modem_call_properties); tcase_set_timeout(tc, 5); return tc; } /* ====================================================================== */ START_TEST(test_modem_error_fix) { GError *error = NULL, *unfixed; modem_error_fix(&error); fail_unless(error == NULL); error = unfixed = g_error_new(DBUS_GERROR, DBUS_GERROR_REMOTE_EXCEPTION, "message%c%s.%s", '\0', "org.ofono.Bogus.Error", "Generic"); modem_error_fix(&error); fail_unless(error == unfixed); g_clear_error(&error); error = unfixed = g_error_new(DBUS_GERROR, DBUS_GERROR_REMOTE_EXCEPTION, "message%c%s.%s", '\0', "org.ofono.Error", "NoSuchError"); modem_error_fix(&error); fail_unless(error == unfixed); g_clear_error(&error); error = unfixed = g_error_new(DBUS_GERROR, DBUS_GERROR_REMOTE_EXCEPTION, "message%c%s.%s", '\0', "org.ofono.Error", "Failed"); modem_error_fix(&error); fail_unless(error != unfixed); g_clear_error(&error); } END_TEST START_TEST(test_call_error_conversion) { GError *error, *dbus, *fixed; guint causetype, cause; char ebuffer[16]; for (causetype = 1; causetype <= 4; causetype++) { for (cause = 0; cause < 128; cause++) { error = modem_call_new_error(causetype, cause, "test"); fail_unless(error != NULL); fail_if(modem_error_name(error, ebuffer, sizeof ebuffer) == ebuffer); dbus = g_error_new(DBUS_GERROR, DBUS_GERROR_REMOTE_EXCEPTION, "%s%c%s.%s", error->message, '\0', modem_error_domain_prefix(error->domain), modem_error_name(error, ebuffer, sizeof ebuffer)); fail_unless(dbus != NULL); fixed = dbus; modem_error_fix(&fixed); fail_if(fixed == dbus); fail_if(fixed->domain != error->domain); fail_if(fixed->code != error->code); g_clear_error(&error); g_clear_error(&fixed); } } } END_TEST START_TEST(test_modem_error_name) { GError *error; char ebuffer[16]; char const *name; name = modem_error_name(NULL, ebuffer, sizeof ebuffer); fail_unless(name == ebuffer); error = g_error_new(MODEM_CALL_ERRORS, 1024, "message"); name = modem_error_name(error, ebuffer, sizeof ebuffer); fail_unless(name == ebuffer); g_clear_error(&error); } END_TEST #define tst(x) { \ error.code = x; \ fail_if(modem_error_name(&error, buffer, sizeof buffer) == buffer); \ remote = g_error_new(DBUS_GERROR, DBUS_GERROR_REMOTE_EXCEPTION, \ "message%c%s.%s", '\0', \ modem_error_domain_prefix(error.domain), \ modem_error_name(&error, NULL, 0)); \ fail_if(remote == NULL); \ fixed = remote; \ modem_error_fix(&fixed); \ fail_if(remote == fixed, "fixing " #x); \ fail_if(fixed->domain != error.domain); \ fail_if(fixed->code != x); \ } while(0) START_TEST(test_modem_call_error_prefix) { GError error = { MODEM_CALL_ERRORS, 0, "error" }; GError *remote, *fixed; char buffer[16]; tst(MODEM_CALL_ERROR_NO_ERROR); tst(MODEM_CALL_ERROR_NO_CALL); tst(MODEM_CALL_ERROR_RELEASE_BY_USER); tst(MODEM_CALL_ERROR_BUSY_USER_REQUEST); tst(MODEM_CALL_ERROR_ERROR_REQUEST); tst(MODEM_CALL_ERROR_CALL_ACTIVE); tst(MODEM_CALL_ERROR_NO_CALL_ACTIVE); tst(MODEM_CALL_ERROR_INVALID_CALL_MODE); tst(MODEM_CALL_ERROR_TOO_LONG_ADDRESS); tst(MODEM_CALL_ERROR_INVALID_ADDRESS); tst(MODEM_CALL_ERROR_EMERGENCY); tst(MODEM_CALL_ERROR_NO_SERVICE); tst(MODEM_CALL_ERROR_NO_COVERAGE); tst(MODEM_CALL_ERROR_CODE_REQUIRED); tst(MODEM_CALL_ERROR_NOT_ALLOWED); tst(MODEM_CALL_ERROR_DTMF_ERROR); tst(MODEM_CALL_ERROR_CHANNEL_LOSS); tst(MODEM_CALL_ERROR_FDN_NOT_OK); tst(MODEM_CALL_ERROR_BLACKLIST_BLOCKED); tst(MODEM_CALL_ERROR_BLACKLIST_DELAYED); tst(MODEM_CALL_ERROR_EMERGENCY_FAILURE); tst(MODEM_CALL_ERROR_NO_SIM); tst(MODEM_CALL_ERROR_DTMF_SEND_ONGOING); tst(MODEM_CALL_ERROR_CS_INACTIVE); tst(MODEM_CALL_ERROR_NOT_READY); tst(MODEM_CALL_ERROR_INCOMPATIBLE_DEST); fail_if(modem_error_domain_prefix(error.domain) == NULL); fail_if(strcmp(MODEM_CALL_ERROR_PREFIX, modem_error_domain_prefix(error.domain))); } END_TEST START_TEST(test_modem_call_error) { GError *error; int causes[] = { MODEM_CALL_ERROR_NO_CALL, MODEM_CALL_ERROR_RELEASE_BY_USER, MODEM_CALL_ERROR_BUSY_USER_REQUEST, MODEM_CALL_ERROR_ERROR_REQUEST, MODEM_CALL_ERROR_CALL_ACTIVE, MODEM_CALL_ERROR_NO_CALL_ACTIVE, MODEM_CALL_ERROR_INVALID_CALL_MODE, MODEM_CALL_ERROR_TOO_LONG_ADDRESS, MODEM_CALL_ERROR_INVALID_ADDRESS, MODEM_CALL_ERROR_EMERGENCY, MODEM_CALL_ERROR_NO_SERVICE, MODEM_CALL_ERROR_NO_COVERAGE, MODEM_CALL_ERROR_CODE_REQUIRED, MODEM_CALL_ERROR_NOT_ALLOWED, MODEM_CALL_ERROR_DTMF_ERROR, MODEM_CALL_ERROR_CHANNEL_LOSS, MODEM_CALL_ERROR_FDN_NOT_OK, MODEM_CALL_ERROR_BLACKLIST_BLOCKED, MODEM_CALL_ERROR_BLACKLIST_DELAYED, MODEM_CALL_ERROR_EMERGENCY_FAILURE, MODEM_CALL_ERROR_NO_SIM, MODEM_CALL_ERROR_DTMF_SEND_ONGOING, MODEM_CALL_ERROR_CS_INACTIVE, MODEM_CALL_ERROR_NOT_READY, MODEM_CALL_ERROR_INCOMPATIBLE_DEST, MODEM_CALL_ERROR_NO_ERROR, }; guint i = 0; for (i = 0; causes[i]; i++) { error = modem_call_new_error(MODEM_CALL_CAUSE_TYPE_LOCAL, causes[i], "kuik"); fail_if(error == NULL); fail_if(error->domain != MODEM_CALL_ERRORS); fail_if(error->code != causes[i]); fail_if(error->message == NULL || strlen(error->message) < 1); fail_if(strncmp(error->message, "kuik: ", 6)); g_clear_error(&error); } /* Try unknown cause type */ error = modem_call_new_error(4, 1, "kuik"); fail_if(error == NULL); fail_if(error->domain != MODEM_CALL_ERRORS); fail_if(error->code != MODEM_CALL_ERROR_GENERIC); fail_if(error->message == NULL || strlen(error->message) < 1); fail_if(strncmp(error->message, "kuik: ", 6)); g_clear_error(&error); /* Try unknown cause code (and no prefix) */ error = modem_call_new_error(MODEM_CALL_CAUSE_TYPE_LOCAL, 255, NULL); fail_if(error == NULL); fail_if(error->domain != MODEM_CALL_ERRORS); fail_if(error->code != MODEM_CALL_ERROR_GENERIC); fail_if(error->message == NULL || strlen(error->message) < 1); g_clear_error(&error); } END_TEST START_TEST(test_modem_call_net_error_prefix) { GError error = { MODEM_CALL_NET_ERRORS, 0, "error" }; GError *remote, *fixed; char buffer[16]; /* Try to re-register MODEM_CALL_NET_ERRORS */ modem_error_register_mapping(MODEM_CALL_NET_ERRORS, MODEM_CALL_NET_ERROR_PREFIX, MODEM_TYPE_CALL_NET_ERROR); fail_if(modem_error_domain_prefix(error.domain) == NULL); fail_if(strcmp(MODEM_CALL_NET_ERROR_PREFIX, modem_error_domain_prefix(error.domain))); tst(MODEM_CALL_NET_ERROR_UNASSIGNED_NUMBER); tst(MODEM_CALL_NET_ERROR_NO_ROUTE); tst(MODEM_CALL_NET_ERROR_CH_UNACCEPTABLE); tst(MODEM_CALL_NET_ERROR_OPER_BARRING); tst(MODEM_CALL_NET_ERROR_NORMAL); tst(MODEM_CALL_NET_ERROR_USER_BUSY); tst(MODEM_CALL_NET_ERROR_NO_USER_RESPONSE); tst(MODEM_CALL_NET_ERROR_ALERT_NO_ANSWER); tst(MODEM_CALL_NET_ERROR_CALL_REJECTED); tst(MODEM_CALL_NET_ERROR_NUMBER_CHANGED); tst(MODEM_CALL_NET_ERROR_NON_SELECT_CLEAR); tst(MODEM_CALL_NET_ERROR_DEST_OUT_OF_ORDER); tst(MODEM_CALL_NET_ERROR_INVALID_NUMBER); tst(MODEM_CALL_NET_ERROR_FACILITY_REJECTED); tst(MODEM_CALL_NET_ERROR_RESP_TO_STATUS); tst(MODEM_CALL_NET_ERROR_NORMAL_UNSPECIFIED); tst(MODEM_CALL_NET_ERROR_NO_CHANNEL); tst(MODEM_CALL_NET_ERROR_NETW_OUT_OF_ORDER); tst(MODEM_CALL_NET_ERROR_TEMPORARY_FAILURE); tst(MODEM_CALL_NET_ERROR_CONGESTION); tst(MODEM_CALL_NET_ERROR_ACCESS_INFO_DISC); tst(MODEM_CALL_NET_ERROR_CHANNEL_NA); tst(MODEM_CALL_NET_ERROR_RESOURCES_NA); tst(MODEM_CALL_NET_ERROR_QOS_NA); tst(MODEM_CALL_NET_ERROR_FACILITY_UNSUBS); tst(MODEM_CALL_NET_ERROR_COMING_BARRED_CUG); tst(MODEM_CALL_NET_ERROR_BC_UNAUTHORIZED); tst(MODEM_CALL_NET_ERROR_BC_NA); tst(MODEM_CALL_NET_ERROR_SERVICE_NA); tst(MODEM_CALL_NET_ERROR_BEARER_NOT_IMPL); tst(MODEM_CALL_NET_ERROR_ACM_MAX); tst(MODEM_CALL_NET_ERROR_FACILITY_NOT_IMPL); tst(MODEM_CALL_NET_ERROR_ONLY_RDI_BC); tst(MODEM_CALL_NET_ERROR_SERVICE_NOT_IMPL); tst(MODEM_CALL_NET_ERROR_INVALID_TI); tst(MODEM_CALL_NET_ERROR_NOT_IN_CUG); tst(MODEM_CALL_NET_ERROR_INCOMPATIBLE_DEST); tst(MODEM_CALL_NET_ERROR_INV_TRANS_NET_SEL); tst(MODEM_CALL_NET_ERROR_SEMANTICAL_ERR); tst(MODEM_CALL_NET_ERROR_INVALID_MANDATORY); tst(MODEM_CALL_NET_ERROR_MSG_TYPE_INEXIST); tst(MODEM_CALL_NET_ERROR_MSG_TYPE_INCOMPAT); tst(MODEM_CALL_NET_ERROR_IE_NON_EXISTENT); tst(MODEM_CALL_NET_ERROR_COND_IE_ERROR); tst(MODEM_CALL_NET_ERROR_MSG_INCOMPATIBLE); tst(MODEM_CALL_NET_ERROR_TIMER_EXPIRY); tst(MODEM_CALL_NET_ERROR_PROTOCOL_ERROR); tst(MODEM_CALL_NET_ERROR_INTERWORKING); } END_TEST START_TEST(test_modem_call_net_error) { GError *error; int causes[] = { MODEM_CALL_NET_ERROR_UNASSIGNED_NUMBER, MODEM_CALL_NET_ERROR_NO_ROUTE, MODEM_CALL_NET_ERROR_CH_UNACCEPTABLE, MODEM_CALL_NET_ERROR_OPER_BARRING, MODEM_CALL_NET_ERROR_NORMAL, MODEM_CALL_NET_ERROR_USER_BUSY, MODEM_CALL_NET_ERROR_NO_USER_RESPONSE, MODEM_CALL_NET_ERROR_ALERT_NO_ANSWER, MODEM_CALL_NET_ERROR_CALL_REJECTED, MODEM_CALL_NET_ERROR_NUMBER_CHANGED, MODEM_CALL_NET_ERROR_NON_SELECT_CLEAR, MODEM_CALL_NET_ERROR_DEST_OUT_OF_ORDER, MODEM_CALL_NET_ERROR_INVALID_NUMBER, MODEM_CALL_NET_ERROR_FACILITY_REJECTED, MODEM_CALL_NET_ERROR_RESP_TO_STATUS, MODEM_CALL_NET_ERROR_NORMAL_UNSPECIFIED, MODEM_CALL_NET_ERROR_NO_CHANNEL, MODEM_CALL_NET_ERROR_NETW_OUT_OF_ORDER, MODEM_CALL_NET_ERROR_TEMPORARY_FAILURE, MODEM_CALL_NET_ERROR_CONGESTION, MODEM_CALL_NET_ERROR_ACCESS_INFO_DISC, MODEM_CALL_NET_ERROR_CHANNEL_NA, MODEM_CALL_NET_ERROR_RESOURCES_NA, MODEM_CALL_NET_ERROR_QOS_NA, MODEM_CALL_NET_ERROR_FACILITY_UNSUBS, MODEM_CALL_NET_ERROR_COMING_BARRED_CUG, MODEM_CALL_NET_ERROR_BC_UNAUTHORIZED, MODEM_CALL_NET_ERROR_BC_NA, MODEM_CALL_NET_ERROR_SERVICE_NA, MODEM_CALL_NET_ERROR_BEARER_NOT_IMPL, MODEM_CALL_NET_ERROR_ACM_MAX, MODEM_CALL_NET_ERROR_FACILITY_NOT_IMPL, MODEM_CALL_NET_ERROR_ONLY_RDI_BC, MODEM_CALL_NET_ERROR_SERVICE_NOT_IMPL, MODEM_CALL_NET_ERROR_INVALID_TI, MODEM_CALL_NET_ERROR_NOT_IN_CUG, MODEM_CALL_NET_ERROR_INCOMPATIBLE_DEST, MODEM_CALL_NET_ERROR_INV_TRANS_NET_SEL, MODEM_CALL_NET_ERROR_SEMANTICAL_ERR, MODEM_CALL_NET_ERROR_INVALID_MANDATORY, MODEM_CALL_NET_ERROR_MSG_TYPE_INEXIST, MODEM_CALL_NET_ERROR_MSG_TYPE_INCOMPAT, MODEM_CALL_NET_ERROR_IE_NON_EXISTENT, MODEM_CALL_NET_ERROR_COND_IE_ERROR, MODEM_CALL_NET_ERROR_MSG_INCOMPATIBLE, MODEM_CALL_NET_ERROR_TIMER_EXPIRY, MODEM_CALL_NET_ERROR_PROTOCOL_ERROR, MODEM_CALL_NET_ERROR_INTERWORKING, 0 }; guint i; for (i = 0; causes[i]; i++) { error = modem_call_new_error(MODEM_CALL_CAUSE_TYPE_NETWORK, causes[i], "kuik"); fail_if(error == NULL); fail_if(error->domain != MODEM_CALL_NET_ERRORS); fail_if(error->code != causes[i]); fail_if(error->message == NULL || strlen(error->message) < 1); fail_if(strncmp(error->message, "kuik: ", 6)); g_clear_error(&error); } /* Try unknown cause code */ error = modem_call_new_error(MODEM_CALL_CAUSE_TYPE_NETWORK, 126, "kuik"); fail_if(error == NULL); fail_if(error->domain != MODEM_CALL_NET_ERRORS); fail_if(error->code != MODEM_CALL_NET_ERROR_INTERWORKING); fail_if(error->message == NULL || strlen(error->message) < 1); fail_if(strncmp(error->message, "kuik: ", 6)); g_clear_error(&error); } END_TEST START_TEST(test_modem_call_state) { guint i; for (i = 0; i <= MODEM_CALL_STATE_DISCONNECTED; i++) { fail_unless(strcmp(modem_call_get_state_name(i), "UNKNOWN")); } fail_if(strcmp(modem_call_get_state_name(i), "UNKNOWN")); } END_TEST static TCase * modem_call_error_tcase(void) { TCase *tc = tcase_create("Test for ModemCallError"); tcase_add_checked_fixture(tc, setup, teardown); tcase_add_test(tc, test_modem_error_fix); tcase_add_test(tc, test_call_error_conversion); tcase_add_test(tc, test_modem_error_name); tcase_add_test(tc, test_modem_call_error_prefix); tcase_add_test(tc, test_modem_call_error); tcase_add_test(tc, test_modem_call_net_error_prefix); tcase_add_test(tc, test_modem_call_net_error); tcase_add_test(tc, test_modem_call_state); tcase_set_timeout(tc, 5); return tc; } /* ====================================================================== */ struct test_cases modem_call_tcases[] = { DECLARE_TEST_CASE(modem_call_error_tcase), DECLARE_TEST_CASE(modem_call_tcase), LAST_TEST_CASE }; telepathy-ring-2.3.24-37ad7111424725a5446e82dac516d7cab88149c7/modem/tests/test-modem-request.c000066400000000000000000000302031251541261300301250ustar00rootroot00000000000000/* * test-modem-request.c - Test cases for ModemRequest * * Copyright (C) 2008-2010 Nokia Corporation * @author Pekka Pessi * * This work is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This work is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this work; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "config.h" #include #include "modem/request.c" #include "modem/ofono.h" #include "modem/service.h" #include "test-modem.h" #include #include #include #include GMainLoop *mainloop; static void setup(void) { if (getenv("SBOX_UNAME_MACHINE")) setenv("DBUS_LOOPBACK", "1", 0); g_type_init(); mainloop = g_main_loop_new (NULL, FALSE); (void)dbus_g_bus_get(DBUS_BUS_SYSTEM, NULL); } static void teardown(void) { g_main_loop_unref(mainloop), mainloop = NULL; } static void weaknotify(gpointer user_data, GObject *object) { *(GObject **)user_data = NULL; } static void callback(gpointer _object) { (void)_object; } static void unref_object(gpointer _object) { fail_unless(G_IS_OBJECT(_object)); g_object_unref(_object); } static void unref_proxy(gpointer _proxy) { fail_unless(DBUS_IS_G_PROXY(_proxy)); g_object_unref(_proxy); } static void callback_to_timeout(DBusGProxy *proxy, DBusGProxyCall *call, void *_request) { g_assert(proxy == NULL); } static unsigned cancel_notified; static void cancel_notify(gpointer _request) { cancel_notified++; modem_request_cancel(_request); } START_TEST(make_call_request) { GObject *object = g_object_new(G_TYPE_OBJECT, NULL); DBusGProxy *proxy; proxy = dbus_g_proxy_new_for_name (dbus_g_bus_get (DBUS_BUS_SYSTEM, NULL), OFONO_BUS_NAME, "/", MODEM_OFACE_MANAGER); (void)callback_to_timeout; g_object_weak_ref(object, weaknotify, &object); g_object_weak_ref(G_OBJECT(proxy), weaknotify, &proxy); fail_if(!object); fail_if(!proxy); ModemRequest *request = _modem_request_new( object, proxy, G_CALLBACK(callback), &object); ModemRequestPrivate *priv; fail_if(!request); fail_if(!(priv = request->priv)); fail_unless(priv->object == object); fail_unless(priv->callback == G_CALLBACK(callback)); fail_unless(priv->user_data == &object); fail_unless(priv->proxy == proxy); fail_unless(priv->call == NULL); fail_unless(modem_request_object(request) == object); fail_unless(modem_request_callback(request) == callback); fail_unless(modem_request_user_data(request) == &object); modem_request_add_cancel_notify(request, cancel_notify); g_object_unref(object); g_object_unref(proxy); fail_if(!object); fail_if(!proxy); modem_request_cancel(request); fail_unless(!object); fail_unless(!proxy); } END_TEST GError *return_error; static void reply_to_invalid(DBusGProxy *proxy, DBusGProxyCall *call, void *_request) { fail_unless(!dbus_g_proxy_end_call(proxy, call, &return_error, G_TYPE_INVALID)); g_main_loop_quit(mainloop); } START_TEST(make_request_to_invalid) { GObject *object = g_object_new(G_TYPE_OBJECT, NULL); DBusGProxy *proxy = dbus_g_proxy_new_for_name( dbus_g_bus_get(DBUS_BUS_SYSTEM, NULL), "com.nokia.invalid.server", "/invalid/server", "com.nokia.invalid.Server"); fail_if(!object); fail_if(!proxy); g_object_weak_ref(object, weaknotify, &object); g_object_weak_ref(G_OBJECT(proxy), weaknotify, &proxy); ModemRequest *request = modem_request_begin( object, proxy, "invalid", reply_to_invalid, G_CALLBACK(callback), &object, G_TYPE_STRING, "kuik", G_TYPE_INVALID); ModemRequestPrivate *priv; fail_if(!request); fail_if(!(priv = request->priv)); fail_unless(priv->object == object); fail_unless(priv->callback == G_CALLBACK(callback)); fail_unless(priv->user_data == &object); fail_unless(priv->proxy == proxy); fail_unless(priv->call != NULL); fail_unless(modem_request_object(request) == object); fail_unless(modem_request_callback(request) == callback); fail_unless(modem_request_user_data(request) == &object); g_main_loop_run(mainloop); fail_unless(return_error != NULL); g_clear_error(&return_error); g_message("You will get a SIGSEGV (process:%u) if you have dbus-glib < 0.88", (unsigned)getpid()); request = modem_request_begin(object, proxy, "invalid", reply_to_invalid, G_CALLBACK(callback), NULL, G_TYPE_STRING, "kuik", G_TYPE_INVALID); fail_if(!request); g_object_unref(object); fail_if(!object); g_object_unref(proxy); fail_if(!proxy); modem_request_cancel(request); fail_unless(!object); fail_unless(!proxy); } END_TEST static gpointer notify_data; static void notified(gpointer user_data) { notify_data = user_data; } START_TEST(notify_in_call_request) { GObject *object = g_object_new(G_TYPE_OBJECT, NULL); DBusGProxy *proxy; proxy = dbus_g_proxy_new_for_name (dbus_g_bus_get (DBUS_BUS_SYSTEM, NULL), OFONO_BUS_NAME, "/", MODEM_OFACE_MANAGER); g_object_weak_ref(object, weaknotify, &object); g_object_weak_ref(G_OBJECT(proxy), weaknotify, &proxy); fail_if(!object); fail_if(!proxy); ModemRequest *request = _modem_request_new( object, proxy, G_CALLBACK(callback), object); ModemRequestPrivate *priv; gpointer data; fail_if(!request); fail_if(!(priv = request->priv)); data = modem_request_get_data(request, "request-data"); fail_if(data != NULL); GQuark quark = g_quark_from_string("data"); fail_unless(modem_request_get_qdata(request, quark) == NULL); fail_unless(modem_request_steal_qdata(request, quark) == NULL); /* Add some data */ modem_request_add_data(request, "data", weaknotify); modem_request_add_qdata(request, quark, weaknotify); modem_request_add_qdatas(request, quark, weaknotify, notified, quark, weaknotify, NULL, NULL); fail_unless(notify_data == weaknotify); notify_data = NULL; fail_unless(modem_request_get_data(request, "atad") == NULL); modem_request_add_data_full(request, "data", weaknotify, notified); modem_request_add_data_full(request, "data", weaknotify, NULL); fail_unless(notify_data == weaknotify); notify_data = NULL; fail_unless(modem_request_steal_data(request, "atad") == NULL); struct _ModemRequestNotify *notify = request->priv->notify; fail_if(notify[0].quark != GUINT_TO_POINTER(quark)); fail_if(notify[0].data != weaknotify); modem_request_add_notifys(request, unref_proxy, proxy, NULL); notify = request->priv->notify; fail_if(notify[1].destroy != unref_proxy); fail_if(notify[1].data != proxy); modem_request_add_notifys(request, unref_object, object, unref_object, g_object_ref(object), NULL); notify = request->priv->notify; fail_if(notify[2].destroy != unref_object); fail_if(notify[2].data != object); fail_if(notify[3].destroy != unref_object); fail_if(notify[3].data != object); data = modem_request_get_data(request, "data"); fail_if(data != weaknotify); modem_request_add_notifys(request, unref_proxy, g_object_ref(proxy), NULL); notify = request->priv->notify; fail_if(notify[4].destroy != unref_proxy); fail_if(notify[4].data != proxy); modem_request_add_notifys(request, unref_proxy, g_object_ref(proxy), NULL); notify = request->priv->notify; fail_if(notify[5].destroy != unref_proxy); fail_if(notify[5].data != proxy); data = modem_request_steal_data(request, "data"); fail_if(data != weaknotify); data = modem_request_steal_data(request, "data"); fail_if(data != NULL); modem_request_cancel(request); fail_unless(!object); fail_unless(!proxy); } END_TEST static TCase * tcase_for_modem_request(void) { TCase *tc = tcase_create("Test for ModemRequest"); tcase_add_checked_fixture(tc, setup, teardown); tcase_add_test(tc, make_call_request); tcase_add_test(tc, make_request_to_invalid); tcase_add_test(tc, notify_in_call_request); tcase_set_timeout(tc, 5); return tc; } /* ====================================================================== */ /* Test driver for glib objects */ #define CHECK_DEBUG_BASE 0 #define CHECK_DEBUG_DERIVED 0 #include "base.h" #include "derived.h" static int n_tags; static char const *tags[1024]; void checktag(char const *tag) { fail_if(n_tags >= G_N_ELEMENTS(tags)); tags[n_tags++] = tag; } #define check_step(n) \ fail_if(strcmp(tags[i++], #n), "Expected " #n) START_TEST(g_object_assumptions) { int i = 0; n_tags = 0; GType type = TYPE_BASE; fail_if(g_type_class_peek(type) != NULL); Base *base = g_object_new(type, "base-readwrite", TRUE, NULL); check_step(base_class_init); check_step(base_constructor); check_step(base_init); check_step(base_set_property); check_step(base_set_property); check_step(base_constructed); check_step(base_set_property); fail_unless(i == n_tags); g_object_unref(base); check_step(base_dispose); check_step(base_finalize); fail_unless(i == n_tags); Derived *derived = g_object_new( TYPE_DERIVED, "derived-readwrite", TRUE, "base-readwrite", TRUE, NULL); check_step(derived_class_init); check_step(derived_constructor); check_step(base_constructor); check_step(base_init); check_step(derived_init); check_step(base_set_property); check_step(base_set_property); check_step(derived_set_property); check_step(derived_set_property); check_step(derived_constructed); check_step(base_constructed); check_step(derived_set_property); /* derived-readwrite */ check_step(base_set_property); /* base-readwrite */ fail_unless(i == n_tags); g_object_unref(derived); check_step(derived_dispose); check_step(base_dispose); check_step(derived_finalize); check_step(base_finalize); fail_unless(i == n_tags); } END_TEST START_TEST(proxy_assumptions) { DBusGProxy *proxy = dbus_g_proxy_new_for_name( dbus_g_bus_get(DBUS_BUS_SYSTEM, NULL), "com.nokia.phone.SIM", "/com/nokia/phone/SIM", "Phone.Sim"); g_object_run_dispose((GObject*)proxy); g_object_run_dispose((GObject*)proxy); g_object_unref(proxy); } END_TEST START_TEST(free_assumptions) { GPtrArray *array; GError *error = NULL; guint i; g_free(NULL); mark_point(); fail_if(g_strdup(NULL) != NULL); g_strfreev(NULL); mark_point(); fail_if(g_strdupv(NULL) != NULL); array = g_ptr_array_new(); fail_if(g_ptr_array_free(array, TRUE) != NULL); array = g_ptr_array_new(); fail_if(g_ptr_array_free(array, FALSE) != NULL); g_message("Expect some CRITICAL warnings from (process:%u)", (unsigned)getpid()); fail_if(g_ptr_array_free(NULL, TRUE) != NULL); fail_if(g_ptr_array_free(NULL, FALSE) != NULL); g_object_unref(NULL); mark_point(); fail_if(g_object_ref(NULL) != NULL); array = g_ptr_array_new(); for (i = 0; i < 3; i++) g_ptr_array_add(array, g_strdup("kuik")); g_ptr_array_add(array, NULL); g_strfreev((char **)g_ptr_array_free(array, FALSE)); array = g_ptr_array_new(); g_strfreev((char **)g_ptr_array_free(array, FALSE)); array = g_ptr_array_new(); g_ptr_array_add(array, NULL); g_strfreev((char **)g_ptr_array_free(array, FALSE)); g_clear_error(&error); g_error_free(NULL); g_message("CRITICAL warnings from (process:%u) end here", (unsigned)getpid()); } END_TEST static TCase * assumptions_tcase(void) { TCase *tc = tcase_create("Test Glib assumptions"); tcase_add_checked_fixture(tc, g_type_init, NULL); tcase_add_test(tc, g_object_assumptions); tcase_add_test(tc, proxy_assumptions); tcase_add_test(tc, free_assumptions); return tc; } /* ====================================================================== */ struct test_cases modem_requests_tcases[] = { DECLARE_TEST_CASE(assumptions_tcase), DECLARE_TEST_CASE(tcase_for_modem_request), LAST_TEST_CASE }; telepathy-ring-2.3.24-37ad7111424725a5446e82dac516d7cab88149c7/modem/tests/test-modem-sms.c000066400000000000000000000630721251541261300272510ustar00rootroot00000000000000/* * test-modem-sms.c - Test cases for ModemSMSService * * Copyright (C) 2008-2010 Nokia Corporation * @author Pekka Pessi * * This work is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This work is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this work; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "config.h" #define MODEM_DEBUG_FLAG (-1) #define _ATFILE_SOURCE (1) #include "modem/debug.h" #include #include #include #include #include #include #include "test-modem.h" #include #include #include #include #include #include #include #include #include #include gchar const default_smsc[] = "+3584544" "00046"; gchar const national_smsc[] = "04544" "00046"; static GMainLoop *mainloop = NULL; static ModemSMSService *sms_service; static struct event { enum { SEND_REPLY = 1, INCOMING, OUTGOING_COMPLETE, OUTGOING_ERROR, STATUS_REPORT, CONNECTED, } reason; gchar *id; gchar *destination; GError *error; SMSGStatusReport *sr; SMSGDeliver *deliver; } events[4 * 256]; static int reported = 0, steps; static guint64 client_connected; static void on_connected(ModemSMSService *modem_sms_service, gpointer _self) { memset(&events[reported], 0, sizeof events[reported]); events[reported].reason = CONNECTED; reported++; client_connected = modem_sms_service_time_connected(modem_sms_service); assert(reported < 1024); /* abort() if not in events */ } static void on_deliver(ModemSMSService *modem_sms_service, SMSGDeliver *received, gpointer _self) { memset(&events[reported], 0, sizeof events[reported]); events[reported].reason = INCOMING; events[reported].id = g_strdup(sms_g_deliver_get_message_token(received)); events[reported].deliver = g_object_ref(received); reported++; assert(reported < 1024); /* abort() if not in events */ } static void send_reply(ModemSMSService *sms_service, ModemRequest *call, gchar const *message_id, GError const *error, gpointer userdata) { (void)sms_service, (void)call; memset(&events[reported], 0, sizeof events[reported]); events[reported].reason = SEND_REPLY; events[reported].id = message_id ? g_strdup(message_id) : NULL; events[reported].error = error ? g_error_copy(error) : NULL; reported++; assert(reported < 1024); /* abort() if not in events */ } static void on_outgoing_complete( ModemSMSService *sms_service, gchar const *message_id, gchar const *destination, gpointer user_data) { memset(&events[reported], 0, sizeof events[reported]); events[reported].reason = OUTGOING_COMPLETE; events[reported].id = message_id ? g_strdup(message_id) : NULL; events[reported].destination = g_strdup(destination); reported++; assert(reported < 1024); /* abort() if not in events */ } void on_outgoing_error( ModemSMSService *self, gchar const *message_id, gchar const *destination, GError const *error, gpointer user_data) { memset(&events[reported], 0, sizeof events[reported]); events[reported].reason = OUTGOING_ERROR; events[reported].id = message_id ? g_strdup(message_id) : NULL; events[reported].destination = g_strdup(destination); events[reported].error = g_error_copy(error); reported++; assert(reported < 1024); /* abort() if not in events */ } void on_status_report( ModemSMSService *self, SMSGStatusReport *status_report, gpointer user_data) { memset(&events[reported], 0, sizeof events[reported]); events[reported].reason = STATUS_REPORT; events[reported].id = g_strdup(sms_g_status_report_get_message_token(status_report)); events[reported].sr = g_object_ref(status_report); reported++; assert(reported < 1024); /* abort() if not in events */ } static void zap_events(void) { guint i; for (i = 0; i < reported; i++) { g_free(events[i].id); if (events[i].error) g_error_free(events[i].error); if (events[i].sr) g_object_unref(events[i].sr); if (events[i].deliver) g_object_unref(events[i].deliver); if (events[i].destination) g_free(events[i].destination); } reported = 0; } static struct { gpointer original; size_t size; guint8 data[16]; } quitter; static gboolean quit_loop (gpointer pointer) { (void)pointer; if (memcmp(quitter.original, quitter.data, quitter.size)) { g_main_loop_quit (mainloop); return FALSE; } return TRUE; } static void run_until(gpointer change, size_t size) { assert(size <= sizeof quitter.data); memcpy(quitter.data, quitter.original = change, quitter.size = size); #if 1 g_idle_add_full(300, quit_loop, (gpointer)quit_loop, NULL); #else g_timeout_add(20, quit_loop, (gpointer)quit_loop); #endif g_main_loop_run (mainloop); } gulong id_connected, id_deliver, id_outgoing_complete, id_outgoing_error, id_status_report; char *test_spooldir; static void echo_setup(void) { setenv("SMS_LOOPBACK", "1", 1); /* sms_g_debug_set_flags(0xffffffff); */ g_type_init(); (void)dbus_g_bus_get(DBUS_BUS_SYSTEM, NULL); mainloop = g_main_loop_new (NULL, FALSE); char *spooldir, *expect = g_strdup_printf("/tmp/test-modem-sms-%u/srr/spool", (unsigned)getpid()); sms_service = g_object_new(MODEM_TYPE_SMS_SERVICE, "srr-spool-directory", expect, NULL); g_object_get(sms_service, "srr-spool-directory", &spooldir, NULL); fail_if(strcmp(spooldir, expect)); test_spooldir = spooldir; g_free(expect); id_deliver = modem_sms_connect_to_deliver( sms_service, on_deliver, events); id_outgoing_complete = modem_sms_connect_to_outgoing_complete( sms_service, on_outgoing_complete, events); id_outgoing_error = modem_sms_connect_to_outgoing_error( sms_service, on_outgoing_error, events); id_status_report = modem_sms_connect_to_status_report( sms_service, on_status_report, events); id_connected = modem_sms_connect_to_connected( sms_service, on_connected, events); fail_unless(modem_sms_service_connect(sms_service)); run_until(&client_connected, sizeof client_connected); fail_unless(modem_sms_service_time_connected(sms_service) != 0); zap_events(); g_object_unref(sms_service); } static void echo_teardown(void) { if (id_connected != 0 && g_signal_handler_is_connected(sms_service, id_connected)) g_signal_handler_disconnect(sms_service, id_connected); id_connected = 0; if (id_deliver != 0 && g_signal_handler_is_connected(sms_service, id_deliver)) g_signal_handler_disconnect(sms_service, id_deliver); id_deliver = 0; if (id_outgoing_complete != 0 && g_signal_handler_is_connected(sms_service, id_outgoing_complete)) g_signal_handler_disconnect(sms_service, id_outgoing_complete); id_outgoing_complete = 0; if (id_outgoing_error != 0 && g_signal_handler_is_connected(sms_service, id_outgoing_error)) g_signal_handler_disconnect(sms_service, id_outgoing_error); id_outgoing_error = 0; if (id_status_report != 0 && g_signal_handler_is_connected(sms_service, id_status_report)) g_signal_handler_disconnect(sms_service, id_status_report); id_status_report = 0; zap_events(); if (sms_service) g_object_unref((GObject *)sms_service), sms_service = NULL; if (test_spooldir) { DIR *dir = opendir(test_spooldir); struct dirent *d; if (dir) { for (d = readdir(dir); d; d = readdir(dir)) { char *path = g_strdup_printf("%s/%s", test_spooldir, d->d_name); unlink(path); g_free(path); } closedir(dir); } char *spooldir = test_spooldir; while (spooldir && strcmp(spooldir, "/tmp")) { rmdir(spooldir); char * slash = strrchr(spooldir, '/'); if (!slash || slash == spooldir) break; *slash = '\0'; } g_free(test_spooldir), test_spooldir = NULL; } g_main_loop_unref (mainloop), mainloop = NULL; } /* ---------------------------------------------------------------------- */ static void echo_text_message(gchar const *text, gchar const *destination) { SMSGSubmit *sms = sms_g_submit_new(); ModemRequest *sending; SMSGDeliver *echo; GError *error = NULL; GPtrArray const *tpdus; if (destination == NULL) destination = "045441041099"; sms_g_submit_set_destination(sms, destination); tpdus = sms_g_submit_text(sms, text, &error); fail_unless(tpdus != NULL); fail_unless(error == NULL); sending = modem_sms_request_send(sms_service, sms, send_reply, events); fail_unless(sending != NULL); g_object_unref(sms), sms = NULL; for (steps = 0; reported < 3 && steps < 100; steps++) run_until(&reported, sizeof reported); fail_unless(events[0].reason == SEND_REPLY); fail_unless(events[0].id != NULL); fail_unless(events[1].reason == OUTGOING_COMPLETE); fail_unless(events[1].id != NULL); fail_if(strcmp(events[1].destination, destination)); fail_unless(events[2].reason == INCOMING); fail_unless(events[2].id != NULL); fail_unless(events[2].deliver != NULL); echo = events[2].deliver; fail_if(strcmp(sms_g_deliver_get_originator(echo), destination)); fail_if(strcmp(sms_g_deliver_get_text(echo), text)); fail_unless(sms_g_deliver_is_text(echo)); fail_unless(sms_g_deliver_is_type(echo, "text/plain")); } START_TEST(test_modem_sms_echo_text) { echo_text_message("echo text message via loopback", NULL); zap_events(); echo_text_message("Diipa daapa with exactly 40 characters.." "Diipa daapa with exactly 40 characters.." "Diipa daapa with exactly 40 characters.." "Diipa daapa with exactly 40 characters", "05055578901234567899"); zap_events(); #if notyet /* This fails! */ echo_text_message("Diipa daapa with exactly 40 characters.." "Diipa daapa with exactly 40 characters.." "Diipa daapa with exactly 40 characters.." "Diipa daapa with exactly 40 characters..", "05055578901234567899"); zap_events(); char text[] = "Diipa daapa with exactly 40 characters.." "Diipa daapa with exactly 40 characters.." "Diipa daapa with exactly 40 characters.." "Diipa daapa with exactly 40 characters.." "Diipa daapa with exactly 40 characters.." "Diipa daapa with exactly 40 characters.." "Diipa daapa with exactly 40 characters.." "Diipa daapa with exactly 40 characters.."; int i; for (i = sizeof(text); i >= 0; i--) { text[i] = '\0'; echo_text_message(text, "05055578901234567899"); zap_events(); } #endif } END_TEST START_TEST(test_modem_sms_echo_empty) { echo_text_message("", NULL); } END_TEST START_TEST(test_modem_sms_echo_with_content_type) { SMSGSubmit *sms = sms_g_submit_new_type("text/x-vcard"); ModemRequest *sending; SMSGDeliver *echo; GError *error = NULL; GPtrArray const *tpdus; gchar const text[] = "echo text message via loopback"; sms_g_submit_set_destination(sms, "04544104109"); tpdus = sms_g_submit_text(sms, text, &error); fail_unless(tpdus != NULL); fail_unless(error == NULL); sending = modem_sms_request_send(sms_service, sms, send_reply, events); fail_unless(sending != NULL); g_object_unref(sms), sms = NULL; for (steps = 0; reported < 3 && steps < 100; steps++) run_until(&reported, sizeof reported); fail_unless(events[0].reason == SEND_REPLY); fail_unless(events[0].id != NULL); fail_unless(events[1].reason == OUTGOING_COMPLETE); fail_unless(events[1].id != NULL); fail_if(strcmp(events[1].destination, "04544104109")); fail_unless(events[2].reason == INCOMING); fail_unless(events[2].id != NULL); fail_unless(events[2].deliver != NULL); echo = events[2].deliver; fail_if(strcmp(sms_g_deliver_get_originator(echo), "04544104109")); GArray const *binary = sms_g_deliver_get_binary(echo); fail_if(memcmp(text, binary->data, binary->len)); fail_unless(sms_g_deliver_is_type(echo, "text/x-vcard")); } END_TEST START_TEST(test_modem_sms_echo_binary) { SMSGSubmit *sms = sms_g_submit_new(); ModemRequest *sending; SMSGDeliver *echo; GError *error = NULL; GPtrArray const *tpdus; gchar const binary[] = "echo binary message via loopback"; GArray bsend[1] = {{ (gpointer)binary, sizeof binary }}; GArray *becho; guint sport, dport; sms_g_submit_set_destination(sms, "04544104109"); sms_g_submit_set_sms_class(sms, 1); sms_g_submit_set_dport(sms, 0xe4); sms_g_submit_set_sport(sms, 243); tpdus = sms_g_submit_binary(sms, bsend, &error); fail_unless(tpdus != NULL); fail_unless(error == NULL); sending = modem_sms_request_send(sms_service, sms, send_reply, events); fail_unless(sending != NULL); g_object_unref(sms), sms = NULL; for (steps = 0; reported < 3 && steps < 100; steps++) run_until(&reported, sizeof reported); fail_unless(events[0].reason == SEND_REPLY); fail_unless(events[0].id != NULL); fail_unless(events[1].reason == OUTGOING_COMPLETE); fail_unless(events[1].id != NULL); fail_if(strcmp(events[1].destination, "04544104109")); fail_unless(events[2].reason == INCOMING); fail_unless(events[2].id != NULL); fail_unless(events[2].deliver != NULL); echo = events[2].deliver; fail_if(strcmp(sms_g_deliver_get_originator(echo), "04544104109")); g_object_get(echo, "binary", &becho, "destination-port", &dport, "source-port", &sport, NULL); fail_if(!becho); fail_if(becho->len != sizeof binary); fail_if(memcmp(becho->data, binary, sizeof binary)); g_boxed_free(DBUS_TYPE_G_UCHAR_ARRAY, becho); fail_unless(dport == 0xe4); fail_unless(sport == 243); fail_unless(sms_g_deliver_is_type(echo, "text/x-vcalendar")); } END_TEST START_TEST(test_modem_sms_echo_large_binary) { SMSGSubmit *sms = sms_g_submit_new(); ModemRequest *sending; SMSGDeliver *echo; GError *error = NULL; GPtrArray const *tpdus; gchar binary[512] = "echo binary message via loopback"; GArray bsend[1] = {{ (gpointer)binary, sizeof binary }}; GArray *becho; guint sport, dport; guint i; for (i = strlen(binary) + 1; i < sizeof binary; i++) binary[i] = (i % 64) + 32; sms_g_submit_set_destination(sms, "04544104109"); sms_g_submit_set_sms_class(sms, 1); sms_g_submit_set_dport(sms, 226); sms_g_submit_set_sport(sms, 243); for (i = 1; i < sizeof binary; i++) { bsend->len = i; tpdus = sms_g_submit_binary(sms, bsend, &error); fail_unless(tpdus != NULL); fail_unless(error == NULL); sending = modem_sms_request_send(sms_service, sms, send_reply, events); fail_unless(sending != NULL); for (steps = 0; reported < 3 && steps < 1000; steps++) run_until(&reported, sizeof reported); fail_unless(events[0].reason == SEND_REPLY); fail_unless(events[0].id != NULL); fail_unless(events[1].reason == OUTGOING_COMPLETE); fail_unless(events[1].id != NULL); fail_if(strcmp(events[1].destination, "04544104109")); fail_unless(events[2].reason == INCOMING); fail_unless(events[2].id != NULL); fail_unless(events[2].deliver != NULL); echo = events[2].deliver; fail_if(strcmp(sms_g_deliver_get_originator(echo), "04544104109")); g_object_get(echo, "binary", &becho, "destination-port", &dport, "source-port", &sport, NULL); fail_if(!becho); if (becho->len != i) { printf("sent %u bytes, received %u\n", i, becho->len); } fail_if(becho->len != i); fail_if(memcmp(becho->data, binary, i)); fail_unless(dport == 226); fail_unless(sport == 243); g_boxed_free(DBUS_TYPE_G_UCHAR_ARRAY, becho); fail_unless(sms_g_deliver_is_vcard(echo)); fail_unless(sms_g_deliver_is_type(echo, "text/x-vcard")); zap_events(); } g_object_unref(sms), sms = NULL; } END_TEST START_TEST(test_modem_sms_echo_srr) { char text[] = "wait for status report"; char destination[] = "0505556666"; SMSGSubmit *sms; ModemRequest *sending; SMSGDeliver *echo; GError *error = NULL; GPtrArray const *tpdus; sms = sms_g_submit_new(); sms_g_submit_set_destination(sms, destination); g_object_set(sms, "status-report-request", 1, NULL); tpdus = sms_g_submit_text(sms, text, &error); fail_unless(tpdus != NULL); fail_unless(error == NULL); sending = modem_sms_request_send(sms_service, sms, send_reply, events); fail_unless(sending != NULL); g_object_unref(sms), sms = NULL; for (steps = 0; reported < 4 && steps < 100; steps++) run_until(&reported, sizeof reported); fail_unless(events[0].reason == SEND_REPLY); fail_unless(events[0].id != NULL); fail_unless(events[1].reason == OUTGOING_COMPLETE); fail_unless(events[1].id != NULL); fail_if(strcmp(events[1].destination, destination)); fail_unless(events[2].reason == INCOMING); fail_unless(events[2].id != NULL); fail_unless(events[2].deliver != NULL); fail_unless(events[3].reason == STATUS_REPORT); echo = events[2].deliver; fail_if(strcmp(sms_g_deliver_get_originator(echo), destination)); fail_if(strcmp(sms_g_deliver_get_text(echo), text)); fail_unless(sms_g_deliver_is_text(echo)); fail_unless(sms_g_deliver_is_type(echo, "text/plain")); } END_TEST static TCase * echo_tcase(void) { TCase *tc = tcase_create("2 - Loopback tests"); tcase_add_checked_fixture(tc, echo_setup, echo_teardown); { tcase_add_test(tc, test_modem_sms_echo_text); tcase_add_test(tc, test_modem_sms_echo_empty); tcase_add_test(tc, test_modem_sms_echo_with_content_type); tcase_add_test(tc, test_modem_sms_echo_binary); tcase_add_test(tc, test_modem_sms_echo_large_binary); tcase_add_test(tc, test_modem_sms_echo_srr); } tcase_set_timeout(tc, 40); return tc; } /* ====================================================================== */ static char *test_content_types[] = { "text/x-vcalendar", NULL }; static void sms_setup(void) { g_type_init(); (void)dbus_g_bus_get(DBUS_BUS_SYSTEM, NULL); mainloop = g_main_loop_new (NULL, FALSE); } static void sms_teardown(void) { g_main_loop_unref(mainloop), mainloop = NULL; } START_TEST(sms_service_defaults) { char *spooldir = NULL; char **content_types = NULL; char *smsc = NULL; unsigned validity_period = 0; sms_service = g_object_new(MODEM_TYPE_SMS_SERVICE, "content-types", test_content_types, "service-centre", "+0454400046", "validity-period", 300, NULL); g_object_get(sms_service, "content-types", &content_types, "service-centre", &smsc, "validity-period", &validity_period, NULL); fail_if(content_types == NULL); fail_if(content_types[0] == NULL); fail_if(content_types[1] != NULL); fail_if(strcmp(content_types[0], test_content_types[0])); fail_if(smsc == NULL); fail_if(strlen(smsc) == 0); fail_if(strcmp(smsc, "+0454400046") != 0); fail_if(validity_period != 300); g_strfreev(content_types); g_free(smsc); /* Try to set too long validity-period */ g_object_set(sms_service, "validity-period", 64 * 7 * 24 * 60 * 60, NULL); g_object_get(sms_service, "validity-period", &validity_period, NULL); fail_if(validity_period != 300); g_object_unref(sms_service); sms_service = g_object_new(MODEM_TYPE_SMS_SERVICE, "srr-spool-directory", "", NULL); g_object_get(sms_service, "srr-spool-directory", &spooldir, NULL); fail_if(spooldir != NULL); g_object_run_dispose(G_OBJECT(sms_service)); fail_if(modem_sms_service_connect(sms_service)); g_object_unref(sms_service); } END_TEST static TCase * tcase_for_modem_sms_defaults(void) { TCase *tc = tcase_create("3 - Default values"); tcase_add_checked_fixture(tc, sms_setup, sms_teardown); { tcase_add_test(tc, sms_service_defaults); } tcase_set_timeout(tc, 40); return tc; } /* ====================================================================== */ static void modem_sms_api_init(void) { g_type_init(); (void)dbus_g_bus_get(DBUS_BUS_SYSTEM, NULL); mainloop = g_main_loop_new (NULL, FALSE); } static void modem_sms_api_teardown(void) { g_main_loop_unref(mainloop), mainloop = NULL; } static int got_sms_connected; static void on_sms_connected(ModemSMSService *sms_service, gpointer user_data) { got_sms_connected++; g_main_loop_quit(mainloop); } #if notyet GError *reply_error; static void reply_to_sms_request(ModemSMSService *self, ModemRequest *request, GError *error, gpointer user_data) { reply_error = g_error_copy(error); g_main_loop_quit(mainloop); } #endif START_TEST(modem_sms_api) { char **strv; gpointer p; GError *error; fail_if(modem_sms_service_is_connected(NULL)); fail_if(modem_sms_service_is_connecting(NULL)); sms_service = g_object_new(MODEM_TYPE_SMS_SERVICE, "content-types", test_content_types, NULL); error = NULL; fail_if(modem_sms_service_time_connected(sms_service) != 0); fail_if(modem_sms_service_is_connected(sms_service)); fail_if(modem_sms_service_is_connecting(sms_service)); strv = modem_sms_list_stored(sms_service); fail_if(strv == NULL); g_strfreev(strv); p = modem_sms_get_stored_message(sms_service, "/kuik"); fail_if(p != NULL); modem_sms_request_expunge(sms_service, "/kuik", NULL, NULL); mark_point(); g_signal_connect(sms_service, "connected", G_CALLBACK(on_sms_connected), &got_sms_connected); fail_unless(modem_sms_service_connect(sms_service)); fail_unless(modem_sms_service_is_connecting(sms_service)); g_main_loop_run(mainloop); fail_unless(modem_sms_service_is_connected(sms_service)); g_object_unref(sms_service); } END_TEST static TCase * tcase_for_modem_sms_api(void) { TCase *tc = tcase_create("modem-sms-api"); tcase_add_checked_fixture(tc, modem_sms_api_init, modem_sms_api_teardown); { tcase_add_test(tc, modem_sms_api); } tcase_set_timeout(tc, 40); return tc; } /* ====================================================================== */ #undef MODEM_DEBUG_FLAG #include "modem/sms-service.c" static void modem_sms_error_init(void) { g_type_init(); (void)dbus_g_bus_get(DBUS_BUS_SYSTEM, NULL); } static void modem_sms_error_teardown(void) { } START_TEST(test_modem_sms_errors) { GError *error; guint code; for (code = 1000; code <= 1050; code ++) { error = modem_sms_new_error(code, NULL); fail_unless(error != NULL); fail_unless(error->domain == MODEM_SMS_NET_ERRORS); g_error_free(error); } for (;code <= 1070; code ++) { error = modem_sms_new_error(code, NULL); fail_unless(error != NULL); fail_unless(error->domain == MODEM_SMS_ERRORS, "libsmserror %u: %s.%s\n", code, g_quark_to_string(error->domain), modem_error_name(error, "???", 0)); g_error_free(error); } for (;code <= 1082; code ++) { error = modem_sms_new_error(code, NULL); fail_unless(error != NULL); fail_unless(error->domain == MODEM_SMS_ERRORS, "libsmserror %u: %s.%s\n", code, g_quark_to_string(error->domain), modem_error_name(error, "???", 0)); g_error_free(error); } for (code = 1200; code <= 1208; code ++) { error = modem_sms_new_error(code, NULL); fail_unless(error != NULL); fail_unless(error->domain == MODEM_SIM_ERRORS, "libsmserror %u: %s.%s\n", code, g_quark_to_string(error->domain), modem_error_name(error, "???", 0)); g_error_free(error); } /* 1210 is duplicate, 1211 is unknown generic sms error */ for (;code <= 1210; code ++) { error = modem_sms_new_error(code, NULL); fail_unless(error != NULL); if (error->domain != MODEM_SMS_ERRORS) printf("libsmserror %u: %s.%s\n", code, g_quark_to_string(error->domain), modem_error_name(error, "???", 0)); fail_unless(error->domain == MODEM_SMS_ERRORS); g_error_free(error); } error = modem_sms_new_error(SMS_CAUSE_ROUTING_FAILED, NULL); fail_unless(error != NULL); fail_unless(modem_sms_error_is_temporary(error)); g_error_free(error); } END_TEST START_TEST(test_sms_error_conversion) { GError *error, *dbus, *fixed; guint i; char ebuffer[16]; for (i = 980; i < 1200; i++) { error = modem_sms_new_error(i, "test"); fail_unless(error != NULL); fail_if(modem_error_name(error, ebuffer, sizeof ebuffer) == ebuffer); dbus = g_error_new(DBUS_GERROR, DBUS_GERROR_REMOTE_EXCEPTION, "%s%c%s.%s", error->message, '\0', modem_error_domain_prefix(error->domain), modem_error_name(error, ebuffer, sizeof ebuffer)); fail_unless(dbus != NULL); fixed = dbus; modem_error_fix(&fixed); fail_if(fixed == dbus); fail_if(fixed->domain != error->domain); fail_if(fixed->code != error->code); g_clear_error(&error); g_clear_error(&fixed); } } END_TEST static TCase * modem_sms_errors_tcase(void) { TCase *tc = tcase_create("modem-sms-errors"); tcase_add_checked_fixture(tc, modem_sms_error_init, modem_sms_error_teardown); { tcase_add_test(tc, test_modem_sms_errors); tcase_add_test(tc, test_sms_error_conversion); } tcase_set_timeout(tc, 5); return tc; } /* ====================================================================== */ struct test_cases modem_sms_tcases[] = { DECLARE_TEST_CASE_OFF_BY_DEFAULT(echo_tcase), DECLARE_TEST_CASE_OFF_BY_DEFAULT(tcase_for_modem_sms_defaults), DECLARE_TEST_CASE_OFF_BY_DEFAULT(tcase_for_modem_sms_api), DECLARE_TEST_CASE(modem_sms_errors_tcase), LAST_TEST_CASE }; telepathy-ring-2.3.24-37ad7111424725a5446e82dac516d7cab88149c7/modem/tests/test-modem-tones.c000066400000000000000000000122511251541261300275700ustar00rootroot00000000000000/* * test-modem-tones.c - Test cases for ModemTones * * Copyright (C) 2008-2010 Nokia Corporation * @author Pekka Pessi * * This work is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This work is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this work; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "config.h" #include #include #include "test-modem.h" #include "modem/debug.h" #include #include #include #include static GMainLoop *mainloop = NULL; static void setup(void) { g_type_init(); mainloop = g_main_loop_new (NULL, FALSE); } static void teardown(void) { g_main_loop_unref(mainloop), mainloop = NULL; } static void tone_stopped(ModemTones *tones, guint source, gpointer data) { fail_if(data == NULL); *(int *)data = 0; g_main_loop_quit(mainloop); } START_TEST(test_modem_tones) { ModemTones *tones; int volume = 0; gint event; guint playing; tones = g_object_new(MODEM_TYPE_TONES, NULL); g_object_get(tones, "volume", &volume, NULL); fail_if(volume >= 0); g_object_set(tones, "volume", -63, NULL); g_object_get(tones, "volume", &volume, NULL); fail_if(volume != -63); modem_tones_stop(tones, 0); fail_if(0 > (event = modem_call_event_tone(MODEM_CALL_STATE_ALERTING, 0, 0))); fail_if(!(playing = modem_tones_start(tones, event, 0))); modem_tones_stop(tones, (unsigned)-1); fail_unless(modem_tones_is_playing(tones, playing)); /* NW cause Busy - no tone with MO-RELEASE */ event = modem_call_event_tone(MODEM_CALL_STATE_DISCONNECTED, 3, 17); fail_if(event >= 0); fail_if(playing = modem_tones_start(tones, event, 0)); /* NW cause Busy - BUSY with MT-RELEASE */ event = modem_call_event_tone(MODEM_CALL_STATE_DISCONNECTED, 3, 17); fail_unless(event == TONES_EVENT_BUSY); fail_unless(playing = modem_tones_start_full(tones, event, 0, 100, tone_stopped, &playing)); g_main_loop_run(mainloop); fail_if(playing); fail_unless(playing = modem_tones_start_full(tones, event, 0, 100, tone_stopped, &playing)); modem_tones_stop(tones, playing); fail_if(modem_tones_is_playing (tones, playing)); fail_unless(playing = modem_tones_start_full(tones, event, 0, 100, tone_stopped, &playing)); modem_tones_stop(tones, playing ^ 13); fail_unless(playing); modem_tones_stop(tones, 0); fail_if(modem_tones_is_playing (tones, playing)); fail_unless(playing = modem_tones_start_full(tones, event, 0, 100, tone_stopped, &playing)); fail_unless(playing); g_object_run_dispose(G_OBJECT(tones)); fail_if(modem_tones_is_playing (tones, playing)); g_message("Expect a **CRITICAL** message from the following line:"); fail_if(playing = modem_tones_start_full(tones, event, 0, 100, tone_stopped, &playing)); g_message("Expect a **CRITICAL** message from the following line:"); modem_tones_stop(tones, 0); g_object_unref(tones); tones = g_object_new(MODEM_TYPE_TONES, NULL); fail_unless(playing = modem_tones_start_full(tones, event, 0, 100, tone_stopped, &playing)); /* modem_tones_start() will always stop the tone */ fail_if(modem_tones_start_full(tones, -1, 0, 100, tone_stopped, &playing)); fail_if(modem_tones_is_playing (tones, playing)); #if 0 fail_if(playing); if (status == MODEM_CALL_STATE_MT_RELEASE) fail_unless(playing); if (status == MODEM_CALL_STATE_MO_RELEASE) fail_unless(modem_tones_is_playing(tones) == call); modem_tones_for_call_event(tones, call, status, 1, MODEM_CALL_ERROR_BLACKLIST_DELAYED); fail_unless(modem_tones_is_playing(tones) == call); } modem_tones_user_connection(tones, 1); modem_tones_stop(tones, NULL); } for (causetype = 1; causetype <= 3; causetype++) { for (cause = 1; cause <= 127; cause++) { modem_tones_user_connection(tones, 0); modem_tones_for_call_event(tones, call, MODEM_CALL_STATE_MT_RELEASE, causetype, cause); modem_tones_for_call_event(tones, call, MODEM_CALL_STATE_TERMINATED, causetype, cause); modem_tones_for_call_event(tones, call, MODEM_CALL_STATE_IDLE, 0, 0); modem_tones_user_connection(tones, 1); } } got_call_connected = 0; modem_call_service_disconnect(call_service); fail_unless(got_call_connected != 0); #endif g_object_unref(tones); } END_TEST static TCase * tcase_for_modem_tones(void) { TCase *tc = tcase_create("Test for ModemCallService"); tcase_add_checked_fixture(tc, setup, teardown); tcase_add_test(tc, test_modem_tones); tcase_set_timeout(tc, 10); return tc; } /* ====================================================================== */ struct test_cases modem_tones_tcases[] = { DECLARE_TEST_CASE_OFF_BY_DEFAULT(tcase_for_modem_tones), LAST_TEST_CASE }; telepathy-ring-2.3.24-37ad7111424725a5446e82dac516d7cab88149c7/modem/tests/test-modem.c000066400000000000000000000036641251541261300264520ustar00rootroot00000000000000/* * test-modem.c - A check-based test runner for the modem library * * Copyright (C) 2008-2010 Nokia Corporation * @author Pekka Pessi * * This work is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This work is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this work; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "config.h" #include #include "test-modem.h" #include #include #include #include #include int main(int argc, char *argv[]) { struct common_args *args; int failed = 0; Suite *suite = suite_create("Unit tests for the modem library"); SRunner *runner; modem_debug_set_flags_from_env(); args = parse_common_args(argc, argv); filter_add_tcases(suite, modem_requests_tcases, args->tests); /*filter_add_tcases(suite, modem_sms_tcases, args->tests);*/ filter_add_tcases(suite, modem_sim_tcases, args->tests); filter_add_tcases(suite, modem_tones_tcases, args->tests); filter_add_tcases(suite, modem_call_service_tcases, args->tests); filter_add_tcases(suite, modem_call_tcases, args->tests); runner = srunner_create(suite); if (args->xml) srunner_set_xml(runner, args->xml); srunner_run_all(runner, CK_ENV); failed = srunner_ntests_failed(runner); free_common_args(args); srunner_free(runner); exit(failed ? EXIT_FAILURE : EXIT_SUCCESS); } telepathy-ring-2.3.24-37ad7111424725a5446e82dac516d7cab88149c7/modem/tests/test-modem.h000066400000000000000000000023201251541261300264430ustar00rootroot00000000000000/* * test-modem.h - Test cases * * Copyright (C) 2008-2010 Nokia Corporation * @author Pekka Pessi * * This work is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This work is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this work; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef TEST_MODEM_H #define TEST_MODEM_H #include extern struct test_cases modem_call_service_tcases[]; extern struct test_cases modem_call_tcases[]; extern struct test_cases modem_requests_tcases[]; extern struct test_cases modem_tones_tcases[]; extern struct test_cases modem_sms_tcases[]; extern struct test_cases modem_sim_tcases[]; #endif telepathy-ring-2.3.24-37ad7111424725a5446e82dac516d7cab88149c7/modem/tests/test-sim.c000066400000000000000000000133721251541261300261360ustar00rootroot00000000000000/* * test-sim.c - Test cases for the SIM service * * Copyright (C) 2008-2010 Nokia Corporation * @author Pekka Pessi * * This work is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This work is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this work; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "modem/sim-service.c" #include #include #include "test-modem.h" #include #include #include #include #include static TCase * modem_sim_state_tcase(void) { TCase *tc = tcase_create("modem-sim-state"); tcase_add_checked_fixture(tc, NULL, NULL); //tcase_add_test(tc, test_modem_sim_state_mapping); return tc; } /* ====================================================================== */ #if XXX static GMainLoop *mainloop = NULL; static void modem_sim_api_init(void) { g_type_init(); (void)dbus_g_bus_get(DBUS_BUS_SYSTEM, NULL); mainloop = g_main_loop_new (NULL, FALSE); } GError *reply_error; #if 0 static void reply_to_sim_state_request(ModemSIMService *sim, ModemRequest *request, guint state, GError *error, gpointer user_data) { if (error) reply_error = g_error_copy(error); else g_clear_error(&reply_error); g_main_loop_quit(mainloop); } #endif static int got_sim_connected; static void on_sim_connected(ModemSIMService *sim, gpointer user_data) { got_sim_connected++; g_main_loop_quit(mainloop); } START_TEST(modem_sim_api) { char const *imsi; char *s; guint state; GError *error; ModemSIMService *sim; fail_if(modem_sim_service_is_connected(NULL)); fail_if(modem_sim_service_is_connecting(NULL)); fail_if(modem_sim_get_state(NULL) != MODEM_SIM_STATE_UNKNOWN); fail_if(modem_sim_get_imsi(NULL) != NULL); sim = g_object_new(MODEM_TYPE_SIM_SERVICE, NULL); fail_unless(sim != NULL); error = NULL; fail_if(modem_sim_service_is_connected(sim)); fail_if(modem_sim_service_is_connecting(sim)); fail_if(modem_sim_get_state(sim) != MODEM_SIM_STATE_UNKNOWN); imsi = modem_sim_get_imsi(sim); fail_if(strcmp(imsi, "")); g_signal_connect(sim, "connected", G_CALLBACK(on_sim_connected), &got_sim_connected); fail_unless(modem_sim_service_connect(sim)); fail_unless(modem_sim_service_is_connecting(sim)); g_main_loop_run(mainloop); fail_unless(modem_sim_service_is_connected(sim)); fail_unless(modem_sim_service_connect(sim)); fail_unless(!modem_sim_service_is_connecting(sim)); fail_if(modem_sim_get_state(sim) == MODEM_SIM_STATE_UNKNOWN); imsi = modem_sim_get_imsi(sim); fail_unless(strcmp(imsi, "")); g_object_get(sim, "imsi", &s, "state", &state, NULL); fail_unless(state == modem_sim_get_state(sim)); fail_unless(strcmp(s, modem_sim_get_imsi(sim)) == 0); g_free(s); #if 0 request = modem_sim_request_state( sim, reply_to_sim_state_request, &reply_error); #endif g_main_loop_run(mainloop); fail_if(reply_error); fail_unless(modem_sim_get_state(sim) != MODEM_SIM_STATE_UNKNOWN); got_sim_connected = 0; modem_sim_service_disconnect(sim); fail_unless(got_sim_connected); g_object_unref(sim); } END_TEST START_TEST(modem_sim_api2) { ModemSIMService *sim; sim = g_object_new(MODEM_TYPE_SIM_SERVICE, NULL); fail_unless(sim != NULL); modem_sim_service_connect(sim); g_object_unref(sim); sim = g_object_new(MODEM_TYPE_SIM_SERVICE, NULL); fail_unless(sim != NULL); modem_sim_service_connect(sim); modem_sim_service_disconnect(sim); fail_if(modem_sim_service_is_connected(sim)); fail_if(modem_sim_service_is_connecting(sim)); modem_sim_service_connect(sim); fail_if(modem_sim_service_is_connected(sim)); fail_if(modem_sim_service_is_connecting(sim)); modem_sim_service_disconnect(sim); g_object_unref(sim); sim = g_object_new(MODEM_TYPE_SIM_SERVICE, NULL); fail_unless(sim != NULL); modem_sim_service_connect(sim); g_object_run_dispose(G_OBJECT(sim)); fail_if(modem_sim_service_is_connected(sim)); fail_if(modem_sim_service_is_connecting(sim)); modem_sim_service_connect(sim); g_object_unref(sim); } END_TEST START_TEST(modem_sim_requests) { ModemSIMService *sim; sim = g_object_new(MODEM_TYPE_SIM_SERVICE, NULL); g_signal_connect(sim, "connected", G_CALLBACK(on_sim_connected), &got_sim_connected); fail_if(0); /* XXX/KV: convert to base class methods */ #if 0 g_object_unref(sim->priv->proxy); sim->priv->proxy = dbus_g_proxy_new_for_name( dbus_g_bus_get(DBUS_BUS_SYSTEM, NULL), "invalid.server", "/invalid/server", "invalid.Server"); #endif fail_unless(modem_sim_service_connect(sim)); fail_unless(modem_sim_service_is_connecting(sim)); g_main_loop_run(mainloop); fail_if(modem_sim_service_is_connected(sim)); g_object_unref(sim); } END_TEST #endif static TCase * modem_sim_api_tcase(void) { TCase *tc = tcase_create("modem-sim"); #if XXX tcase_add_checked_fixture(tc, modem_sim_api_init, NULL); { tcase_add_test(tc, modem_sim_api); tcase_add_test(tc, modem_sim_api2); tcase_add_test(tc, modem_sim_requests); } tcase_set_timeout(tc, 40); #endif return tc; } struct test_cases modem_sim_tcases[] = { DECLARE_TEST_CASE(modem_sim_state_tcase), DECLARE_TEST_CASE_OFF_BY_DEFAULT(modem_sim_api_tcase), LAST_TEST_CASE }; telepathy-ring-2.3.24-37ad7111424725a5446e82dac516d7cab88149c7/modem/tones.c000066400000000000000000000206511251541261300243550ustar00rootroot00000000000000/* * modem/tones.c - Call signaling tones handling * Copyright (C) 2008 Nokia Corporation * @author Pekka Pessi * * This work is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This work is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this work; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "config.h" #define MODEM_DEBUG_FLAG MODEM_LOG_AUDIO #include "modem/debug.h" #include "modem/tones.h" #include "modem/request-private.h" #include "modem/errors.h" #include #include G_DEFINE_TYPE(ModemTones, modem_tones, G_TYPE_OBJECT); struct _ModemTonesPrivate { DBusGProxy *proxy; GTimer *timer; int volume; guint source; guint playing; int event; int evolume; guint duration; /* Timeout context */ guint timeout; ModemTonesStoppedNotify *notify; gpointer data; GQueue stop_requests[1]; unsigned dispose_has_run:2; }; static void modem_tones_init(ModemTones *self) { self->priv = G_TYPE_INSTANCE_GET_PRIVATE( self, MODEM_TYPE_TONES, ModemTonesPrivate); self->priv->timer = g_timer_new(); self->priv->proxy = dbus_g_proxy_new_for_name(dbus_g_bus_get(DBUS_BUS_SYSTEM, NULL), "com.Nokia.Telephony.Tones", "/com/Nokia/Telephony/Tones", "com.Nokia.Telephony.Tones"); g_queue_init(self->priv->stop_requests); } static void modem_tones_dispose(GObject *object) { ModemTones *self = MODEM_TONES(object); ModemTonesPrivate *priv = self->priv; if (priv->dispose_has_run) return; priv->dispose_has_run = 1; modem_tones_stop(self, 0); priv->dispose_has_run = 2; while (!g_queue_is_empty(priv->stop_requests)) { modem_request_cancel(g_queue_pop_head(priv->stop_requests)); } g_assert(!priv->playing); g_object_run_dispose(G_OBJECT(priv->proxy)); if (G_OBJECT_CLASS(modem_tones_parent_class)->dispose) G_OBJECT_CLASS(modem_tones_parent_class)->dispose(object); } static void modem_tones_finalize(GObject *object) { ModemTones *self = MODEM_TONES(object); ModemTonesPrivate *priv = self->priv; g_object_unref(priv->proxy); g_timer_destroy(priv->timer); memset(priv, 0, (sizeof *priv)); G_OBJECT_CLASS(modem_tones_parent_class)->finalize(object); } /* Properties */ enum { PROP_NONE, PROP_VOLUME, LAST_PROPERTY }; static void modem_tones_get_property(GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { ModemTones *self = MODEM_TONES(object); ModemTonesPrivate *priv = self->priv; switch(property_id) { case PROP_VOLUME: g_value_set_int(value, priv->volume); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); break; } } static void modem_tones_set_property(GObject *obj, guint property_id, const GValue *value, GParamSpec *pspec) { ModemTones *self = MODEM_TONES(obj); ModemTonesPrivate *priv = self->priv; switch (property_id) { case PROP_VOLUME: priv->volume = g_value_get_int(value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, property_id, pspec); break; } } static void modem_tones_class_init(ModemTonesClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS(klass); g_type_class_add_private(klass, sizeof (ModemTonesPrivate)); object_class->get_property = modem_tones_get_property; object_class->set_property = modem_tones_set_property; object_class->dispose = modem_tones_dispose; object_class->finalize = modem_tones_finalize; /* Properties */ g_object_class_install_property( object_class, PROP_VOLUME, g_param_spec_int("volume", "Volume in dBm0", "Volume describes the power level of the tone, " "expressed in dBm0. " "Power levels range from 0 to -63 dBm0.", /* min */ -63, /* max */ 0, /* default */ -9, G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS)); } /* ------------------------------------------------------------------------- */ static gboolean modem_tones_timeout(gpointer _self); static void modem_tones_timeout_removed(gpointer _self); static void reply_to_stop_tone(DBusGProxy *proxy, DBusGProxyCall *call, void *_request); guint modem_tones_start_full(ModemTones *self, int event, int volume, unsigned duration, ModemTonesStoppedNotify *notify, gpointer data) { ModemTonesPrivate *priv = self->priv; g_return_val_if_fail(!priv->dispose_has_run, 0); volume += priv->volume; if (volume > 0) volume = 0; else if (volume < -63) volume = -63; if (event == TONES_EVENT_DROPPED) { if (duration > 1200) duration = 1200; } modem_tones_stop(self, 0); if (event < 0) return 0; if (priv->source == 0) priv->source++; priv->playing = priv->source++; priv->event = event; priv->evolume = volume; priv->duration = duration; if (duration) { priv->timeout = g_timeout_add_full(G_PRIORITY_DEFAULT, duration, modem_tones_timeout, self, modem_tones_timeout_removed); } priv->data = data; priv->notify = notify; g_timer_start(priv->timer); DEBUG("calling StartEventTone(%u, %d, %u) with %u", priv->event, priv->evolume, priv->duration, priv->playing); dbus_g_proxy_call_no_reply(priv->proxy, "StartEventTone", G_TYPE_UINT, priv->event, G_TYPE_INT, priv->evolume, G_TYPE_UINT, priv->duration, G_TYPE_INVALID); return priv->playing; } guint modem_tones_start(ModemTones *self, int event, unsigned duration) { return modem_tones_start_full(self, event, 0, duration, NULL, NULL); } guint modem_tones_is_playing(ModemTones const *self, guint playing) { double played; if (!MODEM_IS_TONES(self) || !self->priv->playing) return 0; if (playing != 0 && playing != self->priv->playing) return 0; played = 1000 * g_timer_elapsed(self->priv->timer, NULL) + 0.5; if (played < 1.0) return 1; if (played < (double)UINT_MAX) return (guint)played; else return UINT_MAX; } int modem_tones_playing_event(ModemTones const *self, guint playing) { if (!MODEM_IS_TONES(self) || !self->priv->playing) return TONES_NONE; if (playing != 0 && playing != self->priv->playing) return TONES_NONE; return self->priv->event; } static gboolean modem_tones_timeout(gpointer _self) { MODEM_TONES(_self)->priv->timeout = 0; modem_tones_stop(MODEM_TONES(_self), 0); return FALSE; } static void modem_tones_timeout_removed(gpointer _self) { MODEM_TONES(_self)->priv->timeout = 0; } void modem_tones_stop(ModemTones *self, guint source) { ModemTonesPrivate *priv; ModemTonesStoppedNotify *notify; gpointer data; ModemRequest *stopping; DEBUG("(%p, %u)", self, source); g_return_if_fail(self); priv = self->priv; g_return_if_fail(priv->dispose_has_run <= 1); if (!priv->playing) return; if (source && priv->playing != source) return; if (priv->timeout) g_source_remove(priv->timeout); g_assert(priv->timeout == 0); source = priv->playing, priv->playing = 0; notify = priv->notify; data = priv->data; priv->notify = NULL, priv->data = NULL; if (notify) { stopping = modem_request_with_timeout( self, priv->proxy, "StopTone", reply_to_stop_tone, G_CALLBACK(notify), data, 5000, G_TYPE_INVALID); g_queue_push_tail(priv->stop_requests, stopping); modem_request_add_data(stopping, "modem-tones-stop-source", GUINT_TO_POINTER(source)); } } static void reply_to_stop_tone(DBusGProxy *proxy, DBusGProxyCall *call, void *_request) { ModemRequest *request = _request; ModemTones *self = modem_request_object(request); ModemTonesStoppedNotify *notify = modem_request_callback(request); guint source = GPOINTER_TO_UINT( modem_request_get_data(request, "modem-tones-stop-source")); gpointer data = modem_request_user_data(request); GError *error = NULL; if (!dbus_g_proxy_end_call(proxy, call, &error, G_TYPE_INVALID)) { g_error_free(error); } g_queue_remove(self->priv->stop_requests, _request); notify(self, source, data); } telepathy-ring-2.3.24-37ad7111424725a5446e82dac516d7cab88149c7/modem/tones.h000066400000000000000000000064161251541261300243650ustar00rootroot00000000000000/* * modem/tones.h - Call signaling tones handling * * Copyright (C) 2007 Nokia Corporation * @author Pekka Pessi * * This work is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This work is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this work; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _MODEM_TONES_H_ #define _MODEM_TONES_H_ #include G_BEGIN_DECLS typedef struct _ModemTones ModemTones; typedef struct _ModemTonesClass ModemTonesClass; typedef struct _ModemTonesPrivate ModemTonesPrivate; struct _ModemTonesClass { GObjectClass parent_class; }; struct _ModemTones { GObject parent; ModemTonesPrivate *priv; }; GType modem_tones_get_type(void); /* TYPE MACROS */ #define MODEM_TYPE_TONES \ (modem_tones_get_type()) #define MODEM_TONES(obj) \ (G_TYPE_CHECK_INSTANCE_CAST((obj), MODEM_TYPE_TONES, ModemTones)) #define MODEM_TONES_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST((klass), MODEM_TYPE_TONES, ModemTonesClass)) #define MODEM_IS_TONES(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE((obj), MODEM_TYPE_TONES)) #define MODEM_IS_TONES_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE((klass), MODEM_TYPE_TONES)) #define MODEM_TONES_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), MODEM_TYPE_TONES, ModemTonesClass)) enum { TONES_STOP = -2, TONES_NONE = -1, TONES_EVENT_DTMF_0 = 0, TONES_EVENT_DTMF_1 = 1, TONES_EVENT_DTMF_2 = 2, TONES_EVENT_DTMF_3 = 3, TONES_EVENT_DTMF_4 = 4, TONES_EVENT_DTMF_5 = 5, TONES_EVENT_DTMF_6 = 6, TONES_EVENT_DTMF_7 = 7, TONES_EVENT_DTMF_8 = 8, TONES_EVENT_DTMF_9 = 9, TONES_EVENT_DTMF_ASTERISK = 10, TONES_EVENT_DTMF_HASH = 11, TONES_EVENT_DTMF_A = 12, TONES_EVENT_DTMF_B = 13, TONES_EVENT_DTMF_C = 14, TONES_EVENT_DTMF_D = 15, TONES_EVENT_DIAL = 66, TONES_EVENT_RINGING = 70, TONES_EVENT_BUSY = 72, TONES_EVENT_CONGESTION = 73, TONES_EVENT_SPECIAL_INFORMATION = 74, TONES_EVENT_CALL_WAITING = 79, TONES_EVENT_RADIO_PATH_ACK = 256, TONES_EVENT_RADIO_PATH_UNAVAILABLE = 257, TONES_EVENT_DROPPED = 257, }; guint modem_tones_start(ModemTones *, int event, unsigned duration); typedef void ModemTonesStoppedNotify(ModemTones *, guint event, gpointer data); guint modem_tones_start_full(ModemTones *, int event, int volume, unsigned duration, ModemTonesStoppedNotify *notify, gpointer data); guint modem_tones_is_playing(ModemTones const *self, guint source); int modem_tones_playing_event(ModemTones const *self, guint playing); void modem_tones_stop(ModemTones *, guint source); G_END_DECLS #endif /* #ifndef _MODEM_TONES_H_ */ telepathy-ring-2.3.24-37ad7111424725a5446e82dac516d7cab88149c7/ring-extensions/000077500000000000000000000000001251541261300251105ustar00rootroot00000000000000telepathy-ring-2.3.24-37ad7111424725a5446e82dac516d7cab88149c7/ring-extensions/Channel_Future.xml000066400000000000000000000061011251541261300305320ustar00rootroot00000000000000 Copyright (C) 2008 Collabora Ltd. Copyright (C) 2008 Nokia Corporation

This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version.

This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.

This interface contains functionality which we intend to incorporate into the Channel interface in future. It should be considered to be conceptually part of the core Channel interface, but without API or ABI guarantees.

If we add new functionality to the Channel interface, libraries that use generated code (notably telepathy-glib) will have it as part of their ABI forever, meaning we can't make incompatible changes. By using this interface as a staging area for future Channel functionality, we can try out new properties, signals and methods as application-specific extensions, then merge them into the core Channel interface when we have enough implementation experience to declare them to be stable.

The name is by analogy to Python's __future__ pseudo-module.

(in Channel.FUTURE pseudo-interface)

The ChannelBundle to which this channel belongs.

A channel's Bundle property can never change.

Older connection managers might not have this property. Clients (particularly the channel dispatcher) SHOULD recover by considering each channel to be in a bundle containing only that channel, distinct from all other bundles, which has no additional interfaces.

Channel_Interface_Dialstrings.xml000066400000000000000000000106201251541261300334450ustar00rootroot00000000000000telepathy-ring-2.3.24-37ad7111424725a5446e82dac516d7cab88149c7/ring-extensions> Copyright (C) 2005, 2006 Collabora Limited Copyright (C) 2005, 2006 Nokia Corporation Copyright (C) 2006 INdT

This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version.

This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.

An interface that gives a Channel the ability to send dial strings. The dial strings are used mainly for things like 2nd stage dialing or avoiding human interaction with interactive voice response systems (IVR). The syntax of dial string is in common with those defined in RFC4967. Dial string. Desired duration of each tone in milliseconds, or 0 for implementation-specific default . Desired duration of pause between tones in milliseconds, or 0 for implementation-specific default .

Sends the dial string on the channel.

The channel will send the dial string using appropriate means, such as RFC 4733 audio events or SIP INFO payload.

If the dial string is sent as distinct tones, each the tone will take the given duration or the implementation-specific default, if duration 0 is given.

The given stream ID or dial string was invalid. The requested event is not available on this stream.
Cancel sending the current dial string. Dialstring to be sent (optional).

A dial string being sent on this stream. The signal is provided for visual feedback indicating the fact that the channel is currently used to send dialtones and the media input from user is not actually getting through the audio stream. It also serves as a cue for Cancel method.

Sending the dial string on this stream has been stopped. The signal is provided for feedback indicating the fact that the channel is no more used to send dialtones and the media input from user can get get through the stream.

Channel_Interface_Mergeable_Conference.xml000066400000000000000000000116731251541261300352050ustar00rootroot00000000000000telepathy-ring-2.3.24-37ad7111424725a5446e82dac516d7cab88149c7/ring-extensions Copyright © 2009 Collabora Limited Copyright © 2009 Nokia Corporation

This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version.

This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.

(draft 1)

An interface for multi-user conference channels that can have additional individual channels merged into them after they are created.

This interface addresses part of freedesktop.org bug #24906 (GSM-compatible conference calls). GSM is currently the only protocol known to implement this; PBXs might implement it too.

It might be made into a mandatory-to-implement part of Conference, or kept as a separate interface, when stabilized.

Request that the given channel be incorporated into this channel.

The given channel SHOULD be added to Conference.DRAFT.Channels if and only if the underlying protocol signals the merge in some way. It MUST NOT be added to Conference.DRAFT.InitialChannels (to preserve immutability).

In GSM it is possible to merge additional calls into an ongoing conference.

In XMPP this method could be implemented to merge a 1-1 Text channel into a MUC Text channel by inviting the peer from the Text channel into the MUC, or to merge a 1-1 Jingle call into a Muji call by inviting the peer from the Jingle call into the Muji call. (MUC and Muji channels are both implemented by XMPP MUCs, with Handle_Type_Room.)

A channel with the same ChannelType as this one, but with TargetHandleType = CONTACT.

The given channel isn't suitable for merging into this one: for instance, it might have the wrong channel type or handle type. It will never be possible to merge channels into this particular conference. The given channel is theoretically suitable for merging into this one, but that's not currently possible for some reason (for instance, this SHOULD be raised if a limit on the number of channels in a conference is exceeded). [FIXME: PermissionDenied?]
Channel_Interface_Messages.xml000066400000000000000000000664071251541261300327470ustar00rootroot00000000000000telepathy-ring-2.3.24-37ad7111424725a5446e82dac516d7cab88149c7/ring-extensions Copyright (C) 2008 Collabora Ltd. Copyright (C) 2008 Nokia Corporation

This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version.

This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.

(draft version, not API-stable)

This interface extends the Text interface to support more general messages, including:

  • messages with attachments (like MIME multipart/mixed)
  • groups of alternatives (like MIME multipart/alternative)
  • delivery reports
  • any extra types of message we need in future

It also provides a hook for improved sent message status notification, to be used by the DeliveryReporting interface.

Although this specification supports formatted (rich-text) messages with unformatted alternatives, implementations SHOULD NOT attempt to send formatted messages until the Telepathy specification has also been extended to cover capability discovery for message formatting.

We intend to expose all rich-text messages as XHTML-IM, but on some protocols, formatting is an extremely limited subset of that format (e.g. there are protocols where foreground/background colours, font and size can be set, but only for entire messages). Until we can tell UIs what controls to offer to the user, it's unfriendly to offer the user controls that may have no effect.

If this interface is present, clients that support it SHOULD listen for the MessageSent and MessageReceived signals, and ignore the Sent and Received signal on the Text interface (which are guaranteed to duplicate signals from this interface).

A list of MIME types supported by this channel, with more preferred MIME types appearing earlier in the list. The list MAY include "*/*" to indicate that attachments with arbitrary MIME types can be sent. If the list is empty, this indicates that messages may only include a single "text/plain" part. Flags indicating the level of support for message parts on this channel.

Flags indicating the level of support for message parts on this channel. They are designed such that setting more flags always implies that the channel has more capabilities.

It is assumed that messages containing a textual message body (only), like the messages the Text interface was designed for, are always supported.

There is no flag indicating support for alternatives. This is because the SendMessage implementation can always accept messages containing alternatives, even if the underlying protocol does not, by deleting all alternatives except the first (most preferred) that is supported.

Each of the flags 1, 2, 4 implies the previous flag, so we could have used a simple enumeration here; however, we've defined the message-part support indicator as a flag set for future expansion.
SendMessage will accept messages containing a single part of any type listed in the SupportedContentTypes property, with no accompanying text. SendMessage will accept messages containing a textual message body, plus a single attachment of any type listed in the SupportedContentTypes property. It does not make sense for this flag to be set if Message_Part_Support_Flag_Data_Only is not also set (because the connection manager can trivially provide an empty text part if necessary). SendMessage will accept messages containing a textual message body, plus an arbitrary number of attachments of any type listed in the SupportedContentTypes property. It does not make sense for this flag to be set if Message_Part_Support_Flag_One_Attachment is not also set.

Part of a message's content. In practice, this mapping never appears in isolation - messages are represented by a list of Message_Part mappings.

An example of how a message might look, in a Python-like syntax:

[
  {
    'message-sender': 42,
    'message-sent': 1210067943,
    'message-received': 1210067947,
    'message-type': 0,
    'pending-message-id': 437,
  },
  { 'alternative': 'main',
    'type': 'text/html',
    'content': 'Here is a photo of my cat:<br />' +
               '<img src="cid:catphoto" alt="lol!" />' +
               '<br />Isn't it cute?',
  },
  { 'alternative': 'main',
    'type': 'text/plain',
    'content': 'Here is a photo of my cat:\n[IMG: lol!]\nIsn't it cute?',
  },
  { 'identifier': 'catphoto',
    'type': 'image/jpeg',
    'size': 101000,
    'needs-retrieval': True,
  },
]
          

The first part of the message contains "headers" which refer to the entire message.

It is an error for a connection manager to put keys referring to the message as a whole in the second or subsequent Message_Part, but clients MUST recover from this error by ignoring these keys in the second and subsequent parts.

Well-known keys for the message as a whole, and the corresponding value types, include:

message-sent (u - Unix_Timestamp)
The time the message was sent (if unavailable, the time it arrived at a central server MAY be used). Omitted if no reasonable approximation is available
message-received (u - Unix_Timestamp)
The time the message was received locally. SHOULD always be present.
message-sender (u - Contact_Handle)
The contact who sent the message. If 0 or omitted, the contact who sent the message could not be determined.
message-type (u - Channel_Text_Message_Type)
The type of message; if omitted, Channel_Text_Message_Type_Normal MUST be assumed. SHOULD be omitted for normal chat messages.
pending-message-id (u - Message_ID)
The incoming message ID. This MUST NOT be present on outgoing messages. Clients SHOULD NOT store this key - it is only valid for as long as the message remains unacknowledged.
interface (s - DBus_Interface)
This message is specific to the given interface, which is neither Text nor Messages. It SHOULD be ignored if that interface is not supported. (Note that an 'interface' key can also appear on the second and subsequent parts, where it indicates that that part (only) should be ignored if unsupported.)

The second and subsequent parts contain the message's content, including plain text, formatted text and/or attached files.

In any group of parts with the same non-empty value for the "alternative" key (which represent alternative versions of the same content), more faithful versions of the intended message MUST come before less faithful versions (note that this order is the opposite of MIME "multipart/alternative" parts). Clients SHOULD display the first alternative that they understand.

Specifying the preference order means that if the underlying protocol doesn't support alternatives, the CM can safely delete everything apart from the first supported alternative when sending messages.

Clients SHOULD present all parts that are not redundant alternatives in the order they appear in this array, possibly excluding parts that are referenced by another displayed part. It is implementation-specific how the parts are presented to the user.

This allows CMs to assume that all parts are actually shown to the user, even if they are not explicitly referenced - we do not yet recommend formatted text, and there is no way for plain text to reference an attachment since it has no concept of markup or references. This also forces clients to do something sensible with messages that consist entirely of "attachments", with no "body" at all.

For instance, when displaying the above example, a client that understands the HTML part should display the JPEG image once, between the two lines "Here is a photo of my cat:" and "Isn't it cute?"; it may additionally present the image in some way for a second time, after "Isn't it cute?", or may choose not to.

A client that does not understand HTML, displaying the same message, should display the plain-text part, followed by the JPEG image.

Well-known keys for the second and subsequent parts, and the corresponding value types, include:

identifier (s)
An opaque identifier for this part. Parts of a message MAY reference other parts by treating this identifier as if it were a MIME Content-ID and using the cid: URI scheme.
alternative (s)

If present, this part of the message is an alternative for all other parts with the same value for "alternative". Clients SHOULD only display one of them (this is expected to be used for XHTML messages in a future version of this specification).

If omitted, this part is not an alternative for any other part.

Parts of a message MAY reference the group of alternatives as a whole (i.e. a reference to whichever of them is chosen) by treating this identifier as if it were the MIME Content-ID of a multipart/alternative part, and using the cid: URI scheme.

type (s)

The MIME type of this part. See the documentation for ReceivedMessage for notes on the special status of "text/plain" parts.

Connection managers MUST NOT signal parts without a 'type' key; if a protocol provides no way to determine the MIME type, the connection manager is responsible for guessing it, but MAY fall back to "text/plain" for text and "application/octet-stream" for non-text.

Clients MUST ignore parts without a 'type' key, which are reserved for future expansion.

lang (s)
The natural language of this part, identified by a RFC 3066 language tag. XMPP allows alternative-selection by language as well as by content-type.
size (u)
The size in bytes (if needs-retrieval is true, this MAY be an estimated or approximate size). SHOULD be omitted if 'content' is provided. There's no point in providing the size if you're already providing all the content.
needs-retrieval (b)
If false or omitted, the connection manager already holds this part in memory. If present and true, this part will be retrieved on demand (like MIME's message/external-body), so clients should expect retrieval to take time; if this specification is later extended to provide a streaming version of GetPendingMessageContent, clients should use it for parts with this flag.
truncated (b)
The content available via the 'content' key or GetPendingMessageContent has been truncated by the server or connection manager (equivalent to Channel_Text_Message_Flag_Truncated in the Text interface).
content (s or ay)
The part's content, if it is available and sufficiently small to include here (implies that 'needs-retrieval' is false or omitted). Otherwise, omitted. If the part is human-readable text or HTML, the value for this key MUST be a UTF-8 string (D-Bus signature 's'). If the part is not text, the value MUST be a byte-array (D-Bus signature 'ay'). If the part is a text-based format that is not the main body of the message (e.g. an iCalendar or an attached XML document), the value SHOULD be a UTF-8 string, transcoding from another charset to UTF-8 if necessary, but MAY be a byte-array (of unspecified character set) if transcoding fails or the source charset is not known.
interface (s - DBus_Interface)
This part is specific to the given interface, which is neither Text nor Messages. It SHOULD be ignored if that interface is not supported. (Note that an 'interface' key can also appear on the first part, where it indicates that the entire message should be ignored if unsupported.)

It is an error for a connection manager to put these keys in the first Message_Part, but clients MUST be able to recover from this error by ignoring these keys in the first part.

A key, which SHOULD be one of the well-known keys specified, if possible. The value corresponding to the given key, which must be of one of the types indicated.

An opaque token used to identify sent messages. As a special case, the empty string indicates that there is no particular identification for a message.

CM implementations SHOULD use an identifier expected to be unique, such as a UUID, if possible.

Some protocols can only track a limited number of sent messages in a small message-ID space. As a result, clients MUST NOT assume that message tokens will not be re-used, and SHOULD use some reasonable heuristic to assign delivery reports to messages, such as matching on message content or timestamp (if available), or assuming that the delivery report refers to the most recent message with that ID.

This is a hook for the DeliveryReporting interface, to avoid having to introduce a SendMultiPartMessageAndReturnToken method in that interface.

Submit a message to the server for sending. If this method returns successfully, the message has been submitted to the server and the MessageSent signal is emitted. A corresponding Sent signal on the Text interface MUST also be emitted.

If this method fails, message submission to the server has failed and no signal on this interface (or the Text interface) is emitted.

The message content, including any attachments or alternatives Flags affecting how the message is sent. An opaque token used to match any incoming delivery or failure reports against this message, or an empty string if the message is not readily identifiable. The requested message is malformed and cannot be sent.
Flags altering the way a message is sent. The "most usual" action should always be to have these flags unset. Provide a delivery report via the DeliveryReporting interface, if possible, even if this is not the default for this protocol. Ignored if delivery reports are not possible on this protocol. In some protocols, like XMPP, it is not conventional to request or send delivery notifications. Signals that a message has been submitted for sending. This MUST be emitted exactly once per emission of the Sent signal on the Text interface. This signal allows a process that is not the caller of SendMessage to log sent messages. The double signal-emission means that clients can safely follow the following rule: if the channel has the Messages interface, listen for Messages.MessageSent only; otherwise, listen for Text.Sent only. The message content (see Message_Part for full details). If the message that was passed to SendMessage has a formatted text part that the connection manager recognises, but no text/plain alternative, the CM MUST use the formatted text part to generate a text/plain alternative which is also included in this signal argument. An opaque token used to match any incoming delivery or failure reports against this message, or an empty string if the message is not readily identifiable. A list of incoming messages that have neither been acknowledged nor rejected. This list is a superset of the one returned by ListPendingMessages on the Text interface; its items can be removed using AcknowledgePendingMessages on that interface. The messages with the given IDs have been removed from the PendingMessages list. Clients SHOULD NOT attempt to acknowledge those messages. This completes change notification for the PendingMessages property (previously, there was change notification when pending messages were added, but not when they were removed). The messages that have been removed from the pending message list. Retrieve the content of one or more parts of a pending message. Note that this function may take a considerable amount of time to return if the part's 'needs-retrieval' flag is true; consider extending the default D-Bus method call timeout. Additional API is likely to be added in future, to stream large message parts. The ID of a pending message The desired entries in the array of message parts, identified by their position. The "headers" part (which is not a valid argument to this method) is considered to be part 0, so the valid part numbers start at 1 (for the second Message_Part).

The content of the requested parts. The keys in this mapping are positions in the array of message parts; the values are either of type 's' or 'ay' (UTF-8 text string, or byte array), following the same rules as for the value of the 'content' key in the Message_Part mappings.

If the one of the requested part numbers was greater than zero but referred to a part that had no content (i.e. it had no 'type' key or no 'content' key), it is simply omitted from this mapping; this is not considered to be an error condition.

Either there is no pending message with the given message ID, or one of the part numbers given was 0 or too large.
Signals that a message has been received and added to the pending messages queue. This MUST be emitted exactly once per emission of the Received signal on the Text interface. The double signal-emission means that clients can safely follow the following rule: if the channel has the Messages interface, listen for Messages.MessageReceived only; otherwise, listen for Text.Received only. The message content, including any attachments or alternatives
Channel_Interface_Splittable.xml000066400000000000000000000062431251541261300332730ustar00rootroot00000000000000telepathy-ring-2.3.24-37ad7111424725a5446e82dac516d7cab88149c7/ring-extensions Copyright © 2009 Collabora Limited Copyright © 2009 Nokia Corporation

This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version.

This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.

(draft 1)

An interface for channels that can be made conceptually part of a Conference.DRAFT, and can then be detached from that conference.

This interface addresses part of freedesktop.org bug #24906 (GSM-compatible conference calls). GSM is currently the only protocol known to implement this; PBXs might implement it too.

Request that this channel is removed from any Conference.DRAFT of which it is a part.

This implies that the media streams within the conference are put on hold and the media streams within the member channel leaving the conference are unheld. [FIXME: or, maybe it'd be less surprising if it didn't do this?]

This channel isn't in a conference. This channel is in a conference but can't currently be split away from it.
telepathy-ring-2.3.24-37ad7111424725a5446e82dac516d7cab88149c7/ring-extensions/Makefile.am000066400000000000000000000062151251541261300271500ustar00rootroot00000000000000tools_dir = $(top_srcdir)/tools EXTRA_DIST = all.xml.in $(EXT_IFACES) EXT_IFACES = \ $(srcdir)/Channel_Future.xml \ $(srcdir)/Channel_Interface_Splittable.xml \ $(srcdir)/Channel_Interface_Mergeable_Conference.xml NOT_IFACES = \ $(srcdir)/Channel_Interface_Messages.xml \ $(srcdir)/Channel_Interface_Dialstrings.xml if HAVE_TP_EXTENSIONS all.xml: all.xml.in Makefile.am $(AM_V_GEN) $(SHELL) $(srcdir)/all.xml.in $(EXT_IFACES) > $@ noinst_LIBRARIES = libtpextensions.a libtpextensions_a_SOURCES = \ ring-extensions.h \ gtypes.h \ body.c # message-mixin.h message-mixin.c nodist_libtpextensions_a_SOURCES = \ _gen/signals-marshal.c \ _gen/signals-marshal.h \ _gen/signals-marshal.list \ _gen/enums.h \ _gen/gtypes.h _gen/gtypes-body.h \ _gen/interfaces.h \ _gen/interfaces-body.h \ _gen/svc.h _gen/svc.c BUILT_SOURCES = \ all.xml \ _gen/all.xml \ $(nodist_libtpextensions_a_SOURCES) # extensions.html clean-local: rm -f $(BUILT_SOURCES) _gen/*-gtk-doc.h rmdir _gen CLEANFILES = $(BUILT_SOURCES) INCLUDES = -I${top_builddir} -I${top_srcdir} AM_CFLAGS = $(ERROR_CFLAGS) @DBUS_CFLAGS@ @GLIB_CFLAGS@ @TP_CFLAGS@ AM_LDFLAGS = @TP_LIBS@ @DBUS_LIBS@ @GLIB_LIBS@ # Generated stuff DROP_NAMESPACE = sed -e 's@xmlns:tp="http://telepathy\.freedesktop\.org/wiki/DbusSpec.extensions-v0"@@g' XSLTPROCFLAGS = --nonet --novalid _gen/all.xml: all.xml $(EXT_IFACES) Makefile.am @$(mkdir_p) _gen $(AM_V_GEN)$(PYTHON) $(tools_dir)/xincludator.py \ $< > $@.tmp && mv $@.tmp $@ extensions.html: _gen/all.xml $(tools_dir)/doc-generator.xsl Makefile.am $(AM_V_GEN)$(XSLTPROC) $(XSLTPROCFLAGS) \ --param "allow-undefined-interfaces" "true()" \ $(tools_dir)/doc-generator.xsl \ $< > $@ _gen/svc.c _gen/svc.h: _gen/all.xml $(tools_dir)/glib-ginterface-gen.py \ Makefile.am $(AM_V_GEN)$(PYTHON) $(tools_dir)/glib-ginterface-gen.py \ --filename=_gen/svc --signal-marshal-prefix=ring_tp \ --include='' \ --include='"_gen/signals-marshal.h"' \ --allow-unstable \ --not-implemented-func='tp_dbus_g_method_return_not_implemented' \ $< Ring_Svc_ _gen/signals-marshal.list: _gen/all.xml \ $(tools_dir)/glib-signals-marshal-gen.py \ Makefile.am $(AM_V_GEN)$(PYTHON) $(tools_dir)/glib-signals-marshal-gen.py $< > $@ _gen/signals-marshal.h: _gen/signals-marshal.list Makefile.am $(AM_V_GEN)$(GLIB_GENMARSHAL) --header --prefix=ring_tp_marshal $< > $@ _gen/signals-marshal.c: _gen/signals-marshal.list Makefile.am $(AM_V_GEN){ echo '#include "_gen/signals-marshal.h"' && \ $(GLIB_GENMARSHAL) --body --prefix=ring_tp_marshal $< ; } > $@ _gen/enums.h: _gen/all.xml $(tools_dir)/c-constants-gen.py \ Makefile.am $(AM_V_GEN)$(PYTHON) $(tools_dir)/c-constants-gen.py Tp $< _gen/enums _gen/interfaces.h _gen/interfaces-body.h: _gen/all.xml \ $(tools_dir)/glib-interfaces-gen.py \ Makefile.am $(AM_V_GEN)$(PYTHON) $(tools_dir)/glib-interfaces-gen.py \ Ring _gen/interfaces-body.h _gen/interfaces.h $< _gen/gtypes.h _gen/gtypes-body.h: _gen/all.xml \ $(tools_dir)/glib-gtypes-generator.py Makefile.am $(AM_V_GEN)$(PYTHON) $(tools_dir)/glib-gtypes-generator.py \ $< _gen/gtypes Ring endif telepathy-ring-2.3.24-37ad7111424725a5446e82dac516d7cab88149c7/ring-extensions/Tones.xml000066400000000000000000000135171251541261300267310ustar00rootroot00000000000000 Copyright (C) 2008 Nokia Corporation An interface for controlling tone generator. The event values are defined in RFC 4733. and RFC 2833. The power level of the tone with range from 0 to -63 dBm0. (Note: A preferred volume range is -8 dBm0 to -3 dBm0.) The duration of the tone in milliseconds. The tone starts when the method call is received and lasts for the duration value. The value 0 indicates indefinite duration, the tone is played until StopTone() is called. DTMF Digit 0 DTMF Digit 0 DTMF Digit 0 DTMF Digit 0 DTMF Digit 0 DTMF Digit 0 DTMF Digit 0 DTMF Digit 0 DTMF Digit 0 DTMF Digit 0 DTMF special character * DTMF special character # DTMF Digit 0 DTMF Digit 0 DTMF Digit 0 DTMF Digit 0 If true, the tone is looped indefinitely until StopTone() is called. Description of tone. The power level of the tone with range from 0 to -63 dBm0. (Note: A preferred volume range is -8 dBm0 to -3 dBm0.) The duration of the tone in milliseconds. The tone starts when the previous tone ends and lasts for the specified duration. The value 0 indicates indefinite duration, the tone is played until StopTone() is called. The modulation frequency in Hz. If there is no modulation, this field has a value of zero. The frequencies of the tones to be added, measured in Hz. A value of zero or an empty array indicates silence.

Stop tone generation.

telepathy-ring-2.3.24-37ad7111424725a5446e82dac516d7cab88149c7/ring-extensions/all.xml.in000077500000000000000000000007101251541261300270100ustar00rootroot00000000000000#!/bin/sh cat << EOF Telepathy Extensions for Telepathy-Ring Copyright (C) 2007, 2008 Nokia Corporation ` for iface in "$@" do echo $iface | sed 's-^--;' done ` EOF telepathy-ring-2.3.24-37ad7111424725a5446e82dac516d7cab88149c7/ring-extensions/body.c000066400000000000000000000002221251541261300262050ustar00rootroot00000000000000#include #include #include "_gen/gtypes-body.h" #include "_gen/interfaces-body.h" telepathy-ring-2.3.24-37ad7111424725a5446e82dac516d7cab88149c7/ring-extensions/debug-internal.h000066400000000000000000000001721251541261300301610ustar00rootroot00000000000000/* debug-internal.h for message-mixin.c */ #undef DEBUG_FLAG #define DEBUG_FLAG RING_DEBUG_SMS #include "../src/debug.h" telepathy-ring-2.3.24-37ad7111424725a5446e82dac516d7cab88149c7/ring-extensions/gtypes.h000066400000000000000000000000511251541261300265700ustar00rootroot00000000000000#include "ring-extensions/_gen/gtypes.h" telepathy-ring-2.3.24-37ad7111424725a5446e82dac516d7cab88149c7/ring-extensions/ring-extensions.h000066400000000000000000000003421251541261300304140ustar00rootroot00000000000000#ifndef RING_EXTENSIONS_H #define RING_EXTENSIONS_H #include "ring-extensions/_gen/svc.h" #include "ring-extensions/_gen/enums.h" #include "ring-extensions/_gen/gtypes.h" #include "ring-extensions/_gen/interfaces.h" #endif telepathy-ring-2.3.24-37ad7111424725a5446e82dac516d7cab88149c7/rpm/000077500000000000000000000000001251541261300225525ustar00rootroot00000000000000telepathy-ring-2.3.24-37ad7111424725a5446e82dac516d7cab88149c7/rpm/telepathy-ring.changes000066400000000000000000000015431251541261300270430ustar00rootroot00000000000000* Mon Apr 15 2013 Tom Swindell - 2.3.0 - Updated ring media manager properties specification * Wed Dec 05 2012 John Brooks - 2.2.1 - Added always_dispatch flag for account to stay connected when not online * Thu Nov 29 2012 John Brooks - 2.2.0 - Fixes NEMO#578: Added mission-control plugin to manage creation of the telepathy-ring account - Removed ensure-tpring-account.sh script - Patches moved to upstream repository * Thu Oct 04 2012 Marko Saukko - 2.1.3 - Require telepathy-filesystem. * Thu Aug 23 2012 Tom Swindell - 2.1.3-2 - Added auto-generation of telepathy ring account if not present on device boot. * Tue Jul 31 2012 Marko Saukko - 2.1.3 - Initial packaging for nemo. telepathy-ring-2.3.24-37ad7111424725a5446e82dac516d7cab88149c7/rpm/telepathy-ring.spec000066400000000000000000000035731251541261300263720ustar00rootroot00000000000000# # Do NOT Edit the Auto-generated Part! # Generated by: spectacle version 0.26 # Name: telepathy-ring # >> macros # << macros %define keepstatic 1 Summary: GSM connection manager for the Telepathy framework Version: 2.2.1 Release: 2 Group: System/Libraries License: LGPLv2.1 URL: https://github.com/nemomobile/telepathy-ring/ Source0: %{name}-%{version}.tar.bz2 Source100: telepathy-ring.yaml Requires: ofono Requires: telepathy-mission-control BuildRequires: pkgconfig(glib-2.0) BuildRequires: pkgconfig(check) BuildRequires: pkgconfig(libxslt) BuildRequires: pkgconfig(dbus-1) BuildRequires: pkgconfig(dbus-glib-1) BuildRequires: pkgconfig(uuid) BuildRequires: pkgconfig(telepathy-glib) >= 0.11.7 BuildRequires: pkgconfig(mission-control-plugins) BuildRequires: python >= 2.5 %description %{summary}. %package tests Summary: Tests for %{name} Group: System/Libraries Requires: %{name} = %{version}-%{release} %description tests %{summary}. %package devel Summary: Development files for %{name} Group: Development/Libraries Requires: %{name} = %{version}-%{release} %description devel %{summary}. %prep %setup -q -n %{name}-%{version} # >> setup # << setup %build # >> build pre mkdir m4 || true # << build pre %reconfigure make %{?jobs:-j%jobs} # >> build post # << build post %install rm -rf %{buildroot} # >> install pre # << install pre %make_install # >> install post # << install post %files %defattr(-,root,root,-) %{_datadir}/dbus-1/services/* %{_datadir}/telepathy/managers/* %{_libexecdir}/* %{_libdir}/mission-control-plugins.0/mcp-account-manager-ring.so %doc %{_mandir}/man8/telepathy-ring.8.gz # >> files # << files %files tests %defattr(-,root,root,-) /opt/tests/%{name}/* # >> files tests # << files tests %files devel %defattr(-,root,root,-) %{_libdir}/*.a %{_includedir}/* # >> files devel # << files devel telepathy-ring-2.3.24-37ad7111424725a5446e82dac516d7cab88149c7/rpm/telepathy-ring.yaml000066400000000000000000000022031251541261300263670ustar00rootroot00000000000000Name: telepathy-ring Summary: GSM connection manager for the Telepathy framework Version: 2.2.1 Release: 2 Group: System/Libraries License: LGPLv2.1 URL: https://github.com/nemomobile/telepathy-ring/ Sources: - "%{name}-%{version}.tar.bz2" Description: "%{summary}." Requires: - ofono - telepathy-mission-control PkgBR: - python >= 2.5 PkgConfigBR: - glib-2.0 - check - libxslt - dbus-1 - dbus-glib-1 - uuid - telepathy-glib >= 0.11.7 - mission-control-plugins Configure: reconfigure Builder: make Files: - "%{_datadir}/dbus-1/services/*" - "%{_datadir}/telepathy/managers/*" - "%{_libexecdir}/*" - "%{_libdir}/mission-control-plugins.0/mcp-account-manager-ring.so" - "%doc %{_mandir}/man8/telepathy-ring.8.gz" SubPackages: - Name: tests Summary: Tests for %{name} Group: System/Libraries Description: "%{summary}." Files: - "/opt/tests/%{name}/*" - Name: devel Summary: Development files for %{name} Group: Development/Libraries Description: "%{summary}." Files: - "%{_libdir}/*.a" - "%{_includedir}/*" telepathy-ring-2.3.24-37ad7111424725a5446e82dac516d7cab88149c7/scripts/000077500000000000000000000000001251541261300234435ustar00rootroot00000000000000telepathy-ring-2.3.24-37ad7111424725a5446e82dac516d7cab88149c7/scripts/Makefile.am000066400000000000000000000000331251541261300254730ustar00rootroot00000000000000EXTRA_DIST = start-ring.sh telepathy-ring-2.3.24-37ad7111424725a5446e82dac516d7cab88149c7/scripts/lcov.mk000066400000000000000000000013671251541261300247460ustar00rootroot00000000000000 # ccache breaks -fprofile-arcs export CCACHE_DISABLE=1 OUT=lcov all : lcov-build lcov-clean: find -name "*.gcno" -o -name "*.gcda" -exec rm '{}' ';' rm -rf $(OUT) lcov-build: $(MAKE) CFLAGS="-O0 -fprofile-arcs -ftest-coverage" LDFLAGS="-lgcov" check lcov-report: : # hack: move gcov file from libraries back to source directory for dir in `find -name .libs`; do \ (cd `dirname $$dir`; mv .libs/*.gc?? . || true) 2>/dev/null; \ done mkdir -p $(OUT) lcov -d . -c >$(OUT)/lcov.info 2>/dev/null lcov -l $(OUT)/lcov.info 2>/dev/null |\ egrep '(^/usr|/test.*\.c|signals-marshal)' |\ cut -d: -f1 >$(OUT)/lcov.remove lcov -r $(OUT)/lcov.info `cat $(OUT)/lcov.remove` 2>/dev/null >$(OUT)/lcov.info.clean genhtml -o lcov $(OUT)/lcov.info.clean telepathy-ring-2.3.24-37ad7111424725a5446e82dac516d7cab88149c7/scripts/lcov.sh000066400000000000000000000001761251541261300247460ustar00rootroot00000000000000#!/bin/sh make -f scripts/lcov.mk lcov-clean && \ make -f scripts/lcov.mk lcov-build && \ make -f scripts/lcov.mk lcov-report telepathy-ring-2.3.24-37ad7111424725a5446e82dac516d7cab88149c7/scripts/start-ring.sh000077500000000000000000000007221251541261300260750ustar00rootroot00000000000000#! /bin/sh USER=user # obtain session bus address for Ring test -r /tmp/session_bus_address.$USER && . /tmp/session_bus_address.$USER test -x /usr/bin/waitdbus && /usr/bin/waitdbus session export RING_REALTIME=10 export RING_MEMLOCK=32M # Uncomment to obtain debug output # RING_DEBUG=all # CALL_DEBUG=all # SMS_DEBUG=all # MODEM_DEBUG=all # Make ring wait for Connection requests export RING_PERSIST=1 exec ${RING_VALGRIND} /usr/lib/telepathy/telepathy-ring telepathy-ring-2.3.24-37ad7111424725a5446e82dac516d7cab88149c7/src/000077500000000000000000000000001251541261300225435ustar00rootroot00000000000000telepathy-ring-2.3.24-37ad7111424725a5446e82dac516d7cab88149c7/src/Makefile.am000066400000000000000000000055221251541261300246030ustar00rootroot00000000000000# # Makefile.am for telepathy-ring/src # # Copyright (C) 2007 Nokia Corporation # Contact: Pekka pessi # See file COPYING. # # ----------------------------------------------------------------------------- # Automake options # ----------------------------------------------------------------------------- # Headers and libraries AM_CFLAGS = $(ERROR_CFLAGS) @GLIB_CFLAGS@ @DBUS_CFLAGS@ @TP_CFLAGS@ INCLUDES = -I$(top_srcdir) -I$(top_srcdir)/tests -I${top_builddir} AM_LDFLAGS = # Build targets libexec_PROGRAMS = telepathy-ring noinst_LTLIBRARIES = libtpring.la # ----------------------------------------------------------------------------- # Tests test_PROGRAMS = test-ring TESTS = ${test_PROGRAMS} test_ring_SOURCES = tests/test-ring.h tests/test-ring.c tests/test-ring-util.c test_ring_LDADD = \ libtpring.la $(TP_EXTLIB) \ ../modem/libmodem-glib.la \ ../tests/libtestcommon.la \ @TP_LIBS@ @DBUS_LIBS@ @GLIB_LIBS@ \ @CHECK_LIBS@ -lpthread # ----------------------------------------------------------------------------- # Rules for building the targets libtpring_la_SOURCES = \ base-call-channel.h base-call-channel.c \ ring-call-content.h ring-call-content.c \ ring-call-stream.h ring-call-stream.c \ ring-call-member.h ring-call-member.c \ ring-connection-manager.h ring-connection-manager.c \ ring-protocol.h ring-protocol.c \ ring-connection.h ring-connection.c \ ring-debug.h ring-debug.c \ ring-text-manager.h ring-text-manager.c \ ring-text-channel.h ring-text-channel.c \ ring-media-manager.h ring-media-manager.c \ ring-media-channel.h ring-media-channel.c \ ring-call-channel.h ring-call-channel.c \ ring-streamed-media-mixin.h ring-streamed-media-mixin.c \ ring-member-channel.h ring-member-channel.c \ ring-conference-manager.h ring-conference-manager.c \ ring-conference-channel.h ring-conference-channel.c \ ring-param-spec.h ring-param-spec.c \ ring-emergency-service.h ring-emergency-service.c \ ring-util.h ring-util.c \ util.h util.c TP_EXTLIB = TP_EXTLIB += $(top_builddir)/ring-extensions/libtpextensions.a telepathy_ring_LDADD = \ libtpring.la $(TP_EXTLIB) \ ../modem/libmodem-glib.la \ @TP_LIBS@ @DBUS_LIBS@ @GLIB_LIBS@ \ -lpthread # ---------------------------------------------------------------------- # Install and distribution rules managerdir = $(datadir)/telepathy/managers dist_manager_DATA = ring.manager service = org.freedesktop.Telepathy.ConnectionManager.ring.service # configure script would insert ${prefix}/lib/telepathy-ring in service file .service-in.service: @-rm -f $@ $(AM_V_GEN)sed -e 's![@]libexecdir[@]!${libexecdir}!' '${srcdir}/$@-in' > $@ # Install services servicedir = $(DBUS_SERVICES_DIR) service_DATA = ${service} ${service} : Makefile EXTRA_DIST = ${service}-in ${handlers} CLEANFILES = ${service} telepathy-ring-2.3.24-37ad7111424725a5446e82dac516d7cab88149c7/src/base-call-channel.c000066400000000000000000000223301251541261300261400ustar00rootroot00000000000000/* * base-call-channel.c - Source for RingBaseCallChannel * Copyright © 2009–2010 Collabora Ltd. * @author Sjoerd Simons * @author Tom Swindell * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "config.h" #include #include #include #include #include #include "util.h" #include "ring-call-content.h" #include "ring-call-member.h" #include "base-call-channel.h" #include "ring-connection.h" /* #define DEBUG_FLAG RING_DEBUG_MEDIA #include "debug.h" */ G_DEFINE_TYPE(RingBaseCallChannel, ring_base_call_channel, TP_TYPE_BASE_MEDIA_CALL_CHANNEL); static void ring_base_call_channel_hangup ( TpBaseCallChannel *base, guint reason, const gchar *detailed_reason, const gchar *message); static void ring_base_call_channel_close (TpBaseChannel *base); /* properties */ enum { PROP_OBJECT_PATH_PREFIX = 1, LAST_PROPERTY }; /* private structure */ struct _RingBaseCallChannelPrivate { gchar *object_path_prefix; gboolean dispose_has_run; /* handle -> CallMember object hash */ GHashTable *members; }; static void ring_base_call_channel_init (RingBaseCallChannel *self) { RingBaseCallChannelPrivate *priv = G_TYPE_INSTANCE_GET_PRIVATE (self, RING_TYPE_BASE_CALL_CHANNEL, RingBaseCallChannelPrivate); self->priv = priv; priv->members = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, g_object_unref); } static void ring_base_call_channel_dispose (GObject *object); static void ring_base_call_channel_finalize (GObject *object); static void ring_base_call_channel_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { RingBaseCallChannel *self = RING_BASE_CALL_CHANNEL (object); RingBaseCallChannelPrivate *priv = self->priv; switch (property_id) { case PROP_OBJECT_PATH_PREFIX: g_value_set_string (value, priv->object_path_prefix); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void ring_base_call_channel_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { RingBaseCallChannel *self = RING_BASE_CALL_CHANNEL (object); RingBaseCallChannelPrivate *priv = self->priv; switch (property_id) { case PROP_OBJECT_PATH_PREFIX: priv->object_path_prefix = g_value_dup_string (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static gchar * ring_base_call_channel_get_object_path_suffix (TpBaseChannel *base) { RingBaseCallChannel *self = RING_BASE_CALL_CHANNEL (base); RingBaseCallChannelPrivate *priv = self->priv; g_assert (priv->object_path_prefix != NULL); return g_strdup_printf ("%s/CallChannel%p", priv->object_path_prefix, self); } static void ring_base_call_channel_class_init ( RingBaseCallChannelClass *ring_base_call_channel_class) { GObjectClass *object_class = G_OBJECT_CLASS (ring_base_call_channel_class); TpBaseChannelClass *base_channel_class = TP_BASE_CHANNEL_CLASS (ring_base_call_channel_class); TpBaseCallChannelClass *tp_base_call_channel_class = TP_BASE_CALL_CHANNEL_CLASS (ring_base_call_channel_class); GParamSpec *param_spec; g_type_class_add_private (ring_base_call_channel_class, sizeof (RingBaseCallChannelPrivate)); object_class->get_property = ring_base_call_channel_get_property; object_class->set_property = ring_base_call_channel_set_property; object_class->dispose = ring_base_call_channel_dispose; object_class->finalize = ring_base_call_channel_finalize; base_channel_class->get_object_path_suffix = ring_base_call_channel_get_object_path_suffix; base_channel_class->close = ring_base_call_channel_close; tp_base_call_channel_class->hangup = ring_base_call_channel_hangup; param_spec = g_param_spec_string ("object-path-prefix", "Object path prefix", "prefix of the object path", NULL, G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_OBJECT_PATH_PREFIX, param_spec); } void ring_base_call_channel_dispose (GObject *object) { RingBaseCallChannel *self = RING_BASE_CALL_CHANNEL (object); RingBaseCallChannelPrivate *priv = self->priv; if (priv->dispose_has_run) return; self->priv->dispose_has_run = TRUE; tp_clear_pointer (&priv->members, g_hash_table_unref); if (G_OBJECT_CLASS (ring_base_call_channel_parent_class)->dispose) G_OBJECT_CLASS (ring_base_call_channel_parent_class)->dispose (object); } void ring_base_call_channel_finalize (GObject *object) { RingBaseCallChannel *self = RING_BASE_CALL_CHANNEL (object); RingBaseCallChannelPrivate *priv = self->priv; g_free (priv->object_path_prefix); G_OBJECT_CLASS (ring_base_call_channel_parent_class)->finalize (object); } RingCallContent * ring_base_call_channel_add_content (RingBaseCallChannel *self, const gchar *name, TpCallContentDisposition disposition) { TpBaseChannel *base = TP_BASE_CHANNEL (self); gchar *object_path; TpBaseCallContent *content; gchar *escaped; /* FIXME could clash when other party in a one-to-one call creates a stream * with the same media type and name */ escaped = tp_escape_as_identifier (name); object_path = g_strdup_printf ("%s/Content_%s", tp_base_channel_get_object_path (base), escaped); g_free (escaped); content = g_object_new (RING_TYPE_CALL_CONTENT, "connection", tp_base_channel_get_connection (base), "object-path", object_path, "disposition", disposition, //"media-type", wocky_jingle_media_type_to_tp (mtype), "name", name, NULL); g_free (object_path); tp_base_call_channel_add_content (TP_BASE_CALL_CHANNEL (self), content); return RING_CALL_CONTENT (content); } static void call_member_flags_changed_cb (RingCallMember *member, TpCallMemberFlags flags, gpointer user_data) { TpBaseCallChannel *base = TP_BASE_CALL_CHANNEL (user_data); tp_base_call_channel_update_member_flags (base, ring_call_member_get_handle (member), flags, 0, TP_CALL_STATE_CHANGE_REASON_PROGRESS_MADE, "", ""); } RingCallMember * ring_base_call_channel_get_member_from_handle ( RingBaseCallChannel *self, TpHandle handle) { return g_hash_table_lookup (self->priv->members, GUINT_TO_POINTER (handle)); } RingCallMember * ring_base_call_channel_ensure_member_from_handle ( RingBaseCallChannel *self, TpHandle handle) { RingBaseCallChannelPrivate *priv = self->priv; RingCallMember *m; m = g_hash_table_lookup (priv->members, GUINT_TO_POINTER (handle)); if (m == NULL) { m = RING_CALL_MEMBER (g_object_new (RING_TYPE_CALL_MEMBER, "target", handle, "call", self, NULL)); g_hash_table_insert (priv->members, GUINT_TO_POINTER (handle), m); tp_base_call_channel_update_member_flags (TP_BASE_CALL_CHANNEL (self), ring_call_member_get_handle (m), ring_call_member_get_flags (m), 0, TP_CALL_STATE_CHANGE_REASON_PROGRESS_MADE, "", ""); ring_signal_connect_weak (m, "flags-changed", G_CALLBACK (call_member_flags_changed_cb), G_OBJECT (self)); } return m; } void ring_base_call_channel_remove_member (RingBaseCallChannel *self, RingCallMember *member) { TpHandle h = ring_call_member_get_handle (member); g_assert (g_hash_table_lookup (self->priv->members, GUINT_TO_POINTER (h))== member); ring_call_member_shutdown (member); tp_base_call_channel_remove_member (TP_BASE_CALL_CHANNEL (self), ring_call_member_get_handle (member), 0, TP_CALL_STATE_CHANGE_REASON_PROGRESS_MADE, "", ""); g_hash_table_remove (self->priv->members, GUINT_TO_POINTER (h)); } static void ring_base_call_channel_shutdown_all_members (RingBaseCallChannel *self) { GHashTableIter iter; gpointer value; g_hash_table_iter_init (&iter, self->priv->members); while (g_hash_table_iter_next (&iter, NULL, &value)) ring_call_member_shutdown (RING_CALL_MEMBER (value)); } static void ring_base_call_channel_hangup (TpBaseCallChannel *base, guint reason, const gchar *detailed_reason, const gchar *message) { ring_base_call_channel_shutdown_all_members ( RING_BASE_CALL_CHANNEL (base)); } static void ring_base_call_channel_close (TpBaseChannel *base) { ring_base_call_channel_shutdown_all_members ( RING_BASE_CALL_CHANNEL (base)); TP_BASE_CHANNEL_CLASS (ring_base_call_channel_parent_class)->close (base); } telepathy-ring-2.3.24-37ad7111424725a5446e82dac516d7cab88149c7/src/base-call-channel.h000066400000000000000000000062541251541261300261540ustar00rootroot00000000000000/* * base-call-channel.h - Header for RingBaseCallChannel * Copyright © 2009–2010 Collabora Ltd. * @author Sjoerd Simons * @author Tom Swindell * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __RING_BASE_CALL_CHANNEL_H__ #define __RING_BASE_CALL_CHANNEL_H__ #include #include #include #include "ring-call-content.h" #include "ring-call-member.h" G_BEGIN_DECLS typedef struct _RingBaseCallChannel RingBaseCallChannel; typedef struct _RingBaseCallChannelPrivate RingBaseCallChannelPrivate; typedef struct _RingBaseCallChannelClass RingBaseCallChannelClass; struct _RingBaseCallChannelClass { TpBaseMediaCallChannelClass parent_class; }; struct _RingBaseCallChannel { TpBaseMediaCallChannel parent; RingBaseCallChannelPrivate *priv; }; GType ring_base_call_channel_get_type (void); /* TYPE MACROS */ #define RING_TYPE_BASE_CALL_CHANNEL \ (ring_base_call_channel_get_type ()) #define RING_BASE_CALL_CHANNEL(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ RING_TYPE_BASE_CALL_CHANNEL, RingBaseCallChannel)) #define RING_BASE_CALL_CHANNEL_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST((klass), \ RING_TYPE_BASE_CALL_CHANNEL, RingBaseCallChannelClass)) #define RING_IS_BASE_CALL_CHANNEL(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE((obj), RING_TYPE_BASE_CALL_CHANNEL)) #define RING_IS_BASE_CALL_CHANNEL_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE((klass), RING_TYPE_BASE_CALL_CHANNEL)) #define RING_BASE_CALL_CHANNEL_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), \ RING_TYPE_BASE_CALL_CHANNEL, RingBaseCallChannelClass)) RingCallMember *ring_base_call_channel_ensure_member ( RingBaseCallChannel *self, const gchar *jid); void ring_base_call_channel_remove_member (RingBaseCallChannel *self, RingCallMember *member); RingCallMember *ring_base_call_channel_ensure_member_from_handle ( RingBaseCallChannel *self, TpHandle handle); RingCallMember * ring_base_call_channel_get_member_from_handle ( RingBaseCallChannel *self, TpHandle handle); RingCallContent * ring_base_call_channel_add_content ( RingBaseCallChannel *self, const gchar *name, TpCallContentDisposition disposition); void ring_base_call_channel_remove_content (RingBaseCallChannel *self, RingCallContent *content); GHashTable *ring_base_call_channel_get_members (RingBaseCallChannel *self); G_END_DECLS #endif /* #ifndef __RING_BASE_CALL_CHANNEL_H__*/ org.freedesktop.Telepathy.ConnectionManager.ring.service-in000066400000000000000000000001501251541261300360340ustar00rootroot00000000000000telepathy-ring-2.3.24-37ad7111424725a5446e82dac516d7cab88149c7/src[D-BUS Service] Name=org.freedesktop.Telepathy.ConnectionManager.ring Exec=@libexecdir@/telepathy-ring telepathy-ring-2.3.24-37ad7111424725a5446e82dac516d7cab88149c7/src/ring-call-channel.c000066400000000000000000001700111251541261300261650ustar00rootroot00000000000000/* * ring-call-channel.c - Source for RingCallChannel * Handles peer-to-peer calls * * Copyright (C) 2007-2009 Nokia Corporation * @author Pekka Pessi * Copyright (C) 2013-14 Jolla Ltd * * This work is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This work is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this work; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ /* * Based on telepathy-glib/examples/cm/echo/chan.c: * * """ * Copyright (C) 2007 Collabora Ltd. * Copyright (C) 2007 Nokia Corporation * * Copying and distribution of this file, with or without modification, * are permitted in any medium without royalty provided the copyright * notice and this notice are preserved. * """ */ #include "config.h" #define DEBUG_FLAG RING_DEBUG_MEDIA #include "ring-debug.h" #include "ring-call-channel.h" #include "ring-member-channel.h" #include "ring-emergency-service.h" #include "ring-util.h" #include "modem/service.h" #include "modem/call.h" #include "modem/tones.h" #include "modem/errors.h" #include #include #include #include #include #include #include #include #include #include #include #include "ring-connection.h" #include "ring-media-manager.h" #include "ring-param-spec.h" #if !defined(TP_CHANNEL_CALL_STATE_CONFERENCE_HOST) /* Added in tp-spec 0.19.11 */ #define TP_CHANNEL_CALL_STATE_CONFERENCE_HOST (32) #endif #include #include #include #include #include struct _RingCallChannelPrivate { guint anon_modes; char *dial2nd; char *emergency_service; char *initial_emergency_service; TpHandle peer_handle, initial_remote; char *accepted; ModemRequest *creating_call; struct { char *message; TpHandle actor; TpChannelGroupChangeReason reason; guchar causetype, cause; guint retry_source, retry_count; } release; struct { RingConferenceChannel *conference; TpHandle handle; } member; uint8_t state; unsigned constructed:1, released:1, closing:1, disposed:1; unsigned call_instance_seen:1; unsigned originating:1, terminating:1; unsigned :0; unsigned call_state; /* Channel.Interface.CallState bits */ struct { gulong emergency; gulong waiting, on_hold, forwarded; gulong notify_multiparty; } signals; }; /* properties */ enum { PROP_NONE, PROP_ANON_MODES, PROP_CURRENT_SERVICE_POINT, /* o.f.T.C.I.ServicePoint.CurrentServicePoint */ PROP_INITIAL_SERVICE_POINT, /* o.f.T.C.I.ServicePoint.InitialServicePoint */ /* ring-specific properties */ PROP_EMERGENCY_SERVICE, PROP_INITIAL_EMERGENCY_SERVICE, PROP_TERMINATING, PROP_ORIGINATING, PROP_MEMBER, PROP_MEMBER_MAP, PROP_CONFERENCE, PROP_PEER, PROP_INITIAL_REMOTE, LAST_PROPERTY }; static void ring_call_channel_implement_media_channel(RingMediaChannelClass *); static TpDBusPropertiesMixinIfaceImpl ring_call_channel_dbus_property_interfaces[]; static void ring_channel_call_state_iface_init(gpointer, gpointer); static void ring_channel_splittable_iface_init(gpointer, gpointer); static gboolean ring_call_channel_add_member( GObject *obj, TpHandle handle, const char *message, GError **error); static gboolean ring_call_channel_remove_member_with_reason( GObject *obj, TpHandle handle, const char *message, guint reason, GError **error); static gboolean ring_call_channel_accept_pending( GObject *, TpHandle handle, const char *message, GError **error); static gboolean ring_call_channel_request_remote( GObject *, TpHandle handle, const char *message, GError **error); static gboolean ring_call_channel_remote_pending( RingCallChannel *, TpHandle handle, const char *message); G_DEFINE_TYPE_WITH_CODE( RingCallChannel, ring_call_channel, RING_TYPE_MEDIA_CHANNEL, G_IMPLEMENT_INTERFACE(RING_TYPE_MEMBER_CHANNEL, NULL); G_IMPLEMENT_INTERFACE(TP_TYPE_SVC_CHANNEL_INTERFACE_GROUP, tp_group_mixin_iface_init); G_IMPLEMENT_INTERFACE(TP_TYPE_SVC_CHANNEL_INTERFACE_CALL_STATE, ring_channel_call_state_iface_init); G_IMPLEMENT_INTERFACE(TP_TYPE_SVC_CHANNEL_INTERFACE_SERVICE_POINT, NULL); G_IMPLEMENT_INTERFACE(RING_TYPE_SVC_CHANNEL_INTERFACE_SPLITTABLE, ring_channel_splittable_iface_init)); const char *ring_call_channel_interfaces[] = { RING_MEDIA_CHANNEL_INTERFACES, TP_IFACE_CHANNEL_INTERFACE_GROUP, TP_IFACE_CHANNEL_INTERFACE_CALL_STATE, TP_IFACE_CHANNEL_INTERFACE_SERVICE_POINT, RING_IFACE_CHANNEL_INTERFACE_SPLITTABLE, NULL }; static void ring_call_channel_fill_immutable_properties(TpBaseChannel *base, GHashTable *props); static void ring_call_channel_update_state(RingMediaChannel *_self, guint state, guint causetype, guint cause); static void ring_call_channel_play_error_tone(RingCallChannel *self, guint state, guint causetype, guint cause); static gboolean ring_call_channel_close(RingMediaChannel *_self, gboolean immediately); static void ring_call_channel_set_call_instance(RingMediaChannel *_self, ModemCall *ci); static gboolean ring_call_channel_validate_media_handle (gpointer _self, guint *handle, GError **); static gboolean ring_call_channel_create_streams (gpointer _self, TpHandle handle, gboolean audio, gboolean video, GError **); static guint ring_call_channel_get_member_handle(RingCallChannel *self); static void on_modem_call_state_dialing(RingCallChannel *self); static void on_modem_call_state_incoming(RingCallChannel *self); static void on_modem_call_state_waiting(RingCallChannel *self); static void on_modem_call_state_mo_alerting(RingCallChannel *self); #ifdef nomore static void on_modem_call_state_mt_alerting(RingCallChannel *self); static void on_modem_call_state_answered(RingCallChannel *self); #endif static void on_modem_call_state_active(RingCallChannel *self); static void on_modem_call_state_mo_release(RingCallChannel *, guint causetype, guint cause); static void on_modem_call_state_mt_release(RingCallChannel *, guint causetype, guint cause); static void on_modem_call_state_terminated(RingCallChannel *, guint causetype, guint cause); void reply_to_hangup (ModemCall *call_instance, ModemRequest *request, GError *error, gpointer user_data); static void ring_call_channel_released(RingCallChannel *self, TpHandle actor, TpChannelGroupChangeReason reason, char const *message, GError *error, char const *debug); static void on_modem_call_emergency(ModemCall *, char const *, RingCallChannel *); static void on_modem_call_on_hold(ModemCall *, int onhold, RingCallChannel *); static void on_modem_call_forwarded(ModemCall *, RingCallChannel *); static void on_modem_call_notify_multiparty(ModemCall *ci, GParamSpec *pspec, gpointer user_data); static void on_modem_call_waiting(ModemCall *, RingCallChannel *); /* ====================================================================== */ /* GObject interface */ static void ring_call_channel_init(RingCallChannel *self) { self->priv = G_TYPE_INSTANCE_GET_PRIVATE( self, RING_TYPE_CALL_CHANNEL, RingCallChannelPrivate); } static void ring_call_channel_constructed(GObject *object) { RingCallChannel *self = RING_CALL_CHANNEL(object); RingCallChannelPrivate *priv = self->priv; TpBaseChannel *base = TP_BASE_CHANNEL (self); TpBaseConnection *connection = tp_base_channel_get_connection (base); TpHandle self_handle; if (G_OBJECT_CLASS(ring_call_channel_parent_class)->constructed) G_OBJECT_CLASS(ring_call_channel_parent_class)->constructed(object); if (priv->peer_handle != 0) g_assert(priv->peer_handle == tp_base_channel_get_target_handle (base)); self_handle = tp_base_connection_get_self_handle(connection); tp_group_mixin_init( object, G_STRUCT_OFFSET(RingCallChannel, group), tp_base_connection_get_handles(connection, TP_HANDLE_TYPE_CONTACT), self_handle); priv->constructed = 1; } static void ring_call_channel_emit_initial(RingMediaChannel *_self) { TpGroupMixin *mixin = TP_GROUP_MIXIN(_self); RingCallChannel *self = RING_CALL_CHANNEL(_self); RingCallChannelPrivate *priv = self->priv; TpBaseChannel *base = TP_BASE_CHANNEL (self); TpHandle self_handle = mixin->self_handle; TpIntSet *member_set = tp_intset_new(); TpIntSet *local_pending_set = NULL, *remote_pending_set = NULL; char const *message = ""; TpChannelGroupChangeReason reason = 0; TpChannelGroupFlags add = 0, del = 0; tp_intset_add(member_set, tp_base_channel_get_initiator (base)); if (!tp_base_channel_is_requested (base)) { /* Incoming call */ message = "Channel created for incoming call"; reason = TP_CHANNEL_GROUP_CHANGE_REASON_INVITED; local_pending_set = tp_intset_new(); tp_intset_add(local_pending_set, self_handle); } else if (priv->initial_remote) { /* Outgoing call */ message = "Channel created for outgoing call"; reason = TP_CHANNEL_GROUP_CHANGE_REASON_INVITED; remote_pending_set = tp_intset_new(); tp_intset_add(remote_pending_set, priv->initial_remote); add |= TP_CHANNEL_GROUP_FLAG_CAN_RESCIND; } else { /* Outgoing call, but without handle */ message = "Channel created"; reason = TP_CHANNEL_GROUP_CHANGE_REASON_NONE; if (tp_base_channel_get_target_handle (base) == 0) add |= TP_CHANNEL_GROUP_FLAG_CAN_ADD; } add |= TP_CHANNEL_GROUP_FLAG_PROPERTIES; add |= TP_CHANNEL_GROUP_FLAG_MEMBERS_CHANGED_DETAILED; add |= TP_CHANNEL_GROUP_FLAG_CAN_REMOVE; tp_group_mixin_change_flags(G_OBJECT(self), add, del); tp_group_mixin_change_members(G_OBJECT(self), message, member_set, NULL, local_pending_set, remote_pending_set, tp_base_channel_get_initiator (base), reason); tp_intset_destroy(member_set); if (local_pending_set) tp_intset_destroy(local_pending_set); if (remote_pending_set) tp_intset_destroy(remote_pending_set); } static void ring_call_channel_get_property(GObject *obj, guint property_id, GValue *value, GParamSpec *pspec) { RingCallChannel *self = RING_CALL_CHANNEL(obj); RingCallChannelPrivate *priv = self->priv; switch (property_id) { case PROP_ANON_MODES: g_value_set_uint(value, priv->anon_modes); break; case PROP_ORIGINATING: g_value_set_boolean(value, priv->originating); break; case PROP_TERMINATING: g_value_set_boolean(value, priv->terminating); break; case PROP_MEMBER: g_value_set_uint(value, ring_call_channel_get_member_handle(self)); break; case PROP_MEMBER_MAP: g_value_take_boxed( value, ring_member_channel_get_handlemap(RING_MEMBER_CHANNEL(self))); break; case PROP_CONFERENCE: { char *object_path = NULL; if (priv->member.conference) { g_object_get(priv->member.conference, "object-path", &object_path, NULL); } else { object_path = strdup("/"); } g_value_take_boxed(value, object_path); } break; case PROP_INITIAL_SERVICE_POINT: g_value_take_boxed(value, ring_emergency_service_new(priv->initial_emergency_service)); break; case PROP_CURRENT_SERVICE_POINT: g_value_take_boxed(value, ring_emergency_service_new(priv->emergency_service)); break; case PROP_EMERGENCY_SERVICE: g_value_set_string(value, priv->emergency_service); break; case PROP_INITIAL_EMERGENCY_SERVICE: g_value_set_string(value, priv->initial_emergency_service); break; case PROP_PEER: g_value_set_uint(value, priv->peer_handle); break; case PROP_INITIAL_REMOTE: g_value_set_uint(value, priv->initial_remote); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, property_id, pspec); break; } } static void ring_call_channel_set_property(GObject *obj, guint property_id, const GValue *value, GParamSpec *pspec) { RingCallChannel *self = RING_CALL_CHANNEL(obj); RingCallChannelPrivate *priv = self->priv; TpBaseChannel *base = TP_BASE_CHANNEL (self); switch (property_id) { case PROP_ANON_MODES: priv->anon_modes = g_value_get_uint(value); break; case PROP_ORIGINATING: priv->originating = g_value_get_boolean(value); break; case PROP_TERMINATING: priv->terminating = g_value_get_boolean(value); break; case PROP_INITIAL_EMERGENCY_SERVICE: priv->initial_emergency_service = g_value_dup_string(value); break; case PROP_PEER: if (priv->peer_handle == 0) { if (tp_base_channel_get_target_handle (base) == 0 || tp_base_channel_get_target_handle (base) == g_value_get_uint(value)) priv->peer_handle = g_value_get_uint(value); } break; case PROP_INITIAL_REMOTE: priv->initial_remote = g_value_get_uint(value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, property_id, pspec); break; } } static void ring_call_channel_dispose(GObject *object) { RingCallChannel *self = RING_CALL_CHANNEL(object); RingCallChannelPrivate *priv = self->priv; TpBaseChannel *base = TP_BASE_CHANNEL (object); if (self->priv->disposed) return; self->priv->disposed = TRUE; if (priv->member.handle) { TpHandleRepoIface *repo = tp_base_connection_get_handles( tp_base_channel_get_connection (base), TP_HANDLE_TYPE_CONTACT); tp_handle_unref(repo, priv->member.handle); priv->member.handle = 0; } if (priv->release.retry_source) { g_source_remove(priv->release.retry_source); priv->release.retry_source = 0; } ((GObjectClass *)ring_call_channel_parent_class)->dispose(object); } static void ring_call_channel_finalize(GObject *object) { RingCallChannel *self = RING_CALL_CHANNEL(object); RingCallChannelPrivate *priv = self->priv; tp_group_mixin_finalize(object); g_free(priv->emergency_service); g_free(priv->initial_emergency_service); g_free(priv->dial2nd); g_free(priv->accepted); g_free(priv->release.message); G_OBJECT_CLASS(ring_call_channel_parent_class)->finalize(object); DEBUG("exit"); } /* ====================================================================== */ /* GObjectClass */ static void ring_call_channel_class_init(RingCallChannelClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS(klass); TpBaseChannelClass *base_chan_class = TP_BASE_CHANNEL_CLASS (klass); g_type_class_add_private(klass, sizeof (RingCallChannelPrivate)); object_class->constructed = ring_call_channel_constructed; object_class->get_property = ring_call_channel_get_property; object_class->set_property = ring_call_channel_set_property; object_class->dispose = ring_call_channel_dispose; object_class->finalize = ring_call_channel_finalize; base_chan_class->interfaces = ring_call_channel_interfaces; base_chan_class->target_handle_type = TP_HANDLE_TYPE_CONTACT; base_chan_class->fill_immutable_properties = ring_call_channel_fill_immutable_properties; ring_call_channel_implement_media_channel (RING_MEDIA_CHANNEL_CLASS(klass)); klass->dbus_properties_class.interfaces = ring_call_channel_dbus_property_interfaces; tp_dbus_properties_mixin_class_init(object_class, G_STRUCT_OFFSET(RingCallChannelClass, dbus_properties_class)); tp_group_mixin_init_dbus_properties(object_class); tp_group_mixin_class_init( object_class, G_STRUCT_OFFSET(RingCallChannelClass, group_class), ring_call_channel_add_member, NULL); tp_group_mixin_class_set_remove_with_reason_func( object_class, ring_call_channel_remove_member_with_reason); g_object_class_install_property( object_class, PROP_ANON_MODES, ring_param_spec_anon_modes()); g_object_class_install_property( object_class, PROP_INITIAL_SERVICE_POINT, g_param_spec_boxed("initial-service-point", "Initial Service Point", "The service point initially associated with this channel", TP_STRUCT_TYPE_SERVICE_POINT, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property( object_class, PROP_CURRENT_SERVICE_POINT, g_param_spec_boxed("current-service-point", "Current Service Point", "The service point currently associated with this channel", TP_STRUCT_TYPE_SERVICE_POINT, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property( object_class, PROP_EMERGENCY_SERVICE, g_param_spec_string("emergency-service", "Emergency Service", "Emergency service associated with this channel", "", G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property( object_class, PROP_INITIAL_EMERGENCY_SERVICE, g_param_spec_string("initial-emergency-service", "Initial Emergency Service", "Emergency service initially associated with this channel", NULL, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); g_object_class_install_property( object_class, PROP_ORIGINATING, g_param_spec_boolean("originating", "Mobile-Originated Call", "Call associated with this channel is mobile-originated", FALSE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS)); g_object_class_install_property( object_class, PROP_TERMINATING, g_param_spec_boolean("terminating", "Mobile-Terminating Call", "Call associated with this channel is mobile-terminating", FALSE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS)); g_object_class_override_property( object_class, PROP_MEMBER, "member-handle"); g_object_class_override_property( object_class, PROP_MEMBER_MAP, "member-map"); g_object_class_override_property( object_class, PROP_CONFERENCE, "member-conference"); g_object_class_override_property( object_class, PROP_PEER, "peer"); g_object_class_install_property( object_class, PROP_INITIAL_REMOTE, g_param_spec_uint("initial-remote", "Initial Remote Handle", "Handle added to the remote pending set initially", 0, G_MAXUINT32, 0, /* min, max, default */ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); } /* ====================================================================== */ /** * org.freedesktop.DBus properties */ /* Properties for o.f.T.Channel.Interface.ServicePoint */ static TpDBusPropertiesMixinPropImpl service_point_properties[] = { { "InitialServicePoint", "initial-service-point" }, { "CurrentServicePoint", "current-service-point" }, { NULL } }; static TpDBusPropertiesMixinIfaceImpl ring_call_channel_dbus_property_interfaces[] = { { TP_IFACE_CHANNEL_INTERFACE_SERVICE_POINT, tp_dbus_properties_mixin_getter_gobject_properties, NULL, service_point_properties, }, { NULL } }; static void ring_call_channel_fill_immutable_properties(TpBaseChannel *base, GHashTable *props) { RingCallChannel *self = RING_CALL_CHANNEL (base); GObject *obj = (GObject *) self; TP_BASE_CHANNEL_CLASS (ring_call_channel_parent_class)->fill_immutable_properties ( base, props); tp_dbus_properties_mixin_fill_properties_hash (obj, props, TP_IFACE_CHANNEL_INTERFACE_SERVICE_POINT, "CurrentServicePoint", NULL); if (!RING_STR_EMPTY(self->priv->initial_emergency_service)) tp_dbus_properties_mixin_fill_properties_hash (obj, props, TP_IFACE_CHANNEL_INTERFACE_SERVICE_POINT, "InitialServicePoint", NULL); } /* ====================================================================== */ /* RingMediaChannel implementation */ static ModemRequest *ring_call_channel_create(RingCallChannel *, GError **error); /** Close channel */ static gboolean ring_call_channel_close(RingMediaChannel *_self, gboolean immediately) { RingCallChannel *self = RING_CALL_CHANNEL(_self); RingCallChannelPrivate *priv = RING_CALL_CHANNEL(self)->priv; priv->closing = 1; if (priv->member.conference) { TpHandle actor = priv->release.actor; TpChannelGroupChangeReason reason = priv->release.reason; ring_conference_channel_emit_channel_removed( priv->member.conference, RING_MEMBER_CHANNEL(self), "Member channel closed", actor, reason); /* above emit calls ring_member_channel_left() */ g_assert(priv->member.conference == NULL); } if (self->base.call_instance) { if (!priv->release.message) priv->release.message = g_strdup("Channel closed"); modem_call_request_release(self->base.call_instance, NULL, NULL); return immediately; } else if (priv->creating_call) { if (immediately) { modem_request_cancel(priv->creating_call); priv->creating_call = NULL; g_object_unref(self); } else if (!priv->release.message) priv->release.message = g_strdup("Channel closed"); return immediately; } return TRUE; } static gboolean ring_call_channel_alert_tone_needed(RingCallChannel *self) { ModemCallService *call_service = ring_media_channel_get_call_service (self); GValue alert = G_VALUE_INIT; g_value_init (&alert, G_TYPE_BOOLEAN); g_object_get_property(call_service, "alert-tone-needed", &alert); return g_value_get_boolean(&alert); } static void ring_call_channel_update_state(RingMediaChannel *_self, guint state, guint causetype, guint cause) { RingCallChannel *self = RING_CALL_CHANNEL(_self); #if nomore RingCallChannelPrivate *priv = self->priv; #endif switch (state) { case MODEM_CALL_STATE_DIALING: case MODEM_CALL_STATE_INCOMING: case MODEM_CALL_STATE_WAITING: case MODEM_CALL_STATE_ACTIVE: ring_media_channel_stop_playing(RING_MEDIA_CHANNEL(self), TRUE); break; case MODEM_CALL_STATE_ALERTING: if (ring_call_channel_alert_tone_needed(self)) { DEBUG ("play local alert tone (no in-band audio)"); ring_media_channel_play_tone (RING_MEDIA_CHANNEL(self), TONES_EVENT_RINGING, 0, 0); } break; case MODEM_CALL_STATE_DISCONNECTED: ring_call_channel_play_error_tone(self, state, causetype, cause); break; #if nomore case MODEM_CALL_STATE_TERMINATED: if (!priv->released) { ring_call_channel_play_error_tone(self, state, causetype, cause); break; } /* FALLTHROUGH */ #endif case MODEM_CALL_STATE_INVALID: ring_media_channel_idle_playing(RING_MEDIA_CHANNEL(self)); break; default: break; } switch (state) { case MODEM_CALL_STATE_DIALING: on_modem_call_state_dialing(self); break; case MODEM_CALL_STATE_INCOMING: on_modem_call_state_incoming(self); break; case MODEM_CALL_STATE_ALERTING: on_modem_call_state_mo_alerting(self); break; case MODEM_CALL_STATE_WAITING: on_modem_call_state_waiting(self); break; case MODEM_CALL_STATE_ACTIVE: on_modem_call_state_active(self); break; case MODEM_CALL_STATE_DISCONNECTED: switch (causetype) { case MODEM_CALL_CAUSE_TYPE_LOCAL: on_modem_call_state_mo_release(self, causetype, cause); break; case MODEM_CALL_CAUSE_TYPE_REMOTE: on_modem_call_state_mt_release(self, causetype, cause); break; case MODEM_CALL_CAUSE_TYPE_NETWORK: on_modem_call_state_terminated(self, causetype, cause); break; } break; default: break; } } static void ring_call_channel_play_error_tone(RingCallChannel *self, guint state, guint causetype, guint cause) { int event_tone; guint duration = 5000; int volume = 0; guint hold; if (!self->base.call_instance) return; event_tone = modem_call_event_tone(state, causetype, cause); if (state == MODEM_CALL_STATE_DISCONNECTED && causetype == MODEM_CALL_CAUSE_TYPE_REMOTE && event_tone != TONES_STOP && event_tone != TONES_NONE) { TpGroupMixin *mixin = TP_GROUP_MIXIN(self); if (tp_handle_set_size(mixin->local_pending)) { /* Case remote end drops call before it is accepted */ event_tone = TONES_NONE; } } hold = TP_LOCAL_HOLD_STATE_UNHELD; g_object_get(self, "hold-state", &hold, NULL); if (hold != TP_LOCAL_HOLD_STATE_UNHELD && hold != TP_LOCAL_HOLD_STATE_PENDING_UNHOLD) { /* XXX - dropped tone damped 3dB if call was on hold */ event_tone = TONES_EVENT_DROPPED, duration = 1200, volume = -3; } ring_media_channel_play_tone(RING_MEDIA_CHANNEL(self), event_tone, volume, duration); } static void ring_call_channel_set_call_instance(RingMediaChannel *_self, ModemCall *ci) { RingCallChannel *self = RING_CALL_CHANNEL(_self); RingCallChannelPrivate *priv = self->priv; if (ci) { priv->call_instance_seen = 1; #define CONNECT(n, f) \ g_signal_connect(ci, n, G_CALLBACK(on_modem_call_ ## f), self) priv->signals.waiting = CONNECT("waiting", waiting); priv->signals.emergency = CONNECT("emergency", emergency); priv->signals.on_hold = CONNECT("on-hold", on_hold); priv->signals.forwarded = CONNECT("forwarded", forwarded); priv->signals.notify_multiparty = CONNECT("notify::multiparty", notify_multiparty); #undef CONNECT } else { ci = self->base.call_instance; #define DISCONNECT(n) \ if (priv->signals.n && \ g_signal_handler_is_connected(ci, priv->signals.n)) { \ g_signal_handler_disconnect(ci, priv->signals.n); \ } (priv->signals.n = 0) DISCONNECT(waiting); DISCONNECT(emergency); DISCONNECT(on_hold); DISCONNECT(forwarded); DISCONNECT(notify_multiparty); #undef DISCONNECT } } static gboolean ring_call_channel_validate_media_handle (gpointer _self, guint *handlep, GError **error) { DEBUG("enter"); RingCallChannel *self = RING_CALL_CHANNEL(_self); TpBaseChannel *base = TP_BASE_CHANNEL (self); TpGroupMixin *mixin = TP_GROUP_MIXIN(self); TpHandleRepoIface *repo; RingCallChannelPrivate *priv = self->priv; guint handle = *handlep; /* generic checks */ if (handle == 0) handle = priv->peer_handle; if (handle == 0) handle = tp_base_channel_get_target_handle (base); repo = tp_base_connection_get_handles( tp_base_channel_get_connection(base), TP_HANDLE_TYPE_CONTACT); if (!tp_handle_is_valid(repo, handle, error)) return FALSE; if (handle == mixin->self_handle) { g_set_error(error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT, "cannot establish call streams with yourself"); return FALSE; } if (!modem_call_is_valid_address(tp_handle_inspect(repo, handle))) { g_set_error(error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT, "Invalid handle %u=\"%s\" for media streams", handle, tp_handle_inspect(repo, handle)); return FALSE; } if (!tp_handle_set_is_member(self->group.members, handle) && !tp_handle_set_is_member(self->group.remote_pending, handle) && handle != tp_base_channel_get_target_handle (base)) { g_set_error(error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT, "given handle %u is not a member of the channel", handle); return FALSE; } *handlep = handle; return TRUE; } static void reply_to_modem_call_request_dial(ModemCallService *_service, ModemRequest *request, ModemCall *instance, GError *error, gpointer _channel); static gboolean ring_call_channel_create_streams (gpointer _self, TpHandle handle, gboolean audio, gboolean video, GError **error) { RingCallChannel *self = RING_CALL_CHANNEL(_self); RingCallChannelPrivate *priv = self->priv; ModemCallService *call_service = ring_media_channel_get_call_service (_self); (void)audio; (void)video; if (priv->peer_handle == 0) g_object_set(self, "peer", handle, NULL); if (handle != priv->peer_handle) { g_set_error(error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT, "Invalid handle"); return FALSE; } if (call_service == NULL) { g_set_error (error, TP_ERROR, TP_ERROR_NOT_AVAILABLE, "Modem does not support voice calls"); return FALSE; } if (priv->call_instance_seen) { DEBUG("Already associated with a call"); return TRUE; } priv->call_instance_seen = 1; return ring_call_channel_create(self, error) != NULL; } void ring_call_channel_initial_audio(RingCallChannel *self, RingMediaManager *manager, gpointer channelrequest) { ModemRequest *request; GError *error = NULL; DEBUG("%s(%p, %p, %p) called", __func__, self, manager, channelrequest); self->priv->call_instance_seen = 1; request = ring_call_channel_create(self, &error); if (request) { modem_request_add_qdatas( request, g_quark_from_static_string("RingChannelRequest"), channelrequest, NULL, g_type_qname(RING_TYPE_MEDIA_MANAGER), g_object_ref(manager), g_object_unref, NULL); } else { ring_media_manager_emit_new_channel(manager, channelrequest, self, error); g_error_free(error); } } static ModemRequest * ring_call_channel_create(RingCallChannel *self, GError **error) { RingCallChannelPrivate *priv = self->priv; TpHandle handle = priv->peer_handle; char const *destination; ModemClirOverride clir; char *number = NULL; ModemCallService *service; ModemRequest *request; destination = ring_connection_inspect_contact ( RING_CONNECTION(tp_base_channel_get_connection(TP_BASE_CHANNEL(self))), handle); if (RING_STR_EMPTY(destination)) { g_set_error(error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT, "Invalid handle"); return NULL; } if (priv->anon_modes & TP_ANONYMITY_MODE_CLIENT_INFO) clir = MODEM_CLIR_OVERRIDE_ENABLED; else if (priv->anon_modes & TP_ANONYMITY_MODE_SHOW_CLIENT_INFO) clir = MODEM_CLIR_OVERRIDE_DISABLED; else clir = MODEM_CLIR_OVERRIDE_DEFAULT; if (priv->dial2nd) g_free(priv->dial2nd), priv->dial2nd = NULL; modem_call_split_address(destination, &number, &priv->dial2nd, &clir); if (priv->dial2nd) DEBUG("2nd stage dialing: \"%s\"", priv->dial2nd); service = ring_media_channel_get_call_service (RING_MEDIA_CHANNEL (self)); request = modem_call_request_dial (service, number, clir, reply_to_modem_call_request_dial, self); g_free(number); if (request) { priv->creating_call = request; g_object_ref(self); } return request; } static void reply_to_modem_call_request_dial(ModemCallService *_service, ModemRequest *request, ModemCall *ci, GError *error, gpointer _channel) { RingCallChannel *self = RING_CALL_CHANNEL(_channel); RingCallChannelPrivate *priv = self->priv; TpBaseChannel *base = TP_BASE_CHANNEL (self); GError *error0 = NULL; TpChannelGroupChangeReason reason; char *debug; gpointer channelrequest; if (request == priv->creating_call) { priv->creating_call = NULL; g_object_unref(self); } channelrequest = modem_request_steal_data(request, "RingChannelRequest"); if (channelrequest) { RingMediaManager *manager; manager = modem_request_get_qdata(request, g_type_qname(RING_TYPE_MEDIA_MANAGER)); if (ci) g_object_set(self, "initial-remote", tp_base_channel_get_target_handle (base), NULL); ring_media_manager_emit_new_channel(manager, channelrequest, self, NULL); } if (ci) { g_assert(self->base.call_instance == NULL); g_object_set(self, "call-instance", ci, NULL); if (priv->release.message == NULL) ring_media_channel_set_state(RING_MEDIA_CHANNEL(self), MODEM_CALL_STATE_DIALING, 0, 0); else modem_call_request_release(ci, NULL, NULL); return; } ring_media_channel_play_tone(RING_MEDIA_CHANNEL(self), modem_call_error_tone(error), 0, 4000); reason = ring_channel_group_error_reason(error); ring_warning("Call.Dial: message=\"%s\" reason=%s (%u) cause=%s.%s", error->message, ring_util_reason_name(reason), reason, modem_error_domain_prefix(error->domain), modem_error_name(error, NULL, 0)); debug = g_strdup_printf("Dial() failed: reason=%s (%u) cause=%s.%s", ring_util_reason_name(reason), reason, modem_error_domain_prefix(error->domain), modem_error_name(error, NULL, 0)); ring_call_channel_released(self, priv->peer_handle, reason, error->message, error, debug); if (error0) g_error_free(error0); g_free(debug); if (!ring_media_channel_is_playing(RING_MEDIA_CHANNEL(self))) ring_media_channel_close(RING_MEDIA_CHANNEL(self)); } static void ring_call_channel_implement_media_channel(RingMediaChannelClass *media_class) { media_class->emit_initial = ring_call_channel_emit_initial; media_class->close = ring_call_channel_close; media_class->update_state = ring_call_channel_update_state; media_class->set_call_instance = ring_call_channel_set_call_instance; ring_streamed_media_mixin_class_init (G_OBJECT_CLASS(media_class), G_STRUCT_OFFSET(RingCallChannelClass, base_class.streamed_media_class), ring_call_channel_validate_media_handle, ring_call_channel_create_streams); } /* ---------------------------------------------------------------------- */ /* Implement org.freedesktop.Telepathy.Channel.Interface.CallState */ static void get_call_states(TpSvcChannelInterfaceCallState *iface, DBusGMethodInvocation *context) { RingCallChannel *self = RING_CALL_CHANNEL(iface); RingCallChannelPrivate *priv = self->priv; GHashTable *call_states; call_states = g_hash_table_new (NULL, NULL); if (priv->peer_handle) { g_hash_table_replace (call_states, GUINT_TO_POINTER (priv->peer_handle), GUINT_TO_POINTER ((guint)priv->call_state)); } tp_svc_channel_interface_call_state_return_from_get_call_states (context, call_states); g_hash_table_destroy (call_states); } static void ring_channel_call_state_iface_init(gpointer g_iface, gpointer iface_data) { TpSvcChannelInterfaceCallStateClass *klass = g_iface; #define IMPLEMENT(x) tp_svc_channel_interface_call_state_implement_##x( \ klass, x) IMPLEMENT(get_call_states); #undef IMPLEMENT } static void ring_update_call_state(RingCallChannel *self, unsigned set_states, unsigned zap_states) { RingCallChannelPrivate *priv = RING_CALL_CHANNEL(self)->priv; unsigned call_state, old_state = priv->call_state; call_state = priv->call_state = (old_state & ~zap_states) | set_states; if (priv->peer_handle && call_state != old_state) { DEBUG("emitting %s(%u, %u)", "CallStateChanged", priv->peer_handle, call_state); tp_svc_channel_interface_call_state_emit_call_state_changed( TP_SVC_CHANNEL_INTERFACE_CALL_STATE(self), priv->peer_handle, call_state); } } /** Remote end has put us on hold */ static void on_modem_call_on_hold(ModemCall *ci, gboolean onhold, RingCallChannel *self) { if (onhold) ring_update_call_state(self, TP_CHANNEL_CALL_STATE_HELD, 0); else ring_update_call_state(self, 0, TP_CHANNEL_CALL_STATE_HELD); } /** This call has been forwarded */ static void on_modem_call_forwarded(ModemCall *ci, RingCallChannel *self) { ring_update_call_state(self, TP_CHANNEL_CALL_STATE_FORWARDED, 0); } static void on_modem_call_notify_multiparty(ModemCall *ci, GParamSpec *pspec, gpointer user_data) { RingCallChannel *self = RING_CALL_CHANNEL (user_data); RingCallChannelPrivate *priv = self->priv; gboolean multiparty_member; DEBUG (""); g_object_get(ci, "multiparty", &multiparty_member, NULL); /* * This does _not_ cover membership in peer hosted conferences * (i.e. when there is no local conference channel). **/ if (priv->member.conference && multiparty_member == FALSE) { TpHandle actor = 0; /* unknown actor */ TpChannelGroupChangeReason reason = TP_CHANNEL_GROUP_CHANGE_REASON_SEPARATED; ring_conference_channel_emit_channel_removed( priv->member.conference, RING_MEMBER_CHANNEL(self), "Conference call split", actor, reason); /* above emit calls ring_member_channel_left() */ g_assert(priv->member.conference == NULL); } } /* MO call is waiting */ static void on_modem_call_waiting(ModemCall *ci, RingCallChannel *self) { ring_update_call_state(self, TP_CHANNEL_CALL_STATE_QUEUED, 0); } /* Invoked when MO call targets an emergency service */ static void on_modem_call_emergency(ModemCall *ci, char const *emergency_service, RingCallChannel *self) { RingCallChannelPrivate *priv = self->priv; DEBUG("%s", emergency_service); if (g_strcmp0 (emergency_service, priv->emergency_service) != 0) { RingEmergencyService *esp; g_free(priv->emergency_service); priv->emergency_service = g_strdup(emergency_service); g_object_notify(G_OBJECT(self), "emergency-service"); DEBUG("emitting ServicePointChanged"); esp = ring_emergency_service_new(emergency_service); tp_svc_channel_interface_service_point_emit_service_point_changed( (TpSvcChannelInterfaceServicePoint *)self, esp); ring_emergency_service_free(esp); } } /* ---------------------------------------------------------------------- */ /** * Interface Telepathy.Channel.Interface.Group (version 0.15) * using TpGroupMixin from telepathy-glib * * Methods: * AddMembers ( au: contacts, s: message ) -> nothing * GetAllMembers ( ) -> au, au, au * GetGroupFlags ( ) -> u * GetHandleOwners ( au: handles ) -> au * GetLocalPendingMembers ( ) -> au * GetLocalPendingMembersWithInfo ( ) -> a(uuus) * GetMembers ( ) -> au * GetRemotePendingMembers ( ) -> au * GetSelfHandle ( ) -> u * RemoveMembers ( au: contacts, s: message ) -> nothing * Signals: * -> GroupFlagsChanged ( u: added, u: removed ) * -> MembersChanged ( s: message, au: added, au: removed, * au: local_pending, au: remote_pending, * u: actor, u: reason ) * * TPGroupMixin interface: * ring_call_channel_add_member() * ring_call_channel_remove_member_with_reason() */ static gboolean ring_call_channel_add_member(GObject *iface, TpHandle handle, const char *message, GError **error) { TpGroupMixin *mixin = TP_GROUP_MIXIN(iface); DEBUG("enter"); if (tp_handle_set_is_member(mixin->members, handle)) return TRUE; else if (tp_handle_set_is_member(mixin->local_pending, handle)) /* Incoming call */ return ring_call_channel_accept_pending(iface, handle, message, error); else /* Outgoing call */ return ring_call_channel_request_remote(iface, handle, message, error); } static gboolean ring_call_channel_remove_member_with_reason(GObject *iface, TpHandle handle, const char *message, guint reason, GError **error) { RingCallChannel *self = RING_CALL_CHANNEL(iface); RingCallChannelPrivate *priv = self->priv; TpGroupMixin *mixin = TP_GROUP_MIXIN(iface); TpIntSet *set; DEBUG("enter"); if (handle != mixin->self_handle && handle != priv->peer_handle) { g_set_error(error, TP_ERROR, TP_ERROR_PERMISSION_DENIED, "handle %u cannot be removed", handle); return FALSE; } if (priv->release.message) /* Already releasing */ return FALSE; tp_group_mixin_change_flags(iface, 0, TP_CHANNEL_GROUP_FLAG_CAN_REMOVE | TP_CHANNEL_GROUP_FLAG_CAN_RESCIND); if (self->base.call_instance) { priv->release.message = g_strdup(message ? message : "Call released"); priv->release.actor = mixin->self_handle; priv->release.reason = reason; priv->release.retry_count = 0; modem_call_request_release(self->base.call_instance, reply_to_hangup, self); } else { /* Remove handle from set */ set = tp_intset_new (); tp_intset_add(set, handle); tp_group_mixin_change_members(iface, message, NULL, set, NULL, NULL, mixin->self_handle, reason); tp_intset_destroy(set); ring_media_channel_close(RING_MEDIA_CHANNEL(self)); } return TRUE; } /* Add handle to 'remote pending' set */ gboolean ring_call_channel_request_remote(GObject *iface, TpHandle handle, const char *message, GError **error) { RingCallChannel *self = RING_CALL_CHANNEL(iface); char const *destination; destination = ring_connection_inspect_contact( RING_CONNECTION(tp_base_channel_get_connection(TP_BASE_CHANNEL(self))), handle); DEBUG("Trying to add %u=\"%s\" to remote pending", handle, destination); g_assert(handle != 0); if (!modem_call_is_valid_address(destination)) { g_set_error(error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT, "Invalid handle for media channel"); return FALSE; } g_object_set(self, "peer", handle, NULL); if (handle == self->priv->peer_handle) { ring_call_channel_remote_pending(self, handle, message); return TRUE; } else { g_set_error(error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT, "Only one target allowed for media channel"); return FALSE; } } gboolean ring_call_channel_remote_pending(RingCallChannel *self, TpHandle handle, const char *message) { RingCallChannelPrivate *priv = self->priv; GObject *object = G_OBJECT(self); TpGroupMixin *mixin = TP_GROUP_MIXIN(self); TpIntSet *remote_pending_set; gboolean done; g_assert(handle == priv->peer_handle); if (handle == 0) return FALSE; if (tp_handle_set_is_member(mixin->remote_pending, handle)) return TRUE; remote_pending_set = tp_intset_new(); tp_intset_add(remote_pending_set, handle); done = tp_group_mixin_change_members(object, message, NULL, NULL, NULL, remote_pending_set, mixin->self_handle, TP_CHANNEL_GROUP_CHANGE_REASON_INVITED); tp_intset_destroy(remote_pending_set); if (done) { tp_group_mixin_change_flags(object, TP_CHANNEL_GROUP_FLAG_CAN_REMOVE | TP_CHANNEL_GROUP_FLAG_CAN_RESCIND, TP_CHANNEL_GROUP_FLAG_CAN_ADD); } return done; } gboolean retry_modem_call_request_release(gpointer user_data) { RingCallChannel *self = RING_CALL_CHANNEL(user_data); RingCallChannelPrivate *priv = self->priv; priv->release.retry_source = 0; modem_call_request_release(self->base.call_instance, reply_to_hangup, self); return FALSE; } void reply_to_answer (ModemCall *call_instance, ModemRequest *request, GError *error, gpointer user_data) { DEBUG ("%s: %s", (char *)user_data, error ? error->message : "ok"); g_free (user_data); } void reply_to_hangup (ModemCall *call_instance, ModemRequest *request, GError *error, gpointer user_data) { RingCallChannel *self = RING_CALL_CHANNEL(user_data); RingCallChannelPrivate *priv = self->priv; DEBUG ("%s", error ? error->message : "ok"); if (error) { if (error->code == MODEM_OFONO_ERROR_IN_PROGRESS && priv->release.retry_count <= 5) { /* retry for up to 32s, increasing delay each time */ int delay = (1 << priv->release.retry_count); ring_message("Error in hangup %s, retry %d in %ds", error->message, priv->release.retry_count, delay); priv->release.retry_source = g_timeout_add_seconds(delay, retry_modem_call_request_release, self); ++priv->release.retry_count; } else { ring_message("Error in hangup, giving up %s", error->message); } } } gboolean ring_call_channel_accept_pending(GObject *iface, TpHandle handle, const char *message, GError **error) { RingCallChannel *self = RING_CALL_CHANNEL(iface); guint state = 0; DEBUG("accepting an incoming call"); if (self->base.call_instance == NULL) { g_set_error(error, TP_ERROR, TP_ERROR_NOT_AVAILABLE, "Missing call instance"); return FALSE; } g_object_get(self->base.call_instance, "state", &state, NULL); if (state == MODEM_CALL_STATE_DISCONNECTED) { g_set_error(error, TP_ERROR, TP_ERROR_NOT_AVAILABLE, "Invalid call state"); return FALSE; } if (!self->priv->accepted) self->priv->accepted = g_strdup(message ? message : "Call accepted"); modem_call_request_answer(self->base.call_instance, reply_to_answer, g_strdup(self->base.nick)); return TRUE; } static void on_modem_call_state_incoming(RingCallChannel *self) { RingCallChannelPrivate *priv = self->priv; if (!priv->terminating) g_object_set(self, "terminating", TRUE, NULL); } static void on_modem_call_state_dialing(RingCallChannel *self) { RingCallChannelPrivate *priv = self->priv; if (!priv->originating) g_object_set(self, "originating", TRUE, NULL); if (!priv->closing) ring_call_channel_remote_pending(self, priv->peer_handle, "Call created"); } static void on_modem_call_state_mo_alerting(RingCallChannel *self) { RingCallChannelPrivate *priv = self->priv; ring_call_channel_remote_pending(self, priv->peer_handle, "Call alerting"); ring_update_call_state(self, TP_CHANNEL_CALL_STATE_RINGING, TP_CHANNEL_CALL_STATE_QUEUED); } static void on_modem_call_state_waiting(RingCallChannel *self) { } static void on_modem_call_state_active(RingCallChannel *self) { RingCallChannelPrivate *priv = self->priv; TpGroupMixin *mixin = TP_GROUP_MIXIN(self); if (tp_handle_set_size(mixin->local_pending) || tp_handle_set_size(mixin->remote_pending)) { char const *message = "Call answered"; TpHandle who = 0; TpIntSet *add = tp_intset_new(); if (tp_handle_set_size(mixin->local_pending)) who = mixin->self_handle; else if (tp_handle_set_size(mixin->remote_pending)) who = priv->peer_handle; if (self->priv->accepted) message = self->priv->accepted; tp_intset_add(add, mixin->self_handle); if (priv->peer_handle) tp_intset_add(add, priv->peer_handle); tp_group_mixin_change_members((GObject *)self, message, add, NULL, NULL, NULL, who, 0); tp_intset_destroy(add); /* Allow removal, deny rescind and adding */ tp_group_mixin_change_flags((GObject *)self, TP_CHANNEL_GROUP_FLAG_CAN_REMOVE, TP_CHANNEL_GROUP_FLAG_CAN_RESCIND | TP_CHANNEL_GROUP_FLAG_CAN_ADD); } ring_update_call_state(self, 0, TP_CHANNEL_CALL_STATE_RINGING | TP_CHANNEL_CALL_STATE_QUEUED); if (priv->dial2nd) { /* p equals 0b1100, 0xC, or "DTMF Control Digits Separator" in the 3GPP TS 11.11 section 10.5.1 "Extended BCD coding" table, According to 3GPP TS 02.07 appendix B.3.4, 'p', or DTMF Control Digits Separator is used "to distinguish between the addressing digits (i.e. the phone number) and the DTMF digits." According to the B.3.4, "upon the called party answering the ME shall send the DTMF digits automatically to the network after a delay of 3 seconds. Upon subsequent occurrences of the separator, the ME shall pause again for 3 seconds (± 20 %) before sending any further DTMF digits." According to 3GPP TS 11.11 section 10.5.1 note 6, "A second or subsequent 'C'" will be interpreted as a 3 second pause. */ if (!ring_media_channel_send_dialstring(RING_MEDIA_CHANNEL(self), 1, priv->dial2nd, 0, 0, NULL)) { DEBUG("Ignoring dialstring \"%s\"", priv->dial2nd); } g_free(priv->dial2nd), priv->dial2nd = NULL; } } static void on_modem_call_state_mo_release(RingCallChannel *self, guint causetype, guint cause) { RingCallChannelPrivate *priv = self->priv; char const *message = priv->release.message; TpHandle actor = priv->release.actor; TpChannelGroupChangeReason reason = priv->release.reason; GError *error = NULL; char *debug; int details = 0; error = modem_call_new_error(causetype, cause, NULL); if (!actor) { if (/*MODEM_CALL_CAUSE_FROM_GSM(causetype)*/1) { /* Cancelled by modem for unknown reasons? */ message = error->message; reason = ring_channel_group_release_reason(causetype, cause); details = causetype && cause && reason != TP_CHANNEL_GROUP_CHANGE_REASON_BUSY && reason != TP_CHANNEL_GROUP_CHANGE_REASON_NONE; } else { /* Call cancelled by MO but did not come from GSM; likely intentional */ message = ""; actor = TP_GROUP_MIXIN(self)->self_handle; reason = 0; details = 0; } } DEBUG("MO_RELEASE: message=\"%s\" reason=%s (%u) cause=%s.%s (%u.%u)", message, ring_util_reason_name(reason), reason, modem_error_domain_prefix(error->domain), modem_error_name(error, NULL, 0), causetype, cause); debug = g_strdup_printf("mo-release: reason=%s (%u) cause=%s.%s (%u.%u)", ring_util_reason_name(reason), reason, modem_error_domain_prefix(error->domain), modem_error_name(error, NULL, 0), causetype, cause); ring_call_channel_released(self, actor, reason, message, details ? error : NULL, debug); g_error_free(error); g_free(debug); } static void on_modem_call_state_mt_release(RingCallChannel *self, guint causetype, guint cause) { char const *message; TpHandle actor; TpChannelGroupChangeReason reason; GError *error; char *debug; int details; message = "Call released"; actor = self->priv->peer_handle; reason = ring_channel_group_release_reason(causetype, cause); error = modem_call_new_error(causetype, cause, NULL); details = causetype && cause && reason != TP_CHANNEL_GROUP_CHANGE_REASON_BUSY && reason != TP_CHANNEL_GROUP_CHANGE_REASON_NONE; message = error->message; DEBUG("MT_RELEASE: message=\"%s\" reason=%s (%u) cause=%s.%s (%u.%u)", message, ring_util_reason_name(reason), reason, modem_error_domain_prefix(error->domain), modem_error_name(error, NULL, 0), causetype, cause); debug = g_strdup_printf("mt-release: reason=%s (%u) cause=%s.%s (%u.%u)", ring_util_reason_name(reason), reason, modem_error_domain_prefix(error->domain), modem_error_name(error, NULL, 0), causetype, cause); ring_call_channel_released(self, actor, reason, message, details ? error : NULL, debug); g_error_free(error); g_free(debug); } static void on_modem_call_state_terminated(RingCallChannel *self, guint causetype, guint cause) { RingCallChannelPrivate *priv = self->priv; char const *message; TpHandle actor; TpChannelGroupChangeReason reason; GError *error; char *debug; int details; if (priv->released) return; message = "Call terminated"; actor = priv->peer_handle; reason = ring_channel_group_release_reason(causetype, cause); error = modem_call_new_error(causetype, cause, NULL); details = causetype && cause && reason != TP_CHANNEL_GROUP_CHANGE_REASON_BUSY && reason != TP_CHANNEL_GROUP_CHANGE_REASON_NONE; message = error->message; DEBUG("TERMINATED: message=\"%s\" reason=%s (%u) cause=%s.%s (%u.%u)", message, ring_util_reason_name(reason), reason, modem_error_domain_prefix(error->domain), modem_error_name(error, NULL, 0), causetype, cause); debug = g_strdup_printf("terminated: reason=%s (%u) cause=%s.%s (%u.%u)", ring_util_reason_name(reason), reason, modem_error_domain_prefix(error->domain), modem_error_name(error, NULL, 0), causetype, cause); ring_call_channel_released(self, actor, reason, message, details ? error : NULL, debug); g_error_free(error); g_free(debug); } static void ring_call_channel_released(RingCallChannel *self, TpHandle actor, TpChannelGroupChangeReason reason, char const *message, GError *error, char const *debug) { TpIntSet *removed; char *dbus_error = NULL; if (self->priv->released) return; self->priv->released = 1; ring_update_call_state(self, 0, TP_CHANNEL_CALL_STATE_RINGING | TP_CHANNEL_CALL_STATE_QUEUED | TP_CHANNEL_CALL_STATE_HELD); /* update flags accordingly -- deny adding/removal/rescind */ tp_group_mixin_change_flags((GObject *)self, 0, TP_CHANNEL_GROUP_FLAG_CAN_ADD | TP_CHANNEL_GROUP_FLAG_CAN_REMOVE | TP_CHANNEL_GROUP_FLAG_CAN_RESCIND); if (error) dbus_error = modem_error_fqn(error); removed = tp_intset_new(); tp_intset_add(removed, TP_GROUP_MIXIN(self)->self_handle); tp_intset_add(removed, self->priv->peer_handle); ring_util_group_change_members(self, NULL, removed, NULL, NULL, actor ? "actor" : "", G_TYPE_UINT, actor, "change-reason", G_TYPE_UINT, reason, message ? "message" : "", G_TYPE_STRING, message, dbus_error ? "error" : "", G_TYPE_STRING, dbus_error, "debug-message", G_TYPE_STRING, debug, NULL); tp_intset_destroy(removed); if (self->priv->member.conference) { ring_conference_channel_emit_channel_removed( self->priv->member.conference, RING_MEMBER_CHANNEL(self), message, actor, reason); /* above emit calls ring_member_channel_left() */ g_assert(self->priv->member.conference == NULL); } g_free(dbus_error); } /* ---------------------------------------------------------------------- */ /* Conference member */ gboolean ring_member_channel_is_in_conference(RingMemberChannel const *iface) { return iface && RING_CALL_CHANNEL(iface)->priv->member.conference != NULL; } RingConferenceChannel * ring_member_channel_get_conference(RingMemberChannel const *iface) { if (!iface || !RING_IS_CALL_CHANNEL(iface)) return FALSE; RingCallChannel const *self = RING_CALL_CHANNEL(iface); RingCallChannelPrivate const *priv = self->priv; return priv->member.conference; } gboolean ring_member_channel_can_become_member(RingMemberChannel const *iface, GError **error) { if (!iface || !RING_IS_CALL_CHANNEL(iface)) { g_set_error(error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT, "Member is not valid channel object"); return FALSE; } RingCallChannel const *self = RING_CALL_CHANNEL(iface); RingCallChannelPrivate const *priv = self->priv; if (!priv->peer_handle) { g_set_error(error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT, "Member channel has no target"); return FALSE; } if (priv->member.conference) { g_set_error(error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT, "Member channel is already in conference"); return FALSE; } if (!self->base.call_instance) { g_set_error(error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT, "Member channel has no ongoing call"); return FALSE; } if (!modem_call_can_join(self->base.call_instance)) { g_set_error(error, TP_ERROR, TP_ERROR_NOT_AVAILABLE, "Member channel in state %s", modem_call_get_state_name( modem_call_get_state(self->base.call_instance))); return FALSE; } return TRUE; } static guint ring_call_channel_get_member_handle(RingCallChannel *self) { RingCallChannelPrivate *priv = self->priv; TpHandle handle = priv->member.handle; TpHandle owner = priv->peer_handle; if (handle == 0) { TpHandleRepoIface *repo = tp_base_connection_get_handles( tp_base_channel_get_connection(TP_BASE_CHANNEL(self)), TP_HANDLE_TYPE_CONTACT); gpointer context = ring_network_normalization_context(); char *object_path, *unique, *membername; g_object_get(self, "object-path", &object_path, NULL); unique = strrchr(object_path, '/'); membername = g_strdup_printf( "%s/%s", tp_handle_inspect(repo, owner), unique + strcspn(unique, "0123456789")); handle = tp_handle_ensure(repo, membername, context, NULL); priv->member.handle = handle; g_free(object_path); g_free(membername); } return handle; } GHashTable * ring_member_channel_get_handlemap(RingMemberChannel *iface) { RingCallChannel *self = RING_CALL_CHANNEL(iface); RingCallChannelPrivate const *priv = self->priv; TpHandle handle = ring_call_channel_get_member_handle(self); TpHandle owner = priv->peer_handle; GHashTable *handlemap = g_hash_table_new(NULL, NULL); g_hash_table_replace(handlemap, GUINT_TO_POINTER(handle), GUINT_TO_POINTER(owner)); return handlemap; } gboolean ring_member_channel_release(RingMemberChannel *iface, const char *message, TpChannelGroupChangeReason reason, GError **error) { RingCallChannel *self = RING_CALL_CHANNEL(iface); RingCallChannelPrivate *priv = self->priv; if (priv->release.message) { g_set_error(error, TP_ERROR, TP_ERROR_NOT_AVAILABLE, "already releasing"); return FALSE; } if (self->base.call_instance == NULL) { g_set_error(error, TP_ERROR, TP_ERROR_NOT_AVAILABLE, "no call instance"); return FALSE; } g_assert(message); priv->release.message = g_strdup(message ? message : "Call Released"); priv->release.actor = TP_GROUP_MIXIN(iface)->self_handle; priv->release.reason = reason; priv->release.retry_count = 0; modem_call_request_release(self->base.call_instance, reply_to_hangup, self); return TRUE; } void ring_member_channel_joined(RingMemberChannel *iface, RingConferenceChannel *conference) { RingCallChannel *self = RING_CALL_CHANNEL(iface); RingCallChannelPrivate *priv = self->priv; if (priv->member.conference) { DEBUG("switching to a new conference"); if (priv->member.conference == conference) return; ring_conference_channel_emit_channel_removed( priv->member.conference, iface, "Joined new conference", self->group.self_handle, TP_CHANNEL_GROUP_CHANGE_REASON_INVITED); /* above emit calls ring_member_channel_left() */ g_assert(priv->member.conference == NULL); } g_assert(priv->member.conference == NULL); priv->member.conference = g_object_ref(conference); DEBUG("%s joined conference %s", RING_MEDIA_CHANNEL(self)->nick, RING_CONFERENCE_CHANNEL(conference)->nick); } void ring_member_channel_left(RingMemberChannel *iface) { RingCallChannel *self = RING_CALL_CHANNEL(iface); RingCallChannelPrivate *priv = self->priv; if (priv->member.conference) { g_object_unref(priv->member.conference); priv->member.conference = NULL; DEBUG("Leaving conference"); } else { DEBUG("got Left but not in conference"); } } /* ---------------------------------------------------------------------- */ /* org.freedesktop.Telepathy.Channel.Interface.Splittable */ static ModemCallReply ring_call_channel_request_split_reply; static void ring_member_channel_method_split( RingSvcChannelInterfaceSplittable *iface, DBusGMethodInvocation *context) { RingCallChannel *self = RING_CALL_CHANNEL(iface); GError *error; DEBUG("enter"); if (ring_member_channel_is_in_conference(RING_MEMBER_CHANNEL(self))) { RingConferenceChannel *conference = self->priv->member.conference; if (ring_conference_channel_has_members(conference) <= 1) { /* * This is handles a race condition between two last members * of a conference. If one is currently leaving, and client * tries to Split() out the other, this branch is hit. We try * to follow Split() semantics even in this case. */ ring_warning("Only one member left in conference unable to split"); /* * Make sure the remaining call is unheld to follow * Split() semantics the callerexpects. **/ modem_call_request_hold(self->base.call_instance, 0, NULL, context); return; } ModemRequest *request; request = modem_call_request_split(self->base.call_instance, ring_call_channel_request_split_reply, context); modem_request_add_data_full(request, "tp-request", context, ring_method_return_internal_error); return; } g_set_error(&error, TP_ERROR, TP_ERROR_NOT_AVAILABLE, "Not a member channel"); dbus_g_method_return_error(context, error); g_error_free(error); } static void ring_call_channel_request_split_reply(ModemCall *instance, ModemRequest *request, GError *error, gpointer _context) { DBusGMethodInvocation *context; context = modem_request_steal_data(request, "tp-request"); g_assert(context); g_assert(context == _context); if (error) { DEBUG("split failed: " GERROR_MSG_FMT, GERROR_MSG_CODE(error)); GError tperror[1] = {{ TP_ERROR, TP_ERROR_NOT_AVAILABLE, "Cannot LeaveConference" }}; dbus_g_method_return_error(context, tperror); } else { DEBUG("enter"); ring_svc_channel_interface_splittable_return_from_split (context); } } static void ring_channel_splittable_iface_init(gpointer g_iface, gpointer iface_data) { RingSvcChannelInterfaceSplittableClass *klass = g_iface; #define IMPLEMENT(x) \ ring_svc_channel_interface_splittable_implement_##x \ (klass, ring_member_channel_method_ ## x) IMPLEMENT(split); #undef IMPLEMENT } telepathy-ring-2.3.24-37ad7111424725a5446e82dac516d7cab88149c7/src/ring-call-channel.h000066400000000000000000000054651251541261300262040ustar00rootroot00000000000000/* * ring-call-channel.h - Header for RingCallChannel * * Copyright (C) 2007-2009 Nokia Corporation * @author Pekka Pessi * @author Tom Swindell * * This work is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This work is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this work; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef RING_CALL_CHANNEL_H #define RING_CALL_CHANNEL_H #include #include #include G_BEGIN_DECLS typedef struct _RingCallChannel RingCallChannel; typedef struct _RingCallChannelClass RingCallChannelClass; typedef struct _RingCallChannelPrivate RingCallChannelPrivate; G_END_DECLS #include "ring-conference-channel.h" G_BEGIN_DECLS struct _RingCallChannelClass { RingMediaChannelClass base_class; TpGroupMixinClass group_class; TpDBusPropertiesMixinClass dbus_properties_class; }; struct _RingCallChannel { RingMediaChannel base; TpGroupMixin group; RingCallChannelPrivate *priv; }; GType ring_call_channel_get_type(void); /* TYPE MACROS */ #define RING_TYPE_CALL_CHANNEL \ (ring_call_channel_get_type()) #define RING_CALL_CHANNEL(obj) \ (G_TYPE_CHECK_INSTANCE_CAST((obj), RING_TYPE_CALL_CHANNEL, RingCallChannel)) #define RING_CALL_CHANNEL_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST((klass), RING_TYPE_CALL_CHANNEL, RingCallChannelClass)) #define RING_IS_CALL_CHANNEL(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE((obj), RING_TYPE_CALL_CHANNEL)) #define RING_IS_CALL_CHANNEL_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE((klass), RING_TYPE_CALL_CHANNEL)) #define RING_CALL_CHANNEL_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), RING_TYPE_CALL_CHANNEL, RingCallChannelClass)) /*********************************************************************** * Additional declarations (not based on generated templates) ***********************************************************************/ void ring_call_channel_initial_audio(RingCallChannel *self, RingMediaManager *manager, gpointer channelrequest); G_END_DECLS #endif /* #ifndef RING_CALL_CHANNEL_H*/ telepathy-ring-2.3.24-37ad7111424725a5446e82dac516d7cab88149c7/src/ring-call-content.c000066400000000000000000000104361251541261300262330ustar00rootroot00000000000000/* * ring-call-content.c - a Content object owned by a Call channel * Copyright ©2010 Collabora Ltd. * Copyright ©2010 Nokia Corporation * @author Will Thompson * @author Tom Swindell * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "ring-call-content.h" #include #include #define DEBUG_FLAG RING_DEBUG_MEDIA #include "ring-debug.h" struct _RingCallContentPrivate { RingCallStream *stream; }; static void implement_call_content (gpointer klass, gpointer unused G_GNUC_UNUSED); G_DEFINE_TYPE (RingCallContent, ring_call_content, TP_TYPE_BASE_MEDIA_CALL_CONTENT); static void ring_call_content_init (RingCallContent *self) { self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, RING_TYPE_CALL_CONTENT, RingCallContentPrivate); } static void ring_call_content_constructed (GObject *object) { RingCallContent *self = RING_CALL_CONTENT (object); RingCallContentPrivate *priv = self->priv; TpBaseCallContent *base = TP_BASE_CALL_CONTENT (self); gchar *stream_path; if (G_OBJECT_CLASS (ring_call_content_parent_class)->constructed != NULL) G_OBJECT_CLASS (ring_call_content_parent_class)->constructed (object); stream_path = g_strdup_printf ("%s/%s", tp_base_call_content_get_object_path (base), "stream"); priv->stream = ring_call_stream_new ( RING_CONNECTION(tp_base_call_content_get_connection (base)), stream_path); tp_base_call_content_add_stream (base, TP_BASE_CALL_STREAM (priv->stream)); g_free (stream_path); } static void ring_call_content_dispose (GObject *object) { RingCallContent *self = RING_CALL_CONTENT (object); RingCallContentPrivate *priv = self->priv; tp_clear_object (&priv->stream); if (G_OBJECT_CLASS (ring_call_content_parent_class)->dispose != NULL) G_OBJECT_CLASS (ring_call_content_parent_class)->dispose (object); } static void ring_call_content_class_init (RingCallContentClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); TpBaseCallContentClass *base_class = TP_BASE_CALL_CONTENT_CLASS(klass); object_class->constructed = ring_call_content_constructed; object_class->dispose = ring_call_content_dispose; g_type_class_add_private (klass, sizeof (RingCallContentPrivate)); } RingCallContent * ring_call_content_new (RingConnection *connection, const gchar *object_path, TpHandle creator) { return g_object_new (RING_TYPE_CALL_CONTENT, "connection", connection, "object-path", object_path, "name", "audio", "media-type", TP_MEDIA_STREAM_TYPE_AUDIO, "creator", creator, "disposition", TP_CALL_CONTENT_DISPOSITION_INITIAL, NULL); } RingCallStream * ring_call_content_get_stream (RingCallContent *self) { g_return_val_if_fail (RING_IS_CALL_CONTENT (self), NULL); return self->priv->stream; } static void ring_call_content_remove ( RingCallContent *self, DBusGMethodInvocation *context) { /* We could just leave all this out — the base class leaves Remove() * unimplemented, so TP_ERROR_NOT_IMPLEMENTED would be returned just like * this. But I think having a less generic error message is worth thirty(!) * lines of boilerplate. */ GError error = { TP_ERRORS, TP_ERROR_NOT_IMPLEMENTED, "Removing contents is not supported for cellular calls." }; dbus_g_method_return_error (context, &error); } static void implement_call_content (gpointer klass, gpointer unused G_GNUC_UNUSED) { #define IMPLEMENT(x) ring_svc_call_content_implement_##x (\ klass, ring_call_content_##x) IMPLEMENT (remove); #undef IMPLEMENT } telepathy-ring-2.3.24-37ad7111424725a5446e82dac516d7cab88149c7/src/ring-call-content.h000066400000000000000000000050741251541261300262420ustar00rootroot00000000000000/* * ring-call-content.h - header for a Content object owned by a Call channel * Copyright ©2010 Collabora Ltd. * Copyright ©2010 Nokia Corporation * @author Will Thompson * @author Tom Swindell * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef RING_CALL_CONTENT_H #define RING_CALL_CONTENT_H #include #include #include "ring-connection.h" #include "ring-call-stream.h" typedef struct _RingCallContent RingCallContent; typedef struct _RingCallContentClass RingCallContentClass; typedef struct _RingCallContentPrivate RingCallContentPrivate; struct _RingCallContentClass { TpBaseMediaCallContentClass parent_class; }; struct _RingCallContent { TpBaseMediaCallContent parent; RingCallContentPrivate *priv; }; GType ring_call_content_get_type (void); RingCallContent *ring_call_content_new (RingConnection *connection, const gchar *object_path, TpHandle creator); RingCallStream *ring_call_content_get_stream (RingCallContent *self); static void ring_call_content_remove(RingCallContent *self, DBusGMethodInvocation *context); /* TYPE MACROS */ #define RING_TYPE_CALL_CONTENT \ (ring_call_content_get_type ()) #define RING_CALL_CONTENT(obj) \ (G_TYPE_CHECK_INSTANCE_CAST((obj), RING_TYPE_CALL_CONTENT, RingCallContent)) #define RING_CALL_CONTENT_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST((klass), RING_TYPE_CALL_CONTENT,\ RingCallContentClass)) #define RING_IS_CALL_CONTENT(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE((obj), RING_TYPE_CALL_CONTENT)) #define RING_IS_CALL_CONTENT_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE((klass), RING_TYPE_CALL_CONTENT)) #define RING_CALL_CONTENT_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), RING_TYPE_CALL_CONTENT, \ RingCallContentClass)) #endif /* RING_CALL_CONTENT_H */ telepathy-ring-2.3.24-37ad7111424725a5446e82dac516d7cab88149c7/src/ring-call-member.c000066400000000000000000000371221251541261300260310ustar00rootroot00000000000000/* * ring-call-member.c - Source for CallMember * Copyright (C) 2010 Collabora Ltd. * @author Sjoerd Simons * @author Tom Swindell * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "config.h" #include #include #include "util.h" #include "base-call-channel.h" #include "ring-connection.h" #include "ring-call-member.h" G_DEFINE_TYPE(RingCallMember, ring_call_member, G_TYPE_OBJECT) /* signal enum */ enum { FLAGS_CHANGED, CONTENT_ADDED, CONTENT_REMOVED, LAST_SIGNAL }; static guint signals[LAST_SIGNAL] = { 0 }; /* properties */ enum { PROP_CALL = 1, PROP_TARGET }; /* private structure */ struct _RingCallMemberPrivate { TpHandle target; RingBaseCallChannel *call; TpCallMemberFlags flags; GList *contents; gchar *transport_ns; gboolean accepted; gboolean dispose_has_run; }; #define RING_CALL_MEMBER_GET_PRIVATE(o) \ (G_TYPE_INSTANCE_GET_PRIVATE ((o), RING_TYPE_CALL_MEMBER, \ RingCallMemberPrivate)) static void ring_call_member_init (RingCallMember *self) { RingCallMemberPrivate *priv = RING_CALL_MEMBER_GET_PRIVATE (self); self->priv = priv; priv->accepted = FALSE; } static void ring_call_member_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { RingCallMember *self = RING_CALL_MEMBER (object); RingCallMemberPrivate *priv = self->priv; switch (property_id) { case PROP_CALL: g_value_set_object (value, ring_call_member_get_connection (self)); break; case PROP_TARGET: g_value_set_uint (value, priv->target); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void ring_call_member_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { RingCallMember *self = RING_CALL_MEMBER (object); RingCallMemberPrivate *priv = self->priv; switch (property_id) { case PROP_CALL: priv->call = g_value_get_object (value); g_assert (priv->call != NULL); break; case PROP_TARGET: priv->target = g_value_get_uint (value); g_assert (priv->target != 0); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void ring_call_member_dispose (GObject *object); static void ring_call_member_finalize (GObject *object); static void ring_call_member_class_init ( RingCallMemberClass *ring_call_member_class) { GObjectClass *object_class = G_OBJECT_CLASS (ring_call_member_class); GParamSpec *param_spec; g_type_class_add_private (ring_call_member_class, sizeof (RingCallMemberPrivate)); object_class->dispose = ring_call_member_dispose; object_class->finalize = ring_call_member_finalize; object_class->get_property = ring_call_member_get_property; object_class->set_property = ring_call_member_set_property; param_spec = g_param_spec_object ("call", "Call", "The base call object that contains this member", RING_TYPE_BASE_CALL_CHANNEL, G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_CALL, param_spec); param_spec = g_param_spec_uint ("target", "Target", "the target handle of member", 0, G_MAXUINT, 0, G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_TARGET, param_spec); signals[FLAGS_CHANGED] = g_signal_new ("flags-changed", G_OBJECT_CLASS_TYPE (ring_call_member_class), G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED, 0, NULL, NULL, g_cclosure_marshal_VOID__UINT, G_TYPE_NONE, 1, G_TYPE_UINT); signals[CONTENT_ADDED] = g_signal_new ("content-added", G_OBJECT_CLASS_TYPE (ring_call_member_class), G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED, 0, NULL, NULL, g_cclosure_marshal_VOID__OBJECT, G_TYPE_NONE, 1, G_TYPE_OBJECT); signals[CONTENT_REMOVED] = g_signal_new ("content-removed", G_OBJECT_CLASS_TYPE (ring_call_member_class), G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED, 0, NULL, NULL, g_cclosure_marshal_VOID__OBJECT, G_TYPE_NONE, 1, G_TYPE_OBJECT); } void ring_call_member_dispose (GObject *object) { RingCallMember *self = RING_CALL_MEMBER (object); RingCallMemberPrivate *priv = self->priv; GList *l; if (priv->dispose_has_run) return; priv->dispose_has_run = TRUE; for (l = priv->contents ; l != NULL; l = g_list_next (l)) g_object_unref (l->data); tp_clear_pointer (&priv->contents, g_list_free); /* release any references held by the object here */ if (G_OBJECT_CLASS (ring_call_member_parent_class)->dispose) G_OBJECT_CLASS (ring_call_member_parent_class)->dispose (object); } void ring_call_member_finalize (GObject *object) { RingCallMember *self = RING_CALL_MEMBER (object); RingCallMemberPrivate *priv = self->priv; g_free (priv->transport_ns); priv->transport_ns = NULL; G_OBJECT_CLASS (ring_call_member_parent_class)->finalize (object); } /* static void remote_state_changed_cb (WockyJingleSession *session, gpointer user_data) { RingCallMember *self = RING_CALL_MEMBER (user_data); RingCallMemberPrivate *priv = self->priv; TpCallMemberFlags newflags = 0; if (wocky_jingle_session_get_remote_ringing (session)) newflags |= TP_CALL_MEMBER_FLAG_RINGING; if (wocky_jingle_session_get_remote_hold (session)) newflags |= TP_CALL_MEMBER_FLAG_HELD; if (priv->flags == newflags) return; priv->flags = newflags; DEBUG ("Call members flags changed to: %d", priv->flags); g_signal_emit (self, signals[FLAGS_CHANGED], 0, priv->flags); } */ /* static void member_content_removed_cb (RingCallMemberContent *mcontent, gpointer user_data) { RingCallMember *self = RING_CALL_MEMBER (user_data); RingCallMemberPrivate *priv = self->priv; priv->contents = g_list_remove (priv->contents, mcontent); g_signal_emit (self, signals[CONTENT_REMOVED], 0, mcontent); g_object_unref (mcontent); } static void ring_call_member_add_member_content (RingCallMember *self, RingCallMemberContent *content) { RingCallMemberPrivate *priv = self->priv; priv->contents = g_list_prepend (priv->contents, content); ring_signal_connect_weak (content, "removed", G_CALLBACK (member_content_removed_cb), G_OBJECT (self)); g_signal_emit (self, signals[CONTENT_ADDED], 0, content); } */ /* This function handles additional contents added by the remote side */ /* static void new_content_cb (WockyJingleSession *session, WockyJingleContent *c, gpointer user_data) { RingCallMember *self = RING_CALL_MEMBER (user_data); RingCallMemberContent *content = NULL; if (wocky_jingle_content_is_created_by_us (c)) return; content = ring_call_member_content_from_jingle_content (c, self); ring_call_member_add_member_content (self, content); } static gboolean call_member_update_existing_content (RingCallMember *self, WockyJingleContent *content) { GList *l; for (l = self->priv->contents; l != NULL ; l = g_list_next (l)) { RingCallMemberContent *mcontent = RING_CALL_MEMBER_CONTENT (l->data); if (ring_call_member_content_has_jingle_content (mcontent)) continue; if (!tp_strdiff (ring_call_member_content_get_name (mcontent), wocky_jingle_content_get_name (content))) { ring_call_member_content_set_jingle_content (mcontent, content); return TRUE; } } return FALSE; } void ring_call_member_set_session (RingCallMember *self, WockyJingleSession *session) { RingCallMemberPrivate *priv = self->priv; GList *c, *contents; g_assert (priv->session == NULL); g_assert (session != NULL); DEBUG ("Setting session: %p -> %p\n", self, session); priv->session = g_object_ref (session); contents = wocky_jingle_session_get_contents (session); for (c = contents ; c != NULL; c = g_list_next (c)) { WockyJingleContent *content = WOCKY_JINGLE_CONTENT (c->data); if (priv->transport_ns == NULL) { g_object_get (content, "transport-ns", &priv->transport_ns, NULL); } if (!call_member_update_existing_content (self, content)) { RingCallMemberContent *mcontent = ring_call_member_content_from_jingle_content (content, self); ring_call_member_add_member_content (self, mcontent); } } g_object_notify (G_OBJECT (self), "session"); ring_signal_connect_weak (priv->session, "remote-state-changed", G_CALLBACK (remote_state_changed_cb), G_OBJECT (self)); ring_signal_connect_weak (priv->session, "new-content", G_CALLBACK (new_content_cb), G_OBJECT (self)); if (priv->accepted) ring_call_member_accept (self); g_list_free (contents); } WockyJingleSession * ring_call_member_get_session (RingCallMember *self) { return self->priv->session; } */ TpCallMemberFlags ring_call_member_get_flags (RingCallMember *self) { return self->priv->flags; } TpHandle ring_call_member_get_handle ( RingCallMember *self) { return self->priv->target; } GList * ring_call_member_get_contents (RingCallMember *self) { RingCallMemberPrivate *priv = self->priv; return priv->contents; } /* RingCallMemberContent * ring_call_member_ensure_content (RingCallMember *self, const gchar *name, WockyJingleMediaType mtype) { RingCallMemberPrivate *priv = self->priv; GList *l; RingCallMemberContent *content = NULL; for (l = priv->contents ; l != NULL; l = g_list_next (l)) { RingCallMemberContent *c = RING_CALL_MEMBER_CONTENT (l->data); if (ring_call_member_content_get_media_type (c) == mtype && !tp_strdiff (ring_call_member_content_get_name (c), name)) { content = c; break; } } if (content == NULL) { content = ring_call_member_content_new (name, mtype, self); ring_call_member_add_member_content (self, content); } return content; } RingCallMemberContent * ring_call_member_create_content (RingCallMember *self, const gchar *name, WockyJingleMediaType mtype, WockyJingleContentSenders senders, GError **error) { RingCallMemberPrivate *priv = self->priv; const gchar *content_ns; WockyJingleContent *c; RingCallMemberContent *content; const gchar *peer_resource; g_assert (priv->session != NULL); peer_resource = wocky_jingle_session_get_peer_resource (priv->session); DEBUG ("Creating new content %s, type %d", name, mtype); if (peer_resource != NULL) DEBUG ("existing call, using peer resource %s", peer_resource); else DEBUG ("existing call, using bare JID"); content_ns = jingle_pick_best_content_type (ring_call_member_get_connection (self), priv->target, peer_resource, mtype); if (content_ns == NULL) { g_set_error (error, TP_ERROR, TP_ERROR_NOT_AVAILABLE, "Content type %d not available for this resource", mtype); return NULL; } DEBUG ("Creating new jingle content with ns %s : %s", content_ns, priv->transport_ns); c = wocky_jingle_session_add_content (priv->session, mtype, senders, name, content_ns, priv->transport_ns); g_assert (c != NULL); content = ring_call_member_content_from_jingle_content (c, self); ring_call_member_add_member_content (self, content); return content; } void ring_call_member_accept (RingCallMember *self) { self->priv->accepted = TRUE; if (self->priv->session != NULL) wocky_jingle_session_accept (self->priv->session); } */ /** * Start a new session using the existing contents for this member. For now * assumes we're using the latest jingle dialect and ice-udp * FIXME: make dialect and transport selection more dynamic? */ /* gboolean ring_call_member_open_session (RingCallMember *self, GError **error) { RingCallMemberPrivate *priv = self->priv; RingConnection *conn = ring_call_member_get_connection (self); WockyJingleFactory *jf; WockyJingleSession *session; gchar *jid; jid = ring_peer_to_jid (conn, priv->target, NULL); jf = ring_jingle_mint_get_factory (conn->jingle_mint); g_return_val_if_fail (jf != NULL, FALSE); session = wocky_jingle_factory_create_session (jf, jid, WOCKY_JINGLE_DIALECT_V032, FALSE); DEBUG ("Created a jingle session: %p", session); priv->transport_ns = g_strdup (NS_JINGLE_TRANSPORT_ICEUDP); ring_call_member_set_session (self, session); g_free (jid); return TRUE; } gboolean ring_call_member_start_session (RingCallMember *self, const gchar *audio_name, const gchar *video_name, GError **error) { RingCallMemberPrivate *priv = self->priv; TpBaseChannel *base_channel = TP_BASE_CHANNEL (priv->call); TpHandle target = tp_base_channel_get_target_handle (base_channel); const gchar *resource; WockyJingleDialect dialect; gchar *jid; const gchar *transport; WockyJingleFactory *jf; WockyJingleSession *session; // FIXME might need to wait on capabilities, also don't need transport // and dialect already if (!jingle_pick_best_resource (ring_call_member_get_connection (self), target, audio_name != NULL, video_name != NULL, &transport, &dialect, &resource)) { g_set_error (error, TP_ERROR, TP_ERROR_NOT_CAPABLE, "member does not have the desired audio/video capabilities"); return FALSE; } jid = ring_peer_to_jid (ring_call_member_get_connection (self), target, resource); jf = ring_jingle_mint_get_factory ( ring_call_member_get_connection (self)->jingle_mint); g_return_val_if_fail (jf != NULL, FALSE); session = wocky_jingle_factory_create_session (jf, jid, dialect, FALSE); g_free (jid); ring_call_member_set_session (self, session); priv->transport_ns = g_strdup (transport); if (audio_name != NULL) ring_call_member_create_content (self, audio_name, WOCKY_JINGLE_MEDIA_TYPE_AUDIO, WOCKY_JINGLE_CONTENT_SENDERS_BOTH, NULL); if (video_name != NULL) ring_call_member_create_content (self, video_name, WOCKY_JINGLE_MEDIA_TYPE_VIDEO, WOCKY_JINGLE_CONTENT_SENDERS_BOTH, NULL); return TRUE; } */ RingConnection * ring_call_member_get_connection (RingCallMember *self) { TpBaseChannel *base_chan = TP_BASE_CHANNEL (self->priv->call); return RING_CONNECTION (tp_base_channel_get_connection (base_chan)); } const gchar * ring_call_member_get_transport_ns (RingCallMember *self) { return self->priv->transport_ns; } void ring_call_member_shutdown (RingCallMember *self) { RingCallMemberPrivate *priv = self->priv; /* removing the content will remove it from our list */ while (priv->contents != NULL) ring_call_member_content_remove ( RING_CALL_MEMBER_CONTENT (priv->contents->data)); } telepathy-ring-2.3.24-37ad7111424725a5446e82dac516d7cab88149c7/src/ring-call-member.h000066400000000000000000000045471251541261300260430ustar00rootroot00000000000000/* * ring-call-member.h - Header for CallMember * Copyright (C) 2010 Collabora Ltd. * @author Sjoerd Simons * @author Tom Swindell * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __RING_CALL_MEMBER_H__ #define __RING_CALL_MEMBER_H__ #include #include G_BEGIN_DECLS typedef struct _RingCallMember RingCallMember; typedef struct _RingCallMemberPrivate RingCallMemberPrivate; typedef struct _RingCallMemberClass RingCallMemberClass; struct _RingCallMemberClass { GObjectClass parent_class; }; struct _RingCallMember { GObject parent; RingCallMemberPrivate *priv; }; GType ring_call_member_get_type (void); /* TYPE MACROS */ #define RING_TYPE_CALL_MEMBER \ (ring_call_member_get_type ()) #define RING_CALL_MEMBER(obj) \ (G_TYPE_CHECK_INSTANCE_CAST((obj), RING_TYPE_CALL_MEMBER, \ RingCallMember)) #define RING_CALL_MEMBER_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST((klass), RING_TYPE_CALL_MEMBER, \ RingCallMemberClass)) #define RING_IS_CALL_MEMBER(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE((obj), RING_TYPE_CALL_MEMBER)) #define RING_IS_CALL_MEMBER_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE((klass), RING_TYPE_CALL_MEMBER)) #define RING_CALL_MEMBER_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), RING_TYPE_CALL_MEMBER, \ RingCallMemberClass)) TpHandle ring_call_member_get_handle(RingCallMember *self); TpCallMemberFlags ring_call_member_get_flags(RingCallMember *self); RingConnection * ring_call_member_get_connection(RingCallMember *self); void ring_call_member_shutdown(RingCallMember *self); G_END_DECLS #endif /* #ifndef __RING_CALL_MEMBER_H__*/ telepathy-ring-2.3.24-37ad7111424725a5446e82dac516d7cab88149c7/src/ring-call-stream.c000066400000000000000000000047521251541261300260600ustar00rootroot00000000000000/* * ring-call-stream.c - a Stream object owned by a RingCallContent * Copyright ©2010 Collabora Ltd. * Copyright ©2010 Nokia Corporation * @author Will Thompson * @author Tom Swindell * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "ring-call-stream.h" #define DEBUG_FLAG RING_DEBUG_MEDIA #include "ring-debug.h" static void implement_call_stream (gpointer klass, gpointer unused); G_DEFINE_TYPE(RingCallStream, ring_call_stream, TP_TYPE_BASE_MEDIA_CALL_STREAM); static void ring_call_stream_init (RingCallStream *self) { } static void ring_call_stream_class_init (RingCallStreamClass *klass) { } RingCallStream * ring_call_stream_new (RingConnection *connection, const gchar *object_path) { return g_object_new (RING_TYPE_CALL_STREAM, "connection", connection, "object-path", object_path, NULL); } static void ring_call_stream_set_sending ( RingCallStream *self, gboolean send, DBusGMethodInvocation *context) { GError error = { TP_ERRORS, TP_ERROR_NOT_IMPLEMENTED, "SetSending is not supported for cellular calls." }; /* Maybe we should put the call on hold/resume? */ dbus_g_method_return_error (context, &error); } static void ring_call_stream_request_receiving ( RingCallStream *self, TpHandle contact, gboolean receive, DBusGMethodInvocation *context) { GError error = { TP_ERRORS, TP_ERROR_NOT_IMPLEMENTED, "RequestReceiving is not supported for cellular calls." }; dbus_g_method_return_error (context, &error); } static void implement_call_stream (gpointer klass, gpointer unused G_GNUC_UNUSED) { #define IMPLEMENT(x) ring_svc_call_stream_implement_##x (\ klass, ring_call_stream_##x) IMPLEMENT (set_sending); IMPLEMENT (request_receiving); #undef IMPLEMENT } telepathy-ring-2.3.24-37ad7111424725a5446e82dac516d7cab88149c7/src/ring-call-stream.h000066400000000000000000000046411251541261300260620ustar00rootroot00000000000000/* * ring-call-stream.h - header for a Stream object owned by a RingCallContent * Copyright ©2010 Collabora Ltd. * Copyright ©2010 Nokia Corporation * @author Will Thompson * @author Tom Swindell * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef RING_CALL_STREAM_H #define RING_CALL_STREAM_H #include #include #include "ring-connection.h" G_BEGIN_DECLS typedef struct _RingCallStream RingCallStream; typedef struct _RingCallStreamPrivate RingCallStreamPrivate; typedef struct _RingCallStreamClass RingCallStreamClass; struct _RingCallStreamClass { TpBaseMediaCallStreamClass parent_class; }; struct _RingCallStream { TpBaseMediaCallStream parent; RingCallStreamPrivate *priv; }; GType ring_call_stream_get_type (void); RingCallStream *ring_call_stream_new (RingConnection *connection, const gchar *object_path); /* TYPE MACROS */ #define RING_TYPE_CALL_STREAM \ (ring_call_stream_get_type ()) #define RING_CALL_STREAM(obj) \ (G_TYPE_CHECK_INSTANCE_CAST((obj), RING_TYPE_CALL_STREAM, RingCallStream)) #define RING_CALL_STREAM_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST((klass), RING_TYPE_CALL_STREAM,\ RingCallStreamClass)) #define RING_IS_CALL_STREAM(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE((obj), RING_TYPE_CALL_STREAM)) #define RING_IS_CALL_STREAM_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE((klass), RING_TYPE_CALL_STREAM)) #define RING_CALL_STREAM_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), RING_TYPE_CALL_STREAM, \ RingCallStreamClass)) void ring_call_stream_update_member_states(RingCallStream *self); G_END_DECLS #endif /* RING_CALL_STREAM_H */ telepathy-ring-2.3.24-37ad7111424725a5446e82dac516d7cab88149c7/src/ring-conference-channel.c000066400000000000000000001466641251541261300274020ustar00rootroot00000000000000/* * ring-conference-channel.c - Source for RingConferenceChannel * * Copyright (C) 2007-2010 Nokia Corporation * @author Pekka Pessi * @author Kai Vehmanen * * This work is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This work is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this work; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ /* * Based on telepathy-glib/examples/cm/echo/chan.c: * * """ * Copyright (C) 2007 Collabora Ltd. * Copyright (C) 2007 Nokia Corporation * * Copying and distribution of this file, with or without modification, * are permitted in any medium without royalty provided the copyright * notice and this notice are preserved. * """ */ #include "config.h" #define DEBUG_FLAG RING_DEBUG_MEDIA #include "ring-debug.h" #include "ring-conference-channel.h" #include "ring-member-channel.h" #include "ring-util.h" #include "ring-extensions/gtypes.h" #include "modem/call.h" #include "modem/errors.h" #include #include #include #include #include #include #include #include #include #include #include #include "ring-connection.h" #include "ring-param-spec.h" #include #include #include #include #include #define MODEM_MAX_CALLS (7) struct _RingConferenceChannelPrivate { GQueue requests[1]; /* Requests towards the modem */ RingInitialMembers *initial_members; RingMemberChannel *members[MODEM_MAX_CALLS]; int is_current[MODEM_MAX_CALLS]; TpIntSet *current, *pending; struct { gulong left, joined; } signals; struct stream_state { guint id; /* nonzero when active, 0 otherwise */ TpHandle handle; TpMediaStreamType type; TpMediaStreamState state; TpMediaStreamDirection direction; TpMediaStreamPendingSend pending; } audio[1]; struct { gboolean state; gboolean requested; } hold; unsigned streams_created:1, conf_created:1, hangup:1, closing:1, disposed:1, :0; }; /* properties */ enum { PROP_NONE, /* o.f.T.Channel.Interfaces */ PROP_INITIAL_CHANNELS, PROP_CHANNELS, /* KVXXX: add PROP_TONES */ LAST_PROPERTY }; static TpDBusPropertiesMixinIfaceImpl ring_conference_channel_dbus_property_interfaces[]; static void ring_conference_channel_dtmf_iface_init (gpointer g_iface, gpointer iface_data); static void ring_conference_channel_hold_iface_init (gpointer g_iface, gpointer iface_data); static void ring_conference_channel_streamed_media_iface_init (gpointer g_iface, gpointer iface_data); static void ring_conference_channel_mergeable_conference_iface_init (gpointer, gpointer); static gboolean ring_conference_channel_close ( RingConferenceChannel *_self, gboolean immediately); static gboolean ring_conference_channel_close_impl ( RingConferenceChannel *self, gboolean immediately, gboolean hangup); static gboolean ring_conference_channel_add_member( GObject *obj, TpHandle handle, const char *message, GError **error); static gboolean ring_conference_channel_remove_member_with_reason( GObject *obj, TpHandle handle, const char *message, guint reason, GError **error); static GPtrArray *ring_conference_get_channels( RingConferenceChannel const *self); static void ring_conference_channel_fill_immutable_properties(TpBaseChannel *base, GHashTable *props); static void ring_conference_channel_emit_channel_merged( RingConferenceChannel *channel, RingMemberChannel *member, gboolean current); static void ring_conference_channel_release(RingConferenceChannel *self, unsigned causetype, unsigned cause, GError const *error); static gboolean ring_conference_channel_create_streams(RingConferenceChannel *_self, guint handle, gboolean audio, gboolean video, GError **error); /* ====================================================================== */ /* GObject interface */ G_DEFINE_TYPE_WITH_CODE( RingConferenceChannel, ring_conference_channel, TP_TYPE_BASE_CHANNEL, G_IMPLEMENT_INTERFACE(TP_TYPE_SVC_DBUS_PROPERTIES, tp_dbus_properties_mixin_iface_init); G_IMPLEMENT_INTERFACE(TP_TYPE_SVC_CHANNEL_INTERFACE_DTMF, ring_conference_channel_dtmf_iface_init); G_IMPLEMENT_INTERFACE(TP_TYPE_SVC_CHANNEL_INTERFACE_HOLD, ring_conference_channel_hold_iface_init); G_IMPLEMENT_INTERFACE(TP_TYPE_SVC_CHANNEL_TYPE_STREAMED_MEDIA, ring_conference_channel_streamed_media_iface_init); G_IMPLEMENT_INTERFACE(TP_TYPE_SVC_CHANNEL_INTERFACE_GROUP, tp_group_mixin_iface_init); G_IMPLEMENT_INTERFACE(TP_TYPE_SVC_CHANNEL_INTERFACE_CONFERENCE, NULL); G_IMPLEMENT_INTERFACE(RING_TYPE_SVC_CHANNEL_INTERFACE_MERGEABLE_CONFERENCE, ring_conference_channel_mergeable_conference_iface_init)); const char *ring_conference_channel_interfaces[] = { TP_IFACE_CHANNEL_INTERFACE_DTMF, TP_IFACE_CHANNEL_INTERFACE_HOLD, TP_IFACE_CHANNEL_INTERFACE_GROUP, TP_IFACE_CHANNEL_INTERFACE_CONFERENCE, RING_IFACE_CHANNEL_INTERFACE_MERGEABLE_CONFERENCE, NULL }; static ModemRequest * ring_conference_channel_queue_request (RingConferenceChannel *self, ModemRequest *request) { if (request) g_queue_push_tail (self->priv->requests, request); return request; } static ModemRequest * ring_conference_channel_dequeue_request (RingConferenceChannel *self, ModemRequest *request) { if (request) g_queue_remove (self->priv->requests, request); return request; } static ModemCallService * ring_conference_channel_get_call_service (RingConferenceChannel *self) { TpBaseChannel *base = TP_BASE_CHANNEL (self); TpBaseConnection *base_connection; RingConnection *connection; ModemOface *oface; base_connection = tp_base_channel_get_connection (base); connection = RING_CONNECTION (base_connection); oface = ring_connection_get_modem_interface (connection, MODEM_OFACE_CALL_MANAGER); if (oface) return MODEM_CALL_SERVICE (oface); else return NULL; } static void modem_call_service_join_reply (ModemCallService *service, ModemRequest *request, GError *error, gpointer _self) { RingConferenceChannel *self = RING_CONFERENCE_CHANNEL(_self); RingMemberChannel *member; char const *member_path; DBusGMethodInvocation *context; GError *clearerror = NULL; ring_conference_channel_dequeue_request (self, request); context = modem_request_steal_data(request, "tp-request"); if (!error) { RingConnection *conn = RING_CONNECTION ( tp_base_channel_get_connection (TP_BASE_CHANNEL (self))); member_path = modem_request_get_data(request, "member-object-path"); member = ring_connection_lookup_channel(conn, member_path); if (ring_conference_channel_join(self, member, &error)) { ring_svc_channel_interface_mergeable_conference_return_from_merge (context); DEBUG("ok"); return; } clearerror = error; } DEBUG("return error \"%s\"", error->message); dbus_g_method_return_error(context, error); g_clear_error(&clearerror); } static void ring_mergeable_conference_merge(RingSvcChannelInterfaceMergeableConference *iface, const char *channel_path, DBusGMethodInvocation *context) { RingConferenceChannel *self = RING_CONFERENCE_CHANNEL(iface); RingMemberChannel *member; GError *error = NULL; DEBUG("enter"); member = ring_connection_lookup_channel( RING_CONNECTION(tp_base_channel_get_connection(TP_BASE_CHANNEL(self))), channel_path); if (!member || !RING_IS_MEMBER_CHANNEL(member)) { error = g_error_new(TP_ERRORS, TP_ERROR_INVALID_ARGUMENT, "Invalid channel path"); } else if (!ring_member_channel_can_become_member(member, &error)) { ; } else { ModemRequest *request; ModemCallService *service; service = ring_conference_channel_get_call_service (self); request = modem_call_request_conference (service, modem_call_service_join_reply, self); ring_conference_channel_queue_request (self, request); modem_request_add_data_full(request, "member-object-path", g_strdup(channel_path), g_free); modem_request_add_data_full(request, "tp-request", context, ring_method_return_internal_error); return; } DEBUG("return error \"%s\"", error->message); dbus_g_method_return_error(context, error); g_clear_error(&error); } /* * Telepathy.Channel.Interface.DTMF DBus interface - version 0.19.6 */ static RingMemberChannel * ring_conference_get_first_active_member (RingConferenceChannel *self) { RingConferenceChannelPrivate *priv = self->priv; int i; for (i = 0; i < MODEM_MAX_CALLS; i++) { RingMemberChannel *member = priv->members[i]; if (member && priv->is_current[i]) return member; } return 0; } /** DBus method StartTone ( u: stream_id, y: event ) -> nothing * * Start sending a DTMF tone on this stream. Where possible, the tone will * continue until StopTone is called. On certain protocols, it may only be * possible to send events with a predetermined length. In this case, the * implementation may emit a fixed-length tone, and the StopTone method call * should return NotAvailable. */ static void ring_conference_channel_dtmf_start_tone(TpSvcChannelInterfaceDTMF *iface, guint stream_id, guchar event, DBusGMethodInvocation *context) { RingConferenceChannel *self = RING_CONFERENCE_CHANNEL (iface); RingMemberChannel *member = ring_conference_get_first_active_member (self); if (member) { TpSvcChannelInterfaceDTMF *dtmf_if = TP_SVC_CHANNEL_INTERFACE_DTMF (member); g_assert (dtmf_if != NULL); return ring_media_channel_dtmf_start_tone (dtmf_if, stream_id, event, context); } else { GError error[] = {{ TP_ERRORS, TP_ERROR_NOT_AVAILABLE, "No member channels to send DTMFs through" }}; dbus_g_method_return_error(context, error); } } /** DBus method StopTone ( u: stream_id ) -> nothing * * Stop sending any DTMF tone which has been started using the StartTone * method. If there is no current tone, this method will do nothing. */ static void ring_conference_channel_dtmf_stop_tone(TpSvcChannelInterfaceDTMF *iface, guint stream_id, DBusGMethodInvocation *context) { RingConferenceChannel *self = RING_CONFERENCE_CHANNEL (iface); RingMemberChannel *member = ring_conference_get_first_active_member (self); if (member) { TpSvcChannelInterfaceDTMF *dtmf_if = TP_SVC_CHANNEL_INTERFACE_DTMF (member); g_assert (dtmf_if != NULL); return ring_media_channel_dtmf_stop_tone (dtmf_if, stream_id, context); } else { GError error[] = {{ TP_ERRORS, TP_ERROR_NOT_AVAILABLE, "No member channels to send DTMFs through" }}; dbus_g_method_return_error(context, error); } } static void ring_conference_channel_dtmf_iface_init (gpointer g_iface, gpointer iface_data) { TpSvcChannelInterfaceDTMFClass *klass = (TpSvcChannelInterfaceDTMFClass *)g_iface; #define IMPLEMENT(x) tp_svc_channel_interface_dtmf_implement_##x( \ klass, ring_conference_channel_dtmf_##x) IMPLEMENT(start_tone); IMPLEMENT(stop_tone); #undef IMPLEMENT } /* ---------------------------------------------------------------------- */ /* Implement org.freedesktop.Telepathy.Channel.Interface.Hold */ static void ring_conference_channel_get_hold_state (TpSvcChannelInterfaceHold *iface, DBusGMethodInvocation *context) { RingConferenceChannel *self = RING_CONFERENCE_CHANNEL (iface); RingConferenceChannelPrivate *priv = self->priv; guint i; guint hold_state = NUM_TP_LOCAL_HOLD_STATES; /* known invalid value */ guint hold_reason = NUM_TP_LOCAL_HOLD_STATE_REASONS; gboolean state_set = FALSE; for (i = 0; i < MODEM_MAX_CALLS; i++) { RingMemberChannel *member = priv->members[i]; DEBUG("get_hold_state: %i/%p", i, member); if (member && priv->is_current[i]) { guint nextstate; g_object_get (member, "hold-state", &nextstate, NULL); if (state_set == FALSE) { hold_state = nextstate; g_object_get (member, "hold-state-reason", &hold_reason, NULL); state_set = TRUE; } else { /* note: the current modem/ofono APIs do not provide means * to get notifications about conf call state (only * about individual calls that might be members of * a conf-call, so we have to fall back to composing * the state by looking at member channel states */ if (hold_state != nextstate) { ring_warning ( "member call hold state inconsistant (call %i %d, conf %d)\n", i + 1, nextstate, hold_state); } } } } /* * As the member channels won't know about this, we have * to check separately whether it was the conf channel that * requested the hold. */ if (hold_state && priv->hold.requested) { hold_reason = TP_LOCAL_HOLD_STATE_REASON_REQUESTED; } tp_svc_channel_interface_hold_return_from_get_hold_state (context, hold_state, hold_reason); } static void reply_to_request_swap_calls (ModemCallService *_service, ModemRequest *request, GError *error, gpointer _self) { RingConferenceChannel *self = RING_CONFERENCE_CHANNEL(_self); RingConferenceChannelPrivate *priv = self->priv; guint hold = (priv->hold.requested == TRUE) ? TP_LOCAL_HOLD_STATE_HELD : TP_LOCAL_HOLD_STATE_UNHELD; ring_conference_channel_dequeue_request (self, request); DEBUG(""); priv->hold.state = priv->hold.requested; /* XXX: this can potentially confuse the client as the individual * member channel states might not be yet updated... * should we perhaps stay in PENDING state until all * the participant calls have changed state (or failed * to do so)? */ tp_svc_channel_interface_hold_emit_hold_state_changed( (TpSvcChannelInterfaceHold *)self, hold, TP_LOCAL_HOLD_STATE_REASON_REQUESTED); } static void ring_conference_channel_request_hold (TpSvcChannelInterfaceHold *iface, gboolean hold, DBusGMethodInvocation *context) { RingConferenceChannel *self = RING_CONFERENCE_CHANNEL (iface); RingConferenceChannelPrivate *priv = self->priv; GError *error = NULL; DEBUG ("(%u) on %s", hold, self->nick); if (priv->conf_created == FALSE) { g_set_error (&error, TP_ERRORS, TP_ERROR_DISCONNECTED, "No conference call available"); } else if (hold == priv->hold.state || hold == priv->hold.requested) { tp_svc_channel_interface_hold_return_from_request_hold(context); return; } else { ModemCallService *service = ring_conference_channel_get_call_service (self); guint holdstate = (hold == TRUE) ? TP_LOCAL_HOLD_STATE_PENDING_HOLD : TP_LOCAL_HOLD_STATE_PENDING_UNHOLD; priv->hold.requested = hold; tp_svc_channel_interface_hold_emit_hold_state_changed( (TpSvcChannelInterfaceHold *)self, holdstate, TP_LOCAL_HOLD_STATE_REASON_REQUESTED); ring_conference_channel_queue_request (self, modem_call_service_swap_calls(service, reply_to_request_swap_calls, self)); tp_svc_channel_interface_hold_return_from_request_hold(context); return; } DEBUG ("request_hold(%u) on %s: %s", hold, self->nick, error->message); dbus_g_method_return_error (context, error); g_clear_error (&error); } static void ring_conference_channel_hold_iface_init (gpointer g_iface, gpointer iface_data) { TpSvcChannelInterfaceHoldClass *klass = g_iface; #define IMPLEMENT(x) tp_svc_channel_interface_hold_implement_##x( \ klass, ring_conference_channel_##x) IMPLEMENT(get_hold_state); IMPLEMENT(request_hold); #undef IMPLEMENT } /* ====================================================================== */ /** * Telepathy.Channel.Type.StreamedMedia DBus interface */ /* * KVXXX: move most of below to a common util file * (can be shared with ring-media-channel) */ #define TP_CHANNEL_STREAM_TYPE \ (dbus_g_type_get_struct("GValueArray", \ G_TYPE_UINT, \ G_TYPE_UINT, \ G_TYPE_UINT, \ G_TYPE_UINT, \ G_TYPE_UINT, \ G_TYPE_UINT, \ G_TYPE_INVALID)) enum { RING_MEDIA_STREAM_ID_AUDIO = 1, }; /** Update media stream state. * * @retval 0 if nothing changed * @retval 1 (or nonzero) if state changed */ static int update_media_stream(RingConferenceChannel *self, TpHandle handle, struct stream_state *ss, guint id, TpMediaStreamType type, TpMediaStreamState state, TpMediaStreamDirection direction, TpMediaStreamPendingSend pending) { int changed = 0; if (type != TP_MEDIA_STREAM_TYPE_AUDIO) return 0; if (state == TP_MEDIA_STREAM_STATE_DISCONNECTED) { if (ss->id == id) { changed = 1; /* emit StreamRemoved */ tp_svc_channel_type_streamed_media_emit_stream_removed( (TpSvcChannelTypeStreamedMedia *)self, id); memset(ss, 0, sizeof ss); } return changed; } /* emit StreamAdded */ if (ss->id != id) { if (handle == 0) g_object_get(self, "peer", &handle, NULL); changed = 1; ss->id = id; ss->handle = handle; ss->type = type; if (DEBUGGING) { DEBUG("emitting StreamAdded(%u, %d, %s)", id, handle, type == TP_MEDIA_STREAM_TYPE_AUDIO ? "AUDIO" : type == TP_MEDIA_STREAM_TYPE_VIDEO ? "VIDEO" : "???"); } tp_svc_channel_type_streamed_media_emit_stream_added( (TpSvcChannelTypeStreamedMedia *)self, ss->id, ss->handle, ss->type); } /* emit StreamStateChanged */ if (ss->state != state) { changed = 1; ss->state = state; if (DEBUGGING) { DEBUG("emitting StreamStateChanged(%u, %s)", ss->id, state == TP_MEDIA_STREAM_STATE_DISCONNECTED ? "DISCONNECTED" : state == TP_MEDIA_STREAM_STATE_CONNECTING ? "CONNECTING" : state == TP_MEDIA_STREAM_STATE_CONNECTED ? "CONNECTED" : "???"); } tp_svc_channel_type_streamed_media_emit_stream_state_changed ( (TpSvcChannelTypeStreamedMedia *)self, ss->id, state); } /* emit StreamDirectionChanged */ if (ss->direction != direction || ss->pending != pending) { changed = 1; ss->direction = direction; ss->pending = pending; if (DEBUGGING) { DEBUG("emitting StreamDirectionChanged(%u, %s,%s%s%s)", ss->id, direction == TP_MEDIA_STREAM_DIRECTION_BIDIRECTIONAL ? "BIDIRECTIONAL" : direction == TP_MEDIA_STREAM_DIRECTION_SEND ? "SEND" : direction == TP_MEDIA_STREAM_DIRECTION_RECEIVE ? "RECV" : "NONE", pending & TP_MEDIA_STREAM_PENDING_REMOTE_SEND ? " remote" : "", pending & TP_MEDIA_STREAM_PENDING_LOCAL_SEND ? " local" : "", pending == 0 ? " 0" : ""); } tp_svc_channel_type_streamed_media_emit_stream_direction_changed( (TpSvcChannelTypeStreamedMedia *)self, ss->id, ss->direction, ss->pending); } return changed; } static void free_media_stream_list(GPtrArray *list) { if (list) { const GType ElementType = TP_CHANNEL_STREAM_TYPE; guint i; for (i = list->len; i-- > 0;) g_boxed_free(ElementType, g_ptr_array_index(list, i)); g_ptr_array_free(list, TRUE); } } /* Return a pointer to GValue with boxed stream struct */ static gpointer describe_stream(struct stream_state *ss) { const GType ElementType = TP_CHANNEL_STREAM_TYPE; GValue element[1] = {{ 0 }}; g_value_init(element, ElementType); g_value_take_boxed(element, dbus_g_type_specialized_construct(ElementType)); dbus_g_type_struct_set(element, 0, ss->id, 1, ss->handle, 2, ss->type, 3, ss->state, 4, ss->direction, 5, ss->pending, G_MAXUINT); return g_value_get_boxed(element); } static GPtrArray * list_media_streams(RingConferenceChannel *self) { RingConferenceChannelPrivate *priv = self->priv; GPtrArray *list; size_t size; size = (priv->audio->id != 0); list = g_ptr_array_sized_new(size); if (priv->audio->id) g_ptr_array_add(list, describe_stream(priv->audio)); return list; } static gpointer describe_null_media(TpMediaStreamType tptype) { struct stream_state ss[1] = {{ 0 }}; ss->type = tptype; return describe_stream(ss); } static gboolean ring_conference_channel_validate_media_handle(RingConferenceChannel *_self, guint *handlep, GError **error) { if (*handlep != 0) { g_set_error (error, TP_ERRORS, TP_ERROR_INVALID_ARGUMENT, "Conference channel carries a multiparty stream only"); return FALSE; } return TRUE; } /** DBus method ListStreams ( ) -> a(uuuuuu) * * Returns an array of structs representing the streams currently active * within this channel. Each stream is identified by an unsigned integer * which is unique for each stream within the channel. * * Returns * * a(uuuuuu) * An array of structs containing: * * * the stream identifier * * the contact handle who the stream is with * (or 0 if the stream represents more than a single member) * * the type of the stream * * the current stream state * * the current direction of the stream * * the current pending send flags */ static void ring_conference_channel_list_streams(TpSvcChannelTypeStreamedMedia *iface, DBusGMethodInvocation *context) { RingConferenceChannel *self = RING_CONFERENCE_CHANNEL(iface); GPtrArray *list; list = list_media_streams(self); tp_svc_channel_type_streamed_media_return_from_list_streams(context, list); free_media_stream_list(list); } /** DBus method RequestStreams ( u: contact_handle, au: types ) -> a(uuuuuu) * * Request that streams be established to exchange the given types of media * with the given member. In general this will try and establish a * bidirectional stream, but on some protocols it may not be possible to * indicate to the peer that you would like to receive media, so a send-only * stream will be created initially. In the cases where the stream requires * remote agreement (eg you wish to receive media from them), the * StreamDirectionChanged signal will be emitted with the * MEDIA_STREAM_PENDING_REMOTE_SEND flag set, and the signal emitted again * with the flag cleared when the remote end has replied. * */ static void ring_conference_channel_request_streams(TpSvcChannelTypeStreamedMedia *iface, guint handle, const GArray *media_types, DBusGMethodInvocation *context) { RingConferenceChannel *self = RING_CONFERENCE_CHANNEL(iface); RingConferenceChannelPrivate *priv = self->priv; GError *error = NULL; GPtrArray *list; guint i, create_audio_stream = 0; struct stream_state audio[1]; DEBUG("(...) on %s", self->nick); if (!ring_conference_channel_validate_media_handle(self, &handle, &error)) { dbus_g_method_return_error(context, error); g_clear_error(&error); return; } *audio = *priv->audio; /* We can create media only when call has not been initiated */ for (i = 0; i < media_types->len; i++) { guint media_type = g_array_index(media_types, guint, i); if (media_type == TP_MEDIA_STREAM_TYPE_AUDIO && !create_audio_stream) create_audio_stream = update_media_stream(self, handle, audio, RING_MEDIA_STREAM_ID_AUDIO, media_type, TP_MEDIA_STREAM_STATE_CONNECTING, TP_MEDIA_STREAM_DIRECTION_NONE, TP_MEDIA_STREAM_PENDING_LOCAL_SEND | TP_MEDIA_STREAM_PENDING_REMOTE_SEND); } if (create_audio_stream) *priv->audio = *audio; list = g_ptr_array_sized_new(media_types->len); for (i = 0; i < media_types->len; i++) { guint media_type = g_array_index(media_types, guint, i); gpointer element; if (media_type == TP_MEDIA_STREAM_TYPE_AUDIO && create_audio_stream) element = describe_stream(priv->audio), create_audio_stream = 0; else element = describe_null_media(media_type); g_ptr_array_add(list, element); } ring_conference_channel_create_streams( self, handle, create_audio_stream, FALSE, &error); tp_svc_channel_type_streamed_media_return_from_request_streams( context, list); free_media_stream_list(list); DEBUG("exit"); } static void ring_conference_channel_streamed_media_iface_init (gpointer g_iface, gpointer iface_data) { TpSvcChannelTypeStreamedMediaClass *klass = (TpSvcChannelTypeStreamedMediaClass *)g_iface; #define IMPLEMENT(x) tp_svc_channel_type_streamed_media_implement_##x( \ klass, ring_conference_channel_##x) IMPLEMENT(list_streams); /* IMPLEMENT(remove_streams); */ /* IMPLEMENT(request_stream_direction); */ IMPLEMENT(request_streams); #undef IMPLEMENT } static void ring_conference_channel_mergeable_conference_iface_init(gpointer g_iface, gpointer iface_data) { RingSvcChannelInterfaceMergeableConferenceClass *klass = (RingSvcChannelInterfaceMergeableConferenceClass *)g_iface; #define IMPLEMENT(x) \ ring_svc_channel_interface_mergeable_conference_implement_##x \ (klass, ring_mergeable_conference_##x) IMPLEMENT(merge); #undef IMPLEMENT } static void ring_conference_channel_init(RingConferenceChannel *self) { self->priv = G_TYPE_INSTANCE_GET_PRIVATE( self, RING_TYPE_CONFERENCE_CHANNEL, RingConferenceChannelPrivate); } static void ring_conference_channel_constructed(GObject *object) { RingConferenceChannel *self = RING_CONFERENCE_CHANNEL(object); TpBaseChannel *base = TP_BASE_CHANNEL (self); TpBaseConnection *connection = tp_base_channel_get_connection(base); const gchar *object_path; char *nick; if (G_OBJECT_CLASS(ring_conference_channel_parent_class)->constructed) G_OBJECT_CLASS(ring_conference_channel_parent_class)->constructed(object); tp_group_mixin_init( object, G_STRUCT_OFFSET(RingConferenceChannel, group), tp_base_connection_get_handles(connection, TP_HANDLE_TYPE_CONTACT), connection->self_handle); object_path = tp_base_channel_get_object_path (base); g_assert(object_path != NULL); nick = strrchr(object_path, '/'); ++nick; g_assert (nick != NULL); self->nick = g_strdup(nick); DEBUG("(%p) with %s", self, self->nick); tp_base_channel_register (base); } void ring_conference_channel_emit_initial(RingConferenceChannel *_self) { DEBUG("enter"); RingConferenceChannel *self = RING_CONFERENCE_CHANNEL(_self); RingConferenceChannelPrivate *priv = self->priv; RingConnection *connection = RING_CONNECTION(tp_base_channel_get_connection(TP_BASE_CHANNEL(self))); TpGroupMixin *group = TP_GROUP_MIXIN(self); char const *message; TpChannelGroupChangeReason reason; TpChannelGroupFlags add = 0, del = 0; char const *member_path; RingMemberChannel *member; int i; message = "Conference created"; reason = TP_CHANNEL_GROUP_CHANGE_REASON_INVITED; priv->current = tp_intset_new (); tp_intset_add (priv->current, group->self_handle); priv->pending = tp_intset_new (); for (i = 0; i < priv->initial_members->len; i++) { member_path = priv->initial_members->odata[i]; member = ring_connection_lookup_channel (connection, member_path); if (member) { ring_conference_channel_emit_channel_merged ( self, RING_MEMBER_CHANNEL(member), TRUE); } else { DEBUG("No member channel %s found\n", member_path); } } tp_group_mixin_change_members((GObject *)self, message, priv->current, NULL, NULL, priv->pending, group->self_handle, reason); tp_intset_destroy(priv->current); priv->current = NULL; tp_intset_destroy(priv->pending); priv->pending = NULL; add |= TP_CHANNEL_GROUP_FLAG_CAN_REMOVE; if (tp_handle_set_size(group->remote_pending)) add |= TP_CHANNEL_GROUP_FLAG_CAN_RESCIND; add |= TP_CHANNEL_GROUP_FLAG_PROPERTIES; add |= TP_CHANNEL_GROUP_FLAG_CHANNEL_SPECIFIC_HANDLES; add |= TP_CHANNEL_GROUP_FLAG_MEMBERS_CHANGED_DETAILED; tp_group_mixin_change_flags((GObject *)self, add, del); } static void ring_conference_channel_get_property(GObject *obj, guint property_id, GValue *value, GParamSpec *pspec) { RingConferenceChannel *self = RING_CONFERENCE_CHANNEL(obj); RingConferenceChannelPrivate *priv = self->priv; switch (property_id) { case PROP_INITIAL_CHANNELS: g_value_set_boxed(value, priv->initial_members); break; case PROP_CHANNELS: g_value_take_boxed(value, ring_conference_get_channels(self)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, property_id, pspec); break; } } static void ring_conference_channel_set_property(GObject *obj, guint property_id, const GValue *value, GParamSpec *pspec) { RingConferenceChannel *self = RING_CONFERENCE_CHANNEL(obj); RingConferenceChannelPrivate *priv = self->priv; switch (property_id) { case PROP_INITIAL_CHANNELS: priv->initial_members = g_value_dup_boxed(value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, property_id, pspec); break; } } static void ring_conference_channel_dispose(GObject *obj) { RingConferenceChannel *self = RING_CONFERENCE_CHANNEL(obj); if (self->priv->disposed) return; self->priv->disposed = TRUE; guint i; for (i = 0; i < MODEM_MAX_CALLS; i++) { if (self->priv->members[i]) { g_object_unref(self->priv->members[i]), self->priv->members[i] = NULL; } } while (!g_queue_is_empty (self->priv->requests)) { modem_request_cancel (g_queue_pop_head (self->priv->requests)); } ((GObjectClass *)ring_conference_channel_parent_class)->dispose(obj); } static void ring_conference_channel_finalize(GObject *obj) { RingConferenceChannel *self = RING_CONFERENCE_CHANNEL(obj); RingConferenceChannelPrivate *priv = self->priv; tp_group_mixin_finalize(obj); g_boxed_free(TP_ARRAY_TYPE_OBJECT_PATH_LIST, priv->initial_members); g_free (self->nick); priv->initial_members = NULL; G_OBJECT_CLASS(ring_conference_channel_parent_class)->finalize(obj); DEBUG("exit"); } /* ====================================================================== */ /* GObjectClass */ static void ring_conference_channel_class_init(RingConferenceChannelClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS(klass); TpBaseChannelClass *base_chan_class = TP_BASE_CHANNEL_CLASS (klass); g_type_class_add_private(klass, sizeof (RingConferenceChannelPrivate)); object_class->constructed = ring_conference_channel_constructed; object_class->get_property = ring_conference_channel_get_property; object_class->set_property = ring_conference_channel_set_property; object_class->dispose = ring_conference_channel_dispose; object_class->finalize = ring_conference_channel_finalize; base_chan_class->channel_type = TP_IFACE_CHANNEL_TYPE_STREAMED_MEDIA; base_chan_class->close = (TpBaseChannelCloseFunc) ring_conference_channel_close; base_chan_class->interfaces = ring_conference_channel_interfaces; base_chan_class->target_handle_type = TP_HANDLE_TYPE_NONE; base_chan_class->fill_immutable_properties = ring_conference_channel_fill_immutable_properties; klass->dbus_properties_class.interfaces = ring_conference_channel_dbus_property_interfaces; tp_dbus_properties_mixin_class_init( object_class, G_STRUCT_OFFSET(RingConferenceChannelClass, dbus_properties_class)); tp_group_mixin_class_init( object_class, G_STRUCT_OFFSET(RingConferenceChannelClass, group_class), ring_conference_channel_add_member, NULL); tp_group_mixin_class_set_remove_with_reason_func( object_class, ring_conference_channel_remove_member_with_reason); tp_group_mixin_init_dbus_properties(object_class); g_object_class_install_property( object_class, PROP_INITIAL_CHANNELS, g_param_spec_boxed("initial-channels", "Initial member channels", "The two initial member channels used " "when the conference channel is constructed.", TP_ARRAY_TYPE_OBJECT_PATH_LIST, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); g_object_class_install_property( object_class, PROP_CHANNELS, g_param_spec_boxed("channels", "Current member channels", "The current member channels participating in " "the conference.", TP_ARRAY_TYPE_OBJECT_PATH_LIST, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); } /* ====================================================================== */ /** * org.freedesktop.DBus properties */ /* Properties for com.nokia.Telepathy.Channel.Interface.Conference */ static TpDBusPropertiesMixinPropImpl conference_properties[] = { { "InitialChannels", "initial-channels" }, { "Channels", "channels" }, { NULL } }; static TpDBusPropertiesMixinIfaceImpl ring_conference_channel_dbus_property_interfaces[] = { { TP_IFACE_CHANNEL_INTERFACE_CONFERENCE, tp_dbus_properties_mixin_getter_gobject_properties, NULL, conference_properties, }, { NULL } }; static void ring_conference_channel_fill_immutable_properties(TpBaseChannel *base, GHashTable *props) { TP_BASE_CHANNEL_CLASS (ring_conference_channel_parent_class)->fill_immutable_properties ( base, props); tp_dbus_properties_mixin_fill_properties_hash (G_OBJECT(base), props, TP_IFACE_CHANNEL_INTERFACE_CONFERENCE, "InitialChannels", NULL); } /* ---------------------------------------------------------------------- */ /** * Interface Telepathy.Channel.Interface.Group (version 0.15) * using TpGroupMixin from telepathy-glib * * Methods: * AddMembers ( au: contacts, s: message ) -> nothing * GetAllMembers ( ) -> au, au, au * GetGroupFlags ( ) -> u * GetHandleOwners ( au: handles ) -> au * GetLocalPendingMembers ( ) -> au * GetLocalPendingMembersWithInfo ( ) -> a(uuus) * GetMembers ( ) -> au * GetRemotePendingMembers ( ) -> au * GetSelfHandle ( ) -> u * RemoveMembers ( au: contacts, s: message ) -> nothing * Signals: * -> GroupFlagsChanged ( u: added, u: removed ) * -> MembersChanged ( s: message, au: added, au: removed, * au: local_pending, au: remote_pending, * u: actor, u: reason ) * * TPGroupMixin interface: * ring_conference_channel_add_member() * ring_conference_channel_remove_member_with_reason() */ static gboolean ring_conference_channel_add_member(GObject *iface, TpHandle handle, const char *message, GError **error) { TpGroupMixin *mixin = TP_GROUP_MIXIN(iface); DEBUG("enter"); if (tp_handle_set_is_member(mixin->members, handle)) return TRUE; else return FALSE; } static void reply_to_modem_call_request_hangup_conference (ModemCallService *_service, ModemRequest *request, GError *error, gpointer _self) { RingConferenceChannel *self = RING_CONFERENCE_CHANNEL(_self); RingConferenceChannelPrivate *priv = self->priv; TpBaseChannel *base = TP_BASE_CHANNEL (self); ring_conference_channel_dequeue_request (self, request); DEBUG(""); priv->conf_created = FALSE; tp_base_channel_destroyed (base); } /* * Requests modem to hangup the multiparty call. */ static void ring_conference_do_hangup (RingConferenceChannel *self) { RingConferenceChannelPrivate *priv = self->priv; ModemCallService *service; priv->hangup = TRUE; service = ring_conference_channel_get_call_service (self); ring_conference_channel_queue_request (self, modem_call_request_hangup_conference (service, reply_to_modem_call_request_hangup_conference, self)); } static gboolean ring_conference_channel_remove_member_with_reason(GObject *iface, TpHandle handle, const char *message, guint reason, GError **error) { RingConferenceChannel *self = RING_CONFERENCE_CHANNEL(iface); RingConferenceChannelPrivate *priv = self->priv; RingMemberChannel *member; TpGroupMixin *mixin = TP_GROUP_MIXIN(iface); TpIntSet *removing; TpHandle selfhandle, memberhandle; int i; DEBUG("enter"); selfhandle = tp_base_connection_get_self_handle( tp_base_channel_get_connection(TP_BASE_CHANNEL(self))); removing = tp_intset_new(); if (handle == selfhandle) { tp_intset_add(removing, handle); ring_conference_do_hangup (self); } for (i = 0; i < MODEM_MAX_CALLS; i++) { member = priv->members[i]; if (!member) continue; g_object_get(member, "member-handle", &memberhandle, NULL); if (handle == memberhandle) { tp_intset_add(removing, memberhandle); if (!ring_member_channel_release(member, message, reason, error)) goto error; break; } } if (tp_intset_size(removing) == 0) { g_set_error(error, TP_ERRORS, TP_ERROR_PERMISSION_DENIED, "handle %u cannot be removed", handle); goto error; } /* Remove handles from member set */ tp_group_mixin_change_members(iface, message, NULL, removing, NULL, NULL, selfhandle, reason); tp_intset_destroy(removing), removing = NULL; guint del = 0; if (tp_handle_set_size(mixin->members) < 1) del |= TP_CHANNEL_GROUP_FLAG_CAN_REMOVE; if (tp_handle_set_size(mixin->remote_pending) < 1) del |= TP_CHANNEL_GROUP_FLAG_CAN_RESCIND; if (del) tp_group_mixin_change_flags(iface, 0, del); return TRUE; error: tp_intset_destroy(removing); return FALSE; } /* ---------------------------------------------------------------------- */ /* com.nokia.Telepathy.Channel.Interface.Conference */ guint ring_conference_channel_has_members(RingConferenceChannel const *self) { int i, n = 0; if (self) { for (i = 0; i < MODEM_MAX_CALLS; i++) { if (self->priv->members[i] != NULL) n++; } } return n; } static void ring_conference_channel_emit_channel_merged(RingConferenceChannel *self, RingMemberChannel *member, gboolean current) { RingConferenceChannelPrivate *priv = self->priv; int i; char *member_object_path; GHashTable *member_map; TpHandle member_handle; /* XXXKV: fill with correct values */ GHashTable *member_props = tp_dbus_properties_mixin_make_properties_hash (G_OBJECT (member), NULL, NULL, NULL); g_object_get(member, "object-path", &member_object_path, "member-map", &member_map, "member-handle", &member_handle, NULL); for (i = 0; i < MODEM_MAX_CALLS; i++) { if (member == priv->members[i]) { priv->is_current[i] = current; goto emit_members_changed; } } for (i = 0; i < MODEM_MAX_CALLS; i++) { if (priv->members[i] == NULL) break; } g_assert(i < MODEM_MAX_CALLS); if (i >= MODEM_MAX_CALLS) goto error; priv->members[i] = g_object_ref(member); priv->is_current[i] = current; DEBUG("emitting MemberChannelAdded(%s)", strrchr(member_object_path, '/') + 1); /* XXX: This used to take member_map, which could be useful */ tp_svc_channel_interface_conference_emit_channel_merged( self, member_object_path, member_handle, member_props); emit_members_changed: DEBUG("%s member handle %u for %s", current ? "current" : "pending", member_handle, strrchr(member_object_path, '/') + 1); if (current) ring_member_channel_joined(member, self); gpointer owner, owned; GHashTableIter j[1]; for (g_hash_table_iter_init(j, member_map); g_hash_table_iter_next(j, &owned, &owner);) { tp_group_mixin_add_handle_owner((GObject *)self, GPOINTER_TO_UINT(owned), GPOINTER_TO_UINT(owner)); } if (!priv->pending) { TpGroupMixin *mixin = TP_GROUP_MIXIN(self); TpIntSet *members = tp_intset_new(); tp_intset_add(members, member_handle); tp_group_mixin_change_members((GObject *)self, "New conference members", current ? members : NULL, NULL, NULL, current ? NULL : members, current ? 0 : mixin->self_handle, TP_CHANNEL_GROUP_CHANGE_REASON_INVITED); tp_intset_destroy(members); TpChannelGroupFlags add = 0, del = 0; /* Allow removal and rescind */ add = TP_CHANNEL_GROUP_FLAG_CAN_REMOVE; if (tp_handle_set_size(mixin->remote_pending) == 0) /* Deny rescind */ del = TP_CHANNEL_GROUP_FLAG_CAN_RESCIND; else if (!current) add |= TP_CHANNEL_GROUP_FLAG_CAN_RESCIND; tp_group_mixin_change_flags((GObject *)self, add, del); } else { /* Report all handles at once */ if (current) tp_intset_add(priv->current, member_handle); else tp_intset_add(priv->pending, member_handle); } error: g_hash_table_destroy(member_map); g_hash_table_destroy(member_props); g_free(member_object_path); } void ring_conference_channel_emit_channel_removed( RingConferenceChannel *self, RingMemberChannel *member, char const *message, TpHandle actor, TpChannelGroupChangeReason reason) { RingConferenceChannelPrivate *priv = self->priv; guint i, n; char *object_path; TpHandle member_handle; GHashTable *details; if (!member) return; if (priv->closing) return; n = ring_conference_channel_has_members(self); for (i = 0; i < MODEM_MAX_CALLS; i++) { if (member == priv->members[i]) { priv->members[i] = NULL; priv->is_current[i] = 0; break; } } if (i >= MODEM_MAX_CALLS) return; /* note: as this function may lead to tearing down the whole * conference, keep a local self-reference */ g_object_ref (self); while (member) { TpIntSet *remove = tp_intset_new(); g_object_get(member, "object-path", &object_path, "member-handle", &member_handle, NULL); tp_intset_add(remove, member_handle); DEBUG("emitting MemberChannelRemoved(%s, %u, %u)", strrchr(object_path, '/') + 1, actor, reason); details = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, (GDestroyNotify) tp_g_value_slice_free); g_hash_table_insert (details, "actor", tp_g_value_slice_new_uint (actor)); g_hash_table_insert (details, "change-reason", tp_g_value_slice_new_uint (reason)); tp_svc_channel_interface_conference_emit_channel_removed( self, object_path, details); g_free(object_path); g_hash_table_destroy(details); ring_member_channel_left(member); g_object_unref((GObject *)member), member = NULL; tp_group_mixin_change_members((GObject *)self, message, NULL, remove, NULL, NULL, actor, reason); tp_intset_destroy(remove); if (n > 2) break; /* The conference channel goes away */ message = "Deactivating conference" ; actor = self->group.self_handle; reason = TP_CHANNEL_GROUP_CHANGE_REASON_SEPARATED; for (i = 0; i < MODEM_MAX_CALLS; i++) { if (priv->members[i]) { member = priv->members[i]; priv->members[i] = NULL; priv->is_current[i] = 0; break; } } } if (n > 2) goto out; DEBUG("Too few members, close channel %p", self); /* * Last member channel removed, close channel but do * not hangup. */ ring_conference_channel_close_impl (self, FALSE, FALSE); out: g_object_unref (self); } static GPtrArray * ring_conference_get_channels(RingConferenceChannel const *self) { RingConferenceChannelPrivate const *priv = self->priv; GPtrArray *list; int i; char *object_path; list = g_ptr_array_sized_new(MODEM_MAX_CALLS); for (i = 0; i < MODEM_MAX_CALLS; i++) { if (priv->members[i] && priv->is_current[i]) { g_object_get(priv->members[i], "object-path", &object_path, NULL); g_ptr_array_add(list, object_path); } } return list; } gboolean ring_conference_channel_check_initial_members(RingConferenceChannel const *channel, RingInitialMembers const *maybe) { if (channel == NULL || maybe == NULL) return FALSE; if (!RING_IS_CONFERENCE_CHANNEL(channel)) return FALSE; RingInitialMembers const *initial = channel->priv->initial_members; if (initial->len != maybe->len) return FALSE; guint i, j; for (i = 0; i < maybe->len; i++) { for (j = 0; j < initial->len; j++) { if (strcmp(initial->odata[j], maybe->odata[i]) == 0) break; } if (j == initial->len) return FALSE; } return TRUE; } gboolean ring_conference_channel_join(RingConferenceChannel *self, RingMemberChannel *member, GError **error) { RingConferenceChannelPrivate *priv = RING_CONFERENCE_CHANNEL(self)->priv; int i; if (!RING_IS_CONFERENCE_CHANNEL(self)) { g_set_error(error, TP_ERRORS, TP_ERROR_INVALID_ARGUMENT, "Not a Conference Channel"); return FALSE; } #if 0 if (!ring_member_channel_can_become_member(member, error)) { return FALSE; } #endif for (i = 0; i < MODEM_MAX_CALLS; i++) { if (member == priv->members[i]) { priv->is_current[i] = TRUE; } } ring_conference_channel_emit_channel_merged(self, member, FALSE); return TRUE; } /* ====================================================================== */ /* RingMediaChannel interface */ /** * Implements closing the channel * * @see ring_conference_channel_close() for the interface callback */ static gboolean ring_conference_channel_close_impl (RingConferenceChannel *self, gboolean immediately, gboolean hangup) { RingConferenceChannelPrivate *priv = RING_CONFERENCE_CHANNEL(self)->priv; int i; priv->closing = TRUE; for (i = 0; i < MODEM_MAX_CALLS; i++) { RingMemberChannel *member = priv->members[i]; if (member) { ring_member_channel_left(member); g_object_unref(member); } priv->members[i] = NULL; } if (hangup) { ring_conference_do_hangup (self); } else { TpBaseChannel *base = TP_BASE_CHANNEL (self); priv->conf_created = FALSE; DEBUG("emitting close without hanging up"); tp_base_channel_destroyed (base); } return TRUE; } /** * Implements the TpBaseChannel close method. */ static gboolean ring_conference_channel_close (RingConferenceChannel *self, gboolean immediately) { /** * When Close() is requested explicitly, hangup the conference * call (releases all member calls as well). */ return ring_conference_channel_close_impl (self, immediately, TRUE); } static void ring_conference_channel_release(RingConferenceChannel *self, unsigned causetype, unsigned cause, GError const *error0) { RingConferenceChannelPrivate *priv = self->priv; char const *message; TpIntSet *removed; TpChannelGroupChangeReason reason; GError *error = NULL; char *debug = NULL; char *dbus_error = NULL; int i, details; if (error0) { reason = TP_CHANNEL_GROUP_CHANGE_REASON_ERROR; error = (GError *)error0; } else { reason = ring_channel_group_release_reason(causetype, cause); error = modem_call_new_error(causetype, cause, NULL); } message = error->message; dbus_error = modem_error_fqn(error); details = causetype && cause && reason != TP_CHANNEL_GROUP_CHANGE_REASON_BUSY && reason != TP_CHANNEL_GROUP_CHANGE_REASON_NONE; DEBUG("TERMINATED: message=\"%s\" reason=%s (%u) cause=%s.%s (%u.%u)", message, ring_util_reason_name(reason), reason, modem_error_domain_prefix(error->domain), modem_error_name(error, NULL, 0), causetype, cause); debug = g_strdup_printf("conference terminated: " "reason=%s (%u) cause=%s.%s (%u.%u)", ring_util_reason_name(reason), reason, modem_error_domain_prefix(error->domain), modem_error_name(error, NULL, 0), causetype, cause); /* update flags accordingly -- deny adding/removal/rescind */ tp_group_mixin_change_flags((GObject *)self, 0, TP_CHANNEL_GROUP_FLAG_CAN_ADD | TP_CHANNEL_GROUP_FLAG_CAN_REMOVE | TP_CHANNEL_GROUP_FLAG_CAN_RESCIND); removed = tp_intset_new(); tp_intset_add(removed, TP_GROUP_MIXIN(self)->self_handle); for (i = 0; i < MODEM_MAX_CALLS; i++) { RingMemberChannel *member = priv->members[i]; if (member) { uint member_handle = 0; g_object_get(member, "member-handle", &member_handle, NULL); tp_intset_add(removed, member_handle); } } ring_util_group_change_members(self, NULL, removed, NULL, NULL, "change-reason", G_TYPE_UINT, reason, message ? "message" : "", G_TYPE_STRING, message, dbus_error ? "error" : "", G_TYPE_STRING, dbus_error, "debug-message", G_TYPE_STRING, debug, NULL); tp_intset_destroy(removed); g_free(dbus_error); if (error0 == NULL) g_error_free(error); g_free(debug); } static void reply_to_modem_call_request_conference(ModemCallService *_service, ModemRequest *request, GError *error, gpointer _channel); static gboolean ring_conference_channel_create_streams (RingConferenceChannel *_self, guint handle, gboolean audio, gboolean video, GError **error) { RingConferenceChannel *self = RING_CONFERENCE_CHANNEL(_self); RingConferenceChannelPrivate *priv = self->priv; ModemCallService *service; TpBaseConnection *baseconn; (void)audio; (void)video; if (priv->streams_created) { DEBUG("Already associated with a conference"); return TRUE; } priv->streams_created = 1; baseconn = tp_base_channel_get_connection (TP_BASE_CHANNEL (self)); if (!ring_connection_validate_initial_members (RING_CONNECTION (baseconn), priv->initial_members, error)) { return FALSE; } service = ring_conference_channel_get_call_service (self); ring_conference_channel_queue_request (self, modem_call_request_conference (service, reply_to_modem_call_request_conference, self)); return TRUE; } void ring_conference_channel_initial_audio (RingConferenceChannel *self) { RingConferenceChannelPrivate *priv = self->priv; ModemCallService *service; ModemRequest *request; DEBUG ("(%p)", self); priv->streams_created = 1; service = ring_conference_channel_get_call_service (self), request = modem_call_request_conference (service, reply_to_modem_call_request_conference, self); ring_conference_channel_queue_request (self, request); } static void reply_to_modem_call_request_conference(ModemCallService *_service, ModemRequest *request, GError *error, gpointer _self) { RingConferenceChannel *self = RING_CONFERENCE_CHANNEL(_self); RingConferenceChannelPrivate *priv = self->priv; ring_conference_channel_dequeue_request (self, request); g_assert(priv->conf_created == FALSE); if (error) { DEBUG("Call.CreateMultiparty with channel %s (%p) failed: " GERROR_MSG_FMT, self->nick, self, GERROR_MSG_CODE(error)); ring_conference_channel_release(_self, 0, 0, error); ring_conference_channel_close_impl(_self, FALSE, FALSE); return; } priv->conf_created = TRUE; DEBUG("VoiceCallManager.CreateMultiparty with channel %s (%p) returned", self->nick, self); } /* ---------------------------------------------------------------------- */ GType ring_object_path_list_get_type(void) { static GType type; if (G_UNLIKELY(!type)) type = dbus_g_type_get_collection("GPtrArray", DBUS_TYPE_G_OBJECT_PATH); return type; } telepathy-ring-2.3.24-37ad7111424725a5446e82dac516d7cab88149c7/src/ring-conference-channel.h000066400000000000000000000065231251541261300273740ustar00rootroot00000000000000/* * ring-conference-channel.h - Header for RingConferenceChannel * * Copyright (C) 2007-2010 Nokia Corporation * @author Pekka Pessi * * This work is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This work is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this work; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef RING_CONFERENCE_CHANNEL_H #define RING_CONFERENCE_CHANNEL_H #include #include #include G_BEGIN_DECLS typedef struct _RingConferenceChannel RingConferenceChannel; typedef struct _RingConferenceChannelClass RingConferenceChannelClass; typedef struct _RingConferenceChannelPrivate RingConferenceChannelPrivate; G_END_DECLS #include "ring-media-manager.h" #include "ring-member-channel.h" G_BEGIN_DECLS struct _RingConferenceChannelClass { TpBaseChannelClass parent_class; TpGroupMixinClass group_class; TpDBusPropertiesMixinClass dbus_properties_class; }; struct _RingConferenceChannel { TpBaseChannel parent; TpGroupMixin group; RingConferenceChannelPrivate *priv; gchar *nick; }; GType ring_conference_channel_get_type(void); /* TYPE MACROS */ #define RING_TYPE_CONFERENCE_CHANNEL \ (ring_conference_channel_get_type()) #define RING_CONFERENCE_CHANNEL(obj) \ (G_TYPE_CHECK_INSTANCE_CAST((obj), RING_TYPE_CONFERENCE_CHANNEL, RingConferenceChannel)) #define RING_CONFERENCE_CHANNEL_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST((klass), RING_TYPE_CONFERENCE_CHANNEL, RingConferenceChannelClass)) #define RING_IS_CONFERENCE_CHANNEL(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE((obj), RING_TYPE_CONFERENCE_CHANNEL)) #define RING_IS_CONFERENCE_CHANNEL_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE((klass), RING_TYPE_CONFERENCE_CHANNEL)) #define RING_CONFERENCE_CHANNEL_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), RING_TYPE_CONFERENCE_CHANNEL, RingConferenceChannelClass)) /*********************************************************************** * Additional declarations (not based on generated templates) ***********************************************************************/ guint ring_conference_channel_has_members(RingConferenceChannel const *); gboolean ring_conference_channel_join(RingConferenceChannel *channel, RingMemberChannel *member, GError **error); void ring_conference_channel_emit_channel_removed( RingConferenceChannel *channel, RingMemberChannel *member, char const *message, guint actor, TpChannelGroupChangeReason reason); void ring_conference_channel_emit_initial(RingConferenceChannel *channel); gboolean ring_conference_channel_check_initial_members ( RingConferenceChannel const *, RingInitialMembers const *); void ring_conference_channel_initial_audio (RingConferenceChannel *self); G_END_DECLS #endif /* #ifndef RING_CONFERENCE_CHANNEL_H*/ telepathy-ring-2.3.24-37ad7111424725a5446e82dac516d7cab88149c7/src/ring-conference-manager.c000066400000000000000000000520371251541261300273720ustar00rootroot00000000000000/* * ring-conference-manager.c - Manager for conference channels * * Copyright (C) 2007-2011 Nokia Corporation * @author Pekka Pessi * @author Lassi Syrjala * @author Kai Vehmanen * * This work is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This work is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this work; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ /* * Based on telepathy-glib/examples/cm/echo/factory.c with notice: * * """ * Copyright (C) 2007 Collabora Ltd. * Copyright (C) 2007 Nokia Corporation * * Copying and distribution of this file, with or without modification, * are permitted in any medium without royalty provided the copyright * notice and this notice are preserved. * """ */ #include "config.h" #define DEBUG_FLAG RING_DEBUG_CONNECTION #include "ring-debug.h" #include "ring-conference-manager.h" #include "ring-conference-channel.h" #include "ring-connection.h" #include "ring-param-spec.h" #include "ring-util.h" #include "ring-extensions/ring-extensions.h" #include "modem/call.h" #include "modem/tones.h" #include #include #include #include #include #include #include static void manager_iface_init (gpointer, gpointer); G_DEFINE_TYPE_WITH_CODE (RingConferenceManager, ring_conference_manager, G_TYPE_OBJECT, G_IMPLEMENT_INTERFACE (TP_TYPE_CHANNEL_MANAGER, manager_iface_init)); struct _RingConferenceManagerPrivate { RingConnection *connection; ModemCallService *call_service; GHashTable *channels; unsigned dispose_has_run:1, :0; struct { gulong status_changed; } signals; }; enum { PROP_NONE, PROP_CONNECTION, PROP_CALL_SERVICE, N_PROPS }; typedef enum { METHOD_COMPATIBLE, METHOD_CREATE, METHOD_ENSURE } RequestotronMethod; /* ---------------------------------------------------------------------- */ static void conference_manager_emit_new_channel (RingConferenceManager *, gpointer request, gpointer channel, GError *error); static gboolean conference_requestotron (RingConferenceManager *, gpointer, GHashTable *); static void set_call_service (RingConferenceManager *, ModemCallService *); static void conference_removed (gpointer _channel); static void on_connection_status_changed (TpBaseConnection *conn, guint status, guint reason, RingConferenceManager *self); #define METHOD(i, x) (i ## _ ## x) /* ---------------------------------------------------------------------- */ /* GObject interface */ static void ring_conference_manager_init (RingConferenceManager *self) { self->priv = G_TYPE_INSTANCE_GET_PRIVATE ( self, RING_TYPE_CONFERENCE_MANAGER, RingConferenceManagerPrivate); self->priv->channels = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, conference_removed); } static void ring_conference_manager_constructed (GObject *object) { RingConferenceManager *self = RING_CONFERENCE_MANAGER(object); RingConferenceManagerPrivate *priv = self->priv; priv->signals.status_changed = g_signal_connect (priv->connection, "status-changed", (GCallback) on_connection_status_changed, self); if (G_OBJECT_CLASS (ring_conference_manager_parent_class)->constructed) G_OBJECT_CLASS (ring_conference_manager_parent_class)->constructed (object); } static void ring_conference_manager_dispose (GObject *object) { RingConferenceManager *self = RING_CONFERENCE_MANAGER (object); RingConferenceManagerPrivate *priv = self->priv; if (priv->dispose_has_run) return; priv->dispose_has_run = TRUE; ring_signal_disconnect (priv->connection, &priv->signals.status_changed); set_call_service (self, NULL); ((GObjectClass *) ring_conference_manager_parent_class)->dispose (object); } static void ring_conference_manager_finalize (GObject *object) { RingConferenceManager *self = RING_CONFERENCE_MANAGER (object); RingConferenceManagerPrivate *priv = self->priv; g_hash_table_destroy (priv->channels); } static void ring_conference_manager_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { RingConferenceManager *self = RING_CONFERENCE_MANAGER (object); RingConferenceManagerPrivate *priv = self->priv; switch (property_id) { case PROP_CONNECTION: g_value_set_object (value, priv->connection); break; case PROP_CALL_SERVICE: g_value_set_pointer (value, priv->call_service); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); } } static void ring_conference_manager_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { RingConferenceManager *self = RING_CONFERENCE_MANAGER (object); RingConferenceManagerPrivate *priv = self->priv; switch (property_id) { case PROP_CONNECTION: /* We don't ref the connection, because it owns a reference to the * factory, and it guarantees that the factory's lifetime is * less than its lifetime */ priv->connection = g_value_get_object (value); break; case PROP_CALL_SERVICE: set_call_service (self, g_value_get_pointer (value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); } } static void ring_conference_manager_class_init (RingConferenceManagerClass *klass) { GObjectClass *object_class = (GObjectClass *) klass; g_type_class_add_private (klass, sizeof (RingConferenceManagerPrivate)); object_class->constructed = ring_conference_manager_constructed; object_class->dispose = ring_conference_manager_dispose; object_class->finalize = ring_conference_manager_finalize; object_class->get_property = ring_conference_manager_get_property; object_class->set_property = ring_conference_manager_set_property; g_object_class_install_property (object_class, PROP_CONNECTION, ring_param_spec_connection ()); g_object_class_install_property (object_class, PROP_CALL_SERVICE, g_param_spec_pointer ("call-service", "Call Manager Object", "oFono Call Manager", G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); } /* ---------------------------------------------------------------------- */ /* RingConferenceManager interface */ RingConferenceChannel * ring_conference_manager_lookup (RingConferenceManager *self, char const *object_path) { g_return_val_if_fail (self != NULL, NULL); g_return_val_if_fail (object_path != NULL, NULL); return g_hash_table_lookup (self->priv->channels, object_path); } static void set_call_service (RingConferenceManager *self, ModemCallService *service) { RingConferenceManagerPrivate *priv = self->priv; if (priv->call_service) { if (priv->call_service) g_object_unref (priv->call_service); priv->call_service = NULL; } if (service) { priv->call_service = g_object_ref (MODEM_CALL_SERVICE (service)); } } static void conference_removed (gpointer _channel) { if (!tp_base_channel_is_destroyed (_channel)) { /* Ensure "Closed" has been emitted */ g_object_run_dispose (_channel); } g_object_unref (_channel); } static void on_connection_status_changed (TpBaseConnection *conn, guint status, guint reason, RingConferenceManager *self) { RingConferenceManagerPrivate *priv = self->priv; if (status == TP_CONNECTION_STATUS_DISCONNECTED) { g_hash_table_remove_all (priv->channels); } } /* ---------------------------------------------------------------------- */ /* TpChannelManagerIface interface */ static GHashTable * conference_channel_fixed_properties (void) { static GHashTable *hash; if (hash) return hash; hash = g_hash_table_new (g_str_hash, g_str_equal); char const *key; GValue *value; key = TP_IFACE_CHANNEL ".ChannelType"; value = tp_g_value_slice_new (G_TYPE_STRING); g_value_set_static_string (value, TP_IFACE_CHANNEL_TYPE_STREAMED_MEDIA); g_hash_table_insert (hash, (gpointer)key, value); return hash; } static char const * const conference_channel_allowed_properties[] = { TP_IFACE_CHANNEL ".InitialChannels", TP_IFACE_CHANNEL ".TargetHandleType", TP_IFACE_CHANNEL_TYPE_STREAMED_MEDIA ".InitialAudio", TP_IFACE_CHANNEL_TYPE_STREAMED_MEDIA ".InitialVideo", NULL }; RingInitialMembers * tp_asv_get_initial_members (GHashTable *properties) { return tp_asv_get_boxed (properties, TP_IFACE_CHANNEL ".InitialChannels", TP_ARRAY_TYPE_OBJECT_PATH_LIST); } static void conference_manager_foreach_channel_class (TpChannelManager *_self, TpChannelManagerChannelClassFunc func, gpointer userdata) { RingConferenceManager *self = RING_CONFERENCE_MANAGER (_self); /* If we're not connected, conferences aren't supported. */ if (self->priv->call_service == NULL) return; func (_self, conference_channel_fixed_properties (), conference_channel_allowed_properties, userdata); } static void conference_manager_foreach_channel (TpChannelManager *_self, TpExportableChannelFunc func, gpointer user_data) { RingConferenceManager *self = RING_CONFERENCE_MANAGER (_self); GHashTableIter i[1]; gpointer channel; for (g_hash_table_iter_init (i, self->priv->channels); g_hash_table_iter_next (i, NULL, &channel);) func (channel, user_data); } static gboolean conference_manager_create_channel (TpChannelManager *_self, gpointer request, GHashTable *properties) { RingConferenceManager *self = RING_CONFERENCE_MANAGER (_self); if (tp_asv_get_initial_members (properties) == NULL || !ring_properties_satisfy (properties, conference_channel_fixed_properties (), conference_channel_allowed_properties)) return FALSE; return conference_requestotron (self, request, properties); } static gboolean conference_manager_ensure_channel (TpChannelManager *_self, gpointer request, GHashTable *properties) { RingConferenceManager *self = RING_CONFERENCE_MANAGER (_self); if (tp_asv_get_initial_members (properties) == NULL || !ring_properties_satisfy (properties, conference_channel_fixed_properties (), conference_channel_allowed_properties)) return FALSE; return conference_requestotron (self, request, properties); } static void manager_iface_init (gpointer ifacep, gpointer data) { TpChannelManagerIface *iface = ifacep; #define IMPLEMENT(x) iface->x = conference_manager_##x IMPLEMENT (foreach_channel); IMPLEMENT (foreach_channel_class); IMPLEMENT (create_channel); IMPLEMENT (ensure_channel); #undef IMPLEMENT } /* ---------------------------------------------------------------------- */ static RingConferenceChannel * conference_manager_find_existing (RingConferenceManager const *self, RingInitialMembers *initial) { RingConferenceManagerPrivate const *priv = self->priv; GHashTableIter i[1]; gpointer existing = NULL; g_hash_table_iter_init (i, priv->channels); while (g_hash_table_iter_next (i, NULL, &existing)) { if (initial->len == 0 || ring_conference_channel_check_initial_members (existing, initial)) return existing; } return NULL; } static char * conference_manager_new_object_path (RingConferenceManager const *self) { RingConferenceManagerPrivate const *priv = self->priv; char const *base_path = TP_BASE_CONNECTION (priv->connection)->object_path; static unsigned object_index; static unsigned object_index_init; if (G_UNLIKELY (object_index_init == 0)) { object_index = (guint)(time(NULL) - (41 * 365 * 24 * 60 * 60)) * 10; object_index_init = 1; } /* Find an unique D-Bus object_path */ for (;;) { char *path = g_strdup_printf ("%s/conf%u", base_path, ++object_index); if (!g_hash_table_lookup (priv->channels, path)) return path; g_free (path); } } static RingConferenceChannel * conference_manager_new_conference (RingConferenceManager *self, RingInitialMembers *initial, gboolean initial_audio) { RingConferenceManagerPrivate *priv = self->priv; char *object_path; RingConferenceChannel *channel; object_path = conference_manager_new_object_path (self); channel = g_object_new (RING_TYPE_CONFERENCE_CHANNEL, "connection", priv->connection, /* KVXXX: "tones", priv->tones, */ "object-path", object_path, "initial-channels", initial, "initial-audio", initial_audio, "initiator-handle", tp_base_connection_get_self_handle ( TP_BASE_CONNECTION (priv->connection)), "requested", TRUE, NULL); g_free (object_path); return channel; } static void on_conference_channel_closed (GObject *_channel, RingConferenceManager *self) { gchar *object_path; g_object_get (_channel, "object-path", &object_path, NULL); g_hash_table_remove (self->priv->channels, object_path); tp_channel_manager_emit_channel_closed (self, object_path); g_free (object_path); } static void conference_manager_emit_new_channel (RingConferenceManager *self, gpointer request, gpointer _channel, GError *error) { DEBUG ("%s (%p, %p, %p, %p) called", __func__, self, request, _channel, error); RingConferenceManagerPrivate *priv = RING_CONFERENCE_MANAGER (self)->priv; GSList *requests = request ? g_slist_prepend (NULL, request) : NULL; if (error == NULL) { RingConferenceChannel *channel = RING_CONFERENCE_CHANNEL (_channel); char *object_path = NULL; g_signal_connect (channel, "closed", G_CALLBACK (on_conference_channel_closed), self); g_object_get (channel, "object-path", &object_path, NULL); DEBUG ("got new channel %p nick %s type %s", channel, channel->nick, G_OBJECT_TYPE_NAME (channel)); g_hash_table_insert (priv->channels, object_path, channel); tp_channel_manager_emit_new_channel (self, TP_EXPORTABLE_CHANNEL (channel), requests); /* Emit Group and StreamedMedia signals */ ring_conference_channel_emit_initial (channel); } else { if (_channel) { RingConferenceChannel *channel = RING_CONFERENCE_CHANNEL (_channel); DEBUG ("new channel %p nick %s failed with " GERROR_MSG_FMT, channel, channel->nick, GERROR_MSG_CODE (error)); } if (request) { tp_channel_manager_emit_request_failed (self, request, error->domain, error->code, error->message); } if (_channel) { g_object_unref (_channel); } } g_slist_free (requests); } static gboolean conference_requestotron (RingConferenceManager *self, gpointer request, GHashTable *properties) { RingInitialMembers *initial_members; gboolean initial_audio, initial_video; RingConferenceChannel *channel; GError *error = NULL; if (self->priv->call_service == NULL) { tp_channel_manager_emit_request_failed (self, request, TP_ERRORS, TP_ERROR_NOT_AVAILABLE, "Modem does not support conferences"); return TRUE; } initial_members = tp_asv_get_initial_members (properties); initial_audio = tp_asv_get_initial_audio (properties, TRUE); initial_video = tp_asv_get_initial_video (properties, FALSE); if (initial_video) { tp_channel_manager_emit_request_failed (self, request, TP_ERRORS, TP_ERROR_NOT_IMPLEMENTED, "Video calls are not supported"); return TRUE; } if (tp_asv_get_uint32 (properties, TP_IFACE_CHANNEL ".TargetHandleType", NULL) != 0) { tp_channel_manager_emit_request_failed (self, request, TP_ERRORS, TP_ERROR_INVALID_ARGUMENT, "Invalid TargetHandleType"); return TRUE; } channel = conference_manager_find_existing (self, initial_members); if (channel) { tp_channel_manager_emit_request_already_satisfied (self, request, TP_EXPORTABLE_CHANNEL (channel)); return TRUE; } if (!ring_conference_manager_validate_initial_members (self, initial_members, &error)) { tp_channel_manager_emit_request_failed (self, request, error->domain, error->code, error->message); g_clear_error (&error); return TRUE; } channel = conference_manager_new_conference (self, initial_members, initial_audio); if (initial_audio) ring_conference_channel_initial_audio (channel); conference_manager_emit_new_channel (self, request, channel, NULL); return TRUE; } gboolean ring_conference_manager_validate_initial_members (RingConferenceManager *self, RingInitialMembers *initial, GError **error) { RingConferenceManagerPrivate *priv = self->priv; RingMemberChannel *ch1, *ch2; if (initial == NULL) { g_set_error (error, TP_ERRORS, TP_ERROR_INVALID_ARGUMENT, "No initial members"); return FALSE; } if (initial->len != 2) { g_set_error (error, TP_ERRORS, TP_ERROR_INVALID_ARGUMENT, "Expecting exactly two initial members"); return FALSE; } ch1 = ring_connection_lookup_channel (priv->connection, initial->odata[0]); ch2 = ring_connection_lookup_channel (priv->connection, initial->odata[1]); if (ch1 == ch2) { g_set_error (error, TP_ERRORS, TP_ERROR_INVALID_ARGUMENT, "Expecting distinct initial members"); return FALSE; } if (!RING_IS_MEMBER_CHANNEL (ch1) || !RING_IS_MEMBER_CHANNEL (ch2)) { g_set_error (error, TP_ERRORS, TP_ERROR_INVALID_ARGUMENT, "Initial member is not a MediaStream channel"); return FALSE; } if (!ring_member_channel_can_become_member (ch1, error)) { g_prefix_error (error, "First initial: "); return FALSE; } if (!ring_member_channel_can_become_member (ch2, error)) { g_prefix_error (error, "Second initial: "); return FALSE; } return TRUE; } #ifdef nomore static void on_modem_call_conference_joined (ModemCallConference *mcc, ModemCall *mc, RingConferenceManager *self) { RingConferenceManagerPrivate *priv = RING_CONFERENCE_MANAGER (self)->priv; ModemCall **members; GPtrArray *initial; guint i; if (modem_call_get_handler (MODEM_CALL (mcc))) return; members = modem_call_service_get_calls (priv->call_service); initial = g_ptr_array_sized_new (MODEM_MAX_CALLS + 1); for (i = 0; members[i]; i++) { if (modem_call_is_member (members[i]) && modem_call_get_handler (members[i])) { RingMemberChannel *member; char *object_path = NULL; member = RING_MEMBER_CHANNEL (modem_call_get_handler (members[i])); g_object_get (member, "object-path", &object_path, NULL); if (object_path) g_ptr_array_add (initial, object_path); } } if (initial->len >= 2) { RingConferenceChannel *channel; char *object_path; object_path = ring_conference_manager_new_object_path (self); channel = (RingConferenceChannel *) g_object_new (RING_TYPE_CONFERENCE_CHANNEL, "connection", priv->connection, "call-instance", mcc, /* KVXXX: "tones", priv->tones, */ "object-path", object_path, "initial-channels", initial, "initial-audio", TRUE, "initiator-handle", tp_base_connection_get_self_handle ( TP_BASE_CONNECTION (priv->connection)), "requested", TRUE, NULL); g_free (object_path); g_assert (channel == modem_call_get_handler (MODEM_CALL (mcc))); conference_manager_emit_new_channel (self, NULL, channel, NULL); } g_ptr_array_add (initial, NULL); g_strfreev ((char **)g_ptr_array_free (initial, FALSE)); g_free (members); } #endif telepathy-ring-2.3.24-37ad7111424725a5446e82dac516d7cab88149c7/src/ring-conference-manager.h000066400000000000000000000050461251541261300273750ustar00rootroot00000000000000/* * ring-conference-manager.h - Manager for conference channels * * Copyright (C) 2007-2010 Nokia Corporation * @author Pekka Pessi * * This work is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This work is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this work; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef RING_CONFERENCE_MANAGER_H #define RING_CONFERENCE_MANAGER_H #include #include #include "modem/call.h" G_BEGIN_DECLS typedef struct _RingConferenceManager RingConferenceManager; typedef struct _RingConferenceManagerClass RingConferenceManagerClass; typedef struct _RingConferenceManagerPrivate RingConferenceManagerPrivate; G_END_DECLS #include G_BEGIN_DECLS struct _RingConferenceManagerClass { GObjectClass parent_class; }; struct _RingConferenceManager { GObject parent; RingConferenceManagerPrivate *priv; }; GType ring_conference_manager_get_type (void); /* TYPE MACROS */ #define RING_TYPE_CONFERENCE_MANAGER \ (ring_conference_manager_get_type()) #define RING_CONFERENCE_MANAGER(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ RING_TYPE_CONFERENCE_MANAGER, RingConferenceManager)) #define RING_CONFERENCE_MANAGER_CLASS(cls) \ (G_TYPE_CHECK_CLASS_CAST ((cls), \ RING_TYPE_CONFERENCE_MANAGER, RingConferenceManagerClass)) #define RING_IS_CONFERENCE_MANAGER(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \ RING_TYPE_CONFERENCE_MANAGER)) #define RING_IS_CONFERENCE_MANAGER_CLASS(cls) \ (G_TYPE_CHECK_CLASS_TYPE ((cls), \ RING_TYPE_CONFERENCE_MANAGER)) #define RING_CONFERENCE_MANAGER_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), \ RING_TYPE_CONFERENCE_MANAGER, RingConferenceManagerClass)) RingConferenceChannel *ring_conference_manager_lookup (RingConferenceManager *, char const *object_path); gboolean ring_conference_manager_validate_initial_members ( RingConferenceManager *, RingInitialMembers *, GError **error); G_END_DECLS #endif telepathy-ring-2.3.24-37ad7111424725a5446e82dac516d7cab88149c7/src/ring-connection-manager.c000066400000000000000000000063041251541261300274160ustar00rootroot00000000000000/* * ring-connection-manager.c - Source for RingConnectionManager * * Copyright (C) 2007-2010 Nokia Corporation * @author Pekka Pessi * * This work is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This work is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this work; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ /* Based on telepathy-glib/examples/cm/extended/manager.c with copyright notice * * """ * manager.c - an example connection manager * * Copyright (C) 2007 Collabora Ltd. * * Copying and distribution of this file, with or without modification, * are permitted in any medium without royalty provided the copyright * notice and this notice are preserved. * """ */ #define DEBUG_FLAG RING_DEBUG_CONNECTION #include "ring-debug.h" #include #include #include "ring-connection-manager.h" #include "ring-connection.h" #include "ring-protocol.h" #include "modem/service.h" #include "modem/sim.h" #include "modem/call.h" #include "modem/sms.h" #include #include #include #include #include struct _RingConnectionManagerPrivate { int dummy; }; G_DEFINE_TYPE (RingConnectionManager, ring_connection_manager, TP_TYPE_BASE_CONNECTION_MANAGER) static void ring_connection_manager_init(RingConnectionManager *self) { self->priv = G_TYPE_INSTANCE_GET_PRIVATE(self, RING_TYPE_CONNECTION_MANAGER, RingConnectionManagerPrivate); modem_service_refresh (modem_service ()); } static void ring_connection_manager_constructed (GObject *obj) { RingConnectionManager *self = RING_CONNECTION_MANAGER (obj); TpBaseConnectionManager *base = (TpBaseConnectionManager *) self; GObjectClass *base_class = (GObjectClass *) ring_connection_manager_parent_class; RingProtocol *protocol; if (base_class->constructed) base_class->constructed (obj); protocol = ring_protocol_new (); tp_base_connection_manager_add_protocol (base, TP_BASE_PROTOCOL (protocol)); g_object_unref (protocol); } static void ring_connection_manager_class_init(RingConnectionManagerClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); TpBaseConnectionManagerClass *parent_class = &klass->parent_class; g_type_class_add_private (klass, sizeof (RingConnectionManagerPrivate)); object_class->constructed = ring_connection_manager_constructed; parent_class->new_connection = NULL; parent_class->cm_dbus_name = "ring"; parent_class->protocol_params = NULL; modem_oface_register_type (MODEM_TYPE_SIM_SERVICE); modem_oface_register_type (MODEM_TYPE_SMS_SERVICE); modem_oface_register_type (MODEM_TYPE_CALL_SERVICE); } telepathy-ring-2.3.24-37ad7111424725a5446e82dac516d7cab88149c7/src/ring-connection-manager.h000066400000000000000000000047371251541261300274330ustar00rootroot00000000000000/* * ring-connection-manager.h - Header for RingConnectionManager * * Copyright (C) 2007-2010 Nokia Corporation * @author Pekka Pessi * * This work is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This work is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this work; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef RING_CONNECTION_MANAGER_H #define RING_CONNECTION_MANAGER_H #include #include G_BEGIN_DECLS #define TEL_PROTOCOL_STRING "tel" typedef struct _RingConnectionManager RingConnectionManager; typedef struct _RingConnectionManagerClass RingConnectionManagerClass; typedef struct _RingConnectionManagerPrivate RingConnectionManagerPrivate; struct _RingConnectionManagerClass { TpBaseConnectionManagerClass parent_class; }; struct _RingConnectionManager { TpBaseConnectionManager parent; RingConnectionManagerPrivate *priv; }; GType ring_connection_manager_get_type(void); /* Type Macros */ #define RING_TYPE_CONNECTION_MANAGER \ (ring_connection_manager_get_type()) #define RING_CONNECTION_MANAGER(obj) \ (G_TYPE_CHECK_INSTANCE_CAST((obj), RING_TYPE_CONNECTION_MANAGER, RingConnectionManager)) #define RING_CONNECTION_MANAGER_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST((klass), RING_TYPE_CONNECTION_MANAGER, RingConnectionManagerClass)) #define RING_IS_CONNECTION_MANAGER(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE((obj), RING_TYPE_CONNECTION_MANAGER)) #define RING_IS_CONNECTION_MANAGER_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE((klass), RING_TYPE_CONNECTION_MANAGER)) #define RING_CONNECTION_MANAGER_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), RING_TYPE_CONNECTION_MANAGER, RingConnectionManagerClass)) G_END_DECLS #endif /* #ifndef RING_CONNECTION_MANAGER_H*/ telepathy-ring-2.3.24-37ad7111424725a5446e82dac516d7cab88149c7/src/ring-connection.c000066400000000000000000001402661251541261300260140ustar00rootroot00000000000000/* * ring-connection.c - Source for RingConnection * * Copyright (C) 2007-2010 Nokia Corporation * @author Pekka Pessi * @author Lassi Syrjala * @author Tom Swindell * * This work is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This work is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this work; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #define DEBUG_FLAG RING_DEBUG_CONNECTION #include "ring-debug.h" #include "ring-connection.h" #include "ring-emergency-service.h" #include "ring-media-manager.h" #include "ring-conference-manager.h" #include "ring-text-manager.h" #include "ring-param-spec.h" #include "ring-util.h" #include #include #include #include #include #include #include #include #include #include #include #include #include "ring-extensions/ring-extensions.h" #include "modem/service.h" #include "modem/modem.h" #include "modem/sim.h" #include "modem/call.h" #include "modem/sms.h" #include #include #include #include #include #include struct _RingConnectionPrivate { /* Properties */ char *imsi; char *imei; char *smsc; guint sms_valid; guint anon_modes; guint anon_supported_modes; RingMediaManager *media; RingTextManager *text; RingConferenceManager *conference; gchar *modem_path; Modem *modem; ModemSIMService *sim; struct { gulong modem_added; gulong modem_removed; gulong modem_interface_added; gulong modem_interface_removed; gulong imsi_notify; } signals; guint connecting_source; unsigned anon_mandatory:1; unsigned sms_reduced_charset:1; unsigned dispose_has_run:1; }; /* properties */ enum { PROP_NONE, PROP_IMSI, /**< IMSI */ PROP_SMSC, /**< SMSC address */ PROP_SMS_VALID, /**< SMS validity period in seconds */ PROP_SMS_REDUCED_CHARSET, /**< SMS reduced charset support */ PROP_MODEM_PATH, /**< Object path of the modem */ PROP_STORED_MESSAGES, /**< List of stored messages */ PROP_KNOWN_SERVICE_POINTS, /**< List of emergency service points */ PROP_ANON_SUPPORTED_MODES, PROP_ANON_MANDATORY, PROP_ANON_MODES, PROP_MODEM, PROP_SIM_SERVICE, N_PROPS }; static void ring_connection_class_init_base_connection(TpBaseConnectionClass *); static void ring_connection_capabilities_iface_init(gpointer, gpointer); static void ring_connection_add_contact_capabilities(GObject *object, GArray const *handles, GHashTable *returns); /*static void ring_connection_stored_messages_iface_init(gpointer, gpointer);*/ static TpDBusPropertiesMixinPropImpl ring_connection_service_point_properties[], ring_connection_cellular_properties[], /*ring_connection_stored_messages_properties[],*/ ring_connection_anon_properties[]; static gboolean ring_connection_cellular_properties_setter(GObject *object, GQuark interface, GQuark name, const GValue *value, gpointer setter_data, GError **error); static TpDBusPropertiesMixinIfaceImpl ring_connection_dbus_property_interfaces[]; /** Inspection result from self handle. */ static char const ring_self_handle_name[] = ""; #define METHOD(i, m) (i ## _ ## m) /* ---------------------------------------------------------------------- */ /* GObject interface */ G_DEFINE_TYPE_WITH_CODE (RingConnection, ring_connection, TP_TYPE_BASE_CONNECTION, G_IMPLEMENT_INTERFACE(TP_TYPE_SVC_DBUS_PROPERTIES, tp_dbus_properties_mixin_iface_init); G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CONNECTION_INTERFACE_CONTACTS, tp_contacts_mixin_iface_init); G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CONNECTION_INTERFACE_CAPABILITIES, ring_connection_capabilities_iface_init); G_IMPLEMENT_INTERFACE(TP_TYPE_SVC_CONNECTION_INTERFACE_SERVICE_POINT, NULL); G_IMPLEMENT_INTERFACE(TP_TYPE_SVC_CONNECTION_INTERFACE_CELLULAR, NULL); G_IMPLEMENT_INTERFACE(TP_TYPE_SVC_CONNECTION_INTERFACE_ANONYMITY, NULL); #if nomore /* XXX: waiting for upstream tp-glib to get this */ G_IMPLEMENT_INTERFACE(RTCOM_TYPE_TP_SVC_CONNECTION_INTERFACE_STORED_MESSAGES, ring_connection_stored_messages_iface_init); #endif ); static char const * const ring_connection_interfaces_always_present[] = { TP_IFACE_CONNECTION_INTERFACE_REQUESTS, TP_IFACE_CONNECTION_INTERFACE_CONTACTS, TP_IFACE_CONNECTION_INTERFACE_CAPABILITIES, TP_IFACE_CONNECTION_INTERFACE_SERVICE_POINT, TP_IFACE_CONNECTION_INTERFACE_CELLULAR, TP_IFACE_CONNECTION_INTERFACE_ANONYMITY, #if nomore RTCOM_TP_IFACE_CONNECTION_INTERFACE_STORED_MESSAGES, #endif NULL }; static void ring_connection_init(RingConnection *self) { self->priv = G_TYPE_INSTANCE_GET_PRIVATE( (self), RING_TYPE_CONNECTION, RingConnectionPrivate); /* Initialize Contacts mixin */ tp_contacts_mixin_init( (GObject *)self, G_STRUCT_OFFSET(RingConnection, contacts_mixin)); /* org.freedesktop.Telepathy.Connection attributes */ tp_base_connection_register_with_contacts_mixin( TP_BASE_CONNECTION(self)); /* org.freedesktop.Telepathy.Connection.Interface.Capabilities attributes */ tp_contacts_mixin_add_contact_attributes_iface((GObject *)self, TP_IFACE_CONNECTION_INTERFACE_CAPABILITIES, ring_connection_add_contact_capabilities); } static void ring_connection_constructed(GObject *object) { RingConnection *self = RING_CONNECTION(object); TpBaseConnection *base = TP_BASE_CONNECTION(object); TpHandleRepoIface *repo; TpHandle self_handle; if (G_OBJECT_CLASS(ring_connection_parent_class)->constructed) G_OBJECT_CLASS(ring_connection_parent_class)->constructed(object); self->priv->anon_supported_modes = TP_ANONYMITY_MODE_CLIENT_INFO | TP_ANONYMITY_MODE_SHOW_CLIENT_INFO, repo = tp_base_connection_get_handles(base, TP_HANDLE_TYPE_CONTACT); self_handle = tp_handle_ensure(repo, ring_self_handle_name, NULL, NULL); tp_base_connection_set_self_handle(base, self_handle); tp_handle_unref(repo, self_handle); g_assert(base->self_handle != 0); self->anon_handle = tp_handle_ensure(repo, "", NULL, NULL); g_assert(self->anon_handle != 0); self->sos_handle = tp_handle_ensure(repo, RING_EMERGENCY_SERVICE_URN, NULL, NULL); g_assert(self->sos_handle != 0); } static void ring_connection_dispose(GObject *object) { RingConnection *self = RING_CONNECTION(object); TpHandleRepoIface *repo = tp_base_connection_get_handles( TP_BASE_CONNECTION(self), TP_HANDLE_TYPE_CONTACT); if (self->priv->dispose_has_run) return; self->priv->dispose_has_run = 1; tp_handle_unref(repo, self->anon_handle), self->anon_handle = 0; tp_handle_unref(repo, self->sos_handle), self->sos_handle = 0; if (self->priv->modem) g_object_unref (self->priv->modem); if (self->priv->sim) g_object_unref(self->priv->sim); G_OBJECT_CLASS(ring_connection_parent_class)->dispose(object); } void ring_connection_finalize(GObject *object) { RingConnection *self = RING_CONNECTION(object); RingConnectionPrivate *priv = self->priv; DEBUG("enter %p", object); /* Free any data held directly by the object here */ g_free (priv->imsi); g_free(priv->smsc); g_free(priv->modem_path); G_OBJECT_CLASS(ring_connection_parent_class)->finalize(object); } static void ring_connection_set_property(GObject *obj, guint property_id, const GValue *value, GParamSpec *pspec) { RingConnection *self = RING_CONNECTION (obj); TpBaseConnection *base = TP_BASE_CONNECTION (obj); RingConnectionPrivate *priv = self->priv; gpointer p; switch (property_id) { case PROP_IMSI: priv->imsi = g_value_dup_string (value); if (base->status == TP_CONNECTION_STATUS_CONNECTED) METHOD (tp_svc_connection_interface_cellular, emit_imsi_changed) (self, priv->imsi); break; case PROP_SMSC: priv->smsc = ring_normalize_isdn(g_value_get_string(value)); if (priv->text) g_object_set(priv->text, "sms-service-centre", priv->smsc, NULL); break; case PROP_SMS_VALID: priv->sms_valid = g_value_get_uint(value); if (priv->text) g_object_set_property (G_OBJECT (priv->text), "sms-validity-period", value); break; case PROP_SMS_REDUCED_CHARSET: priv->sms_reduced_charset = g_value_get_boolean(value); if (priv->text) g_object_set_property (G_OBJECT (priv->text), "sms-reduced-charset", value); break; case PROP_MODEM_PATH: priv->modem_path = g_value_dup_boxed(value); break; case PROP_ANON_MANDATORY: priv->anon_mandatory = g_value_get_boolean(value); break; case PROP_ANON_MODES: priv->anon_modes = g_value_get_uint(value); if (priv->media) g_object_set_property (G_OBJECT (priv->media), "anon-modes", value); if (base->status == TP_CONNECTION_STATUS_CONNECTED) METHOD (tp_svc_connection_interface_anonymity, emit_anonymity_modes_changed) (self, priv->anon_modes); break; case PROP_MODEM: if (priv->modem) g_object_unref (priv->modem); p = g_value_get_pointer (value); priv->modem = p ? g_object_ref (p) : NULL; break; case PROP_SIM_SERVICE: if (priv->sim) g_object_unref (priv->sim); p = g_value_get_pointer (value); priv->sim = p ? g_object_ref (p) : NULL; break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, property_id, pspec); break; } } static void ring_connection_get_property(GObject *obj, guint property_id, GValue *value, GParamSpec *pspec) { RingConnection *self = RING_CONNECTION(obj); RingConnectionPrivate *priv = self->priv; switch (property_id) { case PROP_IMSI: g_value_set_string (value, priv->imsi); break; case PROP_SMSC: g_value_set_string(value, priv->smsc ? priv->smsc : ""); break; case PROP_SMS_VALID: g_value_set_uint(value, priv->sms_valid); break; case PROP_SMS_REDUCED_CHARSET: g_value_set_boolean(value, priv->sms_reduced_charset); break; case PROP_MODEM_PATH: g_value_set_boxed(value, priv->modem_path); break; case PROP_STORED_MESSAGES: #if nomore g_value_take_boxed(value, ring_text_manager_list_stored_messages (priv->text)); #endif break; case PROP_KNOWN_SERVICE_POINTS: g_value_take_boxed(value, ring_media_manager_emergency_services (priv->media)); break; case PROP_ANON_MANDATORY: g_value_set_boolean(value, priv->anon_mandatory); break; case PROP_ANON_SUPPORTED_MODES: g_value_set_uint(value, priv->anon_supported_modes); break; case PROP_ANON_MODES: g_value_set_uint(value, priv->anon_modes); break; case PROP_MODEM: g_value_set_object (value, priv->modem); break; case PROP_SIM_SERVICE: g_value_set_object (value, priv->sim); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, property_id, pspec); break; } } static void ring_connection_class_init(RingConnectionClass *ring_connection_class) { GObjectClass *object_class = G_OBJECT_CLASS(ring_connection_class); g_type_class_add_private(ring_connection_class, sizeof (RingConnectionPrivate)); object_class->constructed = ring_connection_constructed; object_class->dispose = ring_connection_dispose; object_class->finalize = ring_connection_finalize; object_class->set_property = ring_connection_set_property; object_class->get_property = ring_connection_get_property; g_object_class_install_property (object_class, PROP_IMSI, ring_param_spec_imsi (G_PARAM_WRITABLE)); g_object_class_install_property( object_class, PROP_SMSC, ring_param_spec_smsc()); g_object_class_install_property( object_class, PROP_SMS_VALID, ring_param_spec_sms_valid()); g_object_class_install_property( object_class, PROP_SMS_REDUCED_CHARSET, ring_param_spec_sms_reduced_charset()); g_object_class_install_property( object_class, PROP_MODEM_PATH, g_param_spec_boxed("modem-path", "Modem path", "oFono object path of the modem to use", DBUS_TYPE_G_OBJECT_PATH, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property( object_class, PROP_STORED_MESSAGES, g_param_spec_boxed("stored-messages", "Stored messages", "List of messages " "in permanent storage.", G_TYPE_STRV, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property( object_class, PROP_KNOWN_SERVICE_POINTS, g_param_spec_boxed("known-service-points", "Known service points", "List of known emergency service points", TP_ARRAY_TYPE_SERVICE_POINT_INFO_LIST, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property( object_class, PROP_ANON_MANDATORY, g_param_spec_boolean("anon-mandatory", "Anonymity mandatory", "Specifies whether or not the anonymity " "settings should be respected", FALSE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property( object_class, PROP_ANON_SUPPORTED_MODES, g_param_spec_uint("anon-supported-modes", "Supported anonymity modes", "Specifies the supported anonymity modes", 0, G_MAXUINT, TP_ANONYMITY_MODE_CLIENT_INFO | TP_ANONYMITY_MODE_SHOW_CLIENT_INFO, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property( object_class, PROP_ANON_MODES, ring_param_spec_anon_modes()); g_object_class_install_property (object_class, PROP_MODEM, g_param_spec_pointer ("modem", "Modem Object", "The oFono modem object", G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (object_class, PROP_SIM_SERVICE, g_param_spec_pointer ("sim-service", "SIM Manager Object", "The SIM manager from the modem", G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); ring_connection_class_init_base_connection( TP_BASE_CONNECTION_CLASS(ring_connection_class)); ring_connection_class->dbus_properties_class.interfaces = ring_connection_dbus_property_interfaces; tp_dbus_properties_mixin_class_init( object_class, G_STRUCT_OFFSET(RingConnectionClass, dbus_properties_class)); tp_contacts_mixin_class_init( object_class, G_STRUCT_OFFSET(RingConnectionClass, contacts_mixin_class)); } /* ------------------------------------------------------------------------- */ static TpDBusPropertiesMixinIfaceImpl ring_connection_dbus_property_interfaces[] = { { TP_IFACE_CONNECTION_INTERFACE_SERVICE_POINT, tp_dbus_properties_mixin_getter_gobject_properties, NULL, ring_connection_service_point_properties, }, { TP_IFACE_CONNECTION_INTERFACE_CELLULAR, tp_dbus_properties_mixin_getter_gobject_properties, ring_connection_cellular_properties_setter, ring_connection_cellular_properties, }, { TP_IFACE_CONNECTION_INTERFACE_ANONYMITY, tp_dbus_properties_mixin_getter_gobject_properties, tp_dbus_properties_mixin_setter_gobject_properties, ring_connection_anon_properties, }, #if nomore { RTCOM_TP_IFACE_CONNECTION_INTERFACE_STORED_MESSAGES, tp_dbus_properties_mixin_getter_gobject_properties, NULL, ring_connection_stored_messages_properties, }, #endif { NULL } }; /* ------------------------------------------------------------------------- */ /* Validate ISDN number */ static gboolean param_filter_isdn(TpCMParamSpec const *paramspec, GValue *value, GError **error) { const char *str = g_value_get_string(value); unsigned len = 0; if (str == NULL || str[0] == '\0') return TRUE; if (str[0] == '+') str++; for (len = 0; *str; str++) { if (strchr(" .-()", *str)) /* Skip fillers */ continue; if (!strchr("0123456789", *str)) { if (error) g_set_error (error, TP_ERRORS, TP_ERROR_INVALID_ARGUMENT, "Account parameter '%s' with invalid ISDN number", paramspec->name); return FALSE; } else if (++len > 20) { if (error) g_set_error (error, TP_ERRORS, TP_ERROR_INVALID_ARGUMENT, "Account parameter '%s' with ISDN number too long", paramspec->name); return FALSE; } } return TRUE; } /* Validate SMS validity period */ static gboolean param_filter_validity(TpCMParamSpec const *paramspec, GValue *value, GError **error) { guint validity = g_value_get_uint(value); if (validity == 0) return TRUE; /* from 5 minutes to 63 weeks */ if (5 * 60 <= validity && validity <= 63 * 7 * 24 * 60 * 60) return TRUE; g_set_error(error, TP_ERRORS, TP_ERROR_INVALID_ARGUMENT, "Account parameter '%s' with invalid validity period - " "default (0), minimum 5 minutes (%u), maximum 63 weeks (%u)", paramspec->name, 5 * 60, 63 * 7 * 24 * 60 * 60); return FALSE; } static gboolean param_filter_anon_modes(TpCMParamSpec const *paramspec, GValue *value, GError **error) { guint modes = g_value_get_uint(value); /* supported modes: 0, 1, 2, and 3 */ modes &= ~(TP_ANONYMITY_MODE_CLIENT_INFO | TP_ANONYMITY_MODE_SHOW_CLIENT_INFO); if (modes == 0) return TRUE; g_set_error(error, TP_ERRORS, TP_ERROR_INVALID_ARGUMENT, "Account parameter '%s' with invalid value", paramspec->name); return FALSE; } static gboolean param_filter_valid_object_path (TpCMParamSpec const *paramspec, GValue *value, GError **error) { return tp_dbus_check_valid_object_path (g_value_get_boxed (value), error); } TpCMParamSpec ring_connection_params[] = { { TP_IFACE_CONNECTION_INTERFACE_CELLULAR ".IMSI", DBUS_TYPE_STRING_AS_STRING, G_TYPE_STRING, TP_CONN_MGR_PARAM_FLAG_DBUS_PROPERTY, "", .setter_data = "imsi", }, #define CELLULAR_SMS_VALIDITY_PERIOD_PARAM_SPEC (ring_connection_params + 1) { TP_IFACE_CONNECTION_INTERFACE_CELLULAR ".MessageValidityPeriod", DBUS_TYPE_UINT32_AS_STRING, G_TYPE_UINT, TP_CONN_MGR_PARAM_FLAG_DBUS_PROPERTY, GUINT_TO_POINTER(0), 0, param_filter_validity, .setter_data = "sms-validity-period", }, #define CELLULAR_SMS_SERVICE_CENTRE_PARAM_SPEC (ring_connection_params + 2) { TP_IFACE_CONNECTION_INTERFACE_CELLULAR ".MessageServiceCentre", DBUS_TYPE_STRING_AS_STRING, G_TYPE_STRING, TP_CONN_MGR_PARAM_FLAG_DBUS_PROPERTY, "", 0, param_filter_isdn, .setter_data = "sms-service-centre", }, #define CELLULAR_SMS_REDUCED_CHARSET_PARAM_SPEC (ring_connection_params + 3) { TP_IFACE_CONNECTION_INTERFACE_CELLULAR ".MessageReducedCharacterSet", DBUS_TYPE_BOOLEAN_AS_STRING, G_TYPE_BOOLEAN, TP_CONN_MGR_PARAM_FLAG_DBUS_PROPERTY, GUINT_TO_POINTER(0), 0, NULL, .setter_data = "sms-validity-period", }, { TP_IFACE_CONNECTION_INTERFACE_ANONYMITY ".AnonymityMandatory", DBUS_TYPE_BOOLEAN_AS_STRING, G_TYPE_BOOLEAN, TP_CONN_MGR_PARAM_FLAG_DBUS_PROPERTY, GUINT_TO_POINTER(FALSE), .setter_data = "anon-mandatory", }, { TP_IFACE_CONNECTION_INTERFACE_ANONYMITY ".AnonymityModes", DBUS_TYPE_UINT32_AS_STRING, G_TYPE_UINT, TP_CONN_MGR_PARAM_FLAG_DBUS_PROPERTY, GUINT_TO_POINTER(0), 0, param_filter_anon_modes, .setter_data = "anon-supported-modes", }, #define MODEM_PARAM_SPEC (ring_connection_params + 6) { "modem", DBUS_TYPE_OBJECT_PATH_AS_STRING, /* DBUS_TYPE_G_OBJECT_PATH expands to a function call so we have to fill * this in in ring_connection_get_param_specs(). */ (GType) 0, 0, NULL, 0, param_filter_valid_object_path, .setter_data = "modem-path", }, /* Deprecated... */ { "account", DBUS_TYPE_STRING_AS_STRING, G_TYPE_STRING, }, { "password", DBUS_TYPE_STRING_AS_STRING, G_TYPE_STRING, TP_CONN_MGR_PARAM_FLAG_SECRET, }, { NULL } }; TpCMParamSpec * ring_connection_get_param_specs (void) { static gsize once; if (g_once_init_enter (&once)) { TpCMParamSpec *modem = MODEM_PARAM_SPEC; modem->gtype = DBUS_TYPE_G_OBJECT_PATH; g_once_init_leave (&once, 1); } return ring_connection_params; } RingConnection * ring_connection_new(GHashTable *params) { gpointer conn = g_object_new(RING_TYPE_CONNECTION, "protocol", "tel", NULL); guint i; GValue *value; TpCMParamSpec *specs = ring_connection_get_param_specs (); for (i = 0; specs[i].name; i++) { if (!specs[i].setter_data) continue; value = g_hash_table_lookup(params, specs[i].name); if (value) g_object_set_property (conn, specs[i].setter_data, value); } return conn; } /* ---------------------------------------------------------------------- */ /* TpBaseConnection interface */ /* 1 - repo */ /** Return a context used to normalize contacts from network */ gpointer ring_network_normalization_context(void) { return (gpointer)ring_network_normalization_context; } /** Normalize a GSM address. * * A contact handle can be either a phone number (up to 20 digits), * international phone number ("'+' and up to 20 digits), SOS URN or * an alphanumeric address with up to 11 GSM characters. * * Normalized telephone number can contain an an optional service prefix * and dial string. */ char * ring_normalize_contact (char const *input, GError **return_error) { char const *sos; char *s; int i, j; if (g_strcasecmp(input, ring_self_handle_name) == 0) return g_strdup(ring_self_handle_name); if (strlen(input) == strspn(input, "()-. ")) return g_strdup(""); /* Anonymous */ sos = modem_call_get_valid_emergency_urn(input); if (sos) return g_ascii_strdown(g_strdup(sos), -1); if ((s = ring_str_starts_with_case(input, "tel:"))) input = s; s = g_strdup(input); /* Remove usual extra chars like (-. ) */ for (i = j = 0; s[i]; i++) { switch (s[i]) { case '(': case ')': case '-': case '.': case ' ': continue; case 'P': case 'p': s[j++] = 'p'; break; case 'W': case 'w': s[j++] = 'w'; break; default: s[j++] = s[i]; break; } } s[j] = s[i]; if (modem_call_is_valid_address (s) || modem_sms_is_valid_address (s)) return s; if (g_utf8_strlen(input, -1) <= 11) return strcpy(s, input); *return_error = g_error_new(TP_ERRORS, TP_ERROR_INVALID_ARGUMENT, "invalid phone number"); g_free(s); return NULL; } static char * ring_normalize_name (TpHandleRepoIface *repo G_GNUC_UNUSED, char const *input, gpointer context, GError **return_error) { if (context == ring_network_normalization_context ()) return g_strdup (input); return ring_normalize_contact (input, return_error); } static void ring_connection_create_handle_repos(TpBaseConnection *base, TpHandleRepoIface *repos[NUM_TP_HANDLE_TYPES]) { (void)base; repos[TP_HANDLE_TYPE_CONTACT] = (TpHandleRepoIface *) g_object_new(TP_TYPE_DYNAMIC_HANDLE_REPO, "handle-type", TP_HANDLE_TYPE_CONTACT, "normalize-function", ring_normalize_name, NULL); } /* ---------------------------------------------------------------------- */ static char * ring_connection_get_unique_connection_name(TpBaseConnection *base) { (void)base; return g_strdup("ring"); /* There can be only one */ } /* ---------------------------------------------------------------------- */ /* TpBaseConnection interface */ static GPtrArray * ring_connection_create_channel_managers(TpBaseConnection *base) { RingConnection *self = RING_CONNECTION(base); RingConnectionPrivate *priv = self->priv; GPtrArray *channel_managers = g_ptr_array_sized_new(2); DEBUG("enter"); priv->media = g_object_new(RING_TYPE_MEDIA_MANAGER, "connection", self, "anon-modes", priv->anon_modes, NULL); g_ptr_array_add(channel_managers, priv->media); priv->conference = g_object_new (RING_TYPE_CONFERENCE_MANAGER, "connection", self, NULL); g_ptr_array_add(channel_managers, priv->conference); priv->text = g_object_new(RING_TYPE_TEXT_MANAGER, "connection", self, "sms-service-centre", priv->smsc, "sms-validity-period", priv->sms_valid, "sms-reduced-charset", priv->sms_reduced_charset, NULL); g_ptr_array_add(channel_managers, priv->text); return channel_managers; } static void ring_connection_imsi_changed (ModemSIMService *sim, GParamSpec *dummy, gpointer _self) { TpBaseConnection *base = TP_BASE_CONNECTION (_self); DEBUG ("enter"); if (RING_CONNECTION (_self)->priv->sim != sim) return; if (base->status != TP_CONNECTION_STATUS_DISCONNECTED) { tp_base_connection_change_status (base, TP_CONNECTION_STATUS_DISCONNECTED, TP_CONNECTION_STATUS_REASON_NETWORK_ERROR); } } static void ring_connection_modem_interface_added (Modem *modem, ModemOface *oface, gpointer _self) { RingConnection *self = RING_CONNECTION (_self); RingConnectionPrivate *priv = self->priv; DEBUG ("enter with %s of %s", modem_oface_interface (oface), modem_oface_object_path (oface)); if (MODEM_IS_SIM_SERVICE (oface)) { g_object_set (self, "sim-service", oface, NULL); if (priv->imsi) { priv->signals.imsi_notify = g_signal_connect (priv->sim, "notify::imsi", G_CALLBACK (ring_connection_imsi_changed), self); } } else if (MODEM_IS_CALL_SERVICE (oface)) { g_object_set (priv->media, "call-service", oface, NULL); g_object_set (priv->conference, "call-service", oface, NULL); } else if (MODEM_IS_SMS_SERVICE (oface)) { g_object_set (priv->text, "sms-service", oface, NULL); } } static void ring_connection_modem_interface_removed (Modem *modem, ModemOface *oface, gpointer _self) { RingConnection *self = RING_CONNECTION (_self); RingConnectionPrivate *priv = self->priv; DEBUG ("enter with %s of %s", modem_oface_interface (oface), modem_oface_object_path (oface)); if (MODEM_IS_SIM_SERVICE (oface)) { DEBUG ("removed SIM_SERVICE"); g_object_set (self, "sim-service", NULL, NULL); } else if (MODEM_IS_CALL_SERVICE (oface)) { DEBUG ("removed CALL_SERVICE"); g_assert (priv->media); g_object_set (priv->media, "call-service", NULL, NULL); g_object_set (priv->conference, "call-service", NULL, NULL); } else if (MODEM_IS_SMS_SERVICE (oface)) { DEBUG ("removed SMS_SERVICE"); g_assert (priv->text); g_object_set (priv->text, "sms-service", NULL, NULL); } } ModemOface * ring_connection_get_modem_interface (RingConnection *self, char const *name) { return modem_get_interface (self->priv->modem, name); } static void ring_connection_bind_modem (RingConnection *self, Modem *modem) { RingConnectionPrivate *priv = self->priv; TpBaseConnection *base = TP_BASE_CONNECTION (self); g_assert (base->status == TP_CONNECTION_STATUS_CONNECTING || base->status == TP_INTERNAL_CONNECTION_STATUS_NEW); g_return_if_fail (MODEM_IS_MODEM (modem)); g_return_if_fail (priv->modem == NULL); g_object_set (self, "modem", modem, NULL); tp_base_connection_change_status (base, TP_CONNECTION_STATUS_CONNECTED, TP_CONNECTION_STATUS_REASON_REQUESTED); } static void ring_connection_imsi_added (ModemService *modems, Modem *modem, char const *imsi, gpointer _self) { RingConnection *self = RING_CONNECTION (_self); RingConnectionPrivate *priv = self->priv; DEBUG ("enter with imsi=%s", imsi); if (priv->modem) return; if (modem_has_imsi (modem, priv->imsi)) ring_connection_bind_modem (self, modem); } static void ring_connection_imei_added (ModemService *modems, Modem *modem, char const *imei, gpointer _self) { RingConnection *self = RING_CONNECTION (_self); RingConnectionPrivate *priv = self->priv; DEBUG ("enter with imei=%s", imei); if (priv->modem) return; if (modem_has_imei (modem, priv->imei)) ring_connection_bind_modem (self, modem); } static void ring_connection_modem_added (ModemService *modems, Modem *modem, gpointer _self) { RingConnection *self = RING_CONNECTION (_self); RingConnectionPrivate *priv = self->priv; DEBUG ("enter with modem %s", modem_get_modem_path (modem)); if (priv->modem) return; if (strcmp (priv->modem_path, modem_get_modem_path (modem))) return; ring_connection_bind_modem (self, modem); } static void ring_connection_modem_powered (ModemService *modems, Modem *modem, gpointer _self) { RingConnection *self = RING_CONNECTION (_self); RingConnectionPrivate *priv = self->priv; DEBUG ("enter with modem %s", modem_get_modem_path (modem)); if (priv->modem) return; ring_connection_bind_modem (self, modem); } static void ring_connection_modem_removed (ModemService *modems, Modem *modem, gpointer _self) { TpBaseConnection *base = TP_BASE_CONNECTION (_self); DEBUG ("enter"); if (RING_CONNECTION (_self)->priv->modem != modem) return; if (base->status != TP_CONNECTION_STATUS_DISCONNECTED) { tp_base_connection_change_status (base, TP_CONNECTION_STATUS_DISCONNECTED, TP_CONNECTION_STATUS_REASON_NETWORK_ERROR); } } static gboolean ring_connection_start_connecting(TpBaseConnection *base, GError **return_error) { RingConnection *self = RING_CONNECTION(base); RingConnectionPrivate *priv = self->priv; ModemService *manager; Modem *modem; GError *error = NULL; DEBUG("called"); g_assert(base->status == TP_INTERNAL_CONNECTION_STATUS_NEW); error = NULL; manager = modem_service (); if (priv->imsi && strlen (priv->imsi)) { /* Connect to modem with IMSI */ modem = modem_service_find_by_imsi (manager, priv->imsi); if (!modem) priv->signals.modem_added = g_signal_connect (manager, "imsi-added", G_CALLBACK (ring_connection_imsi_added), self); } else if (priv->imei && strlen (priv->imei)) { /* Connect to modem with given IMEI */ modem = modem_service_find_by_imei (manager, priv->imei); if (!modem) priv->signals.modem_added = g_signal_connect (manager, "imei-added", G_CALLBACK (ring_connection_imei_added), self); } else if (priv->modem_path) { /* Connect to modem with given path */ modem = modem_service_find_by_path (manager, priv->modem_path); if (!modem) priv->signals.modem_added = g_signal_connect (manager, "modem-added", G_CALLBACK (ring_connection_modem_added), self); } else { /* Connect to first modem that is powered on */ modem = modem_service_find_best (manager); if (!modem) priv->signals.modem_added = g_signal_connect (manager, "modem-powered", G_CALLBACK (ring_connection_modem_powered), self); } if (modem) { ring_connection_bind_modem (self, modem); } return TRUE; } /** * ring_connection_connected * * Called after a connection becomes connected. */ static void ring_connection_connected (TpBaseConnection *base) { RingConnection *self = RING_CONNECTION (base); RingConnectionPrivate *priv = self->priv; ModemOface **interfaces; int i; DEBUG ("called"); if (priv->connecting_source) { g_source_remove (priv->connecting_source); priv->connecting_source = 0; } ring_signal_disconnect (modem_service (), &priv->signals.modem_added); priv->signals.modem_interface_added = g_signal_connect (priv->modem, "interface-added", G_CALLBACK (ring_connection_modem_interface_added), self); priv->signals.modem_interface_removed = g_signal_connect (priv->modem, "interface-removed", G_CALLBACK (ring_connection_modem_interface_removed), self); priv->signals.modem_removed = g_signal_connect (modem_service (), "modem-removed", G_CALLBACK (ring_connection_modem_removed), self); interfaces = modem_list_interfaces (priv->modem); if (interfaces) { for (i = 0; interfaces[i]; i++) { ModemOface *iface = interfaces[i]; ring_connection_modem_interface_added (priv->modem, iface, self); } } g_free (interfaces); } /** * ring_connection_disconnected * * Called after a connection becomes disconnected. * * Not called unless start_connecting has been called. */ static void ring_connection_disconnected(TpBaseConnection *base) { RingConnection *self = RING_CONNECTION(base); RingConnectionPrivate *priv = self->priv; DEBUG("called"); if (priv->connecting_source) { g_source_remove (priv->connecting_source); priv->connecting_source = 0; } ring_signal_disconnect (modem_service (), &priv->signals.modem_added); ring_signal_disconnect (modem_service (), &priv->signals.modem_removed); ring_signal_disconnect (priv->modem, &priv->signals.modem_interface_added); ring_signal_disconnect (priv->modem, &priv->signals.modem_interface_removed); ring_signal_disconnect (priv->sim, &priv->signals.imsi_notify); g_object_set (self, "modem", NULL, NULL); g_object_set (self, "sim-service", NULL, NULL); g_object_set (priv->media, "call-service", NULL, NULL); g_object_set (priv->text, "sms-service", NULL, NULL); } /** Called after connection has been disconnected. * * Shuts down connection and eventually calls * tp_base_connection_finish_shutdown(). */ static void ring_connection_shut_down(TpBaseConnection *base) { DEBUG("called"); tp_base_connection_finish_shutdown(base); } static void ring_connection_class_init_base_connection(TpBaseConnectionClass *klass) { /* Implement pure-virtual methods */ #define IMPLEMENT(x) klass->x = ring_connection_##x IMPLEMENT(create_handle_repos); /* IMPLEMENT(create_channel_factories); */ IMPLEMENT(get_unique_connection_name); /* IMPLEMENT(connecting) */ IMPLEMENT (connected); IMPLEMENT(disconnected); IMPLEMENT(shut_down); IMPLEMENT(start_connecting); klass->interfaces_always_present = (char const **)ring_connection_interfaces_always_present; IMPLEMENT(create_channel_managers); #undef IMPLEMENT } /* ---------------------------------------------------------------------- */ /* RingConnection interface */ gchar ** ring_connection_dup_implemented_interfaces (void) { return g_strdupv ((GStrv)ring_connection_interfaces_always_present); } char const * ring_connection_inspect_contact(RingConnection const *self, TpHandle contact) { TpHandleRepoIface *repo; if (contact == 0) return ""; repo = tp_base_connection_get_handles(TP_BASE_CONNECTION(self), TP_HANDLE_TYPE_CONTACT); return tp_handle_inspect(repo, contact); } gpointer ring_connection_lookup_channel (RingConnection const *self, char const *object_path) { RingConnectionPrivate *priv = RING_CONNECTION(self)->priv; gpointer channel; channel = ring_media_manager_lookup (priv->media, object_path); if (channel) return channel; channel = ring_text_manager_lookup (priv->text, object_path); if (channel) return channel; channel = ring_conference_manager_lookup (priv->conference, object_path); return channel; } gboolean ring_connection_validate_initial_members(RingConnection *self, RingInitialMembers *initial, GError **error) { return ring_media_manager_validate_initial_members( self->priv->media, initial, error); } /* ---------------------------------------------------------------------- */ /* org.freedesktop.Telepathy.Connection.Interface.Anonymity */ static TpDBusPropertiesMixinPropImpl ring_connection_anon_properties[] = { { "SupportedAnonymityModes", "anon-supported-modes", "anon-supported-modes" }, { "AnonymityMandatory", "anon-mandatory", "anon-mandatory" }, { "AnonymityModes", "anon-modes", "anon-modes" }, { NULL } }; /* ---------------------------------------------------------------------- */ /* org.freedesktop.Telepathy.Connection.Interface.Cellular */ static TpDBusPropertiesMixinPropImpl ring_connection_cellular_properties[] = { { "IMSI", "imsi", "imsi" }, { "MessageValidityPeriod", "sms-validity-period", "sms-validity-period" }, { "MessageServiceCentre", "sms-service-centre", "sms-service-centre" }, { "MessageReducedCharacterSet", "sms-reduced-charset", "sms-reduced-charset" }, { NULL } }; static gboolean ring_connection_cellular_properties_setter(GObject *object, GQuark interface, GQuark aname, const GValue *value, gpointer setter_data, GError **error) { char const *name = setter_data; TpCMParamSpec const *param_spec; if (name == NULL) { g_set_error(error, TP_ERRORS, TP_ERROR_PERMISSION_DENIED, "This property is read-only"); return FALSE; } if (strcmp(name, "sms-validity-period") == 0) { param_spec = CELLULAR_SMS_VALIDITY_PERIOD_PARAM_SPEC; } else if (strcmp(name, "sms-service-centre") == 0) { param_spec = CELLULAR_SMS_SERVICE_CENTRE_PARAM_SPEC; } else if (strcmp(name, "sms-reduced-charset") == 0) { param_spec = CELLULAR_SMS_REDUCED_CHARSET_PARAM_SPEC; } else { g_set_error(error, TP_ERRORS, TP_ERROR_INVALID_ARGUMENT, "Unknown property"); return FALSE; } if (param_spec->filter && !param_spec->filter( param_spec, (GValue *)value, error)) { return FALSE; } g_object_set_property(object, name, value); return TRUE; } /* ---------------------------------------------------------------------- */ /* Connection.Interface.ServicePoint */ static TpDBusPropertiesMixinPropImpl ring_connection_service_point_properties[] = { { "KnownServicePoints", "known-service-points" }, { NULL} }; /* ---------------------------------------------------------------------- */ /* Connection.Interface.StoredMessages */ #if nomore static TpDBusPropertiesMixinPropImpl ring_connection_stored_messages_properties[] = { { "StoredMessages", "stored-messages" }, { NULL } }; static void ring_connection_deliver_stored_messages( RTComTpSvcConnectionInterfaceStoredMessages *iface, char const **messages, DBusGMethodInvocation *context) { ring_text_manager_deliver_stored_messages( RING_CONNECTION(iface)->priv->text, messages, context); } static void ring_connection_expunge_messages( RTComTpSvcConnectionInterfaceStoredMessages *iface, char const **messages, DBusGMethodInvocation *context) { ring_text_manager_expunge_messages( RING_CONNECTION(iface)->priv->text, messages, context); } static void ring_connection_set_storage_state( RTComTpSvcConnectionInterfaceStoredMessages *iface, gboolean out_of_storage, DBusGMethodInvocation *context) { ring_text_manager_set_storage_state( RING_CONNECTION(iface)->priv->text, out_of_storage, context); } static void ring_connection_stored_messages_iface_init(gpointer g_iface, gpointer iface_data) { RTComTpSvcConnectionInterfaceStoredMessagesClass *klass = g_iface; #define IMPLEMENT(x) \ rtcom_tp_svc_connection_interface_stored_messages_implement_##x( \ klass, ring_connection_ ## x) IMPLEMENT(deliver_stored_messages); IMPLEMENT(expunge_messages); IMPLEMENT(set_storage_state); #undef IMPLEMENT } #endif /* ---------------------------------------------------------------------- */ /* Connection.Interface.Capabilities */ static GValueArray * ring_capability_pair_new(char const *channel_type, guint type_specific_flags) { GValue value[1]; memset(value, 0, sizeof value); g_value_init(value, TP_STRUCT_TYPE_CAPABILITY_PAIR); g_value_take_boxed(value, dbus_g_type_specialized_construct(TP_STRUCT_TYPE_CAPABILITY_PAIR)); dbus_g_type_struct_set(value, 0, channel_type, 1, type_specific_flags, G_MAXUINT); return g_value_get_boxed(value); } static void ring_capability_pair_free(gpointer value) { g_boxed_free(TP_STRUCT_TYPE_CAPABILITY_PAIR, value); } static GValueArray * ring_capability_change_new(guint handle, char const *channel_type, guint old_generic, guint new_generic, guint old_specific, guint new_specific) { GValue value[1]; memset(value, 0, sizeof value); g_value_init(value, TP_STRUCT_TYPE_CAPABILITY_CHANGE); g_value_take_boxed(value, dbus_g_type_specialized_construct(TP_STRUCT_TYPE_CAPABILITY_CHANGE)); dbus_g_type_struct_set(value, 0, handle, 1, channel_type, 2, old_generic, 3, new_generic, 4, old_specific, 5, new_specific, G_MAXUINT); return g_value_get_boxed(value); } static void ring_capability_change_free(gpointer value) { g_boxed_free(TP_STRUCT_TYPE_CAPABILITY_CHANGE, value); } GValueArray * ring_contact_capability_new(guint handle, char const *channel_type, guint generic, guint specific) { GValue value[1]; memset(value, 0, sizeof value); g_value_init(value, TP_STRUCT_TYPE_CONTACT_CAPABILITY); g_value_take_boxed(value, dbus_g_type_specialized_construct(TP_STRUCT_TYPE_CONTACT_CAPABILITY)); dbus_g_type_struct_set(value, 0, handle, 1, channel_type, 2, generic, 3, specific, G_MAXUINT); return g_value_get_boxed(value); } void ring_contact_capability_free(gpointer value) { g_boxed_free(TP_STRUCT_TYPE_CONTACT_CAPABILITY, value); } static void ring_connection_advertise_capabilities( TpSvcConnectionInterfaceCapabilities *iface, const GPtrArray *add, const char **remove, DBusGMethodInvocation *context) { RingConnection *self = RING_CONNECTION(iface); RingConnectionPrivate *priv = self->priv; TpBaseConnection *base = TP_BASE_CONNECTION(self); guint handle; char const media[] = TP_IFACE_CHANNEL_TYPE_STREAMED_MEDIA; char const text[] = TP_IFACE_CHANNEL_TYPE_TEXT; guint add_media = 0, add_text = 0; guint remove_media = 0, remove_text = 0; guint old_media = 0, old_text = 0; guint new_media = 0, new_text = 0; GPtrArray *returns, *emits; guint i; for (i = 0; i < add->len; i++) { char *channel_type; guint type_specific_flags; GValue capability_pair[1]; memset(capability_pair, 0, sizeof capability_pair); g_value_init(capability_pair, TP_STRUCT_TYPE_CAPABILITY_PAIR); g_value_set_static_boxed(capability_pair, add->pdata[i]); dbus_g_type_struct_get(capability_pair, 0, &channel_type, 1, &type_specific_flags, G_MAXUINT); if (g_str_equal(channel_type, media)) add_media |= type_specific_flags; else if (g_str_equal(channel_type, text)) add_text |= type_specific_flags; g_free(channel_type); } for (i = 0; remove[i]; i++) { char const *channel_type = remove[i]; if (g_str_equal(channel_type, media)) remove_media |= ~0; else if (g_str_equal(channel_type, text)) remove_text |= ~0; } returns = g_ptr_array_sized_new(2); g_object_get(priv->media, "capability-flags", &old_media, NULL); g_object_set(priv->media, "capability-flags", (old_media | add_media) & ~ remove_media, NULL); g_object_get(priv->media, "capability-flags", &new_media, NULL); if (new_media) { g_ptr_array_add(returns, ring_capability_pair_new(media, new_media)); } if (old_media != new_media) DEBUG("changed %s caps %x (old was %x)", "StreamedMedia", new_media, old_media); g_object_get(priv->text, "capability-flags", &old_text, NULL); g_object_set(priv->text, "capability-flags", (old_text | add_text) & ~ remove_text, NULL); g_object_get(priv->text, "capability-flags", &new_text, NULL); if (new_text) { g_ptr_array_add(returns, ring_capability_pair_new(text, new_text)); } if (old_text != new_text) DEBUG("changed %s caps %x (old was %x)", "Text", new_text, old_text); tp_svc_connection_interface_capabilities_return_from_advertise_capabilities( context, returns); for (i = 0; i < returns->len; i++) ring_capability_pair_free(returns->pdata[i]); g_ptr_array_free(returns, TRUE); if (old_media == new_media && old_text == new_text) return; /* Emit CapabilitiesChanged */ handle = tp_base_connection_get_self_handle(base); emits = g_ptr_array_sized_new(2); if (old_media != new_media) { guint generic = TP_CONNECTION_CAPABILITY_FLAG_CREATE | TP_CONNECTION_CAPABILITY_FLAG_INVITE; g_ptr_array_add(emits, ring_capability_change_new(handle, media, old_media ? generic : 0, new_media ? generic : 0, old_media, new_media)); } if (old_text != new_text) { guint generic = TP_CONNECTION_CAPABILITY_FLAG_CREATE; g_ptr_array_add(emits, ring_capability_change_new(handle, text, old_text ? generic : 0, new_text ? generic : 0, old_text, new_text)); } DEBUG("emitting CapabilitiesChanged"); tp_svc_connection_interface_capabilities_emit_capabilities_changed(self, emits); for (i = 0; i < emits->len; i++) ring_capability_change_free(emits->pdata[i]); g_ptr_array_free(emits, TRUE); } static void ring_connection_get_capabilities( TpSvcConnectionInterfaceCapabilities *iface, GArray const *handles, DBusGMethodInvocation *context) { RingConnection *self = RING_CONNECTION(iface); RingConnectionPrivate *priv = self->priv; TpBaseConnection *base = TP_BASE_CONNECTION(self); GError *error = NULL; TpHandleRepoIface *repo; GPtrArray *returns; guint i, n = handles->len; guint const *array = (gpointer)handles->data; TP_BASE_CONNECTION_ERROR_IF_NOT_CONNECTED(base, context); repo = tp_base_connection_get_handles(base, TP_HANDLE_TYPE_CONTACT); if (!tp_handles_are_valid(repo, handles, TRUE, &error)) { dbus_g_method_return_error(context, error); g_error_free(error); return; } returns = g_ptr_array_sized_new(2 * n); for (i = 0; i < n; i++) { guint handle = array[i]; if (handle != 0) { ring_media_manager_add_capabilities(priv->media, handle, returns); ring_text_manager_add_capabilities(priv->text, handle, returns); } } tp_svc_connection_interface_capabilities_return_from_get_capabilities(context, returns); for (i = 0; i < returns->len; i++) ring_contact_capability_free(returns->pdata[i]); g_ptr_array_free(returns, TRUE); } static void ring_connection_capabilities_iface_init(gpointer g_iface, gpointer iface_data) { TpSvcConnectionInterfaceCapabilitiesClass *klass = g_iface; #define IMPLEMENT(x) \ tp_svc_connection_interface_capabilities_implement_##x ( \ klass, ring_connection_##x) IMPLEMENT(advertise_capabilities); IMPLEMENT(get_capabilities); #undef IMPLEMENT } /** Add contact attributes belonging to interface * org.freedesktop.Telepathy.Connection.Interface.Capabilities */ static void ring_connection_add_contact_capabilities(GObject *object, GArray const *handles, GHashTable *returns) { RingConnection *self = RING_CONNECTION(object); RingConnectionPrivate *priv = self->priv; guint i, n = handles->len; guint const *array = (gpointer)handles->data; for (i = 0; i < n; i++) { GPtrArray *caps = g_ptr_array_sized_new(2); GValue *value; guint handle = array[i]; if (handle != 0) { ring_media_manager_add_capabilities(priv->media, handle, caps); ring_text_manager_add_capabilities(priv->text, handle, caps); } if (caps->len) { value = tp_g_value_slice_new(TP_ARRAY_TYPE_CONTACT_CAPABILITY_LIST); g_value_take_boxed(value, caps); tp_contacts_mixin_set_contact_attribute(returns, handle, TP_IFACE_CONNECTION_INTERFACE_CAPABILITIES "/caps", value); } else { g_ptr_array_free(caps, TRUE); } } } telepathy-ring-2.3.24-37ad7111424725a5446e82dac516d7cab88149c7/src/ring-connection.h000066400000000000000000000063421251541261300260150ustar00rootroot00000000000000/* * ring-connection.h - Header for RingConnection * * Copyright (C) 2007-2010 Nokia Corporation * @author Pekka Pessi * @author Tom Swindell * * This work is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This work is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this work; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef RING_CONNECTION_H #define RING_CONNECTION_H #include #include #include #include #include "ring-util.h" G_BEGIN_DECLS typedef struct _RingConnection RingConnection; typedef struct _RingConnectionClass RingConnectionClass; typedef struct _RingConnectionPrivate RingConnectionPrivate; struct _RingConnectionClass { TpBaseConnectionClass parent_class; TpDBusPropertiesMixinClass dbus_properties_class; TpContactsMixinClass contacts_mixin_class; }; struct _RingConnection { TpBaseConnection parent; TpContactsMixin contacts_mixin; TpHandle anon_handle, sos_handle; RingConnectionPrivate *priv; }; GType ring_connection_get_type(void); /* Type macros */ #define RING_TYPE_CONNECTION \ (ring_connection_get_type()) #define RING_CONNECTION(obj) \ (G_TYPE_CHECK_INSTANCE_CAST((obj), RING_TYPE_CONNECTION, RingConnection)) #define RING_CONNECTION_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST((klass), RING_TYPE_CONNECTION, RingConnectionClass)) #define RING_IS_CONNECTION(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE((obj), RING_TYPE_CONNECTION)) #define RING_IS_CONNECTION_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE((klass), RING_TYPE_CONNECTION)) #define RING_CONNECTION_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), RING_TYPE_CONNECTION, RingConnectionClass)) /* Extensions to TpBaseConnection */ TpCMParamSpec *ring_connection_get_param_specs (void); gpointer ring_connection_params_alloc(void); void ring_connection_params_free(gpointer p); gchar **ring_connection_dup_implemented_interfaces (void); char *ring_normalize_contact (char const *input, GError **return_error); RingConnection *ring_connection_new(GHashTable *params); gboolean ring_connection_check_status(RingConnection *self); char const *ring_connection_inspect_contact(RingConnection const *, TpHandle); gboolean ring_connection_contact_is_special(RingConnection const *, TpHandle); gpointer ring_connection_lookup_channel(RingConnection const *, char const *); gboolean ring_connection_validate_initial_members(RingConnection *self, RingInitialMembers *initial, GError **error); struct _ModemOface *ring_connection_get_modem_interface (RingConnection *, char const *); G_END_DECLS #endif /* #ifndef RING_CONNECTION_H */ telepathy-ring-2.3.24-37ad7111424725a5446e82dac516d7cab88149c7/src/ring-debug.c000066400000000000000000000056501251541261300247400ustar00rootroot00000000000000/* * ring-debug.c - Debugging utilities * * Copyright (C) 2007-2010 Nokia Corporation * @author Pekka Pessi * * This work is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This work is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this work; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "config.h" #include "ring-debug.h" #ifdef ENABLE_DEBUG #include #include #include void modem_debug_set_flags(int flags); void modem_debug_set_flags_from_env(void); static RingDebugFlags ring_debug_flags = 0; static const GDebugKey ring_debug_keys[] = { { "media-channel", RING_DEBUG_MEDIA }, { "call", RING_DEBUG_MEDIA }, { "media", RING_DEBUG_MEDIA }, { "connection", RING_DEBUG_CONNECTION }, { "text-channel", RING_DEBUG_SMS }, { "text", RING_DEBUG_SMS }, { "sms", RING_DEBUG_SMS }, }; void ring_debug_set_flags (RingDebugFlags new_flags) { ring_debug_flags |= new_flags; } gboolean ring_debug_flag_is_set (RingDebugFlags flag) { return (flag & ring_debug_flags) != 0; } void ring_debug (RingDebugFlags flag, const char *format, ...) { if (flag & ring_debug_flags) { va_list args; va_start (args, format); g_logv ("Ring", G_LOG_LEVEL_DEBUG, format, args); va_end (args); } } #endif /* ENABLE_DEBUG */ void ring_message (const char *format, ...) { va_list ap; va_start (ap, format); g_logv ("Ring", G_LOG_LEVEL_MESSAGE, format, ap); va_end (ap); } void ring_warning (const char *format, ...) { va_list ap; va_start (ap, format); g_logv ("Ring", G_LOG_LEVEL_WARNING, format, ap); va_end (ap); } void ring_critical (const char *format, ...) { va_list ap; va_start (ap, format); g_logv ("Ring", G_LOG_LEVEL_CRITICAL, format, ap); va_end (ap); } void ring_debug_set_flags_from_env (void) { tp_debug_divert_messages(g_getenv("RING_LOGFILE")); const char *flags_string; flags_string = g_getenv("RING_DEBUG"); #if ENABLE_DEBUG if (flags_string) { tp_debug_set_flags (flags_string); ring_debug_set_flags (g_parse_debug_string (flags_string, ring_debug_keys, G_N_ELEMENTS(ring_debug_keys))); } #endif if (flags_string && g_str_equal(flags_string, "all")) modem_debug_set_flags(~0); else modem_debug_set_flags_from_env(); tp_debug_set_persistent(g_getenv("RING_PERSIST") != NULL); } telepathy-ring-2.3.24-37ad7111424725a5446e82dac516d7cab88149c7/src/ring-debug.h000066400000000000000000000043001251541261300247340ustar00rootroot00000000000000/* * ring-debug.h - Debugging utilities * * Copyright (C) 2007-2010 Nokia Corporation * @author Pekka Pessi * * This work is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This work is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this work; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __RING_DEBUG_H__ #define __RING_DEBUG_H_ #include "config.h" #include G_BEGIN_DECLS void ring_debug_set_flags_from_env(void); typedef enum { RING_DEBUG_CONNECTION = 1 << 0, RING_DEBUG_MEDIA = 1 << 1, RING_DEBUG_SMS = 1 << 2, } RingDebugFlags; void ring_debug_set_flags(RingDebugFlags flags); gboolean ring_debug_flag_is_set(RingDebugFlags flag); void ring_debug(RingDebugFlags flag, const char *format, ...) G_GNUC_PRINTF (2, 3); G_END_DECLS #ifdef ENABLE_DEBUG #ifdef DEBUG_FLAG #define DEBUG(format, ...) \ ring_debug(DEBUG_FLAG, "%s: " format, G_STRFUNC, ##__VA_ARGS__) #define DEBUGGING ring_debug_flag_is_set(DEBUG_FLAG) #endif /* DEBUG_FLAG */ #else /* ENABLE_DEBUG */ #ifdef DEBUG_FLAG #define DEBUG(format, ...) #define DEBUGGING 0 #endif /* DEBUG_FLAG */ #endif /* ENABLE_DEBUG */ void ring_message(const char *format, ...) G_GNUC_PRINTF (1, 2); void ring_warning(const char *format, ...) G_GNUC_PRINTF (1, 2); void ring_critical(const char *format, ...) G_GNUC_PRINTF (1, 2); #define GERROR_MSG_FMT "%s (%d@%s)" #define GERROR_MSG_CODE(e) \ (e ? e->message : "no error"), \ (e ? e->code : 0), \ (e ? g_quark_to_string(e->domain) : "") #endif /* __RING_DEBUG_H__ */ telepathy-ring-2.3.24-37ad7111424725a5446e82dac516d7cab88149c7/src/ring-emergency-service.c000066400000000000000000000065471251541261300272740ustar00rootroot00000000000000/* * ring-emergency-service.c - RingEmergencyService and RingEmergencyServiceList * * Copyright (C) 2008 Nokia Corporation * @author Pekka Pessi * * This work is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This work is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this work; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #define DEBUG_FLAG RING_DEBUG_CONNECTION #include "ring-debug.h" #include "ring-emergency-service.h" #include "ring-util.h" #include "modem/call.h" #include #include #include RingEmergencyService * ring_emergency_service_new(char const *service) { GValue value[1] = {{ 0 }}; GType gtype = TP_STRUCT_TYPE_SERVICE_POINT; TpServicePointType service_type; if (service == NULL) service = ""; if (service[0] == '\0') service_type = TP_SERVICE_POINT_TYPE_NONE; else service_type = TP_SERVICE_POINT_TYPE_EMERGENCY; g_value_init(value, gtype); g_value_take_boxed(value, dbus_g_type_specialized_construct(gtype)); dbus_g_type_struct_set(value, 0, service_type, 1, service, G_MAXUINT); return (RingEmergencyService *)g_value_get_boxed(value); } void ring_emergency_service_free(RingEmergencyService *service) { g_boxed_free(TP_STRUCT_TYPE_SERVICE_POINT, (gpointer)service); } /* Return a pointer to a boxed service struct */ RingEmergencyServiceInfo * ring_emergency_service_info_new(char const *service, char * const *aliases) { RingEmergencyService *es; GValue value[1] = {{ 0 }}; GType gtype = TP_STRUCT_TYPE_SERVICE_POINT_INFO; g_value_init(value, gtype); g_value_take_boxed(value, dbus_g_type_specialized_construct(gtype)); es = ring_emergency_service_new(service); dbus_g_type_struct_set(value, 0, es, 1, aliases, G_MAXUINT); ring_emergency_service_free(es); return (RingEmergencyServiceInfo *)g_value_get_boxed(value); } void ring_emergency_service_info_free(RingEmergencyServiceInfo *info) { g_boxed_free(TP_STRUCT_TYPE_SERVICE_POINT_INFO, info); } RingEmergencyServiceInfoList * ring_emergency_service_info_list_new(RingEmergencyServiceInfo *info, ...) { RingEmergencyServiceInfoList *self = g_ptr_array_sized_new(1); va_list ap; for (va_start(ap, info); info; info = va_arg(ap, gpointer)) { g_ptr_array_add(self, info); } va_end(ap); return self; } void ring_emergency_service_info_list_free(RingEmergencyServiceInfoList *list) { guint i; for (i = 0; i < list->len; i++) ring_emergency_service_free(g_ptr_array_index(list, i)); g_ptr_array_free(list, TRUE); } RingEmergencyServiceInfoList * ring_emergency_service_info_list_default (char * const *numbers) { RingEmergencyServiceInfo *base; base = ring_emergency_service_info_new(RING_EMERGENCY_SERVICE_URN, numbers); return ring_emergency_service_info_list_new(base, NULL); } telepathy-ring-2.3.24-37ad7111424725a5446e82dac516d7cab88149c7/src/ring-emergency-service.h000066400000000000000000000037501251541261300272720ustar00rootroot00000000000000/* * ring-emergency-service.c - RingEmergencyService and RingEmergencyServiceList * * Copyright (C) 2008 Nokia Corporation * @author Pekka Pessi * * This work is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This work is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this work; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef RING_EMERGENCY_SERVICE_H #define RING_EMERGENCY_SERVICE_H #include #include #include G_BEGIN_DECLS #define RING_EMERGENCY_SERVICE_URN "urn:service:sos" /* Boxed struct describing RingEmergencyService */ typedef GValueArray RingEmergencyService; typedef GValueArray RingEmergencyServiceInfo; typedef GPtrArray RingEmergencyServiceInfoList; RingEmergencyService *ring_emergency_service_new(char const *service); void ring_emergency_service_free(RingEmergencyService *service); RingEmergencyServiceInfo *ring_emergency_service_info_new(char const *service, char * const *aliases); void ring_emergency_service_info_free(RingEmergencyServiceInfo *service); RingEmergencyServiceInfoList *ring_emergency_service_info_list_new( RingEmergencyServiceInfo *, ...) G_GNUC_NULL_TERMINATED; void ring_emergency_service_info_list_free(RingEmergencyServiceInfoList *); RingEmergencyServiceInfoList *ring_emergency_service_info_list_default( char * const *numbers); G_END_DECLS #endif /* #ifndef RING_EMERGENCY_SERVICE_H*/ telepathy-ring-2.3.24-37ad7111424725a5446e82dac516d7cab88149c7/src/ring-media-channel.c000066400000000000000000001237701251541261300263430ustar00rootroot00000000000000/* * ring-media-channel.c - Source for RingMediaChannel * * Copyright (C) 2007-2010 Nokia Corporation * @author Pekka Pessi * Copyright (C) 2013 Jolla Ltd * * This work is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This work is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this work; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ /* * Based on telepathy-glib/examples/cm/echo/chan.c: * * """ * Copyright (C) 2007 Collabora Ltd. * Copyright (C) 2007 Nokia Corporation * * Copying and distribution of this file, with or without modification, * are permitted in any medium without royalty provided the copyright * notice and this notice are preserved. * """ */ #include "config.h" #define DEBUG_FLAG RING_DEBUG_MEDIA #include "ring-debug.h" #include "ring-media-channel.h" #include "ring-util.h" #include "modem/call.h" #include "modem/errors.h" #include "modem/tones.h" #include #include #include #include #include #include #include #include #include #include #include "ring-connection.h" #include "ring-param-spec.h" #include #include #include #include #include struct _RingMediaChannelPrivate { GQueue requests[1]; /* Requests towards the modem */ uint8_t state; struct { uint8_t state; uint8_t reason; uint8_t requested; /* Hold state requested by client */ } hold; unsigned initial_audio:1; /* property */ unsigned disposed:1; unsigned closing:1; unsigned :0; ModemRequest *control; guint playing; ModemTones *tones; guint close_timer; struct { ModemRequest *request; guchar digit; } dtmf; struct { char *string; /* Dialstring */ unsigned playing:1, canceled:1, stopped:1, :0; } dial; struct { gulong state, terminated; gulong dtmf_tone, dialstring; } signals; }; /* properties */ enum { PROP_NONE, /* DBUs properties */ PROP_HOLD_STATE, /* o.f.T.Channel.Interface.Hold */ PROP_HOLD_REASON, /* o.f.T.Channel.Interface.Hold */ PROP_INITIAL_AUDIO, /* o.f.T.Ch.T.StreamedMedia.InitialAudio */ PROP_INITIAL_VIDEO, /* o.f.T.Ch.T.StreamedMedia.InitialVideo */ PROP_IMMUTABLE_STREAMS, /* o.f.T.Ch.T.StreamedMedia.ImmutableStreams */ /* ring-specific properties */ PROP_PEER, PROP_CALL_INSTANCE, PROP_TONES, LAST_PROPERTY }; static void ring_media_channel_fill_immutable_properties(TpBaseChannel *base, GHashTable *props); static void ring_media_channel_dtmf_iface_init(gpointer, gpointer); static void ring_channel_hold_iface_init(gpointer, gpointer); #if nomore static void ring_channel_dial_strings_iface_init(gpointer, gpointer); #endif G_DEFINE_TYPE_WITH_CODE( RingMediaChannel, ring_media_channel, TP_TYPE_BASE_CHANNEL, G_IMPLEMENT_INTERFACE(TP_TYPE_SVC_DBUS_PROPERTIES, tp_dbus_properties_mixin_iface_init); G_IMPLEMENT_INTERFACE(TP_TYPE_SVC_CHANNEL_INTERFACE_DTMF, ring_media_channel_dtmf_iface_init); G_IMPLEMENT_INTERFACE(TP_TYPE_SVC_CHANNEL_INTERFACE_HOLD, ring_channel_hold_iface_init); #if nomore /* XXX: waiting for upstream tp-glib to provide a similar interface */ G_IMPLEMENT_INTERFACE(RTCOM_TYPE_TP_SVC_CHANNEL_INTERFACE_DIAL_STRINGS, ring_channel_dial_strings_iface_init); #endif G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CHANNEL_TYPE_STREAMED_MEDIA, ring_streamed_media_mixin_iface_init); ); static void ring_media_channel_set_call_instance(RingMediaChannel *self, ModemCall *ci); static void on_modem_call_state(ModemCall *, ModemCallState, RingMediaChannel *); static void on_modem_call_state_dialing(RingMediaChannel *self); static void on_modem_call_state_incoming(RingMediaChannel *self); static void on_modem_call_state_waiting(RingMediaChannel *self); static void on_modem_call_state_mo_alerting(RingMediaChannel *self); #if nomore static void on_modem_call_state_mt_alerting(RingMediaChannel *self); static void on_modem_call_state_answered(RingMediaChannel *self); #endif static void on_modem_call_state_active(RingMediaChannel *self); #if nomore static void on_modem_call_state_hold_initiated(RingMediaChannel *self); #endif static void on_modem_call_state_held(RingMediaChannel *self); #if nomore static void on_modem_call_state_retrieve_initiated(RingMediaChannel *self); #endif static void on_modem_call_state_release(RingMediaChannel *); #if nomore static void on_modem_call_state_terminated(RingMediaChannel *); #endif static void on_modem_call_terminated(ModemCall *, RingMediaChannel *); static void on_modem_call_dtmf_tone(ModemCall *call_instance, gint tone, RingMediaChannel *self); static void on_modem_call_dialstring(ModemCall *, char const *, RingMediaChannel *); /* ====================================================================== */ /* GObject interface */ static void ring_media_channel_init(RingMediaChannel *self) { self->priv = G_TYPE_INSTANCE_GET_PRIVATE( self, RING_TYPE_MEDIA_CHANNEL, RingMediaChannelPrivate); self->priv->hold.requested = -1; ring_streamed_media_mixin_init (G_OBJECT(self), G_STRUCT_OFFSET(RingMediaChannel, streamed_media)); } static void ring_media_channel_constructed(GObject *object) { RingMediaChannel *self = RING_MEDIA_CHANNEL(object); TpBaseChannel *base = TP_BASE_CHANNEL (self); const gchar *object_path; if (G_OBJECT_CLASS(ring_media_channel_parent_class)->constructed) G_OBJECT_CLASS(ring_media_channel_parent_class)->constructed(object); object_path = tp_base_channel_get_object_path (base); g_assert(object_path != NULL); self->nick = strrchr(object_path, '/'); g_assert (self->nick++ != NULL); DEBUG("(%p) with %s", self, self->nick); tp_base_channel_register (base); } static void ring_media_channel_get_property(GObject *obj, guint property_id, GValue *value, GParamSpec *pspec) { RingMediaChannel *self = RING_MEDIA_CHANNEL(obj); RingMediaChannelPrivate *priv = self->priv; switch (property_id) { case PROP_PEER: g_value_set_uint(value, 0); break; case PROP_HOLD_STATE: g_value_set_uint(value, priv->hold.state); break; case PROP_HOLD_REASON: g_value_set_uint(value, priv->hold.reason); break; case PROP_INITIAL_AUDIO: g_value_set_boolean(value, priv->initial_audio); break; case PROP_INITIAL_VIDEO: g_value_set_boolean(value, FALSE); break; case PROP_IMMUTABLE_STREAMS: g_value_set_boolean(value, TRUE); break; case PROP_CALL_INSTANCE: g_value_set_pointer(value, self->call_instance); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, property_id, pspec); break; } } static void ring_media_channel_set_property(GObject *obj, guint property_id, const GValue *value, GParamSpec *pspec) { RingMediaChannel *self = RING_MEDIA_CHANNEL(obj); RingMediaChannelPrivate *priv = self->priv; switch (property_id) { case PROP_PEER: /* these property is writable in the interface, but not actually * meaningfully changable on this channel, so we do nothing */ break; case PROP_HOLD_STATE: priv->hold.state = g_value_get_uint(value); break; case PROP_HOLD_REASON: priv->hold.reason = g_value_get_uint(value); break; case PROP_INITIAL_AUDIO: priv->initial_audio = g_value_get_boolean(value); break; case PROP_CALL_INSTANCE: ring_media_channel_set_call_instance (self, g_value_get_pointer (value)); break; case PROP_TONES: /* media manager owns tones as well as a reference to this channel */ priv->tones = g_value_get_object(value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, property_id, pspec); break; } } static void ring_media_channel_dispose(GObject *object) { RingMediaChannel *self = RING_MEDIA_CHANNEL (object); RingMediaChannelPrivate *priv = self->priv; if (self->priv->disposed) return; self->priv->disposed = TRUE; ring_media_channel_close(self); if (priv->close_timer) g_source_remove(priv->close_timer), priv->close_timer = 0; if (priv->playing) modem_tones_stop(priv->tones, priv->playing); /* if still holding on to a call instance, disconnect */ if (self->call_instance) g_object_set(self, "call-instance", NULL, NULL); ((GObjectClass *)ring_media_channel_parent_class)->dispose(object); } static void ring_media_channel_finalize(GObject *object) { RingMediaChannel *self = RING_MEDIA_CHANNEL(object); RingMediaChannelPrivate *priv = self->priv; gchar *nick = g_strdup (self->nick); ring_streamed_media_mixin_finalize (object); g_free(priv->dial.string); G_OBJECT_CLASS(ring_media_channel_parent_class)->finalize(object); DEBUG("(%p) on %s", object, nick); g_free(nick); } /* ====================================================================== */ /* GObjectClass */ static void ring_media_channel_class_init(RingMediaChannelClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS(klass); TpBaseChannelClass *base_chan_class = TP_BASE_CHANNEL_CLASS (klass); g_type_class_add_private(klass, sizeof (RingMediaChannelPrivate)); object_class->constructed = ring_media_channel_constructed; object_class->get_property = ring_media_channel_get_property; object_class->set_property = ring_media_channel_set_property; object_class->dispose = ring_media_channel_dispose; object_class->finalize = ring_media_channel_finalize; base_chan_class->channel_type = TP_IFACE_CHANNEL_TYPE_STREAMED_MEDIA; base_chan_class->close = (TpBaseChannelCloseFunc) ring_media_channel_close; base_chan_class->fill_immutable_properties = ring_media_channel_fill_immutable_properties; g_object_class_install_property( object_class, PROP_HOLD_STATE, g_param_spec_uint("hold-state", "Hold State", "The hold state of the channel.", TP_LOCAL_HOLD_STATE_UNHELD, TP_LOCAL_HOLD_STATE_PENDING_UNHOLD, TP_LOCAL_HOLD_STATE_UNHELD, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property( object_class, PROP_HOLD_REASON, g_param_spec_uint("hold-state-reason", "Hold State Reason", "The reason for the hold state of the channel.", TP_LOCAL_HOLD_STATE_REASON_NONE, TP_LOCAL_HOLD_STATE_REASON_RESOURCE_NOT_AVAILABLE, TP_LOCAL_HOLD_STATE_REASON_NONE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property( object_class, PROP_PEER, g_param_spec_uint("peer", "Peer handle", "Peer handle for this channel", 0, G_MAXUINT, 0, G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS)); g_object_class_install_property( object_class, PROP_INITIAL_AUDIO, g_param_spec_boolean( "initial-audio", "InitialAudio", "True if the audio stream was requested when channel was created.", FALSE, G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property( object_class, PROP_INITIAL_VIDEO, g_param_spec_boolean( "initial-video", "InitialVideo", "True if the video stream was requested when channel was created.", FALSE, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property( object_class, PROP_IMMUTABLE_STREAMS, g_param_spec_boolean("immutable-streams", "ImmutableStreams", "True if the video stream was requested when channel was created.", TRUE, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (object_class, PROP_CALL_INSTANCE, g_param_spec_pointer ("call-instance", "ModemCall Object", "ModemCall instance for this channel", /* MODEM_TYPE_CALL, */ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property( object_class, PROP_TONES, g_param_spec_object("tones", "ModemTones Object", "ModemTones for this channel", MODEM_TYPE_TONES, G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); } /* ====================================================================== */ /** * org.freedesktop.DBus properties */ static void ring_media_channel_fill_immutable_properties (TpBaseChannel *base, GHashTable *props) { TP_BASE_CHANNEL_CLASS (ring_media_channel_parent_class)-> fill_immutable_properties (base, props); ring_streamed_media_mixin_fill_immutable_properties (base, props); } /* ====================================================================== */ ModemCallService * ring_media_channel_get_call_service (RingMediaChannel *self) { TpBaseChannel *base = TP_BASE_CHANNEL (self); TpBaseConnection *base_connection; RingConnection *connection; ModemOface *oface; base_connection = tp_base_channel_get_connection (base); connection = RING_CONNECTION (base_connection); oface = ring_connection_get_modem_interface (connection, MODEM_OFACE_CALL_MANAGER); if (oface) return MODEM_CALL_SERVICE (oface); else return NULL; } static gboolean ring_media_channel_emit_closed(RingMediaChannel *self); void ring_media_channel_emit_initial(RingMediaChannel *_self) { RingMediaChannel *self = RING_MEDIA_CHANNEL(_self); RingMediaChannelPrivate *priv = self->priv; RING_MEDIA_CHANNEL_GET_CLASS(self)->emit_initial(self); if (priv->initial_audio) { ring_streamed_media_mixin_update_audio (self, 0, TP_MEDIA_STREAM_STATE_CONNECTING, TP_MEDIA_STREAM_DIRECTION_NONE, TP_MEDIA_STREAM_PENDING_LOCAL_SEND | TP_MEDIA_STREAM_PENDING_REMOTE_SEND); } } ModemRequest * ring_media_channel_queue_request (RingMediaChannel *self, ModemRequest *request) { if (request) g_queue_push_tail(self->priv->requests, request); return request; } ModemRequest * ring_media_channel_dequeue_request(RingMediaChannel *self, ModemRequest *request) { if (request) g_queue_remove(self->priv->requests, request); return request; } void ring_media_channel_close(RingMediaChannel *self) { RingMediaChannelPrivate *priv = self->priv; RingMediaChannelClass *cls = RING_MEDIA_CHANNEL_GET_CLASS(self); gboolean ready = TRUE; if (tp_base_channel_is_destroyed (TP_BASE_CHANNEL (self))) return; if (priv->closing) return; priv->closing = TRUE; if (priv->playing) modem_tones_stop(priv->tones, priv->playing); while (!g_queue_is_empty(priv->requests)) modem_request_cancel(g_queue_pop_head(priv->requests)); if (cls->close) ready = cls->close(self, FALSE); if (ready && self->call_instance) g_object_set(self, "call-instance", NULL, NULL); if (!priv->playing && ready && !self->call_instance) { ring_media_channel_emit_closed(self); } else if (!priv->close_timer) { priv->close_timer = g_timeout_add(32000, (GSourceFunc)ring_media_channel_emit_closed, self); } } static gboolean ring_media_channel_emit_closed(RingMediaChannel *self) { RingMediaChannelPrivate *priv = self->priv; TpBaseChannel *base = TP_BASE_CHANNEL (self); RingMediaChannelClass *cls = RING_MEDIA_CHANNEL_GET_CLASS(self); if (priv->close_timer) g_source_remove(priv->close_timer), priv->close_timer = 0; if (tp_base_channel_is_destroyed (TP_BASE_CHANNEL (self))) return FALSE; if (priv->playing) modem_tones_stop(priv->tones, priv->playing); priv->playing = 0; cls->close(self, TRUE); if (self->call_instance) g_object_set(self, "call-instance", NULL, NULL); tp_base_channel_destroyed (base); DEBUG("emitted Closed on %s", self->nick); return FALSE; } /* ====================================================================== */ /* * Telepathy.Channel.Interface.DTMF DBus interface - version 0.15 */ #ifdef nomore static void ring_sending_dial_string(RingMediaChannel *, char const *); #endif static ModemCallReply ring_media_channel_dtmf_start_tone_replied; static ModemCallReply ring_media_channel_dtmf_stop_tone_replied; static char const ring_media_channel_dtmf_events[16] = "0123456789*#ABCD"; /** DBus method StartTone ( u: stream_id, y: event ) -> nothing * * Start sending a DTMF tone on this stream. Where possible, the tone will * continue until StopTone is called. On certain protocols, it may only be * possible to send events with a predetermined length. In this case, the * implementation may emit a fixed-length tone, and the StopTone method call * should return NotAvailable. */ void ring_media_channel_dtmf_start_tone(TpSvcChannelInterfaceDTMF *iface, guint stream_id, guchar event, DBusGMethodInvocation *context) { RingMediaChannel *self = RING_MEDIA_CHANNEL(iface); RingMediaChannelPrivate *priv = self->priv; GError *error = NULL; char const *events = ring_media_channel_dtmf_events; DEBUG("(%u, %u) on %s", stream_id, event, self->nick); if (stream_id == 0 /* XXXX || priv->audio->id != stream_id */) { g_set_error(&error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT, "invalid stream id %u", stream_id); } else if (event >= 16) { g_set_error(&error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT, "event %u is not a known DTMF event", event); } else if (self->call_instance == NULL) { g_set_error(&error, TP_ERROR, TP_ERROR_DISCONNECTED, "Channel is not connected"); } else { ModemRequest *request; if (priv->dtmf.digit || priv->dtmf.request || (priv->dial.string && priv->dial.string[0])) { request = modem_call_stop_dtmf(self->call_instance, ring_media_channel_dtmf_stop_tone_replied, self); ring_media_channel_queue_request(self, request); } #if nomore if (!priv->dial.string) ring_sending_dial_string(self, ""); #endif request = modem_call_start_dtmf(self->call_instance, events[event], ring_media_channel_dtmf_start_tone_replied, self); if (request) { priv->dtmf.request = request; modem_request_add_data_full(request, "tp-request", context, ring_method_return_internal_error); modem_request_add_data(request, "StartTone", GINT_TO_POINTER((gint)events[event])); ring_media_channel_queue_request(self, request); return; } g_set_error(&error, TP_ERROR, TP_ERROR_NETWORK_ERROR, "Failed to send request to modem"); } dbus_g_method_return_error(context, error); g_error_free(error); } static void ring_media_channel_dtmf_start_tone_replied(ModemCall *call_instance, ModemRequest *request, GError *error, gpointer _self) { RingMediaChannel *self = RING_MEDIA_CHANNEL(_self); RingMediaChannelPrivate *priv = self->priv; gpointer context = modem_request_steal_data(request, "tp-request"); ring_media_channel_dequeue_request(self, request); if (priv->dtmf.request == request) priv->dtmf.request = NULL; if (!error) { guchar dtmf = GPOINTER_TO_INT(modem_request_get_data(request, "StartTone")); char const *event = strchr(ring_media_channel_dtmf_events, dtmf); priv->dtmf.digit = dtmf; #if 0 /* No local DTMF playback (handled by UI) */ if (event) { ring_media_channel_play_tone(self, event - ring_media_channel_dtmf_events, -6, 20000); } #endif tp_svc_channel_interface_dtmf_return_from_start_tone(context); } else { dbus_g_method_return_error(context, error); } } /** DBus method StopTone ( u: stream_id ) -> nothing * * Stop sending any DTMF tone which has been started using the StartTone * method. If there is no current tone, this method will do nothing. */ void ring_media_channel_dtmf_stop_tone(TpSvcChannelInterfaceDTMF *iface, guint stream_id, DBusGMethodInvocation *context) { RingMediaChannel *self = RING_MEDIA_CHANNEL(iface); GError *error = NULL; DEBUG("(%u) on %s", stream_id, self->nick); if (ring_streamed_media_mixin_is_audio_stream (iface, stream_id)) { g_set_error(&error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT, "invalid stream id %u", stream_id); } else if (self->call_instance == NULL) { g_set_error(&error, TP_ERROR, TP_ERROR_DISCONNECTED, "Channel is not connected"); } else { ModemRequest *request; request = modem_call_stop_dtmf(self->call_instance, ring_media_channel_dtmf_stop_tone_replied, self); if (request) { modem_request_add_data_full(request, "tp-request", context, ring_method_return_internal_error); ring_media_channel_queue_request(self, request); return; } g_set_error(&error, TP_ERROR, TP_ERROR_NETWORK_ERROR, "Failed to send request to modem"); } dbus_g_method_return_error(context, error); g_error_free(error); } static void ring_media_channel_dtmf_stop_tone_replied(ModemCall *call_instance, ModemRequest *request, GError *error, gpointer _self) { RingMediaChannel *self = RING_MEDIA_CHANNEL(_self); RingMediaChannelPrivate *priv = self->priv; gpointer context = modem_request_steal_data(request, "tp-request"); ring_media_channel_dequeue_request(self, request); priv->dtmf.digit = 0; ring_media_channel_stop_playing(self, FALSE); if (!context) return; if (!error) { tp_svc_channel_interface_dtmf_return_from_stop_tone(context); } else { dbus_g_method_return_error(context, error); } } static void ring_media_channel_dtmf_iface_init(gpointer g_iface, gpointer iface_data) { TpSvcChannelInterfaceDTMFClass *klass = (TpSvcChannelInterfaceDTMFClass *)g_iface; #define IMPLEMENT(x) tp_svc_channel_interface_dtmf_implement_##x( \ klass, ring_media_channel_dtmf_##x) IMPLEMENT(start_tone); IMPLEMENT(stop_tone); #undef IMPLEMENT } /* ---------------------------------------------------------------------- */ /* Implement org.freedesktop.Telepathy.Channel.Interface.Hold */ static int ring_update_hold (RingMediaChannel *self, int hold, int reason); static void get_hold_state(TpSvcChannelInterfaceHold *iface, DBusGMethodInvocation *context) { RingMediaChannel *self = RING_MEDIA_CHANNEL(iface); RingMediaChannelPrivate *priv = self->priv; if (self->call_instance == NULL) { GError *error = NULL; g_set_error(&error, TP_ERROR, TP_ERROR_DISCONNECTED, "Channel is not connected"); dbus_g_method_return_error(context, error); g_error_free(error); } else { tp_svc_channel_interface_hold_return_from_get_hold_state (context, priv->hold.state, priv->hold.reason); } } static ModemCallReply response_to_hold; static void request_hold (TpSvcChannelInterfaceHold *iface, gboolean hold, DBusGMethodInvocation *context) { RingMediaChannel *self = RING_MEDIA_CHANNEL (iface); RingMediaChannelPrivate *priv = self->priv; ModemCall *instance = self->call_instance; GError *error = NULL; uint8_t state, next; ModemCallState expect; DEBUG ("(%u) on %s", hold, self->nick); if (hold) { expect = MODEM_CALL_STATE_ACTIVE; state = TP_LOCAL_HOLD_STATE_HELD; next = TP_LOCAL_HOLD_STATE_PENDING_HOLD; } else { expect = MODEM_CALL_STATE_HELD; state = TP_LOCAL_HOLD_STATE_UNHELD; next = TP_LOCAL_HOLD_STATE_PENDING_UNHOLD; } if (instance == NULL) { g_set_error(&error, TP_ERROR, TP_ERROR_DISCONNECTED, "Channel is not connected"); } else if (state == priv->hold.state || next == priv->hold.state) { priv->hold.reason = TP_LOCAL_HOLD_STATE_REASON_REQUESTED; tp_svc_channel_interface_hold_return_from_request_hold(context); return; } else if (priv->state != expect) { g_set_error (&error, TP_ERROR, TP_ERROR_NOT_AVAILABLE, "Invalid call state %s", modem_call_get_state_name(priv->state)); } else if (priv->control) { g_set_error (&error, TP_ERROR, TP_ERROR_NOT_AVAILABLE, "Call control operation pending"); } else { tp_svc_channel_interface_hold_return_from_request_hold(context); g_object_ref (self); priv->control = modem_call_request_hold (instance, hold, response_to_hold, self); ring_media_channel_queue_request (self, priv->control); priv->hold.requested = state; ring_update_hold (self, next, TP_LOCAL_HOLD_STATE_REASON_REQUESTED); return; } DEBUG ("request_hold(%u) on %s: %s", hold, self->nick, error->message); dbus_g_method_return_error (context, error); g_clear_error (&error); } static void response_to_hold (ModemCall *ci, ModemRequest *request, GError *error, gpointer _self) { RingMediaChannel *self = RING_MEDIA_CHANNEL (_self); RingMediaChannelPrivate *priv = self->priv; if (priv->control == request) priv->control = NULL; ring_media_channel_dequeue_request (self, request); if (error && priv->hold.requested != -1) { uint8_t next; DEBUG ("%s: %s", self->nick, error->message); if (priv->hold.requested) next = TP_LOCAL_HOLD_STATE_UNHELD; else next = TP_LOCAL_HOLD_STATE_HELD; ring_update_hold (self, next, TP_LOCAL_HOLD_STATE_REASON_RESOURCE_NOT_AVAILABLE); priv->hold.requested = -1; } ring_update_hold(self, priv->hold.requested, 0); g_object_unref (self); } static void ring_channel_hold_iface_init(gpointer g_iface, gpointer iface_data) { TpSvcChannelInterfaceHoldClass *klass = g_iface; #define IMPLEMENT(x) tp_svc_channel_interface_hold_implement_##x( \ klass, x) IMPLEMENT(get_hold_state); IMPLEMENT(request_hold); #undef IMPLEMENT } static int ring_update_hold (RingMediaChannel *self, int hold, int reason) { RingMediaChannelPrivate *priv = self->priv; unsigned old = priv->hold.state; char const *name; if (hold == old) return 0; switch (hold) { case TP_LOCAL_HOLD_STATE_UNHELD: name = "Unheld"; if (reason) ; else if (hold == priv->hold.requested) reason = TP_LOCAL_HOLD_STATE_REASON_REQUESTED; else if (old == TP_LOCAL_HOLD_STATE_PENDING_HOLD) reason = TP_LOCAL_HOLD_STATE_REASON_RESOURCE_NOT_AVAILABLE; else reason = TP_LOCAL_HOLD_STATE_REASON_NONE; priv->hold.requested = -1; break; case TP_LOCAL_HOLD_STATE_HELD: name = "Held"; if (reason) ; else if (hold == priv->hold.requested) reason = TP_LOCAL_HOLD_STATE_REASON_REQUESTED; else if (old == TP_LOCAL_HOLD_STATE_PENDING_UNHOLD) reason = TP_LOCAL_HOLD_STATE_REASON_RESOURCE_NOT_AVAILABLE; else reason = TP_LOCAL_HOLD_STATE_REASON_NONE; priv->hold.requested = -1; break; case TP_LOCAL_HOLD_STATE_PENDING_HOLD: name = "Pending_Hold"; break; case TP_LOCAL_HOLD_STATE_PENDING_UNHOLD: name = "Pending_Unhold"; break; default: name = "Unknown"; DEBUG("unknown %s(%d)", "HoldStateChanged", hold); return -1; } g_object_set(self, "hold-state", hold, "hold-state-reason", reason, NULL); DEBUG("emitting %s(%s) for %s", "HoldStateChanged", name, self->nick); tp_svc_channel_interface_hold_emit_hold_state_changed( (TpSvcChannelInterfaceHold *)self, hold, reason); return 0; } /* ---------------------------------------------------------------------- */ /* Implement com.Nokia.Telepathy.Channel.Interface.DialStrings */ #if nomore static void ring_emit_stopped_dial_string(RingMediaChannel *self); static void get_current_dial_strings(RTComTpSvcChannelInterfaceDialStrings *iface, DBusGMethodInvocation *context) { RingMediaChannel *self = RING_MEDIA_CHANNEL(iface); RingMediaChannelPrivate *priv = self->priv; GHashTable *dialstrings = g_hash_table_new (NULL, NULL); if (priv->dial.string) { g_hash_table_replace(dialstrings, GUINT_TO_POINTER(priv->audio->id), priv->dial.string); } rtcom_tp_svc_channel_interface_dial_strings_return_from_get_current_dial_strings (context, dialstrings); g_hash_table_destroy(dialstrings); } static void send_dial_string(RTComTpSvcChannelInterfaceDialStrings *iface, guint id, char const *dialstring, guint duration, guint pause, DBusGMethodInvocation *context) { RingMediaChannel *self = RING_MEDIA_CHANNEL(iface); GError *error = NULL; if (ring_media_channel_send_dialstring(self, id, dialstring, duration, pause, &error)) { rtcom_tp_svc_channel_interface_dial_strings_return_from_send_dial_string(context); return; } dbus_g_method_return_error(context, error); g_error_free(error); } static void cancel_dial_string(RTComTpSvcChannelInterfaceDialStrings *iface, guint id, DBusGMethodInvocation *context) { RingMediaChannel *self = RING_MEDIA_CHANNEL(iface); RingMediaChannelPrivate *priv = self->priv; GError *error = NULL; DEBUG("(%u) for %s", id, self->nick); if (id != priv->audio->id) { g_set_error(&error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT, "Invalid stream"); } else if (priv->audio->state != TP_MEDIA_STREAM_STATE_CONNECTED) { g_set_error(&error, TP_ERROR, TP_ERROR_NOT_AVAILABLE, "Stream is not connected"); } else if (!priv->dial.string) { g_set_error(&error, TP_ERROR, TP_ERROR_NOT_AVAILABLE, "Already sending a dial string"); } else if (modem_call_stop_dtmf(self->call_instance, NULL, NULL) < 0) { g_set_error(&error, TP_ERROR, TP_ERROR_NOT_AVAILABLE, "Channel error"); } else { priv->dial.canceled = TRUE; rtcom_tp_svc_channel_interface_dial_strings_return_from_cancel_dial_string(context); return; } dbus_g_method_return_error(context, error); g_error_free(error); } static void ring_channel_dial_strings_iface_init(gpointer g_iface, gpointer iface_data) { RTComTpSvcChannelInterfaceDialStringsClass *klass = g_iface; #define IMPLEMENT(x) rtcom_tp_svc_channel_interface_dial_strings_implement_##x( \ klass, x) IMPLEMENT(get_current_dial_strings); IMPLEMENT(send_dial_string); IMPLEMENT(cancel_dial_string); #undef IMPLEMENT } static void ring_sending_dial_string(RingMediaChannel *self, char const *dialstring) { RingMediaChannelPrivate *priv = self->priv; char *old; unsigned id = priv->audio->id; if (priv->dial.string && !priv->dial.stopped && !priv->dial.canceled) { if (strcmp(dialstring, priv->dial.string) == 0) { DEBUG("avoid emitting duplicate %s(%u, %s) for %s", "SendingDialString", id, dialstring, self->nick); return; } } DEBUG("emitting %s(%u, %s) for %s", "SendingDialString", id, dialstring, self->nick); rtcom_tp_svc_channel_interface_dial_strings_emit_sending_dial_string( (RTComTpSvcChannelInterfaceDialStrings *)self, id, dialstring); old = priv->dial.string; priv->dial.string = g_strdup(dialstring); priv->dial.stopped = FALSE; priv->dial.canceled = FALSE; g_free(old); } static int ring_stopped_dial_string(RingMediaChannel *self, int canceled) { RingMediaChannelPrivate *priv = self->priv; priv->dial.stopped = TRUE; priv->dial.canceled |= canceled != 0; if (!priv->dial.playing) ring_emit_stopped_dial_string(self); return 0; } static void ring_emit_stopped_dial_string(RingMediaChannel *self) { RingMediaChannelPrivate *priv = self->priv; char *old = priv->dial.string; unsigned id = priv->audio->id; if (old) { gboolean canceled = priv->dial.canceled; DEBUG("emitting %s(%u, %s) for %s", "StoppedDialString", id, canceled ? "True" : "False", self->nick); rtcom_tp_svc_channel_interface_dial_strings_emit_stopped_dial_string( (RTComTpSvcChannelInterfaceDialStrings *)self, id, canceled); g_free(old); } else { DEBUG("AVOID EMITTING StoppedDialString for %s", self->nick); } priv->dial.string = NULL; priv->dial.canceled = FALSE; priv->dial.stopped = FALSE; } #endif gboolean ring_media_channel_send_dialstring(RingMediaChannel *self, guint id, char const *dialstring, guint duration, guint pause, GError **error) { RingMediaChannelPrivate *priv = self->priv; DEBUG("(%u, \"%s\", %u, %u) for %s", id, dialstring, duration, pause, self->nick); (void)duration; (void)pause; if (ring_streamed_media_mixin_is_audio_stream (self, id)) { g_set_error(error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT, "Invalid stream"); return FALSE; } else if (self->call_instance == NULL || !ring_streamed_media_mixin_is_stream_connected (self, id)) { g_set_error(error, TP_ERROR, TP_ERROR_NOT_AVAILABLE, "Channel is not connected"); return FALSE; } else if (priv->dial.string) { g_set_error(error, TP_ERROR, TP_ERROR_NOT_AVAILABLE, "Already sending a dial string"); return FALSE; } else if (modem_call_send_dtmf(self->call_instance, dialstring, NULL, NULL) < 0) { g_set_error(error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT, "Bad dial string"); return FALSE; } else { priv->dial.canceled = FALSE; return TRUE; } } /* ====================================================================== */ /* Signals from ModemCall */ static void ring_media_channel_set_call_instance (RingMediaChannel *self, ModemCall *ci) { RingMediaChannelPrivate *priv = self->priv; ModemCall *old = self->call_instance; if (ci == old) return; if (ci) { modem_call_set_handler(self->call_instance = MODEM_CALL (ci), self); #define CONNECT(n, f) \ g_signal_connect(ci, n, G_CALLBACK(on_modem_call_ ## f), self) priv->signals.state = CONNECT("state", state); priv->signals.terminated = CONNECT("terminated", terminated); priv->signals.dtmf_tone = CONNECT("dtmf-tone", dtmf_tone); priv->signals.dialstring = CONNECT("dialstring", dialstring); #undef CONNECT } else { ModemCall *old = self->call_instance; modem_call_set_handler(old, NULL); #define DISCONNECT(n) \ if (priv->signals.n && \ g_signal_handler_is_connected(old, priv->signals.n)) { \ g_signal_handler_disconnect(old, priv->signals.n); \ } (priv->signals.n = 0) DISCONNECT(state); DISCONNECT(terminated); DISCONNECT(dtmf_tone); DISCONNECT(dialstring); #undef DISCONNECT } RING_MEDIA_CHANNEL_GET_CLASS(self)->set_call_instance(self, ci); if (old) g_object_unref (old); self->call_instance = ci; if (ci) g_object_ref (ci); if (ci == NULL && !priv->playing) ring_media_channel_close(self); } static void on_modem_call_state(ModemCall *ci, ModemCallState state, RingMediaChannel *self) { guint causetype = 0; guint cause = 0; if (state == MODEM_CALL_STATE_DISCONNECTED) { GValue causetype_value = G_VALUE_INIT; g_value_init (&causetype_value, G_TYPE_UINT); g_object_get_property(ci, "causetype", &causetype_value); causetype = g_value_get_uint(&causetype_value); GValue cause_value = G_VALUE_INIT; g_value_init (&cause_value, G_TYPE_UINT); g_object_get_property(ci, "cause", &cause_value); cause = g_value_get_uint(&cause_value); DEBUG("Call disconnected: causetype=%d, cause=%d", causetype, cause); } ring_media_channel_set_state( RING_MEDIA_CHANNEL(self), state, causetype, cause); } void ring_media_channel_set_state(RingMediaChannel *self, guint state, guint causetype, guint cause) { self->priv->state = state; switch (state) { case MODEM_CALL_STATE_DIALING: on_modem_call_state_dialing(self); break; case MODEM_CALL_STATE_INCOMING: on_modem_call_state_incoming(self); break; case MODEM_CALL_STATE_ALERTING: on_modem_call_state_mo_alerting(self); break; case MODEM_CALL_STATE_WAITING: on_modem_call_state_waiting(self); break; case MODEM_CALL_STATE_ACTIVE: on_modem_call_state_active(self); break; case MODEM_CALL_STATE_DISCONNECTED: on_modem_call_state_release(self); break; case MODEM_CALL_STATE_HELD: on_modem_call_state_held(self); break; default: break; } RING_MEDIA_CHANNEL_GET_CLASS(self)->update_state(self, state, causetype, cause); } static void on_modem_call_state_incoming(RingMediaChannel *self) { ring_streamed_media_mixin_update_audio (self, 0, TP_MEDIA_STREAM_STATE_CONNECTING, TP_MEDIA_STREAM_DIRECTION_NONE, TP_MEDIA_STREAM_PENDING_LOCAL_SEND | TP_MEDIA_STREAM_PENDING_REMOTE_SEND); } static void on_modem_call_state_dialing(RingMediaChannel *self) { ring_streamed_media_mixin_update_audio (self, 0, TP_MEDIA_STREAM_STATE_CONNECTING, TP_MEDIA_STREAM_DIRECTION_NONE, TP_MEDIA_STREAM_PENDING_LOCAL_SEND | TP_MEDIA_STREAM_PENDING_REMOTE_SEND); } static void on_modem_call_state_mo_alerting(RingMediaChannel *self) { ring_streamed_media_mixin_update_audio (self, 0, TP_MEDIA_STREAM_STATE_CONNECTED, TP_MEDIA_STREAM_DIRECTION_RECEIVE, TP_MEDIA_STREAM_PENDING_LOCAL_SEND); } #if nomore static void on_modem_call_state_mt_alerting(RingMediaChannel *self) { /* Audio has been connected - at least locally */ ring_streamed_media_mixin_update_audio (self, 0, TP_MEDIA_STREAM_STATE_CONNECTED, TP_MEDIA_STREAM_DIRECTION_SEND, TP_MEDIA_STREAM_PENDING_REMOTE_SEND); } #endif static void on_modem_call_state_waiting(RingMediaChannel *self) { /* Audio has been connected - at least locally */ ring_streamed_media_mixin_update_audio (self, 0, TP_MEDIA_STREAM_STATE_CONNECTED, TP_MEDIA_STREAM_DIRECTION_NONE, TP_MEDIA_STREAM_PENDING_LOCAL_SEND | TP_MEDIA_STREAM_PENDING_REMOTE_SEND); } #if nomore static void on_modem_call_state_answered(RingMediaChannel *self) { /* Call has been answered we might not have radio channel */ ring_streamed_media_mixin_update_audio (self, 0, TP_MEDIA_STREAM_STATE_CONNECTED, TP_MEDIA_STREAM_DIRECTION_SEND, TP_MEDIA_STREAM_PENDING_REMOTE_SEND); } #endif static void on_modem_call_state_active(RingMediaChannel *self) { /* Call should be active now and media channels open. */ ring_streamed_media_mixin_update_audio (self, 0, TP_MEDIA_STREAM_STATE_CONNECTED, TP_MEDIA_STREAM_DIRECTION_BIDIRECTIONAL, 0); ring_update_hold(self, TP_LOCAL_HOLD_STATE_UNHELD, 0); } static void on_modem_call_state_held(RingMediaChannel *self) { ring_streamed_media_mixin_update_audio (self, 0, TP_MEDIA_STREAM_STATE_CONNECTED, TP_MEDIA_STREAM_DIRECTION_NONE, 0); ring_update_hold(self, TP_LOCAL_HOLD_STATE_HELD, 0); } static void on_modem_call_state_release(RingMediaChannel *self) { } #if nomore static void on_modem_call_state_terminated(RingMediaChannel *self) { ring_streamed_media_mixin_update_audio (self, 0, TP_MEDIA_STREAM_STATE_DISCONNECTED, TP_MEDIA_STREAM_DIRECTION_NONE, 0); } #endif static void on_modem_call_terminated(ModemCall *ci, RingMediaChannel *_self) { g_object_set(RING_MEDIA_CHANNEL(_self), "call-instance", NULL, NULL); } static void on_modem_call_dialstring(ModemCall *ci, char const *dialstring, RingMediaChannel *_self) { #if nomore RingMediaChannel *self = RING_MEDIA_CHANNEL(_self); if (dialstring) ring_sending_dial_string(self, dialstring); else ring_stopped_dial_string(self, FALSE); #endif } static void on_modem_call_dtmf_tone(ModemCall *call_instance, gint tone, RingMediaChannel *self) { RingMediaChannelPrivate *priv = RING_MEDIA_CHANNEL(self)->priv; gboolean sending_dial_string; /* When sending single DTMF digits, dial.string is empty */ sending_dial_string = priv->dial.string && priv->dial.string[0] != '\0'; DEBUG("modem playing dtmf-tone %d '%c'", tone, 0 <= tone && tone <= 15 ? "0123456789*#ABCD"[tone] : '?'); if (!sending_dial_string) return; if (0 <= tone && tone <= 14) { #if 0 /* No local DTMF playback (handled by UI) */ ring_media_channel_play_tone(RING_MEDIA_CHANNEL(self), tone, -6, 2000); #endif } else { ring_media_channel_stop_playing(RING_MEDIA_CHANNEL(self), FALSE); } } /* ---------------------------------------------------------------------- */ static void ring_media_channel_stopped_playing(ModemTones *, guint source, gpointer _self); void ring_media_channel_play_tone(RingMediaChannel *self, int tone, int volume, unsigned duration) { RingMediaChannelPrivate *priv = self->priv; if (priv->closing) return; DEBUG("play tone %d for %u ms at %d dBm0", tone, duration, volume); if ((tone >= 0 && !modem_tones_is_playing(priv->tones, 0)) || priv->playing) { priv->playing = modem_tones_start_full(priv->tones, tone, volume, duration, ring_media_channel_stopped_playing, g_object_ref(self)); } } gboolean ring_media_channel_is_playing(RingMediaChannel const *self) { return self && RING_MEDIA_CHANNEL(self)->priv->playing != 0; } void ring_media_channel_idle_playing(RingMediaChannel *self) { RingMediaChannelPrivate *priv = self->priv; if (priv->playing) { int event = modem_tones_playing_event(priv->tones, priv->playing); if (event < TONES_EVENT_RADIO_PATH_ACK && modem_tones_is_playing(priv->tones, priv->playing) > 1200) ring_media_channel_stop_playing(self, FALSE); } } void ring_media_channel_stop_playing(RingMediaChannel *self, gboolean always) { RingMediaChannelPrivate *priv = self->priv; if (!priv->playing) return; modem_tones_stop(priv->tones, priv->playing); } static void ring_media_channel_stopped_playing(ModemTones *tones, guint source, gpointer _self) { RingMediaChannel *self = RING_MEDIA_CHANNEL(_self); RingMediaChannelPrivate *priv = self->priv; if (priv->playing == source) { priv->playing = 0; #if nomore if (priv->dial.stopped) ring_emit_stopped_dial_string(self); #endif if (!self->call_instance) { DEBUG("tone ended, closing"); ring_media_channel_close(RING_MEDIA_CHANNEL(self)); } } g_object_unref(self); } telepathy-ring-2.3.24-37ad7111424725a5446e82dac516d7cab88149c7/src/ring-media-channel.h000066400000000000000000000112041251541261300263340ustar00rootroot00000000000000/* * ring-media-channel.h - Header for RingMediaChannel * * Copyright (C) 2007-2010 Nokia Corporation * @author Pekka Pessi * * This work is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This work is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this work; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef RING_MEDIA_CHANNEL_H #define RING_MEDIA_CHANNEL_H #include #include #include #include #include G_BEGIN_DECLS typedef struct _RingMediaChannel RingMediaChannel; typedef struct _RingMediaChannelClass RingMediaChannelClass; typedef struct _RingMediaChannelPrivate RingMediaChannelPrivate; G_END_DECLS #include "ring-connection.h" #include G_BEGIN_DECLS struct _RingMediaChannelClass { TpBaseChannelClass parent_class; RingStreamedMediaMixinClass streamed_media_class; void (*emit_initial)(RingMediaChannel *self); void (*update_state)(RingMediaChannel *self, guint status, guint causetype, guint cause); gboolean (*close)(RingMediaChannel *, gboolean immediately); void (*set_call_instance)(RingMediaChannel *self, ModemCall *ci); }; struct _RingMediaChannel { TpBaseChannel parent; RingStreamedMediaMixin streamed_media; /* Read-only */ ModemCall *call_instance; char *nick; RingMediaChannelPrivate *priv; }; GType ring_media_channel_get_type(void); /* TYPE MACROS */ #define RING_TYPE_MEDIA_CHANNEL \ (ring_media_channel_get_type()) #define RING_MEDIA_CHANNEL(obj) \ (G_TYPE_CHECK_INSTANCE_CAST((obj), RING_TYPE_MEDIA_CHANNEL, RingMediaChannel)) #define RING_MEDIA_CHANNEL_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST((klass), RING_TYPE_MEDIA_CHANNEL, RingMediaChannelClass)) #define RING_IS_MEDIA_CHANNEL(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE((obj), RING_TYPE_MEDIA_CHANNEL)) #define RING_IS_MEDIA_CHANNEL_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE((klass), RING_TYPE_MEDIA_CHANNEL)) #define RING_MEDIA_CHANNEL_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), RING_TYPE_MEDIA_CHANNEL, RingMediaChannelClass)) /*********************************************************************** * Additional declarations (not based on generated templates) ***********************************************************************/ #define RING_MEDIA_CHANNEL_INTERFACES \ TP_IFACE_CHANNEL_INTERFACE_DTMF, \ TP_IFACE_CHANNEL_INTERFACE_HOLD #if nomore , \ RTCOM_TP_IFACE_CHANNEL_INTERFACE_DIAL_STRINGS #endif #define RING_MEDIA_CHANNEL_CAPABILITY_FLAGS \ (TP_CHANNEL_MEDIA_CAPABILITY_AUDIO) void ring_media_channel_emit_initial(RingMediaChannel *self); void ring_media_channel_close(RingMediaChannel *self); ModemCallService *ring_media_channel_get_call_service (RingMediaChannel *); ModemRequest *ring_media_channel_queue_request(RingMediaChannel *self, ModemRequest *request); ModemRequest *ring_media_channel_dequeue_request(RingMediaChannel *self, ModemRequest *request); gboolean ring_media_channel_send_dialstring(RingMediaChannel *self, guint id, char const *dialstring, guint duration, guint pause, GError **error); void ring_media_channel_play_tone(RingMediaChannel *self, int tone, int volume, unsigned duration); gboolean ring_media_channel_is_playing(RingMediaChannel const *self); void ring_media_channel_idle_playing(RingMediaChannel *self); void ring_media_channel_stop_playing(RingMediaChannel *self, gboolean always); void ring_media_channel_set_state(RingMediaChannel *self, guint state, guint causetype, guint cause); void ring_media_channel_dtmf_start_tone(TpSvcChannelInterfaceDTMF *iface, guint stream_id, guchar event, DBusGMethodInvocation *context); void ring_media_channel_dtmf_stop_tone(TpSvcChannelInterfaceDTMF *iface, guint stream_id, DBusGMethodInvocation *context); G_END_DECLS #endif /* #ifndef RING_MEDIA_CHANNEL_H*/ telepathy-ring-2.3.24-37ad7111424725a5446e82dac516d7cab88149c7/src/ring-media-manager.c000066400000000000000000000732071251541261300263440ustar00rootroot00000000000000/* * ring-media-manager.c - Manager for media channels * * Copyright (C) 2007-2010 Nokia Corporation * @author Pekka Pessi * @author Lassi Syrjala * * This work is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This work is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this work; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ /* * Based on telepathy-glib/examples/cm/echo/factory.c with notice: * * """ * Copyright (C) 2007 Collabora Ltd. * Copyright (C) 2007 Nokia Corporation * * Copying and distribution of this file, with or without modification, * are permitted in any medium without royalty provided the copyright * notice and this notice are preserved. * """ */ #include "config.h" #define DEBUG_FLAG RING_DEBUG_CONNECTION #include "ring-debug.h" #include "ring-media-manager.h" #include "ring-media-channel.h" #include "ring-call-channel.h" #include "ring-connection.h" #include "ring-param-spec.h" #include "ring-util.h" #include "ring-extensions/ring-extensions.h" #include "modem/call.h" #include "modem/tones.h" #include #include #include #include #include #include #include static void channel_manager_iface_init(gpointer, gpointer); G_DEFINE_TYPE_WITH_CODE( RingMediaManager, ring_media_manager, G_TYPE_OBJECT, G_IMPLEMENT_INTERFACE (TP_TYPE_CHANNEL_MANAGER, channel_manager_iface_init)); enum { PROP_NONE, PROP_CONNECTION, PROP_CALL_SERVICE, PROP_ANON_MODES, PROP_CAPABILITY_FLAGS, N_PROPS }; typedef enum { METHOD_COMPATIBLE, METHOD_CREATE, METHOD_ENSURE } RequestotronMethod; /* ---------------------------------------------------------------------- */ static void ring_media_manager_set_call_service (RingMediaManager *self, ModemCallService *service); static void ring_media_manager_connected (RingMediaManager *self); static void ring_media_manager_disconnect(RingMediaManager *self); static void on_connection_status_changed (TpBaseConnection *conn, guint status, guint reason, RingMediaManager *self); static gboolean ring_media_requestotron(RingMediaManager *, gpointer, GHashTable *, RequestotronMethod what); static char *ring_media_manager_new_object_path(RingMediaManager const *self, char const *type); static gboolean ring_media_manager_outgoing_call(RingMediaManager *self, gpointer request, guint target, guint initial_remote, char const *emergency, gboolean initial_audio); static void on_media_channel_closed(GObject *chan, RingMediaManager *self); static void foreach_dispose (gpointer, gpointer, gpointer); static gpointer ring_media_manager_lookup_by_peer(RingMediaManager *self, TpHandle handle); static void on_modem_call_emergency_numbers_changed (ModemCallService *call_service, GParamSpec *dummy, RingMediaManager *self); static void on_modem_call_incoming(ModemCallService *call_service, ModemCall *ci, char const *originator, RingMediaManager *self); static void on_modem_call_created(ModemCallService *call_service, ModemCall *ci, char const *destination, RingMediaManager *self); static void on_modem_call_removed (ModemCallService *, ModemCall *, RingMediaManager *); #define METHOD(i, x) (i ## _ ## x) /* ---------------------------------------------------------------------- */ /* GObject interface */ struct _RingMediaManagerPrivate { RingConnection *connection; guint anon_modes; guint capability_flags; /* Hash by object path */ GHashTable *channels; ModemCallService *call_service; ModemTones *tones; struct { gulong incoming, created, removed; gulong emergency_numbers, joined; gulong status_changed; } signals; unsigned dispose_has_run:1, :0; }; static void ring_media_manager_init(RingMediaManager *self) { self->priv = G_TYPE_INSTANCE_GET_PRIVATE( self, RING_TYPE_MEDIA_MANAGER, RingMediaManagerPrivate); self->priv->channels = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, g_object_unref); self->priv->tones = g_object_new(MODEM_TYPE_TONES, NULL); } static void ring_media_manager_constructed(GObject *object) { RingMediaManager *self = RING_MEDIA_MANAGER(object); RingMediaManagerPrivate *priv = self->priv; priv->signals.status_changed = g_signal_connect (priv->connection, "status-changed", (GCallback) on_connection_status_changed, self); if (G_OBJECT_CLASS(ring_media_manager_parent_class)->constructed) G_OBJECT_CLASS(ring_media_manager_parent_class)->constructed(object); } static void ring_media_manager_dispose(GObject *object) { RingMediaManager *self = RING_MEDIA_MANAGER(object); RingMediaManagerPrivate *priv = self->priv; if (priv->dispose_has_run) return; priv->dispose_has_run = TRUE; ring_signal_disconnect (priv->connection, &priv->signals.status_changed); ring_media_manager_disconnect (self); g_object_run_dispose (G_OBJECT (priv->tones)); ((GObjectClass *) ring_media_manager_parent_class)->dispose(object); } static void ring_media_manager_finalize(GObject *object) { RingMediaManager *self = RING_MEDIA_MANAGER(object); RingMediaManagerPrivate *priv = self->priv; g_object_unref (priv->tones); g_hash_table_destroy (priv->channels); } static void ring_media_manager_get_property(GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { RingMediaManager *self = RING_MEDIA_MANAGER(object); RingMediaManagerPrivate *priv = self->priv; switch (property_id) { case PROP_CONNECTION: g_value_set_object(value, priv->connection); break; case PROP_CALL_SERVICE: g_value_set_pointer (value, priv->call_service); break; case PROP_ANON_MODES: g_value_set_uint(value, priv->anon_modes); break; case PROP_CAPABILITY_FLAGS: g_value_set_uint(value, priv->capability_flags); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); } } static void ring_media_manager_set_property(GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { RingMediaManager *self = RING_MEDIA_MANAGER(object); RingMediaManagerPrivate *priv = self->priv; switch (property_id) { case PROP_CONNECTION: /* We don't ref the connection, because it owns a reference to the * manager, and it guarantees that the manager's lifetime is * less than its lifetime */ priv->connection = g_value_get_object(value); break; case PROP_CALL_SERVICE: ring_media_manager_set_call_service (self, g_value_get_pointer (value)); break; case PROP_ANON_MODES: priv->anon_modes = g_value_get_uint(value); break; case PROP_CAPABILITY_FLAGS: priv->capability_flags = g_value_get_uint(value) & RING_MEDIA_CHANNEL_CAPABILITY_FLAGS; break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); } } static void ring_media_manager_class_init(RingMediaManagerClass *klass) { GObjectClass *object_class = (GObjectClass *) klass; g_type_class_add_private(klass, sizeof (RingMediaManagerPrivate)); object_class->constructed = ring_media_manager_constructed; object_class->dispose = ring_media_manager_dispose; object_class->finalize = ring_media_manager_finalize; object_class->get_property = ring_media_manager_get_property; object_class->set_property = ring_media_manager_set_property; g_object_class_install_property(object_class, PROP_CONNECTION, ring_param_spec_connection()); g_object_class_install_property (object_class, PROP_CALL_SERVICE, g_param_spec_pointer ("call-service", "Call Manager Object", "oFono Call Manager", G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property(object_class, PROP_ANON_MODES, ring_param_spec_anon_modes()); g_object_class_install_property(object_class, PROP_CAPABILITY_FLAGS, ring_param_spec_type_specific_capability_flags(G_PARAM_CONSTRUCT, RING_MEDIA_CHANNEL_CAPABILITY_FLAGS)); } /* ---------------------------------------------------------------------- */ /* RingMediaManager interface */ static void ring_media_manager_set_call_service (RingMediaManager *self, ModemCallService *service) { RingMediaManagerPrivate *priv = self->priv; if (priv->call_service) { ring_media_manager_disconnect (self); } if (service) { priv->call_service = g_object_ref (MODEM_CALL_SERVICE (service)); ring_media_manager_connected (self); } } static void ring_media_manager_connected (RingMediaManager *self) { RingMediaManagerPrivate *priv = self->priv; priv->signals.incoming = g_signal_connect(priv->call_service, "incoming", G_CALLBACK(on_modem_call_incoming), self); priv->signals.created = g_signal_connect(priv->call_service, "created", G_CALLBACK(on_modem_call_created), self); priv->signals.removed = g_signal_connect(priv->call_service, "removed", G_CALLBACK(on_modem_call_removed), self); priv->signals.emergency_numbers = g_signal_connect(priv->call_service, "notify::emergency-numbers", G_CALLBACK(on_modem_call_emergency_numbers_changed), self); modem_call_service_resume (priv->call_service); } /** Disconnect from call service */ static void ring_media_manager_disconnect(RingMediaManager *self) { RingMediaManagerPrivate *priv = self->priv; ring_signal_disconnect (priv->call_service, &priv->signals.incoming); ring_signal_disconnect (priv->call_service, &priv->signals.created); ring_signal_disconnect (priv->call_service, &priv->signals.emergency_numbers); g_hash_table_foreach (priv->channels, foreach_dispose, NULL); g_hash_table_remove_all (priv->channels); if (priv->call_service) g_object_unref (priv->call_service); priv->call_service = NULL; } static gboolean ring_media_manager_is_connected (RingMediaManager *self) { return RING_IS_MEDIA_MANAGER (self) && self->priv->call_service != NULL; } static void on_connection_status_changed (TpBaseConnection *conn, guint status, guint reason, RingMediaManager *self) { if (status == TP_CONNECTION_STATUS_DISCONNECTED) { ring_media_manager_disconnect (self); } } static void foreach_dispose (gpointer key, gpointer _channel, gpointer user_data) { /* Ensure "closed" has been emitted */ if (!tp_base_channel_is_destroyed (_channel)) { g_object_run_dispose (_channel); } } /* ---------------------------------------------------------------------- */ RingEmergencyServiceInfoList * ring_media_manager_emergency_services(RingMediaManager *self) { RingMediaManagerPrivate *priv = RING_MEDIA_MANAGER(self)->priv; char const * const * numbers = NULL; /* * If the list is queried without valid call_service, * default emergency number list is returned */ numbers = modem_call_get_emergency_numbers (priv->call_service); return ring_emergency_service_info_list_default ((char **)numbers); } static void on_modem_call_emergency_numbers_changed (ModemCallService *call_service, GParamSpec *dummy, RingMediaManager *self) { RingMediaManagerPrivate *priv = RING_MEDIA_MANAGER(self)->priv; TpBaseConnection *base = TP_BASE_CONNECTION(priv->connection); gchar **numbers; RingEmergencyServiceInfoList *services; if (base->status != TP_CONNECTION_STATUS_CONNECTED) return; g_object_get (call_service, "emergency-numbers", &numbers, NULL); services = ring_emergency_service_info_list_default (numbers); METHOD (tp_svc_connection_interface_service_point, emit_service_points_changed) (priv->connection, services); ring_emergency_service_info_list_free(services); g_strfreev (numbers); } /* ---------------------------------------------------------------------- */ /* Insert channel-type specific capabilities into array */ void ring_media_manager_add_capabilities(RingMediaManager *self, guint handle, GPtrArray *returns) { RingMediaManagerPrivate *priv = RING_MEDIA_MANAGER(self)->priv; char const *id = ring_connection_inspect_contact(priv->connection, handle); guint selfhandle = tp_base_connection_get_self_handle( (TpBaseConnection *)priv->connection); if (id == NULL) return; if (!ring_media_manager_is_connected (self)) return; /* XXX - should check if we are in emergency call mode only status */ if (handle == selfhandle) { if (priv->capability_flags) g_ptr_array_add(returns, ring_contact_capability_new(handle, TP_IFACE_CHANNEL_TYPE_STREAMED_MEDIA, TP_CONNECTION_CAPABILITY_FLAG_CREATE | TP_CONNECTION_CAPABILITY_FLAG_INVITE, RING_MEDIA_CHANNEL_CAPABILITY_FLAGS)); } else if (modem_call_is_valid_address (id)) { g_ptr_array_add(returns, ring_contact_capability_new(handle, TP_IFACE_CHANNEL_TYPE_STREAMED_MEDIA, TP_CONNECTION_CAPABILITY_FLAG_CREATE | TP_CONNECTION_CAPABILITY_FLAG_INVITE, RING_MEDIA_CHANNEL_CAPABILITY_FLAGS)); } } /* ---------------------------------------------------------------------- */ /* TpChannelManagerIface interface */ static GHashTable * ring_call_channel_fixed_properties(void) { static GHashTable *hash; if (hash) return hash; hash = g_hash_table_new(g_str_hash, g_str_equal); char const *key; GValue *value; key = TP_IFACE_CHANNEL ".TargetHandleType"; value = tp_g_value_slice_new(G_TYPE_UINT); g_value_set_uint(value, TP_HANDLE_TYPE_CONTACT); g_hash_table_insert(hash, (gpointer)key, value); key = TP_IFACE_CHANNEL ".ChannelType"; value = tp_g_value_slice_new(G_TYPE_STRING); g_value_set_static_string(value, TP_IFACE_CHANNEL_TYPE_STREAMED_MEDIA); g_hash_table_insert(hash, (gpointer)key, value); return hash; } static char const * const ring_call_channel_allowed_properties[] = { TP_IFACE_CHANNEL ".TargetHandle", TP_IFACE_CHANNEL ".TargetID", TP_IFACE_CHANNEL_TYPE_STREAMED_MEDIA ".InitialAudio", TP_IFACE_CHANNEL_TYPE_STREAMED_MEDIA ".InitialVideo", NULL }; static GHashTable * ring_anon_channel_fixed_properties(void) { static GHashTable *hash; if (hash) return hash; hash = g_hash_table_new(g_str_hash, g_str_equal); char const *key; GValue *value; key = TP_IFACE_CHANNEL ".TargetHandleType"; value = tp_g_value_slice_new(G_TYPE_UINT); g_value_set_uint(value, TP_HANDLE_TYPE_NONE); g_hash_table_insert(hash, (gpointer)key, value); key = TP_IFACE_CHANNEL ".ChannelType"; value = tp_g_value_slice_new(G_TYPE_STRING); g_value_set_static_string(value, TP_IFACE_CHANNEL_TYPE_STREAMED_MEDIA); g_hash_table_insert(hash, (gpointer)key, value); return hash; } static char const * const ring_anon_channel_allowed_properties[] = { NULL }; static void ring_media_manager_foreach_channel_class(TpChannelManager *_self, TpChannelManagerChannelClassFunc func, gpointer userdata) { RingMediaManager *self = RING_MEDIA_MANAGER(_self); /* If we're not connected, calls aren't supported. */ if (self->priv->call_service == NULL) return; func(_self, ring_call_channel_fixed_properties(), ring_call_channel_allowed_properties, userdata); #if nomore /* anon channels are only for compatibility for RequestChannel */ func(_self, ring_anon_channel_fixed_properties(), ring_anon_channel_allowed_properties, userdata); #endif } static void ring_media_manager_foreach_channel(TpChannelManager *_self, TpExportableChannelFunc func, gpointer user_data) { RingMediaManager *self = RING_MEDIA_MANAGER(_self); GHashTableIter i[1]; gpointer channel; for (g_hash_table_iter_init(i, self->priv->channels); g_hash_table_iter_next(i, NULL, &channel);) func(channel, user_data); } /** Request a RingMediaChannel. */ static gboolean ring_media_manager_request_channel(TpChannelManager *_self, gpointer request, GHashTable *properties) { return ring_media_requestotron( RING_MEDIA_MANAGER(_self), request, properties, METHOD_COMPATIBLE); } /** Create a new RingMediaChannel. */ static gboolean ring_media_manager_create_channel(TpChannelManager *_self, gpointer request, GHashTable *properties) { return ring_media_requestotron( RING_MEDIA_MANAGER(_self), request, properties, METHOD_CREATE); } /** Ensure a new RingMediaChannel. */ static gboolean ring_media_manager_ensure_channel(TpChannelManager *_self, gpointer request, GHashTable *properties) { return ring_media_requestotron( RING_MEDIA_MANAGER(_self), request, properties, METHOD_ENSURE); } static void channel_manager_iface_init(gpointer ifacep, gpointer data) { TpChannelManagerIface *iface = ifacep; #define IMPLEMENT(x) iface->x = ring_media_manager_##x IMPLEMENT(foreach_channel); IMPLEMENT(foreach_channel_class); IMPLEMENT(create_channel); IMPLEMENT(request_channel); IMPLEMENT(ensure_channel); #undef IMPLEMENT } /* ---------------------------------------------------------------------- */ static gboolean ring_media_requestotron(RingMediaManager *self, gpointer request, GHashTable *properties, RequestotronMethod kind) { RingMediaManagerPrivate *priv = self->priv; TpHandle handle; /* If we're not connected, calls aren't supported. */ if (self->priv->call_service == NULL) return FALSE; handle = tp_asv_get_uint32 (properties, TP_IFACE_CHANNEL ".TargetHandle", NULL); if (kind == METHOD_COMPATIBLE && handle == 0 && ring_properties_satisfy(properties, ring_anon_channel_fixed_properties(), ring_anon_channel_allowed_properties)) { return ring_media_manager_outgoing_call(self, request, 0, 0, NULL, FALSE); } if (tp_asv_get_initial_video (properties, FALSE)) { tp_channel_manager_emit_request_failed (self, request, TP_ERRORS, TP_ERROR_NOT_IMPLEMENTED, "Video calls are not supported"); return TRUE; } if (handle != 0 && ring_properties_satisfy(properties, ring_call_channel_fixed_properties(), ring_call_channel_allowed_properties)) { RingCallChannel *channel; char const *target_id; GError *error = NULL; target_id = ring_connection_inspect_contact(priv->connection, handle); if (!modem_call_validate_address(target_id, &error)) { tp_channel_manager_emit_request_failed( self, request, TP_ERRORS, TP_ERROR_INVALID_HANDLE, error->message); g_error_free(error); return TRUE; } /* We do not yet support 'w' */ else if (strchr(target_id, 'w')) { tp_channel_manager_emit_request_failed( self, request, TP_ERRORS, TP_ERROR_NOT_IMPLEMENTED, "Dial strings containing 'w' are not supported"); return TRUE; } if (kind == METHOD_ENSURE) { channel = ring_media_manager_lookup_by_peer(self, handle); if (channel) { tp_channel_manager_emit_request_already_satisfied( self, request, TP_EXPORTABLE_CHANNEL(channel)); return TRUE; } } return ring_media_manager_outgoing_call(self, request, handle, kind == METHOD_COMPATIBLE ? handle : 0, modem_call_get_emergency_service(priv->call_service, target_id), tp_asv_get_initial_audio(properties, FALSE)); } return FALSE; } gboolean ring_media_manager_validate_initial_members(RingMediaManager *self, RingInitialMembers *initial, GError **error) { if (initial == NULL) { g_set_error(error, TP_ERRORS, TP_ERROR_INVALID_ARGUMENT, "No initial members"); return FALSE; } if (initial->len != 2) { g_set_error(error, TP_ERRORS, TP_ERROR_INVALID_ARGUMENT, "Expecting exactly two initial members"); return FALSE; } RingMemberChannel *ch1 = (RingMemberChannel *) ring_media_manager_lookup(self, initial->odata[0]); RingMemberChannel *ch2 = (RingMemberChannel *) ring_media_manager_lookup(self, initial->odata[1]); if (ch1 == ch2) { g_set_error(error, TP_ERRORS, TP_ERROR_INVALID_ARGUMENT, "Expecting distinct initial members"); return FALSE; } if (!RING_IS_MEMBER_CHANNEL(ch1) || !RING_IS_MEMBER_CHANNEL(ch2)) { g_set_error(error, TP_ERRORS, TP_ERROR_INVALID_ARGUMENT, "Initial member is not a MediaStream channel"); return FALSE; } if (!ring_member_channel_can_become_member(ch1, error)) { g_prefix_error(error, "First initial: "); return FALSE; } if (!ring_member_channel_can_become_member(ch2, error)) { g_prefix_error(error, "Second initial: "); return FALSE; } return TRUE; } static char * ring_media_manager_new_object_path(RingMediaManager const *self, char const *type) { RingMediaManagerPrivate const *priv = self->priv; char const *base_path = TP_BASE_CONNECTION(priv->connection)->object_path; static unsigned media_index; static unsigned media_index_init; if (!media_index_init) { media_index = (guint)(time(NULL) - (38 * 365 * 24 * 60 * 60)) * 10; media_index_init = 1; } /* Find an unique D-Bus object_path */ for (;;) { char *path = g_strdup_printf("%s/%s%u", base_path, type, ++media_index); if (!g_hash_table_lookup(priv->channels, path)) { return path; } g_free(path); } } static const gchar* get_nick(TpBaseChannel *channel) { return RING_MEDIA_CHANNEL (channel)->nick; } void ring_media_manager_emit_new_channel(RingMediaManager *self, gpointer request, gpointer _channel, GError *error) { DEBUG("%s(%p, %p, %p, %p) called", __func__, self, request, _channel, error); RingMediaManagerPrivate *priv = RING_MEDIA_MANAGER(self)->priv; TpBaseChannel *channel = _channel ? TP_BASE_CHANNEL (_channel) : NULL; GSList *requests = request ? g_slist_prepend(NULL, request) : NULL; if (error == NULL) { char *object_path = NULL; g_signal_connect( channel, "closed", G_CALLBACK(on_media_channel_closed), self); g_object_get(channel, "object-path", &object_path, NULL); DEBUG("got new channel %p nick %s type %s", channel, get_nick (channel), G_OBJECT_TYPE_NAME (channel)); g_hash_table_insert(priv->channels, object_path, channel); tp_channel_manager_emit_new_channel(self, TP_EXPORTABLE_CHANNEL(channel), requests); /* Emit Group and StreamedMedia signals */ ring_media_channel_emit_initial(RING_MEDIA_CHANNEL (channel)); } else { DEBUG("new channel %p nick %s type %s failed with " GERROR_MSG_FMT, channel, get_nick (channel), G_OBJECT_TYPE_NAME (channel), GERROR_MSG_CODE(error)); if (request) { tp_channel_manager_emit_request_failed(self, request, error->domain, error->code, error->message); } if (_channel) g_object_unref(_channel); } g_slist_free(requests); } static gboolean ring_media_manager_outgoing_call(RingMediaManager *self, gpointer request, TpHandle target, TpHandle initial_remote, char const *emergency, gboolean initial_audio) { RingMediaManagerPrivate *priv = self->priv; TpHandleType htype = target ? TP_HANDLE_TYPE_CONTACT : TP_HANDLE_TYPE_NONE; char *object_path = ring_media_manager_new_object_path(self, "outgoing"); RingCallChannel *channel; TpHandle initiator; initiator = tp_base_connection_get_self_handle( TP_BASE_CONNECTION(priv->connection)); channel = (RingCallChannel *) g_object_new(RING_TYPE_CALL_CHANNEL, "connection", priv->connection, "tones", priv->tones, "object-path", object_path, "initiator-handle", initiator, "handle-type", htype, "handle", target, "peer", target, "initial-remote", initial_remote, "requested", TRUE, "initial-audio", initial_audio, "anon-modes", priv->anon_modes, "initial-emergency-service", emergency, NULL); g_free(object_path); if (initial_audio) ring_call_channel_initial_audio(channel, self, request); else ring_media_manager_emit_new_channel(self, request, channel, NULL); return TRUE; } static void on_media_channel_closed(GObject *chan, RingMediaManager *self) { if (self->priv->channels != NULL) { gchar *object_path; g_object_get (chan, "object-path", &object_path, NULL); g_hash_table_remove (self->priv->channels, object_path); tp_channel_manager_emit_channel_closed (self, object_path); g_free (object_path); } } /** Find a RingMediaChannel by object_path. */ RingMediaChannel * ring_media_manager_lookup(RingMediaManager *self, char const *object_path) { if (self && object_path) return g_hash_table_lookup(self->priv->channels, object_path); else return NULL; } /** Find a RingMediaChannel by peer handle. */ static gpointer ring_media_manager_lookup_by_peer(RingMediaManager *self, TpHandle handle) { GHashTableIter i[1]; gpointer channel; for (g_hash_table_iter_init(i, self->priv->channels); g_hash_table_iter_next(i, NULL, &channel);) { TpHandle peer = 0; g_object_get(channel, "peer", &peer, NULL); if (handle == peer) return channel; } return NULL; } static void on_modem_call_incoming(ModemCallService *call_service, ModemCall *modem_call, char const *originator, RingMediaManager *self) { RingMediaManagerPrivate *priv = self->priv; TpHandleRepoIface *repo; RingCallChannel *channel; TpHandle handle; GError *error = NULL; if (!ring_media_manager_is_connected (self)) return; channel = modem_call_get_handler(modem_call); if (channel) { modem_call_set_handler(modem_call, NULL); ring_critical("Call instance %s already associated with channel.", modem_call_get_name(modem_call)); ring_critical("Closing old channel %s.", RING_MEDIA_CHANNEL(channel)->nick); ring_media_channel_close(RING_MEDIA_CHANNEL(channel)); } repo = tp_base_connection_get_handles( (TpBaseConnection *)(self->priv->connection), TP_HANDLE_TYPE_CONTACT); error = NULL; handle = tp_handle_ensure(repo, originator, ring_network_normalization_context(), &error); if (handle == 0) { DEBUG("tp_handle_ensure:" GERROR_MSG_FMT, GERROR_MSG_CODE(error)); if (error) g_error_free(error); /* Xyzzy - modem_call_request_release() ?? */ return; } /* Incoming call - pass call and handle ownership to new media channel */ char *object_path = ring_media_manager_new_object_path(self, "incoming"); channel = (RingCallChannel *) g_object_new(RING_TYPE_CALL_CHANNEL, "connection", priv->connection, "tones", priv->tones, "object-path", object_path, "initiator-handle", handle, "handle-type", TP_HANDLE_TYPE_CONTACT, "handle", handle, "peer", handle, "requested", FALSE, "initial-audio", TRUE, "anon-modes", priv->anon_modes, "call-instance", modem_call, "terminating", TRUE, NULL); g_free(object_path); ring_media_manager_emit_new_channel(self, NULL, channel, NULL); ring_media_channel_set_state(RING_MEDIA_CHANNEL(channel), MODEM_CALL_STATE_INCOMING, 0, 0); } static void on_modem_call_created(ModemCallService *call_service, ModemCall *modem_call, char const *destination, RingMediaManager *self) { RingMediaManagerPrivate *priv = self->priv; TpHandleRepoIface *repo; RingCallChannel *channel; TpHandle handle; TpHandle initiator; char const *sos; GError *error = NULL; if (!ring_media_manager_is_connected (self)) return; channel = modem_call_get_handler(modem_call); if (channel) return; /* Call created by ring, nothing to do */ /* This is a call created outside ring, create a channel for it */ DEBUG("Freshly created call instance %s not associated with channel.", modem_call_get_name(modem_call)); repo = tp_base_connection_get_handles( (TpBaseConnection *)(self->priv->connection), TP_HANDLE_TYPE_CONTACT); error = NULL; handle = tp_handle_ensure(repo, destination, ring_network_normalization_context(), &error); if (handle == 0) { ring_warning("tp_handle_ensure:" GERROR_MSG_FMT, GERROR_MSG_CODE(error)); if (error) g_error_free(error); /* Xyzzy - modem_call_request_release() ?? */ return; } initiator = tp_base_connection_get_self_handle( TP_BASE_CONNECTION(priv->connection)); sos = modem_call_get_emergency_service(priv->call_service, destination); char *object_path = ring_media_manager_new_object_path(self, "created"); channel = (RingCallChannel *) g_object_new(RING_TYPE_CALL_CHANNEL, "connection", priv->connection, "tones", priv->tones, "object-path", object_path, "handle-type", TP_HANDLE_TYPE_CONTACT, "handle", handle, "initiator-handle", initiator, "peer", handle, "requested", TRUE, "initial-remote", handle, "initial-audio", TRUE, "anon-modes", priv->anon_modes, "call-instance", modem_call, "originating", TRUE, sos ? "initial-emergency-service" : NULL, sos, NULL); g_free(object_path); ring_media_manager_emit_new_channel(self, NULL, channel, NULL); ring_media_channel_set_state(RING_MEDIA_CHANNEL(channel), MODEM_CALL_STATE_DIALING, 0, 0); } static void on_modem_call_removed (ModemCallService *call_service, ModemCall *modem_call, RingMediaManager *self) { RingCallChannel *channel; channel = modem_call_get_handler (modem_call); if (channel) g_object_set (channel, "call-instance", NULL, NULL); } telepathy-ring-2.3.24-37ad7111424725a5446e82dac516d7cab88149c7/src/ring-media-manager.h000066400000000000000000000053241251541261300263440ustar00rootroot00000000000000/* * ring-media-manager.h - Manager for media channels * * Copyright (C) 2007-2010 Nokia Corporation * @author Pekka Pessi * * This work is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This work is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this work; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef RING_MEDIA_MANAGER_H #define RING_MEDIA_MANAGER_H G_BEGIN_DECLS typedef struct _RingMediaManager RingMediaManager; typedef struct _RingMediaManagerClass RingMediaManagerClass; typedef struct _RingMediaManagerPrivate RingMediaManagerPrivate; G_END_DECLS #include #include #include #include #include "modem/call.h" G_BEGIN_DECLS struct _RingMediaManagerClass { GObjectClass parent_class; }; struct _RingMediaManager { GObject parent; RingMediaManagerPrivate *priv; }; GType ring_media_manager_get_type(void); /* TYPE MACROS */ #define RING_TYPE_MEDIA_MANAGER \ (ring_media_manager_get_type()) #define RING_MEDIA_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), \ RING_TYPE_MEDIA_MANAGER, RingMediaManager)) #define RING_MEDIA_MANAGER_CLASS(cls) (G_TYPE_CHECK_CLASS_CAST((cls), \ RING_TYPE_MEDIA_MANAGER, RingMediaManagerClass)) #define RING_IS_MEDIA_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), \ RING_TYPE_MEDIA_MANAGER)) #define RING_IS_MEDIA_MANAGER_CLASS(cls) (G_TYPE_CHECK_CLASS_TYPE((cls), \ RING_TYPE_MEDIA_MANAGER)) #define RING_MEDIA_MANAGER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), \ RING_TYPE_MEDIA_MANAGER, RingMediaManagerClass)) RingMediaChannel *ring_media_manager_lookup(RingMediaManager *self, char const *object_path); RingEmergencyServiceInfoList *ring_media_manager_emergency_services( RingMediaManager *self); void ring_media_manager_add_capabilities(RingMediaManager *, TpHandle, GPtrArray *); void ring_media_manager_emit_new_channel(RingMediaManager *self, gpointer request, gpointer channel, GError *error); gboolean ring_media_manager_validate_initial_members(RingMediaManager *self, RingInitialMembers *initial, GError **error); G_END_DECLS #endif telepathy-ring-2.3.24-37ad7111424725a5446e82dac516d7cab88149c7/src/ring-member-channel.c000066400000000000000000000045321251541261300265250ustar00rootroot00000000000000/* * ring-member-channel.c - Interface for mergeable channels * * Copyright (C) 2008-2010 Nokia Corporation * @author Pekka Pessi * * This work is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This work is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this work; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "config.h" #include "ring-member-channel.h" #include "ring-media-channel.h" #include #include static void ring_member_channel_base_init(gpointer klass) { static gboolean initialized; if (initialized) return; initialized = TRUE; g_object_interface_install_property( klass, g_param_spec_uint("member-handle", "Member Handle", "Handle representing the channel target in conference", 0, G_MAXUINT, 0, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); g_object_interface_install_property( klass, g_param_spec_boxed("member-map", "Mapping from peer to member handle", "Mapping from peer to member handle", TP_HASH_TYPE_HANDLE_OWNER_MAP, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); g_object_interface_install_property( klass, g_param_spec_boxed("member-conference", "Conference Channel", "Conference Channel this object is associated with", DBUS_TYPE_G_OBJECT_PATH, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); } GType ring_member_channel_get_type(void) { static GType type; if (G_UNLIKELY(!type)) { static const GTypeInfo info = { sizeof (RingMemberChannelIface), ring_member_channel_base_init, }; type = g_type_register_static( G_TYPE_INTERFACE, "RingMemberChannel", &info, 0); g_type_interface_add_prerequisite(type, RING_TYPE_MEDIA_CHANNEL); } return type; } telepathy-ring-2.3.24-37ad7111424725a5446e82dac516d7cab88149c7/src/ring-member-channel.h000066400000000000000000000046411251541261300265330ustar00rootroot00000000000000/* * ring-member-channel.h - Interface for mergeable channels * * Copyright (C) 2008-2010 Nokia Corporation * @author Pekka Pessi * * This work is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This work is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this work; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef RING_MEMBER_CHANNEL_H #define RING_MEMBER_CHANNEL_H #include G_BEGIN_DECLS typedef struct _RingMemberChannel RingMemberChannel; typedef struct _RingMemberChannelIface RingMemberChannelIface; G_END_DECLS #include G_BEGIN_DECLS #define RING_TYPE_MEMBER_CHANNEL (ring_member_channel_get_type ()) #define RING_MEMBER_CHANNEL(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ RING_TYPE_MEMBER_CHANNEL, RingMemberChannel)) #define RING_IS_MEMBER_CHANNEL(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \ RING_TYPE_MEMBER_CHANNEL)) #define RING_MEMBER_CHANNEL_GET_INTERFACE(obj) \ (G_TYPE_INSTANCE_GET_INTERFACE ((obj), \ RING_TYPE_MEMBER_CHANNEL, RingMemberChannelIface)) struct _RingMemberChannelIface { GTypeInterface parent; }; GType ring_member_channel_get_type (void); gboolean ring_member_channel_is_in_conference(RingMemberChannel const *iface); gboolean ring_member_channel_can_become_member(RingMemberChannel const *iface, GError **error); RingConferenceChannel *ring_member_channel_get_conference(RingMemberChannel const *iface); GHashTable *ring_member_channel_get_handlemap(RingMemberChannel *); gboolean ring_member_channel_release(RingMemberChannel *iface, const char *message, guint reason, GError **error); void ring_member_channel_joined(RingMemberChannel *iface, RingConferenceChannel *conference); void ring_member_channel_left(RingMemberChannel *iface); G_END_DECLS #endif telepathy-ring-2.3.24-37ad7111424725a5446e82dac516d7cab88149c7/src/ring-param-spec.c000066400000000000000000000121711251541261300256760ustar00rootroot00000000000000/* * ring-param-spec.c - Common param specs for object properties * * Copyright (C) 2007-2010 Nokia Corporation * @author Pekka Pessi * * This work is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This work is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this work; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "config.h" #include "ring-param-spec.h" #include "ring-connection.h" #include "modem/oface.h" #include "modem/sms.h" GParamSpec *ring_param_spec_imsi(guint flags) { return g_param_spec_string("imsi", "IMSI", "Internation Mobile Subscriber Identifer", "", /* default value */ flags | G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); } GParamSpec *ring_param_spec_sms_valid(void) { return g_param_spec_uint("sms-validity-period", "SMS Validity Period", "Period while SMS service centre " "keep trying to deliver SMS.", /* anything above 0 gets rounded up to 5 minutes */ 0, /* 0 means no validity period */ 63 * 7 * 24 * 60 * 60, /* max - 63 weeks */ 0, /* default - set by service centre */ G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS); } GParamSpec *ring_param_spec_sms_reduced_charset(void) { return g_param_spec_boolean("sms-reduced-charset", "SMS reduced character set support", "Whether SMSes should be encoded with " "a reduced character set", FALSE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS); } GParamSpec *ring_param_spec_smsc(void) { return g_param_spec_string("sms-service-centre", "SMS Service Centre", "ISDN Address for SMS Service Centre", "", /* default value */ G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS); } GParamSpec *ring_param_spec_connection(void) { return g_param_spec_object("connection", "Connection object", "The connection that owns this object", TP_TYPE_BASE_CONNECTION, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); } GParamSpec *ring_param_spec_interfaces(void) { return g_param_spec_boxed("interfaces", "List of extra interfaces", "List of extra D-Bus interfaces implemented by this object", G_TYPE_STRV, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); } GParamSpec *ring_param_spec_handle_id(guint flags) { return g_param_spec_string( "handle-id", "Target as string", "The string that would result from inspecting the TargetHandle property", "", flags | G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); } GParamSpec *ring_param_spec_initiator(guint flags) { return g_param_spec_uint( "initiator", "Initiator handle", "The handle of the contact which" "initiated this channel.", 0, G_MAXUINT32, 0, flags | G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS); } GParamSpec *ring_param_spec_initiator_id(guint flags) { return g_param_spec_string( "initiator-id", "Initiator as string", "The string that would result from inspecting the InitiatorHandle property", "", flags | G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); } GParamSpec *ring_param_spec_requested(guint flags) { g_assert(flags & (G_PARAM_CONSTRUCT | G_PARAM_CONSTRUCT_ONLY)); return g_param_spec_boolean( "requested", "Requested Channel", "True if this channel was created in response to a local request.", FALSE, flags | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); } GParamSpec * ring_param_spec_type_specific_capability_flags(guint flags, guint default_value) { g_assert(flags & (G_PARAM_CONSTRUCT | G_PARAM_CONSTRUCT_ONLY)); return g_param_spec_uint( "capability-flags", "Channel-Type-Specific Capability Flags", "Capability flags for the channel type.", 0, G_MAXUINT32, default_value, flags | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); } GParamSpec *ring_param_spec_anon_modes(void) { return g_param_spec_uint("anon-modes", "Anonymity modes", "Specifies the active anonymity modes", 0, G_MAXUINT, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); } GParamSpec * ring_param_spec_service (char const *name, guint flags) { return g_param_spec_pointer (name, "Modem service object", "A modem service used to implement telepathy services", flags | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); } GParamSpec * ring_param_spec_sms_service (guint flags) { return g_param_spec_pointer ("sms-service", "SMS Service", "Modem SMS Service Object", flags | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); } telepathy-ring-2.3.24-37ad7111424725a5446e82dac516d7cab88149c7/src/ring-param-spec.h000066400000000000000000000034421251541261300257040ustar00rootroot00000000000000/* * ring-param-spec.h - Common param specs for object properties * * Copyright (C) 2007-2010 Nokia Corporation * @author Pekka Pessi * * This work is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This work is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this work; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __RING_PARAM_SPEC_H__ #define __RING_PARAM_SPEC_H__ #include G_BEGIN_DECLS GParamSpec *ring_param_spec_imsi (guint flags); GParamSpec *ring_param_spec_sms_valid(void); GParamSpec *ring_param_spec_smsc(void); GParamSpec *ring_param_spec_sms_reduced_charset(void); GParamSpec *ring_param_spec_connection(void); GParamSpec *ring_param_spec_interfaces(void); GParamSpec *ring_param_spec_handle_id(guint flags); GParamSpec *ring_param_spec_initiator(guint flags); GParamSpec *ring_param_spec_initiator_id(guint flags); GParamSpec *ring_param_spec_requested(guint flags); GParamSpec *ring_param_spec_type_specific_capability_flags(guint flags, guint default_value); GParamSpec *ring_param_spec_anon_modes(void); GParamSpec *ring_param_spec_service (char const *name, guint flags); GParamSpec *ring_param_spec_sms_service (guint flags); G_END_DECLS #endif /* #ifndef __RING_PARAM_SPEC_H__*/ telepathy-ring-2.3.24-37ad7111424725a5446e82dac516d7cab88149c7/src/ring-protocol.c000066400000000000000000000072551251541261300255160ustar00rootroot00000000000000/* * ring-protocol.c - source for RingProtocol * Copyright (C) 2007-2010 Collabora Ltd. * Copyright (C) 2007-2010 Nokia Corporation * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "ring-protocol.h" #include #include #include #include #include "ring-connection.h" #include "ring-media-manager.h" #include "ring-text-manager.h" #define NAME "tel" #define ICON_NAME "im-tel" #define VCARD_FIELD_NAME "TEL" #define ENGLISH_NAME "Mobile Telephony" G_DEFINE_TYPE (RingProtocol, ring_protocol, TP_TYPE_BASE_PROTOCOL) static void ring_protocol_init (RingProtocol *self) { } static const TpCMParamSpec * get_parameters (TpBaseProtocol *self G_GNUC_UNUSED) { return ring_connection_get_param_specs (); } static TpBaseConnection * new_connection (TpBaseProtocol *protocol G_GNUC_UNUSED, GHashTable *params, GError **error) { if (dbus_g_bus_get (DBUS_BUS_SYSTEM, error) == NULL) return NULL; return TP_BASE_CONNECTION (ring_connection_new (params)); } static gchar * normalize_contact (TpBaseProtocol *self G_GNUC_UNUSED, const gchar *contact, GError **error) { return ring_normalize_contact (contact, error); } static gchar * identify_account (TpBaseProtocol *self G_GNUC_UNUSED, GHashTable *asv, GError **error) { const gchar *account = tp_asv_get_string (asv, "account"); g_assert (account != NULL); return g_strdup (account); } static GStrv get_interfaces (TpBaseProtocol *self) { return g_new0 (gchar *, 1); } static void get_connection_details (TpBaseProtocol *self, GStrv *connection_interfaces, GType **channel_managers, gchar **icon_name, gchar **english_name, gchar **vcard_field) { if (connection_interfaces) *connection_interfaces = ring_connection_dup_implemented_interfaces (); if (channel_managers) { GType types[] = { RING_TYPE_TEXT_MANAGER, RING_TYPE_MEDIA_MANAGER, G_TYPE_INVALID }; *channel_managers = g_memdup (types, sizeof(types)); } if (icon_name) *icon_name = g_strdup (ICON_NAME); if (english_name) *english_name = g_strdup (ENGLISH_NAME); if (vcard_field) *vcard_field = g_strdup (VCARD_FIELD_NAME); } static void ring_protocol_class_init (RingProtocolClass *klass) { TpBaseProtocolClass *base_class = (TpBaseProtocolClass *) klass; base_class->get_parameters = get_parameters; base_class->new_connection = new_connection; base_class->normalize_contact = normalize_contact; base_class->identify_account = identify_account; base_class->get_interfaces = get_interfaces; base_class->get_connection_details = get_connection_details; } RingProtocol * ring_protocol_new (void) { return g_object_new (RING_TYPE_PROTOCOL, "name", NAME, NULL); } telepathy-ring-2.3.24-37ad7111424725a5446e82dac516d7cab88149c7/src/ring-protocol.h000066400000000000000000000040171251541261300255140ustar00rootroot00000000000000/* * protocol.h - header for RingProtocol * Copyright (C) 2007-2010 Collabora Ltd. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef RING_PROTOCOL_H #define RING_PROTOCOL_H #include #include G_BEGIN_DECLS typedef struct _RingProtocol RingProtocol; typedef struct _RingProtocolPrivate RingProtocolPrivate; typedef struct _RingProtocolClass RingProtocolClass; typedef struct _RingProtocolClassPrivate RingProtocolClassPrivate; struct _RingProtocolClass { TpBaseProtocolClass parent_class; RingProtocolClassPrivate *priv; }; struct _RingProtocol { TpBaseProtocol parent; RingProtocolPrivate *priv; }; GType ring_protocol_get_type (void); #define RING_TYPE_PROTOCOL \ (ring_protocol_get_type ()) #define RING_PROTOCOL(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ RING_TYPE_PROTOCOL, \ RingProtocol)) #define RING_PROTOCOL_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST ((klass), \ RING_TYPE_PROTOCOL, \ RingProtocolClass)) #define IS_RING_PROTOCOL_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE ((klass), \ RING_TYPE_PROTOCOL)) #define RING_PROTOCOL_GET_CLASS(klass) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), \ RING_TYPE_PROTOCOL, \ RingProtocolClass)) RingProtocol *ring_protocol_new (void); G_END_DECLS #endif telepathy-ring-2.3.24-37ad7111424725a5446e82dac516d7cab88149c7/src/ring-streamed-media-mixin.c000066400000000000000000000523671251541261300276640ustar00rootroot00000000000000/* * ring-streamed-media-mixin.c - Source for RingStreamedMediaMixin * Copyright © 2011 Nokia Corporation * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of the * License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 * USA */ #include "config.h" #define DEBUG_FLAG RING_DEBUG_MEDIA #include "ring-debug.h" #include #include "ring-util.h" #include #include #include #include #include #include #include #include #include struct _RingStreamedMediaMixinPrivate { struct stream_state { guint id; /* nonzero when active, 0 otherwise */ TpHandle handle; TpMediaStreamType type; TpMediaStreamState state; TpMediaStreamDirection direction; TpMediaStreamPendingSend pending; } audio[1], video[1]; }; enum { RING_MEDIA_STREAM_ID_AUDIO = 1, RING_MEDIA_STREAM_ID_VIDEO = 2 }; static TpDBusPropertiesMixinPropImpl streamed_media_properties[] = { { "InitialAudio", "initial-audio", NULL }, { "InitialVideo", "initial-video", NULL }, { "ImmutableStreams", "immutable-streams", NULL }, { NULL } }; void ring_streamed_media_mixin_fill_immutable_properties (gpointer iface, GHashTable *properties) { tp_dbus_properties_mixin_fill_properties_hash (G_OBJECT (iface), properties, TP_IFACE_CHANNEL_TYPE_STREAMED_MEDIA, "InitialAudio", TP_IFACE_CHANNEL_TYPE_STREAMED_MEDIA, "InitialVideo", TP_IFACE_CHANNEL_TYPE_STREAMED_MEDIA, "ImmutableStreams", NULL); } GQuark ring_streamed_media_mixin_class_get_offset_quark (void) { static GQuark quark = 0; if (G_UNLIKELY (quark == 0)) quark = g_quark_from_static_string ("RingStreamedMediaMixinClassOffset"); return quark; } GQuark ring_streamed_media_mixin_get_offset_quark (void) { static GQuark quark = 0; if (G_UNLIKELY (quark == 0)) quark = g_quark_from_static_string ("RingStreamedMediaMixinOffset"); return quark; } RingStreamedMediaMixinClass * ring_streamed_media_mixin_class (GObjectClass *object_class) { return RING_STREAMED_MEDIA_MIXIN_CLASS (object_class); } void ring_streamed_media_mixin_class_init (GObjectClass *object_class, glong offset, RingStreamedMediaMixinValidateMediaHandle validate_handle, RingStreamedMediaMixinCreateStreams create_streams) { RingStreamedMediaMixinClass *mixin_class; g_assert (G_IS_OBJECT_CLASS (object_class)); g_assert (create_streams != NULL); g_assert (validate_handle != NULL); g_type_set_qdata (G_OBJECT_CLASS_TYPE (object_class), RING_STREAMED_MEDIA_MIXIN_CLASS_OFFSET_QUARK, GINT_TO_POINTER (offset)); tp_dbus_properties_mixin_implement_interface (object_class, TP_IFACE_QUARK_CHANNEL_TYPE_STREAMED_MEDIA, tp_dbus_properties_mixin_getter_gobject_properties, NULL, streamed_media_properties); mixin_class = RING_STREAMED_MEDIA_MIXIN_CLASS (object_class); mixin_class->validate_handle = validate_handle; mixin_class->create_streams = create_streams; } void ring_streamed_media_mixin_init (GObject *object, gsize offset) { RingStreamedMediaMixin *mixin; g_type_set_qdata (G_OBJECT_TYPE (object), RING_STREAMED_MEDIA_MIXIN_OFFSET_QUARK, GSIZE_TO_POINTER (offset)); mixin = RING_STREAMED_MEDIA_MIXIN (object); mixin->priv = g_slice_new0 (RingStreamedMediaMixinPrivate); } /** * ring_streamed_media_mixin_finalize: (skip) * @obj: An object with this mixin. * * Free resources held by the contacts mixin. * * Since: 0.7.14 * */ void ring_streamed_media_mixin_finalize (GObject *object) { RingStreamedMediaMixin *mixin = RING_STREAMED_MEDIA_MIXIN (object); RingStreamedMediaMixinPrivate *priv = mixin->priv; DEBUG ("%p", object); /* free any data held directly by the object here */ memset (priv->audio, 0, sizeof *priv->audio); memset (priv->video, 0, sizeof *priv->video); g_slice_free (RingStreamedMediaMixinPrivate, priv); } /** * Telepathy.Channel.Type.StreamedMedia DBus interface - version 0.15 * * Methods: * ListStreams ( ) -> a(uuuuuu) * * Returns an array of structs representing the streams currently active * within this channel. Each stream is identified by an unsigned integer * which is unique for each stream within the channel. * * Returns * * a(uuuuuu) * An array of structs containing: * * * the stream identifier * * the contact handle who the stream is with * (or 0 if the stream represents more than a single member) * * the type of the stream * * the current stream state * * the current direction of the stream * * the current pending send flags * * * RemoveStreams ( au: streams ) -> nothing * * Request that the given streams are removed. * * Parameters * * streams - au * An array of stream identifiers (as defined in ListStreams) * * Possible errors * * org.freedesktop.Telepathy.Error.InvalidArgument * Raised when one of the provided arguments is invalid. * * RequestStreamDirection ( u: stream_id, u: stream_direction ) -> nothing * * Request a change in the direction of an existing stream. In particular, * this might be useful to stop sending media of a particular type, or * inform the peer that you are no longer using media that is being sent to * you. * * Depending on the protocol, streams which are no longer sending in either * direction should be removed and a StreamRemoved signal emitted. Some * direction changes can be enforced locally (for example, BIDIRECTIONAL -> * RECEIVE can be achieved by merely stopping sending), others may not be * possible on some protocols, and some need agreement from the remote end. * In this case, the MEDIA_STREAM_PENDING_REMOTE_SEND flag will be set in * the StreamDirectionChanged signal, and the signal emitted again without * the flag to indicate the resulting direction when the remote end has * accepted or rejected the change. Parameters * * stream_id - u * The stream identifier (as defined in ListStreams) * stream_direction - u * The desired stream direction (a value of MediaStreamDirection) * * Possible errors * * org.freedesktop.Telepathy.Error.InvalidArgument * Raised when one of the provided arguments is invalid. * org.freedesktop.Telepathy.Error.NotAvailable * Raised when the requested functionality is temporarily * unavailable. (generic description) * * RequestStreams ( u: contact_handle, au: types ) -> a(uuuuuu) * * Request that streams be established to exchange the given types of media * with the given member. In general this will try and establish a * bidirectional stream, but on some protocols it may not be possible to * indicate to the peer that you would like to receive media, so a send-only * stream will be created initially. In the cases where the stream requires * remote agreement (eg you wish to receive media from them), the * StreamDirectionChanged signal will be emitted with the * MEDIA_STREAM_PENDING_REMOTE_SEND flag set, and the signal emitted again * with the flag cleared when the remote end has replied. Parameters * * contact_handle - u * A contact handle with whom to establish the streams * types - au * An array of stream types (values of MediaStreamType) * * Returns * * a(uuuuuu) * An array of structs (in the same order as the given stream types) * containing: * * * the stream identifier * * the contact handle who the stream is with * (or 0 if the stream represents more than a single member) * * the type of the stream * * the current stream state * * the current direction of the stream * * the current pending send flags * * Possible errors * * org.freedesktop.Telepathy.Error.InvalidHandle * The contact name specified is unknown on this channel or * connection. (generic description) * org.freedesktop.Telepathy.Error.InvalidArgument * Raised when one of the provided arguments is invalid. (generic * description) * org.freedesktop.Telepathy.Error.NotAvailable * Raised when the requested functionality is temporarily * unavailable. (generic description) * * Signals: * -> StreamAdded ( u: stream_id, u: contact_handle, u: stream_type ) * * Emitted when a new stream has been added to this channel. * * Parameters * * stream_id - u * The stream identifier (as defined in ListStreams) * contact_handle - u * The contact handle who the stream is with (or 0 if it represents more than a single member) * stream_type - u * The stream type (a value from MediaStreamType) * * -> StreamDirectionChanged ( u: stream_id, u: stream_direction, u: pending_flags ) * * Emitted when the direction or pending flags of a stream are changed. If * the MEDIA_STREAM_PENDING_LOCAL_SEND flag is set, the remote user has * requested that we begin sending on this stream. RequestStreamDirection * should be called to indicate whether or not this change is acceptable. * Parameters * * stream_id - u * The stream identifier (as defined in ListStreams) * stream_direction - u * The new stream direction (as defined in ListStreams) * pending_flags - u * The new pending send flags (as defined in ListStreams) * * StreamError ( u: stream_id, u: errno, s: message ) * * Emitted when a stream encounters an error. * * Parameters * * stream_id - u * The stream identifier (as defined in ListStreams) * errno - u * A stream error number, one of the values of MediaStreamError * message - s * A string describing the error (for debugging purposes only) * * StreamRemoved ( u: stream_id ) * * Emitted when a stream has been removed from this channel. * * Parameters * * stream_id - u * stream_id - the stream identifier (as defined in ListStreams) * * StreamStateChanged ( u: stream_id, u: stream_state ) * * Emitted when a member's stream's state changes. * * Parameters * * stream_id - u * The stream identifier (as defined in ListStreams) * stream_state - u * The new stream state (as defined in ListStreams) */ #define TP_CHANNEL_STREAM_TYPE \ (dbus_g_type_get_struct("GValueArray", \ G_TYPE_UINT, \ G_TYPE_UINT, \ G_TYPE_UINT, \ G_TYPE_UINT, \ G_TYPE_UINT, \ G_TYPE_UINT, \ G_TYPE_INVALID)) /* Return a pointer to GValue with boxed stream struct */ static gpointer describe_stream (struct stream_state *ss) { const GType ElementType = TP_CHANNEL_STREAM_TYPE; GValue element[1] = {{ 0 }}; g_value_init (element, ElementType); g_value_take_boxed (element, dbus_g_type_specialized_construct (ElementType)); dbus_g_type_struct_set (element, 0, ss->id, 1, ss->handle, 2, ss->type, 3, ss->state, 4, ss->direction, 5, ss->pending, G_MAXUINT); return g_value_get_boxed (element); } static gpointer describe_null_media (TpMediaStreamType tptype) { struct stream_state ss[1] = {{ 0 }}; ss->type = tptype; return describe_stream (ss); } static GPtrArray * list_media_streams (TpSvcChannelTypeStreamedMedia *iface) { RingStreamedMediaMixin *mixin = RING_STREAMED_MEDIA_MIXIN (iface); RingStreamedMediaMixinPrivate *priv = mixin->priv; GPtrArray *list; size_t size; size = (priv->audio->id != 0) + (priv->video->id != 0); list = g_ptr_array_sized_new(size); if (priv->audio->id) g_ptr_array_add(list, describe_stream(priv->audio)); if (priv->video->id) g_ptr_array_add(list, describe_stream(priv->video)); return list; } static void free_media_stream_list (GPtrArray *list) { const GType ElementType = TP_CHANNEL_STREAM_TYPE; guint i; if (list) { for (i = list->len; i-- > 0;) g_boxed_free(ElementType, g_ptr_array_index(list, i)); g_ptr_array_free(list, TRUE); } } /** DBus method ListStreams ( ) -> a(uuuuuu) * * Returns an array of structs representing the streams currently active * within this channel. Each stream is identified by an unsigned integer * which is unique for each stream within the channel. * * Returns * * a(uuuuuu) * An array of structs containing: * * * the stream identifier * * the contact handle who the stream is with * (or 0 if the stream represents more than a single member) * * the type of the stream * * the current stream state * * the current direction of the stream * * the current pending send flags */ static void ring_streamed_media_list_streams (TpSvcChannelTypeStreamedMedia *iface, DBusGMethodInvocation *context) { GPtrArray *list = list_media_streams (iface); tp_svc_channel_type_streamed_media_return_from_list_streams (context, list); free_media_stream_list (list); } /** Update media stream state. * * @retval 0 if nothing changed * @retval 1 (or nonzero) if state changed */ static int update_media_stream (TpSvcChannelTypeStreamedMedia *iface, TpHandle handle, guint id, TpMediaStreamType type, TpMediaStreamState state, TpMediaStreamDirection direction, TpMediaStreamPendingSend pending) { RingStreamedMediaMixin *mixin = RING_STREAMED_MEDIA_MIXIN (iface); RingStreamedMediaMixinPrivate *priv = mixin->priv; struct stream_state *ss; int changed = 0; if (id == TP_MEDIA_STREAM_TYPE_AUDIO) ss = priv->audio; else if (id == TP_MEDIA_STREAM_TYPE_VIDEO) ss = priv->video; else return 0; if (state == TP_MEDIA_STREAM_STATE_DISCONNECTED) { if (ss->id != id) return 0; /* emit StreamRemoved */ tp_svc_channel_type_streamed_media_emit_stream_removed (iface, id); memset (ss, 0, sizeof ss); return 1; } /* emit StreamAdded */ if (ss->id != id) { changed = 1; if (handle == 0) { g_object_get (iface, "peer", &handle, NULL); } ss->id = id; ss->handle = handle; ss->type = type; DEBUG ("emitting StreamAdded(%u, %d, %s)", id, handle, type == TP_MEDIA_STREAM_TYPE_AUDIO ? "AUDIO" : type == TP_MEDIA_STREAM_TYPE_VIDEO ? "VIDEO" : "???"); tp_svc_channel_type_streamed_media_emit_stream_added (iface, id, handle, type); } /* emit StreamStateChanged */ if (ss->state != state) { changed = 1; ss->state = state; DEBUG("emitting StreamStateChanged(%u, %s)", ss->id, state == TP_MEDIA_STREAM_STATE_DISCONNECTED ? "DISCONNECTED" : state == TP_MEDIA_STREAM_STATE_CONNECTING ? "CONNECTING" : state == TP_MEDIA_STREAM_STATE_CONNECTED ? "CONNECTED" : "???"); tp_svc_channel_type_streamed_media_emit_stream_state_changed (iface, ss->id, state); } /* emit StreamDirectionChanged */ if (ss->direction != direction || ss->pending != pending) { changed = 1; ss->direction = direction; ss->pending = pending; DEBUG("emitting StreamDirectionChanged(%u, %s,%s%s%s)", ss->id, direction == TP_MEDIA_STREAM_DIRECTION_BIDIRECTIONAL ? "BIDIRECTIONAL" : direction == TP_MEDIA_STREAM_DIRECTION_SEND ? "SEND" : direction == TP_MEDIA_STREAM_DIRECTION_RECEIVE ? "RECV" : "NONE", pending & TP_MEDIA_STREAM_PENDING_REMOTE_SEND ? " remote" : "", pending & TP_MEDIA_STREAM_PENDING_LOCAL_SEND ? " local" : "", pending == 0 ? " 0" : ""); tp_svc_channel_type_streamed_media_emit_stream_direction_changed (iface, ss->id, ss->direction, ss->pending); } return changed; } /** DBus method RequestStreams ( u: contact_handle, au: types ) -> a(uuuuuu) * * Request that streams be established to exchange the given types of media * with the given member. In general this will try and establish a * bidirectional stream, but on some protocols it may not be possible to * indicate to the peer that you would like to receive media, so a send-only * stream will be created initially. In the cases where the stream requires * remote agreement (eg you wish to receive media from them), the * StreamDirectionChanged signal will be emitted with the * MEDIA_STREAM_PENDING_REMOTE_SEND flag set, and the signal emitted again * with the flag cleared when the remote end has replied. * */ static void ring_streamed_media_request_streams (TpSvcChannelTypeStreamedMedia *iface, guint handle, const GArray *media_types, DBusGMethodInvocation *context) { RingStreamedMediaMixin *mixin = RING_STREAMED_MEDIA_MIXIN (iface); RingStreamedMediaMixinPrivate *priv = mixin->priv; RingStreamedMediaMixinClass *klass; GError *error = NULL; GPtrArray *list; guint i, create_audio_stream = 0, create_video_stream = 0; DEBUG("()"); klass = RING_STREAMED_MEDIA_MIXIN_GET_CLASS (iface); g_assert (klass->validate_handle != NULL); g_assert (klass->create_streams != NULL); if (!klass->validate_handle (iface, &handle, &error)) { dbus_g_method_return_error (context, error); g_clear_error (&error); return; } /* We can create media only when call has not been initiated */ for (i = 0; i < media_types->len; i++) { guint media_type = g_array_index(media_types, guint, i); if (media_type == TP_MEDIA_STREAM_TYPE_AUDIO) { if (priv->audio->id == 0) continue; if (!klass->create_streams (iface, handle, TRUE, FALSE, &error)) { dbus_g_method_return_error(context, error); g_clear_error(&error); return; } create_audio_stream = TRUE; update_media_stream (iface, handle, RING_MEDIA_STREAM_ID_AUDIO, TP_MEDIA_STREAM_TYPE_AUDIO, TP_MEDIA_STREAM_STATE_CONNECTING, TP_MEDIA_STREAM_DIRECTION_NONE, TP_MEDIA_STREAM_PENDING_LOCAL_SEND | TP_MEDIA_STREAM_PENDING_REMOTE_SEND); } } list = g_ptr_array_sized_new (media_types->len); for (i = 0; i < media_types->len; i++) { guint media_type = g_array_index(media_types, guint, i); gpointer element; if (media_type == TP_MEDIA_STREAM_TYPE_AUDIO && create_audio_stream) element = describe_stream(priv->audio), create_audio_stream = 0; else if (media_type == TP_MEDIA_STREAM_TYPE_VIDEO && create_video_stream) element = describe_stream(priv->video), create_video_stream = 0; else element = describe_null_media(media_type); g_ptr_array_add(list, element); } tp_svc_channel_type_streamed_media_return_from_request_streams (context, list); free_media_stream_list (list); DEBUG("exit"); } void ring_streamed_media_mixin_iface_init (gpointer g_iface, gpointer iface_data) { TpSvcChannelTypeStreamedMediaClass *klass = (TpSvcChannelTypeStreamedMediaClass *)g_iface; #define IMPLEMENT(x) tp_svc_channel_type_streamed_media_implement_##x (klass, \ ring_streamed_media_##x) IMPLEMENT (list_streams); IMPLEMENT (request_streams); #undef IMPLEMENT } /** Update audio state. * * @retval 0 if nothing changed * @retval 1 (or nonzero) if state changed */ int ring_streamed_media_mixin_update_audio (gpointer _self, TpHandle handle, TpMediaStreamState state, TpMediaStreamDirection direction, TpMediaStreamPendingSend pending) { return update_media_stream (TP_SVC_CHANNEL_TYPE_STREAMED_MEDIA (_self), handle, RING_MEDIA_STREAM_ID_AUDIO, TP_MEDIA_STREAM_TYPE_AUDIO, state, direction, pending); } gboolean ring_streamed_media_mixin_is_audio_stream (gpointer iface, guint stream_id) { RingStreamedMediaMixin *mixin = RING_STREAMED_MEDIA_MIXIN (iface); return stream_id != 0 && stream_id == mixin->priv->audio->id; } gboolean ring_streamed_media_mixin_is_stream_connected (gpointer iface, guint stream_id) { RingStreamedMediaMixin *mixin = RING_STREAMED_MEDIA_MIXIN (iface); if (stream_id == 0) return FALSE; if (stream_id == mixin->priv->audio->id) return mixin->priv->audio->state == TP_MEDIA_STREAM_STATE_CONNECTED; if (stream_id == mixin->priv->video->id) return mixin->priv->video->state == TP_MEDIA_STREAM_STATE_CONNECTED; return FALSE; } telepathy-ring-2.3.24-37ad7111424725a5446e82dac516d7cab88149c7/src/ring-streamed-media-mixin.h000066400000000000000000000074531251541261300276650ustar00rootroot00000000000000/* * ring-streamed-media-mixin.h - Header for RingStreamedMediaMixin * * Copyright (C) 2011 Nokia Corporation * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of the * License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 * USA */ #ifndef RING_STREAMED_MEDIA_MIXIN_H #define RING_STREAMED_MEDIA_MIXIN_H #include #include G_BEGIN_DECLS typedef struct _RingStreamedMediaMixinClass RingStreamedMediaMixinClass; typedef struct _RingStreamedMediaMixin RingStreamedMediaMixin; typedef struct _RingStreamedMediaMixinPrivate RingStreamedMediaMixinPrivate; typedef gboolean (*RingStreamedMediaMixinValidateMediaHandle) (gpointer, guint *handle, GError **); typedef gboolean (*RingStreamedMediaMixinCreateStreams) (gpointer, guint handle, gboolean audio, gboolean video, GError **); struct _RingStreamedMediaMixinClass { RingStreamedMediaMixinValidateMediaHandle validate_handle; RingStreamedMediaMixinCreateStreams create_streams; }; struct _RingStreamedMediaMixin { RingStreamedMediaMixinPrivate *priv; }; /* TYPE MACROS */ #define RING_STREAMED_MEDIA_MIXIN_CLASS_OFFSET_QUARK \ (ring_streamed_media_mixin_class_get_offset_quark ()) #define RING_STREAMED_MEDIA_MIXIN_CLASS_OFFSET(klass) \ (tp_mixin_class_get_offset (klass, \ RING_STREAMED_MEDIA_MIXIN_CLASS_OFFSET_QUARK)) #define RING_STREAMED_MEDIA_MIXIN_CLASS(klass) \ ((RingStreamedMediaMixinClass *) \ tp_mixin_offset_cast (klass, \ RING_STREAMED_MEDIA_MIXIN_CLASS_OFFSET (klass))) #define RING_STREAMED_MEDIA_MIXIN_GET_CLASS(object) \ ring_streamed_media_mixin_class (G_OBJECT_GET_CLASS (object)) GQuark ring_streamed_media_mixin_class_get_offset_quark (void); void ring_streamed_media_mixin_class_init (GObjectClass *klass, glong offset, RingStreamedMediaMixinValidateMediaHandle validate_handle, RingStreamedMediaMixinCreateStreams create_streams); #define RING_STREAMED_MEDIA_MIXIN_OFFSET_QUARK \ (ring_streamed_media_mixin_get_offset_quark ()) #define RING_STREAMED_MEDIA_MIXIN_OFFSET(object) \ (tp_mixin_instance_get_offset (object, \ RING_STREAMED_MEDIA_MIXIN_OFFSET_QUARK)) #define RING_STREAMED_MEDIA_MIXIN(object) \ ((RingStreamedMediaMixin *) \ tp_mixin_offset_cast (object, \ RING_STREAMED_MEDIA_MIXIN_OFFSET (object))) GQuark ring_streamed_media_mixin_get_offset_quark (void); void ring_streamed_media_mixin_init (GObject *object, gsize offset); void ring_streamed_media_mixin_finalize (GObject *object); void ring_streamed_media_mixin_iface_init (gpointer iface, gpointer data); /* RingStreamedMediaMixin interface */ void ring_streamed_media_mixin_fill_immutable_properties (gpointer iface, GHashTable *properties); int ring_streamed_media_mixin_update_audio (gpointer iface, TpHandle handle, TpMediaStreamState state, TpMediaStreamDirection direction, TpMediaStreamPendingSend pending); gboolean ring_streamed_media_mixin_is_audio_stream (gpointer iface, guint stream_id); gboolean ring_streamed_media_mixin_is_stream_connected (gpointer iface, guint stream_id); G_END_DECLS #endif /* #ifndef RING_STREAMED_MEDIA_MIXIN_H */ telepathy-ring-2.3.24-37ad7111424725a5446e82dac516d7cab88149c7/src/ring-text-channel.c000066400000000000000000000711751251541261300262510ustar00rootroot00000000000000/* * ring-text-channel.c - Source for RingTextChannel * * Copyright (C) 2007-2010 Nokia Corporation * @author Pekka Pessi * @author Lassi Syrjala * * This work is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This work is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this work; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "config.h" #define DEBUG_FLAG RING_DEBUG_SMS #include "ring-debug.h" #include "ring-text-channel.h" #include "ring-text-manager.h" #include "ring-connection.h" #include "ring-param-spec.h" #include "ring-util.h" #include #include #include #include #include #include #include #include #include #include #if nomore #include #include #include #include #include #include #endif #include static void ring_text_channel_destroyable_iface_init(gpointer, gpointer); G_DEFINE_TYPE_WITH_CODE (RingTextChannel, ring_text_channel, TP_TYPE_BASE_CHANNEL, G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CHANNEL_TYPE_TEXT, tp_message_mixin_text_iface_init); G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CHANNEL_INTERFACE_DESTROYABLE, ring_text_channel_destroyable_iface_init); G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CHANNEL_INTERFACE_SMS, NULL); G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CHANNEL_INTERFACE_MESSAGES, tp_message_mixin_messages_iface_init)); static const char * const ring_text_channel_interfaces[] = { TP_IFACE_CHANNEL_INTERFACE_DESTROYABLE, TP_IFACE_CHANNEL_INTERFACE_MESSAGES, TP_IFACE_CHANNEL_INTERFACE_SMS, NULL }; /* type definition stuff */ enum { PROP_NONE, PROP_SMS_FLASH, PROP_SMS_CHANNEL, N_PROPS }; struct _RingTextChannelPrivate { char *destination; GQueue sending[1]; unsigned sms_flash:1; /* c.n.T.Channel.Interface.SMS.Flash */ unsigned :0; }; /* ---------------------------------------------------------------------- */ static void ring_text_base_channel_class_init (RingTextChannelClass *klass); static void ring_text_channel_close (TpBaseChannel *base); #if nomore static void ring_text_channel_set_receive_timestamps(RingTextChannel *self, TpMessage *msg, gpointer sms); #endif /* Sending */ static void modem_sms_request_send_reply(ModemSMSService *, ModemRequest *request, char const *token, GError const *error, gpointer _self); static void ring_text_channel_send(GObject *_self, TpMessage *message, TpMessageSendingFlags flags); /* ---------------------------------------------------------------------- */ /* Message types to send on this channel */ static TpChannelTextMessageType ring_text_channel_message_types[] = { TP_CHANNEL_TEXT_MESSAGE_TYPE_NORMAL, }; static char const text_plain[] = "text/plain"; static char const text_vcard[] = "text/x-vcard"; /* vcard 1.0 */ static char const text_vcalendar[] = "text/x-calendar"; /* vcal 1.0 */ /* Supported MIME types for messages mixin */ char const * const * ring_text_get_content_types(void) { static char const * const content_types[] = { text_plain, text_vcard, #if notyet text_vcalendar, #endif NULL }; return content_types; } /* ---------------------------------------------------------------------- */ /* GObject interface for RingTextChannel */ static void ring_text_channel_init(RingTextChannel *self) { self->priv = G_TYPE_INSTANCE_GET_PRIVATE( self, RING_TYPE_TEXT_CHANNEL, RingTextChannelPrivate); g_queue_init(self->priv->sending); } static void ring_text_channel_constructed(GObject *object) { RingTextChannel *self = RING_TEXT_CHANNEL(object); TpBaseChannel *base = TP_BASE_CHANNEL (self); RingTextChannelPrivate *priv = self->priv; TpBaseConnection *connection = tp_base_channel_get_connection (base); TpHandle target = tp_base_channel_get_target_handle (base); TpHandleRepoIface *repo; char const *target_id; gboolean valid; DEBUG ("(%p)", self); if (G_OBJECT_TYPE (object) != RING_TYPE_TEXT_CHANNEL) DEBUG ("Initializing derived text channel %s", G_OBJECT_TYPE_NAME (object)); if (G_OBJECT_CLASS(ring_text_channel_parent_class)->constructed) G_OBJECT_CLASS(ring_text_channel_parent_class)->constructed(object); repo = tp_base_connection_get_handles (connection, TP_HANDLE_TYPE_CONTACT); target_id = tp_handle_inspect (repo, target); priv->destination = ring_text_channel_destination (target_id); valid = modem_sms_is_valid_address (priv->destination); if (!valid) /* Invalid destination - allow channel creation, but refuse sending */ DEBUG ("Destination '%s' invalid", priv->destination); tp_message_mixin_init (object, G_STRUCT_OFFSET (RingTextChannel, message), connection); if (valid && !priv->sms_flash) { tp_message_mixin_implement_sending (object, ring_text_channel_send, G_N_ELEMENTS (ring_text_channel_message_types), ring_text_channel_message_types, 0, /* No attachments */ TP_DELIVERY_REPORTING_SUPPORT_FLAG_RECEIVE_FAILURES | TP_DELIVERY_REPORTING_SUPPORT_FLAG_RECEIVE_SUCCESSES, ring_text_get_content_types ()); } else { tp_message_mixin_implement_sending (object, NULL, 0, NULL, 0, 0, ring_text_get_content_types ()); } tp_base_channel_register (base); } static void ring_text_channel_get_property(GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { RingTextChannel *self = RING_TEXT_CHANNEL (object); RingTextChannelPrivate *priv = self->priv; switch (property_id) { case PROP_SMS_FLASH: g_value_set_boolean(value, priv->sms_flash); break; case PROP_SMS_CHANNEL: g_value_set_boolean (value, TRUE); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void ring_text_channel_set_property(GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { RingTextChannel *self = RING_TEXT_CHANNEL (object); RingTextChannelPrivate *priv = self->priv; switch (property_id) { case PROP_SMS_FLASH: priv->sms_flash = g_value_get_boolean(value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void ring_text_channel_dispose(GObject *object) { RingTextChannel *self = RING_TEXT_CHANNEL (object); RingTextChannelPrivate *priv = self->priv; while (!g_queue_is_empty (priv->sending)) modem_request_cancel (g_queue_pop_head (priv->sending)); ((GObjectClass *)ring_text_channel_parent_class)->dispose (object); } static void ring_text_channel_finalize(GObject *object) { RingTextChannel *self = RING_TEXT_CHANNEL (object); RingTextChannelPrivate *priv = self->priv; g_free(priv->destination), priv->destination = NULL; while (!g_queue_is_empty(priv->sending)) modem_request_cancel(g_queue_pop_head(priv->sending)); tp_message_mixin_finalize(object); ((GObjectClass *)ring_text_channel_parent_class)->finalize (object); } /* Properties for o.f.T.Channel.Interface.SMS */ static TpDBusPropertiesMixinPropImpl sms_properties[] = { { "Flash", "sms-flash" }, #if HAVE_TP_SMS_CHANNEL { "SMSChannel", "sms-channel" }, #endif { NULL } }; static TpDBusPropertiesMixinIfaceImpl ring_text_channel_dbus_property_interfaces[] = { { TP_IFACE_CHANNEL_INTERFACE_SMS, tp_dbus_properties_mixin_getter_gobject_properties, NULL, sms_properties, }, { NULL } }; static void ring_text_channel_class_init(RingTextChannelClass *klass) { GObjectClass *object_class = (GObjectClass *) klass; static gboolean properties_initialized = FALSE; g_type_class_add_private(klass, sizeof (RingTextChannelPrivate)); object_class->constructed = ring_text_channel_constructed; object_class->set_property = ring_text_channel_set_property; object_class->get_property = ring_text_channel_get_property; object_class->dispose = ring_text_channel_dispose; object_class->finalize = ring_text_channel_finalize; g_object_class_install_property(object_class, PROP_SMS_FLASH, g_param_spec_boolean("sms-flash", "Channel for Flash SMS Messages", "This channel is only used to receive " "Flash SMS messages", FALSE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (object_class, PROP_SMS_CHANNEL, g_param_spec_boolean ("sms-channel", "This channel is used with SMS", "Messages sent and received on this channel are transmitted via SMS", TRUE, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); ring_text_base_channel_class_init (klass); if (properties_initialized) return; properties_initialized = TRUE; klass->dbus_properties_class.interfaces = ring_text_channel_dbus_property_interfaces; tp_dbus_properties_mixin_class_init (object_class, G_STRUCT_OFFSET (RingTextChannelClass, dbus_properties_class)); tp_message_mixin_init_dbus_properties (object_class); } /* ---------------------------------------------------------------------- */ static void ring_text_channel_close (TpBaseChannel *base) { if (tp_message_mixin_has_pending_messages ((gpointer)base, NULL)) { DEBUG ("Resurrecting because of pending messages"); tp_message_mixin_set_rescued ((gpointer)base); tp_base_channel_reopened (base, tp_base_channel_get_target_handle (base)); } else { tp_base_channel_destroyed (base); } } static void ring_text_channel_fill_immutable_properties (TpBaseChannel *base, GHashTable *properties) { TpBaseChannelClass *base_class = TP_BASE_CHANNEL_CLASS (ring_text_channel_parent_class); base_class->fill_immutable_properties (base, properties); tp_dbus_properties_mixin_fill_properties_hash (G_OBJECT (base), properties, TP_IFACE_CHANNEL_INTERFACE_MESSAGES, "MessagePartSupportFlags", #if HAVE_TP_MESSAGE_MIXIN_WITH_DELI TP_IFACE_CHANNEL_INTERFACE_MESSAGES, "DeliveryReportingSupport", #endif TP_IFACE_CHANNEL_INTERFACE_MESSAGES, "SupportedContentTypes", TP_IFACE_CHANNEL_INTERFACE_SMS, "Flash", #if HAVE_TP_SMS_CHANNEL TP_IFACE_CHANNEL_INTERFACE_SMS, "SMSChannel", #endif NULL); } static void ring_text_base_channel_class_init (RingTextChannelClass *klass) { TpBaseChannelClass *base_class = TP_BASE_CHANNEL_CLASS (klass); base_class->channel_type = TP_IFACE_CHANNEL_TYPE_TEXT; base_class->target_handle_type = TP_HANDLE_TYPE_CONTACT; base_class->interfaces = (gchar const **)ring_text_channel_interfaces; base_class->close = ring_text_channel_close; base_class->fill_immutable_properties = ring_text_channel_fill_immutable_properties; } /* ====================================================================== */ /* Channel.Interface.Destroyable */ static void ring_text_channel_destroy (RingTextChannel *self) { tp_message_mixin_clear ((gpointer)self); ring_text_channel_close (TP_BASE_CHANNEL (self)); } static void ring_text_channel_method_destroy(TpSvcChannelInterfaceDestroyable *iface, DBusGMethodInvocation *context) { ring_text_channel_destroy(RING_TEXT_CHANNEL(iface)); tp_svc_channel_interface_destroyable_return_from_destroy(context); } static void ring_text_channel_destroyable_iface_init (gpointer iface, gpointer data) { TpSvcChannelInterfaceDestroyableClass *klass = iface; #define IMPLEMENT(x) \ tp_svc_channel_interface_destroyable_implement_##x \ (klass, ring_text_channel_method_ ## x) IMPLEMENT(destroy); #undef IMPLEMENT } /* ---------------------------------------------------------------------- */ /* message_mixin interface */ static GValue const * my_message_mixin_get_value(TpMessage const *message, guint part, char const *key) { GHashTable const *dict; dict = tp_message_peek((TpMessage *)message, part); if (!dict) return NULL; return g_hash_table_lookup((GHashTable *)dict, key); } static char const * my_message_mixin_get_string(TpMessage const *message, guint part, char const *key, char const *defaults) { GValue const *value = my_message_mixin_get_value(message, part, key); if (value == NULL || !G_VALUE_HOLDS_STRING(value)) return defaults; return g_value_get_string(value); } #if nomore static GArray const * my_message_mixin_get_bytearray(TpMessage const *message, guint part, char const *key, GArray const *defaults) { GValue const *value = my_message_mixin_get_value(message, part, key); if (value == NULL || !G_VALUE_HOLDS(value, DBUS_TYPE_G_UCHAR_ARRAY)) return defaults; return g_value_get_boxed(value); } static guint32 my_message_mixin_get_uint(TpMessage const *message, guint part, char const *key, guint32 defaults) { GValue const *value = my_message_mixin_get_value(message, part, key); if (value == NULL || !G_VALUE_HOLDS_UINT(value)) return defaults; return g_value_get_uint(value); } #endif /** Convert handle inspection to a destination acceptable by modem. * * Remove supported service prefixes and dial strings. */ char * ring_text_channel_destination(char const *inspection) { if (modem_call_is_valid_address(inspection)) { /* Ignore prefix to suppress CLIR (*31#) */ if (ring_str_starts_with(inspection, "*31#")) inspection += 4; /* Ignore dialstring */ return g_strndup(inspection, strcspn(inspection, "PXw")); } else { return g_strdup(""); } } static ModemSMSService * ring_text_channel_get_sms_service (RingTextChannel *self) { TpBaseChannel *base = TP_BASE_CHANNEL (self); TpBaseConnection *base_connection; RingConnection *connection; ModemOface *oface; base_connection = tp_base_channel_get_connection (base); connection = RING_CONNECTION (base_connection); oface = ring_connection_get_modem_interface (connection, MODEM_OFACE_SMS); if (oface) return MODEM_SMS_SERVICE (oface); else return NULL; } static void ring_text_channel_send(GObject *_self, TpMessage *msg, TpMessageSendingFlags flags) { RingTextChannel *self = RING_TEXT_CHANNEL(_self); RingTextChannelPrivate *priv = self->priv; ModemSMSService *sms_service = ring_text_channel_get_sms_service (self); #if nomore gboolean srr; guint32 sms_class; char const *smsc; #endif char const *type; char const *text; ModemRequest *request; GError *error; g_assert(tp_message_count_parts(msg) >= 1); error = NULL; type = my_message_mixin_get_string(msg, 1, "content-type", ""); if (!type[0]) type = my_message_mixin_get_string(msg, 1, "type", ""); if (!type[0]) { GError invalid = { TP_ERRORS, TP_ERROR_INVALID_ARGUMENT, "No content type" }; tp_message_mixin_sent(_self, msg, flags, NULL, &invalid); return; } if (sms_service == NULL) { GError failed = { TP_ERRORS, TP_ERROR_NOT_AVAILABLE, "SMS service is not available" }; tp_message_mixin_sent (_self, msg, flags, NULL, &failed); return; } /* The nomore'd stuff is currently not supported by Ofono */ #if nomore /* Status report request */ srr = (flags & TP_MESSAGE_SENDING_FLAG_REPORT_DELIVERY) != 0; sms_class = my_message_mixin_get_uint(msg, 0, "sms-class", 0xff); if (0 <= sms_class && sms_class <= 3) sms_g_submit_set_sms_class(submit, sms_class); smsc = my_message_mixin_get_string(msg, 0, "sms-service-centre", NULL); if (smsc == NULL || strlen(smsc) == 0) smsc = my_message_mixin_get_string(msg, 0, "sms-smsc", NULL); if (smsc == NULL || strlen(smsc) == 0) smsc = my_message_mixin_get_string(msg, 0, "smsc", NULL); if (smsc != NULL && strlen(smsc) != 0) sms_g_submit_set_smsc(submit, smsc); #endif text = my_message_mixin_get_string(msg, 1, "content", ""); if (g_strcasecmp(type, text_plain) == 0) { DEBUG("Send(destination = %s," /*class = %u,*/ "text = \"%s\")", priv->destination, /*sms_class,*/ text); } #if nomore else if (g_strcasecmp(type, "text/x-vcard") == 0 || g_strcasecmp(type, "text/directory") == 0 || g_strcasecmp(type, "text/vcard") == 0) { GArray b = { (gpointer)text, strlen(text) }; GArray const *binary; binary = my_message_mixin_get_bytearray(msg, 1, "content", &b); if (binary && binary->data) { encoded = sms_g_submit_binary(submit, binary, &error); } else { g_set_error(&error, TP_ERRORS, TP_ERROR_INVALID_ARGUMENT, "No content"); } } #endif else { g_set_error(&error, TP_ERRORS, TP_ERROR_INVALID_ARGUMENT, "Unknown content type"); } request = modem_sms_request_send (sms_service, priv->destination, text, modem_sms_request_send_reply, self); if (request == NULL) { GError failed = { TP_ERRORS, TP_ERROR_NETWORK_ERROR, "Modem connection failed" }; tp_message_mixin_sent(_self, msg, flags, NULL, &failed); return; } modem_request_add_data(request, "tp-message", msg); modem_request_add_data(request, "tp-flags", GUINT_TO_POINTER(flags)); g_queue_push_tail(priv->sending, request); } static void modem_sms_request_send_reply(ModemSMSService *service, ModemRequest *request, char const *token, GError const *send_error, gpointer _self) { RingTextChannel *self = RING_TEXT_CHANNEL(_self); RingTextChannelPrivate *priv = self->priv; TpMessage *msg = modem_request_get_data(request, "tp-message"); GError *error = NULL; guint flags = GPOINTER_TO_UINT(modem_request_get_data(request, "tp-flags")); g_assert(msg); g_queue_remove(priv->sending, request); if (!send_error) { DEBUG("Send(%p) token=\"%s\"", msg, token); tp_message_set_int64(msg, 0, "message-sent", (gint64)time(NULL)); } else { if (send_error && send_error->domain == DBUS_GERROR) g_set_error_literal(&error, TP_ERRORS, TP_ERROR_NETWORK_ERROR, send_error->message); else if (send_error) error = g_error_copy(send_error); else g_set_error_literal(&error, TP_ERRORS, TP_ERROR_NETWORK_ERROR, "Internal error"); DEBUG("Send(%p) GError(%u, '%s, %s)", msg, error->code, g_quark_to_string(error->domain), error->message); } tp_message_mixin_sent((GObject *)self, msg, flags, token, error); } /* ------------------------------------------------------------------------ */ /* RingTextChannel interface */ #if nomore gboolean ring_text_channel_can_handle(gpointer sms) { return sms_g_deliver_is_text(sms) || sms_g_deliver_is_vcard(sms); } void ring_text_channel_receive_deliver(RingTextChannel *self, gpointer sms) { TpBaseChannel *base = TP_BASE_CHANNEL (self); TpBaseConnection *connection = tp_base_channel_get_connection (base); char const *message_token = sms_g_deliver_get_message_token(sms); TpMessage *msg; guint id; DEBUG("enter"); g_assert (ring_text_channel_can_handle(sms)); msg = tp_message_new (connection, 2, 2); tp_message_set_handle (msg, 0, "message-sender", TP_HANDLE_TYPE_CONTACT, tp_base_channel_get_target_handle (base)); tp_message_set_string(msg, 0, "message-token", message_token); ring_text_channel_set_receive_timestamps(self, msg, sms); guint32 sms_class = sms_g_deliver_get_sms_class(sms); if (0 <= sms_class && sms_class <= 3) { tp_message_set_uint32(msg, 0, "sms-class", sms_class); } tp_message_set_string(msg, 0, "sms-service-centre", sms_g_deliver_get_smsc(sms)); #if nomore { char *mwi_type = NULL; guint mwi_line = 0, mwi_messages = 0; gboolean mwi_active = FALSE, mwi_discard = FALSE; TpChannelTextMessageType msg_type; g_object_get(sms, "mwi-type", &mwi_type, "mwi-active", &mwi_active, "mwi-discard", &mwi_discard, "mwi-line", &mwi_line, "mwi-messages", &mwi_messages, NULL); if (mwi_type) { msg_type = TP_CHANNEL_TEXT_MESSAGE_TYPE_NOTICE; /* XXX: waiting for upstream tp-glib to get these */ if (g_str_equal(mwi_type, "voice")) tp_message_set_string(msg, 0, NOKIA_VOICEMAIL_TYPE, "tel"); tp_message_set_string(msg, 0, "sms-mwi-type", mwi_type); tp_message_set_string(msg, 0, NOKIA_MAILBOX_NOTIFICATION, mwi_type); if (strcmp(mwi_type, "return-call")) { tp_message_set_boolean(msg, 0, NOKIA_MAILBOX_HAS_UNREAD, mwi_active); tp_message_set_boolean(msg, 0, "sms-mwi-active", mwi_active); if (mwi_line) tp_message_set_uint32(msg, 0, "sms-mwi-line", mwi_line); if (mwi_messages > 0 && mwi_messages < 255) { tp_message_set_uint32(msg, 0, "sms-mwi-messages", mwi_messages); tp_message_set_uint32(msg, 0, NOKIA_MAILBOX_UNREAD_COUNT, mwi_messages); } if (mwi_discard) { tp_message_set_boolean(msg, 0, "sms-mwi-discard", mwi_discard); tp_message_set_boolean(msg, 0, NOKIA_MAILBOX_DISCARD_TEXT, mwi_discard); } } g_free(mwi_type); } else { msg_type = TP_CHANNEL_TEXT_MESSAGE_TYPE_NORMAL; } tp_message_set_uint32(msg, 0, "message-type", msg_type); } #endif tp_message_set_uint32(msg, 0, "message-type", TP_CHANNEL_TEXT_MESSAGE_TYPE_NORMAL); gboolean string = FALSE, bytes = FALSE; if (sms_g_deliver_is_text(sms)) { tp_message_set_string(msg, 1, "content-type", text_plain); tp_message_set_string(msg, 1, "type", text_plain); string = TRUE; } else if (sms_g_deliver_is_vcard(sms)) { tp_message_set_string(msg, 1, "content-type", text_vcard); tp_message_set_string(msg, 1, "type", text_vcard); bytes = TRUE; } #if notyet else if (sms_g_deliver_is_vcalendar(sms)) { tp_message_set_string(msg, 1, "content-type", text_vcalendar); tp_message_set_string(msg, 1, "type", text_vcalendar); bytes = TRUE; } #endif GArray const *binary = sms_g_deliver_get_binary(sms); char const *text = sms_g_deliver_get_text(sms); if (string) { if (text) { tp_message_set_string(msg, 1, "content", text); } else { tp_message_set_string(msg, 1, "content", ""); } } else if (bytes) { if (binary) { tp_message_set_bytes(msg, 1, "content", binary->len, binary->data); } else if (text) { tp_message_set_bytes(msg, 1, "content", strlen(text), text); } else { tp_message_set_bytes(msg, 1, "content", 0, ""); } } id = tp_message_mixin_take_received((GObject *) self, msg); DEBUG("message mixin received with id=%u", id); } #endif void ring_text_channel_receive_text (RingTextChannel *self, gchar const *message_token, gchar const *message, gint64 message_sent, gint64 message_received, guint32 sms_class) { TpBaseChannel *base = TP_BASE_CHANNEL (self); TpBaseConnection *connection = tp_base_channel_get_connection (base); TpMessage *msg; guint id; DEBUG("enter"); msg = tp_message_new (connection, 2, 2); tp_message_set_handle (msg, 0, "message-sender", TP_HANDLE_TYPE_CONTACT, tp_base_channel_get_target_handle (base)); tp_message_set_string (msg, 0, "message-token", message_token); tp_message_set_uint32 (msg, 0, "message-type", TP_CHANNEL_TEXT_MESSAGE_TYPE_NORMAL); tp_message_set_int64 (msg, 0, "message-sent", message_sent); tp_message_set_int64 (msg, 0, "message-received", message_received); if (0 <= sms_class && sms_class <= 3) { tp_message_set_uint32 (msg, 0, "sms-class", sms_class); } tp_message_set_string (msg, 1, "content-type", text_plain); tp_message_set_string (msg, 1, "type", text_plain); tp_message_set_string (msg, 1, "content", message); id = tp_message_mixin_take_received ((GObject *) self, msg); DEBUG("message mixin received with id=%u", id); } static void ring_text_channel_delivery_report(RingTextChannel *self, char const *token, guint delivery_status, gpointer sr, GError const *error) { TpBaseChannel *base = TP_BASE_CHANNEL (self); TpBaseConnection *connection = tp_base_channel_get_connection (base); TpMessage *msg; guint id; msg = tp_message_new (connection, 1, 1); tp_message_set_handle (msg, 0, "message-sender", TP_HANDLE_TYPE_CONTACT, tp_base_channel_get_target_handle (base)); tp_message_set_uint32 (msg, 0, "message-type", TP_CHANNEL_TEXT_MESSAGE_TYPE_DELIVERY_REPORT); tp_message_set_string(msg, 0, "delivery-token", token); if (delivery_status != TP_DELIVERY_STATUS_UNKNOWN) tp_message_set_uint32(msg, 0, "delivery-status", delivery_status); if (sr) { #if nomore char const *message_token = sms_g_status_report_get_message_token(sr); guint8 failure_cause = sms_g_status_report_get_status(sr); tp_message_set_string(msg, 0, "message-token", message_token); tp_message_set_string(msg, 0, "sms-service-centre", sms_g_status_report_get_smsc(sr)); tp_message_set_uint32(msg, 0, "sms-failure-cause", failure_cause); ring_text_channel_set_receive_timestamps(self, msg, sr); #endif } else { GTimeVal gt[1]; g_get_current_time(gt); tp_message_set_int64(msg, 0, "message-received", (gint64)gt->tv_sec); tp_message_set_int64(msg, 0, "message-sent", (gint64)gt->tv_sec); tp_message_set_string_printf(msg, 0, "message-token", "%s-%08ld-%08ld", token, gt->tv_sec, gt->tv_usec); } if (error) { char const *prefix = modem_error_domain_prefix(error->domain); if (prefix && prefix[0]) { char ebuffer[16]; tp_message_set_string_printf(msg, 0, "delivery-dbus-error", "%s.%s", prefix, modem_error_name(error, ebuffer, sizeof ebuffer)); tp_message_set_string(msg, 0, "delivery-error-message", error->message); } else if (error->domain == TP_ERRORS) { GEnumClass *klass = g_type_class_ref(TP_TYPE_ERROR); GEnumValue *ev = g_enum_get_value (klass, error->code); g_type_class_unref(klass); tp_message_set_string_printf(msg, 0, "delivery-dbus-error", "%s.%s", TP_ERROR_PREFIX, ev->value_nick); tp_message_set_string(msg, 0, "delivery-error-message", error->message); } else { tp_message_set_string_printf(msg, 0, "delivery-error-message", "%s %u: %s", g_quark_to_string(error->domain), error->code, error->message); } } id = tp_message_mixin_take_received((GObject *) self, msg); DEBUG("delivery report received with id=%u", id); } #if nomore static void ring_text_channel_set_receive_timestamps(RingTextChannel *self, TpMessage *msg, gpointer sms) { g_return_if_fail(SMS_G_IS_MESSAGE(sms)); ModemSMSService *sms_service = ring_text_channel_get_sms_service (self); gint64 sent = 0, received = 0, delivered = 0; gint64 now = (gint64)time(NULL); g_object_get(sms, "time-sent", &sent, "time-received", &received, "time-delivered", &delivered, NULL); tp_message_set_int64(msg, 0, "message-sent", sent); tp_message_set_int64(msg, 0, "message-received", received); if (delivered == 0) { tp_message_set_uint64(msg, 0, "stored-message-received", received); } else { tp_message_set_uint64(msg, 0, "stored-message-received", delivered); if (delivered > modem_sms_service_time_connected (sms_service)) tp_message_set_boolean(msg, 0, "rescued", TRUE); } g_object_set(sms, "time-delivered", now, NULL); } #endif void ring_text_channel_outgoing_sms_complete(RingTextChannel *self, char const *token) { ring_text_channel_delivery_report(self, token, TP_DELIVERY_STATUS_ACCEPTED, NULL, NULL); } void ring_text_channel_outgoing_sms_error(RingTextChannel *self, char const *token, GError const *error) { guint delivery_status; if (0 /*modem_sms_error_is_temporary(error)*/) delivery_status = TP_DELIVERY_STATUS_TEMPORARILY_FAILED; else delivery_status = TP_DELIVERY_STATUS_PERMANENTLY_FAILED; ring_text_channel_delivery_report(self, token, delivery_status, NULL, error); } void ring_text_channel_receive_status_report(RingTextChannel *self, char const *token, gboolean success) { guint delivery_status = success ? TP_DELIVERY_STATUS_DELIVERED : TP_DELIVERY_STATUS_PERMANENTLY_FAILED; ring_text_channel_delivery_report(self, token, delivery_status, NULL, NULL); } telepathy-ring-2.3.24-37ad7111424725a5446e82dac516d7cab88149c7/src/ring-text-channel.h000066400000000000000000000061031251541261300262430ustar00rootroot00000000000000/* * ring-text-channel.h - Header for RingTextChannel * * Copyright (C) 2007-2010 Nokia Corporation * @author Pekka Pessi * * This work is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This work is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this work; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __RING_TEXT_CHANNEL_H__ #define __RING_TEXT_CHANNEL_H__ #include #include #include #include G_BEGIN_DECLS typedef struct _RingTextChannel RingTextChannel; typedef struct _RingTextChannelClass RingTextChannelClass; typedef struct _RingTextChannelPrivate RingTextChannelPrivate; struct _RingTextChannelClass { TpBaseChannelClass parent_class; TpDBusPropertiesMixinClass dbus_properties_class; }; struct _RingTextChannel { TpBaseChannel parent; TpMessageMixin message; RingTextChannelPrivate *priv; }; GType ring_text_channel_get_type (void); /* TYPE MACROS */ #define RING_TYPE_TEXT_CHANNEL \ (ring_text_channel_get_type()) #define RING_TEXT_CHANNEL(obj) \ (G_TYPE_CHECK_INSTANCE_CAST((obj), RING_TYPE_TEXT_CHANNEL, RingTextChannel)) #define RING_TEXT_CHANNEL_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST((klass), RING_TYPE_TEXT_CHANNEL, RingTextChannelClass)) #define RING_IS_TEXT_CHANNEL(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE((obj), RING_TYPE_TEXT_CHANNEL)) #define RING_IS_TEXT_CHANNEL_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE((klass), RING_TYPE_TEXT_CHANNEL)) #define RING_TEXT_CHANNEL_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), RING_TYPE_TEXT_CHANNEL, RingTextChannelClass)) #define RING_TEXT_CHANNEL_CAPABILITY_FLAGS (0) char *ring_text_channel_destination(char const *inspection); #if nomore /* FIXME: the gpointers are temporary hacks */ gboolean ring_text_channel_can_handle(gpointer); void ring_text_channel_receive_deliver(RingTextChannel *, gpointer); void ring_text_channel_receive_status_report(RingTextChannel *, gpointer); #endif void ring_text_channel_receive_text (RingTextChannel *self, gchar const *message_token, gchar const *message, gint64 message_sent, gint64 message_received, guint32 sms_class); void ring_text_channel_outgoing_sms_complete(RingTextChannel *, char const *token); void ring_text_channel_outgoing_sms_error(RingTextChannel *, char const *token, GError const *error); char const * const *ring_text_get_content_types(void); G_END_DECLS #endif telepathy-ring-2.3.24-37ad7111424725a5446e82dac516d7cab88149c7/src/ring-text-manager.c000066400000000000000000000724651251541261300262560ustar00rootroot00000000000000/* * ring-text-manager.c - Manager for text channels * * Copyright (C) 2007-2010 Nokia Corporation * @author Pekka Pessi * * This work is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This work is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this work; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ /* * Based on telepathy-glib/examples/cm/echo/factory.c with notice: * * """ * Copyright (C) 2007 Collabora Ltd. * Copyright (C) 2007 Nokia Corporation * * Copying and distribution of this file, with or without modification, * are permitted in any medium without royalty provided the copyright * notice and this notice are preserved. * """ */ #include "config.h" #define DEBUG_FLAG RING_DEBUG_CONNECTION #include "ring-debug.h" #include "ring-text-manager.h" #include "ring-text-channel.h" #include "ring-connection.h" #include "ring-param-spec.h" #include "ring-util.h" #include #include #include #include #include #include #include #include #include static void channel_manager_iface_init(gpointer, gpointer); G_DEFINE_TYPE_WITH_CODE( RingTextManager, ring_text_manager, G_TYPE_OBJECT, G_IMPLEMENT_INTERFACE (TP_TYPE_CHANNEL_MANAGER, channel_manager_iface_init)); enum { PROP_NONE, PROP_CONNECTION, PROP_SMS_SERVICE, PROP_CAPABILITY_FLAGS, /**< Channel-type-specific capabilities */ PROP_SMSC, /**< SMSC address */ PROP_SMS_VALID, /**< SMS validity period in seconds */ PROP_SMS_REDUCED_CHARSET, /**< SMS reduced character set */ N_PROPS }; struct _RingTextManagerPrivate { RingConnection *connection; char *smsc; guint sms_valid; guint capability_flags; /* object_path => RingTextChannel */ GHashTable *channels; TpConnectionStatus status, cstatus; ModemSMSService *sms_service; guint sms_reduced_charset :1; struct { gulong incoming_message, immediate_message; gulong outgoing_sms_complete, outgoing_sms_error; gulong receiving_sms_status_report; #if nomore gulong receiving_sms_deliver; #endif gulong status_changed; } signals; }; /* ------------------------------------------------------------------------ */ static void ring_text_manager_set_sms_service (RingTextManager *, ModemSMSService *); static void ring_text_manager_connected(RingTextManager *self); static void ring_text_manager_disconnect(RingTextManager *self); static void on_connection_status_changed (TpBaseConnection *conn, guint status, guint reason, RingTextManager *self); static gboolean ring_text_requestotron(RingTextManager *self, gpointer request, GHashTable *properties, gboolean require_mine); static RingTextChannel *ring_text_manager_request(RingTextManager *self, gpointer request, TpHandle initiator, TpHandle target, gboolean require_mine, gboolean class0); static gboolean tp_asv_get_sms_channel (GHashTable *properties); static void on_text_channel_closed(RingTextChannel *, RingTextManager *); static void on_sms_service_outgoing_complete(ModemSMSService *, char const *token, char const *destination, gpointer _self); static void on_sms_service_outgoing_error(ModemSMSService *, char const *token, char const *destination, GError const *error, gpointer _self); static void on_sms_service_status_report(ModemSMSService *, char const *destination, char const *token, gboolean const *success, gpointer _self); static void ring_text_manager_receive_status_report(RingTextManager *self, char const *destination, char const *token, gboolean const *success); #if nomore static void on_sms_service_deliver(ModemSMSService *, SMSGDeliver *, gpointer _self); static void ring_text_manager_receive_deliver( RingTextManager *, SMSGDeliver *); static void ring_text_manager_receive_status_report( RingTextManager *, SMSGStatusReport *); #endif static void on_incoming_message (ModemSMSService *, gchar const *message, GHashTable *info, gpointer user_data); static void on_immediate_message (ModemSMSService *, gchar const *message, GHashTable *info, gpointer user_data); /* ------------------------------------------------------------------------ */ /* GObject interface */ static void ring_text_manager_constructed(GObject *object) { RingTextManager *self = RING_TEXT_MANAGER(object); RingTextManagerPrivate *priv = self->priv; priv->signals.status_changed = g_signal_connect (priv->connection, "status-changed", (GCallback) on_connection_status_changed, self); if (G_OBJECT_CLASS(ring_text_manager_parent_class)->constructed) G_OBJECT_CLASS(ring_text_manager_parent_class)->constructed(object); } static void ring_text_manager_init (RingTextManager *self) { self->priv = G_TYPE_INSTANCE_GET_PRIVATE(self, RING_TYPE_TEXT_MANAGER, RingTextManagerPrivate); self->priv->channels = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, g_object_unref); } static void ring_text_manager_dispose(GObject *object) { RingTextManager *self = RING_TEXT_MANAGER(object); RingTextManagerPrivate *priv = self->priv; ring_signal_disconnect (priv->connection, &priv->signals.status_changed); ring_text_manager_disconnect (self); tp_clear_pointer (&self->priv->channels, g_hash_table_unref); G_OBJECT_CLASS(ring_text_manager_parent_class)->dispose(object); } static void ring_text_manager_finalize(GObject *object) { RingTextManager *self = RING_TEXT_MANAGER(object); RingTextManagerPrivate *priv = self->priv; /* Free any data held directly by the object here */ g_free(priv->smsc); G_OBJECT_CLASS(ring_text_manager_parent_class)->finalize(object); } static void ring_text_manager_get_property(GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { RingTextManager *self = RING_TEXT_MANAGER(object); RingTextManagerPrivate *priv = self->priv; switch (property_id) { case PROP_CONNECTION: g_value_set_object(value, priv->connection); break; case PROP_SMS_SERVICE: g_value_set_pointer (value, priv->sms_service); break; case PROP_SMSC: g_value_set_string(value, priv->smsc ? priv->smsc : ""); break; case PROP_SMS_VALID: g_value_set_uint(value, priv->sms_valid); break; case PROP_SMS_REDUCED_CHARSET: g_value_set_boolean(value, priv->sms_reduced_charset); break; case PROP_CAPABILITY_FLAGS: g_value_set_uint(value, priv->capability_flags); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); } } static void ring_text_manager_set_property(GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { RingTextManager *self = RING_TEXT_MANAGER(object); RingTextManagerPrivate *priv = self->priv; switch (property_id) { case PROP_CONNECTION: /* We don't ref the connection, because it owns a reference to the * manager, and it guarantees that the manager's lifetime is * less than its lifetime */ priv->connection = g_value_get_object(value); break; case PROP_SMS_SERVICE: ring_text_manager_set_sms_service (self, g_value_get_pointer (value)); break; case PROP_SMSC: priv->smsc = g_value_dup_string(value); if (priv->sms_service) g_object_set(priv->sms_service, "service-centre", priv->smsc, NULL); break; case PROP_SMS_VALID: priv->sms_valid = g_value_get_uint(value); if (priv->sms_service) g_object_set(priv->sms_service, "validity-period", priv->sms_valid, NULL); break; case PROP_SMS_REDUCED_CHARSET: priv->sms_reduced_charset = g_value_get_boolean(value); if (priv->sms_service) g_object_set(priv->sms_service, "reduced-charset", priv->sms_reduced_charset, NULL); break; case PROP_CAPABILITY_FLAGS: priv->capability_flags = g_value_get_uint(value) & RING_TEXT_CHANNEL_CAPABILITY_FLAGS; break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); } } static void ring_text_manager_class_init(RingTextManagerClass *klass) { GObjectClass *object_class = (GObjectClass *) klass; g_type_class_add_private(klass, sizeof (RingTextManagerPrivate)); object_class->constructed = ring_text_manager_constructed; object_class->get_property = ring_text_manager_get_property; object_class->set_property = ring_text_manager_set_property; object_class->dispose = ring_text_manager_dispose; object_class->finalize = ring_text_manager_finalize; g_object_class_install_property( object_class, PROP_CONNECTION, ring_param_spec_connection()); g_object_class_install_property (object_class, PROP_SMS_SERVICE, ring_param_spec_sms_service (0)); g_object_class_install_property( object_class, PROP_SMSC, ring_param_spec_smsc()); g_object_class_install_property( object_class, PROP_SMS_VALID, ring_param_spec_sms_valid()); g_object_class_install_property( object_class, PROP_SMS_REDUCED_CHARSET, ring_param_spec_sms_reduced_charset()); g_object_class_install_property(object_class, PROP_CAPABILITY_FLAGS, ring_param_spec_type_specific_capability_flags(G_PARAM_CONSTRUCT, RING_TEXT_CHANNEL_CAPABILITY_FLAGS)); } /* ---------------------------------------------------------------------- */ static void ring_text_manager_set_sms_service (RingTextManager *self, ModemSMSService *service) { RingTextManagerPrivate *priv = self->priv; if (priv->sms_service) { ring_text_manager_disconnect (self); } if (service) { priv->sms_service = g_object_ref (MODEM_SMS_SERVICE (service)); ring_text_manager_connected (self); } } gboolean ring_text_manager_is_connected (void const * _self) { RingTextManager const *self = _self; if (RING_IS_TEXT_MANAGER (_self)) return self->priv->sms_service != NULL; return FALSE; } static void ring_text_manager_connected (RingTextManager *self) { RingTextManagerPrivate *priv = self->priv; ModemSMSService *sms = priv->sms_service; priv->signals.incoming_message = modem_sms_connect_to_incoming_message (sms, on_incoming_message, self); priv->signals.immediate_message = modem_sms_connect_to_immediate_message (sms, on_immediate_message, self); priv->signals.outgoing_sms_complete = modem_sms_connect_to_outgoing_complete (sms, on_sms_service_outgoing_complete, self); priv->signals.outgoing_sms_error = modem_sms_connect_to_outgoing_error (sms, on_sms_service_outgoing_error, self); priv->signals.receiving_sms_status_report = modem_sms_connect_to_status_report (sms, on_sms_service_status_report, self); #if nomore priv->signals.receiving_sms_deliver = modem_sms_connect_to_deliver (sms, on_sms_service_deliver, self); #endif } static void ring_text_manager_disconnect (RingTextManager *self) { RingTextManagerPrivate *priv = self->priv; ModemSMSService *sms = priv->sms_service; ring_signal_disconnect (sms, &priv->signals.incoming_message); ring_signal_disconnect (sms, &priv->signals.immediate_message); ring_signal_disconnect (sms, &priv->signals.outgoing_sms_complete); ring_signal_disconnect (sms, &priv->signals.outgoing_sms_error); ring_signal_disconnect (sms, &priv->signals.receiving_sms_status_report); #if nomore ring_signal_disconnect (sms, &priv->signals.receiving_sms_deliver); ring_signal_disconnect (sms, &priv->signals.outgoing_sms_complete); ring_signal_disconnect (sms, &priv->signals.outgoing_sms_error); #endif if (priv->sms_service) g_object_unref (priv->sms_service); priv->sms_service = NULL; } static void on_connection_status_changed (TpBaseConnection *conn, guint status, guint reason, RingTextManager *self) { if (status == TP_CONNECTION_STATUS_DISCONNECTED) { ring_text_manager_dispose (G_OBJECT (self)); } } /* ---------------------------------------------------------------------- */ /* Insert channel-type specific capabilities into array */ void ring_text_manager_add_capabilities(RingTextManager *self, guint handle, GPtrArray *returns) { RingTextManagerPrivate *priv = RING_TEXT_MANAGER(self)->priv; char const *id = ring_connection_inspect_contact(priv->connection, handle); guint selfhandle = tp_base_connection_get_self_handle( (TpBaseConnection *)priv->connection); char *destination; if (id == NULL) return; /* Some UIs create channels even if they do not intend to send anything */ /* Allow them to do so */ destination = ring_text_channel_destination(id); if (handle == selfhandle || modem_sms_is_valid_address (destination)) { g_ptr_array_add(returns, ring_contact_capability_new(handle, TP_IFACE_CHANNEL_TYPE_TEXT, TP_CONNECTION_CAPABILITY_FLAG_CREATE, RING_TEXT_CHANNEL_CAPABILITY_FLAGS)); } g_free(destination); } /* ---------------------------------------------------------------------- */ /* TpChannelManagerIface interface */ static char const * const ring_text_channel_fixed_properties_list[] = { TP_IFACE_CHANNEL ".TargetHandleType", /* Contact */ TP_IFACE_CHANNEL ".ChannelType", /* Text */ NULL }; static char const * const ring_text_channel_allowed_properties[] = { TP_IFACE_CHANNEL ".TargetHandle", TP_IFACE_CHANNEL ".TargetID", #if HAVE_TP_SMS_CHANNEL TP_IFACE_CHANNEL_INTERFACE_SMS ".SMSChannel", #endif NULL }; static GHashTable * ring_text_channel_fixed_properties(void) { static GHashTable *hash; if (hash) return hash; hash = g_hash_table_new(g_str_hash, g_str_equal); char const *key; GValue *value; key = TP_IFACE_CHANNEL ".TargetHandleType"; value = tp_g_value_slice_new(G_TYPE_UINT); g_value_set_uint(value, TP_HANDLE_TYPE_CONTACT); g_hash_table_insert(hash, (gpointer)key, value); key = TP_IFACE_CHANNEL ".ChannelType"; value = tp_g_value_slice_new(G_TYPE_STRING); g_value_set_static_string(value, TP_IFACE_CHANNEL_TYPE_TEXT); g_hash_table_insert(hash, (gpointer)key, value); return hash; } static void ring_text_manager_foreach_channel_class(TpChannelManager *_self, TpChannelManagerChannelClassFunc func, gpointer userdata) { /* Allow clients to request channels even when not connected */ func(_self, ring_text_channel_fixed_properties(), ring_text_channel_allowed_properties, userdata); } static void ring_text_manager_foreach_channel(TpChannelManager *_self, TpExportableChannelFunc func, gpointer user_data) { RingTextManager *self = RING_TEXT_MANAGER(_self); GHashTableIter i[1]; gpointer channel; for (g_hash_table_iter_init(i, self->priv->channels); g_hash_table_iter_next(i, NULL, &channel);) func(channel, user_data); } /** Create a new RingTextChannel */ static gboolean ring_text_manager_create_channel(TpChannelManager *_self, gpointer request, GHashTable *properties) { return ring_text_requestotron(RING_TEXT_MANAGER(_self), request, properties, 1); } /** Request a RingTextChannel */ static gboolean ring_text_manager_request_channel(TpChannelManager *_self, gpointer request, GHashTable *properties) { return ring_text_requestotron(RING_TEXT_MANAGER(_self), request, properties, 0); } static gboolean ring_text_manager_ensure_channel(TpChannelManager *_self, gpointer request, GHashTable *properties) { return ring_text_requestotron(RING_TEXT_MANAGER(_self), request, properties, 0); } static void channel_manager_iface_init(gpointer ifacep, gpointer data) { TpChannelManagerIface *iface = ifacep; #define IMPLEMENT(x) iface->x = ring_text_manager_##x IMPLEMENT(foreach_channel); IMPLEMENT(foreach_channel_class); IMPLEMENT(create_channel); IMPLEMENT(request_channel); IMPLEMENT(ensure_channel); #undef IMPLEMENT } static gboolean ring_text_requestotron(RingTextManager *self, gpointer request, GHashTable *properties, gboolean require_mine) { RingTextManagerPrivate *priv = self->priv; TpHandle initiator, target; initiator = priv->connection->parent.self_handle; target = tp_asv_get_uint32(properties, TP_IFACE_CHANNEL ".TargetHandle", NULL); if (target == 0 || target == priv->connection->parent.self_handle || target == priv->connection->anon_handle) return FALSE; if (!ring_properties_satisfy(properties, ring_text_channel_fixed_properties(), ring_text_channel_allowed_properties)) return FALSE; if (!tp_asv_get_sms_channel (properties)) return FALSE; ring_text_manager_request(self, request, initiator, target, require_mine, 0); return TRUE; } static gboolean tp_asv_get_sms_channel (GHashTable *properties) { GValue *value = g_hash_table_lookup (properties, TP_IFACE_CHANNEL_INTERFACE_SMS ".SMSChannel"); if (value == NULL) return TRUE; else if (!G_VALUE_HOLDS_BOOLEAN (value)) return FALSE; else return g_value_get_boolean (value); } /* ---------------------------------------------------------------------- */ /* RingTextManager interface */ gpointer ring_text_manager_lookup(RingTextManager *self, char const *object_path) { RingTextManagerPrivate *priv = self->priv; return (RingTextChannel *)g_hash_table_lookup(priv->channels, object_path); } static RingTextChannel * ring_text_manager_request(RingTextManager *self, gpointer request, TpHandle initiator, TpHandle handle, gboolean require_mine, gboolean class0) { RingTextManagerPrivate *priv = self->priv; RingTextChannel *channel; char *object_path; object_path = g_strdup_printf("%s/%s%u", priv->connection->parent.object_path, class0 ? "flash" : "text", (unsigned)handle); channel = ring_text_manager_lookup(self, object_path); if (channel) { g_free(object_path); if (require_mine) { char *message; channel = NULL; message = g_strdup_printf("Cannot create: " "channel with target '%s' already exists", ring_connection_inspect_contact(priv->connection, handle)); DEBUG("%s", message); if (request) tp_channel_manager_emit_request_failed(self, request, TP_ERRORS, TP_ERROR_NOT_AVAILABLE, message); g_assert(request); g_free(message); return NULL; } else { if (request) tp_channel_manager_emit_request_already_satisfied( self, request, TP_EXPORTABLE_CHANNEL(channel)); return channel; } } channel = g_object_new (RING_TYPE_TEXT_CHANNEL, "connection", self->priv->connection, "object-path", object_path, "handle-type", TP_HANDLE_TYPE_CONTACT, "handle", handle, "initiator-handle", initiator, "requested", request != NULL, "sms-flash", class0, NULL); g_free(object_path); g_object_get(channel, "object_path", &object_path, NULL); g_hash_table_insert(priv->channels, object_path, channel); g_signal_connect(channel, "closed", (GCallback)on_text_channel_closed, self); GSList *requests = request ? g_slist_prepend(NULL, request) : NULL; tp_channel_manager_emit_new_channel( self, TP_EXPORTABLE_CHANNEL(channel), requests); DEBUG("New channel emitted"); g_slist_free(requests); return channel; } static void on_text_channel_closed(RingTextChannel *channel, RingTextManager *self) { char *object_path; gboolean really_destroyed; guint handle; g_object_get(channel, "object-path", &object_path, "channel-destroyed", &really_destroyed, "handle", &handle, NULL); if (self->priv->channels == NULL) really_destroyed = TRUE; DEBUG(": %s is %s", object_path, really_destroyed ? "destroyed" : "about to be recycled"); tp_channel_manager_emit_channel_closed(self, object_path); if (self->priv->channels == NULL) ; else if (really_destroyed) { g_hash_table_remove(self->priv->channels, object_path); } else { g_object_set(channel, "initiator", handle, "requested", FALSE, NULL); tp_channel_manager_emit_new_channel(self, (TpExportableChannel *)channel, NULL); } g_free(object_path); } static RingTextChannel * get_text_channel(RingTextManager *self, char const *address, gboolean class0, gboolean self_invoked) { TpHandleRepoIface *repo; RingTextChannel *channel = NULL; TpHandle handle, initiator; GError *error = NULL; g_return_val_if_fail (address != NULL, NULL); repo = tp_base_connection_get_handles( (TpBaseConnection *)self->priv->connection, TP_HANDLE_TYPE_CONTACT); error = NULL; handle = tp_handle_ensure(repo, address, ring_network_normalization_context(), &error); if (handle == 0) { DEBUG("tp_handle_ensure: %s: %s (%d@%s)", address, GERROR_MSG_CODE(error)); g_clear_error(&error); /* Xyzzy */ return NULL; } initiator = self_invoked ? self->priv->connection->parent.self_handle : handle; channel = ring_text_manager_request(self, NULL, initiator, handle, 0, class0); if (channel == NULL) tp_handle_unref(repo, handle); return channel; } static void on_sms_service_outgoing_complete(ModemSMSService *service, char const *destination, char const *token, gpointer _self) { RingTextManager *self = _self; RingTextManagerPrivate *priv = self->priv; RingTextChannel *channel; DEBUG("Outgoing complete to %s with %s", destination, token); if (priv->cstatus != TP_CONNECTION_STATUS_CONNECTED) { DEBUG("not yet connected, ignoring"); return; } channel = get_text_channel(self, destination, 0, 1); if (channel) ring_text_channel_outgoing_sms_complete(channel, token); } static void on_sms_service_outgoing_error(ModemSMSService *service, char const *destination, char const *token, GError const *error, gpointer _self) { RingTextManager *self = _self; RingTextManagerPrivate *priv = self->priv; RingTextChannel *channel; DEBUG("Outgoing error to %s with %s", destination, token); if (priv->cstatus != TP_CONNECTION_STATUS_CONNECTED) { DEBUG("not yet connected, ignoring"); return; } channel = get_text_channel(self, destination, 0, 1); if (channel) ring_text_channel_outgoing_sms_error(channel, token, error); } static void on_sms_service_status_report(ModemSMSService *sms_service, char const *destination, char const *token, gboolean const *success, gpointer _self) { ring_text_manager_receive_status_report( RING_TEXT_MANAGER(_self), destination, token, success); } #if nomore static void on_sms_service_deliver(ModemSMSService *sms_service, SMSGDeliver *deliver, gpointer _self) { ring_text_manager_receive_deliver(RING_TEXT_MANAGER(_self), deliver); } #endif /* ---------------------------------------------------------------------- */ static void ring_text_manager_receive_status_report(RingTextManager *self, char const *destination, char const *token, gboolean const *success) { RingTextManagerPrivate *priv = self->priv; RingTextChannel *channel; if (priv->cstatus != TP_CONNECTION_STATUS_CONNECTED) { /* Uh-oh */ DEBUG("not yet connected, ignoring"); return; } channel = get_text_channel(self, destination, 0, 0); if (channel) ring_text_channel_receive_status_report(channel, token, success); } #if nomore static void ring_text_manager_receive_deliver(RingTextManager *self, SMSGDeliver *deliver) { RingTextManagerPrivate *priv = self->priv; RingTextChannel *channel; char const *originator = sms_g_deliver_get_originator(deliver); glong delivered = sms_g_deliver_get_delivered(deliver); DEBUG("SMS-DELIVER from %s%s", originator, delivered ? " from spool" : ""); if (!ring_text_channel_can_handle(deliver)) { DEBUG("cannot handle, ignoring"); return; } if (priv->cstatus != TP_CONNECTION_STATUS_CONNECTED) { DEBUG("not yet connected, ignoring"); return; } int class0 = sms_g_deliver_get_sms_class(deliver) == 0; channel = get_text_channel(self, originator, class0, 0); if (channel) ring_text_channel_receive_deliver(channel, deliver); } #endif static char * generate_token (void) { char *token; uuid_t uu; token = g_new (gchar, 37); uuid_generate_random (uu); uuid_unparse_lower (uu, token); return token; } static void receive_text (RingTextManager *self, RingTextChannel *channel, gchar const *message, GHashTable *info, guint32 sms_class) { char const *sent; char *token; gint64 message_sent = 0; gint64 message_received = (gint64) time(NULL); sent = tp_asv_get_string (info, "SentTime"); if (sent) message_sent = modem_sms_parse_time (sent); if (!message_sent) message_sent = message_received; token = generate_token (); ring_text_channel_receive_text (channel, token, message, message_sent, message_received, sms_class); g_free (token); } static void on_incoming_message (ModemSMSService *sms, gchar const *message, GHashTable *info, gpointer _self) { RingTextManager *self = RING_TEXT_MANAGER (_self); char const *sender; RingTextChannel *channel; g_return_if_fail (info != NULL); g_return_if_fail (message != NULL); sender = tp_asv_get_string (info, "Sender"); g_return_if_fail (sender != NULL); channel = get_text_channel (self, sender, 0, 0); g_return_if_fail (channel != NULL); receive_text (self, channel, message, info, G_MAXUINT32); } static void on_immediate_message (ModemSMSService *sms, gchar const *message, GHashTable *info, gpointer _self) { RingTextManager *self = RING_TEXT_MANAGER (_self); char const *sender; RingTextChannel *channel; g_return_if_fail (info != NULL); g_return_if_fail (message != NULL); sender = tp_asv_get_string (info, "Sender"); g_return_if_fail (sender != NULL); channel = get_text_channel (self, sender, TRUE, 0); g_return_if_fail (channel != NULL); receive_text (self, channel, message, info, 0); } /* ---------------------------------------------------------------------- */ /* StoredMessages interface */ #if nomore static void ring_text_manager_not_connected(gpointer context) { GError error = { TP_ERRORS, TP_ERROR_NOT_AVAILABLE, "SMS service is not available" }; dbus_g_method_return_error(context, &error); } void ring_text_manager_deliver_stored_messages(RingTextManager *self, char const **messages, gpointer context) { #if nomore /* XXX/KV: use ring_text_manager_is_connected () */ if (self->priv->status == TP_CONNECTION_STATUS_CONNECTED) { int i; ModemSMSService *service = self->priv->sms_service; for (i = 0; messages[i]; i++) { gpointer m = modem_sms_get_stored_message(service, messages[i]); if (m == NULL) { DEBUG("%s: not in received", messages[i]); continue; } if (SMS_G_IS_DELIVER(m)) { ring_text_manager_receive_deliver(self, m); } else if (SMS_G_IS_STATUS_REPORT(m)) { ring_text_manager_receive_status_report(self, m); } else { DEBUG("unknown %s in received", G_OBJECT_TYPE_NAME(m)); } } dbus_g_method_return(context); } else { ring_text_manager_not_connected(context); } #endif } void ring_text_manager_expunge_messages(RingTextManager *self, char const **messages, gpointer context) { /* XXX/KV: use ring_text_manager_is_connected () */ if (self->priv->status == TP_CONNECTION_STATUS_CONNECTED) { int i; ModemSMSService *service = self->priv->sms_service; GPtrArray *expunged = g_ptr_array_new(); for (i = 0; messages[i]; i++) { if (modem_sms_request_expunge(service, messages[i], NULL, NULL)) { g_ptr_array_add(expunged, (gpointer)g_strdup(messages[i])); } } g_ptr_array_add(expunged, NULL); dbus_g_method_return(context); rtcom_tp_svc_connection_interface_stored_messages_emit_messages_expunged( self->priv->connection, (char const **)expunged->pdata); g_strfreev((char **)g_ptr_array_free(expunged, FALSE)); } else { ring_text_manager_not_connected(context); } } static ModemSMSServiceReply ring_text_manager_set_storage_status_reply; void ring_text_manager_set_storage_status(RingTextManager *self, gboolean out_of_storage, gpointer context) { /* XXX/KV: use ring_text_manager_is_connected () */ if (self->priv->status == TP_CONNECTION_STATUS_CONNECTED) modem_sms_request_out_of_memory( self->priv->sms_service, out_of_storage, ring_text_manager_set_storage_status_reply, context); else ring_text_manager_not_connected(context); } static void ring_text_manager_set_storage_status_reply(ModemSMSService *service, ModemRequest *request, GError *error, gpointer context) { if (!error) dbus_g_method_return(context); else dbus_g_method_return_error(context, error); } char ** ring_text_manager_list_stored_messages(RingTextManager const *self) { return modem_sms_list_stored(self->priv->sms_service); } #endif telepathy-ring-2.3.24-37ad7111424725a5446e82dac516d7cab88149c7/src/ring-text-manager.h000066400000000000000000000052511251541261300262500ustar00rootroot00000000000000/* * ring-text-manager.h - Manager for text channels * * Copyright (C) 2007-2010 Nokia Corporation * @author Pekka Pessi * * This work is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This work is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this work; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef RING_TEXT_MANAGER_H #define RING_TEXT_MANAGER_H #include #include #include "modem/sms.h" G_BEGIN_DECLS typedef struct _RingTextManager RingTextManager; typedef struct _RingTextManagerClass RingTextManagerClass; typedef struct _RingTextManagerPrivate RingTextManagerPrivate; struct _RingTextManagerClass { GObjectClass parent_class; }; struct _RingTextManager { GObject parent; RingTextManagerPrivate *priv; }; GType ring_text_manager_get_type(void); /* TYPE MACROS */ #define RING_TYPE_TEXT_MANAGER (ring_text_manager_get_type()) #define RING_TEXT_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_CAST( \ (obj), RING_TYPE_TEXT_MANAGER, RingTextManager)) #define RING_TEXT_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST( \ (klass), RING_TYPE_TEXT_MANAGER, RingTextManagerClass)) #define RING_IS_TEXT_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_TYPE( \ (obj), RING_TYPE_TEXT_MANAGER)) #define RING_IS_TEXT_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE( \ (klass), RING_TYPE_TEXT_MANAGER)) #define RING_TEXT_MANAGER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS( \ (obj), RING_TYPE_TEXT_MANAGER, RingTextManagerClass)) gpointer ring_text_manager_lookup(RingTextManager *self, char const *object_path); #if nomore void ring_text_manager_deliver_stored_messages(RingTextManager *, char const **messages, gpointer context); void ring_text_manager_expunge_messages(RingTextManager *, char const **message_identities, gpointer context); void ring_text_manager_set_storage_status(RingTextManager *, gboolean out_of_storage, gpointer context); char **ring_text_manager_list_stored_messages(RingTextManager const *); #endif void ring_text_manager_add_capabilities(RingTextManager *self, guint handle, GPtrArray *returns); G_END_DECLS #endif telepathy-ring-2.3.24-37ad7111424725a5446e82dac516d7cab88149c7/src/ring-util.c000066400000000000000000000335211251541261300246250ustar00rootroot00000000000000/* * ring-util.c - Miscellaneous utility functions * * Copyright (C) 2007-2010 Nokia Corporation * @author Pekka Pessi * @author Tom Swindell * * This work is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This work is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this work; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "config.h" #include "ring-util.h" #include "modem/call.h" #include "modem/errors.h" #include #include #include #include #include char * ring_str_starts_with_case(char const *string, char const *prefix) { size_t len = strlen(prefix); if (g_ascii_strncasecmp(string, prefix, len)) return NULL; return (char *)string + len; } char * ring_str_starts_with(char const *string, char const *prefix) { while (*prefix && *string) if (*prefix++ != *string++) return 0; return *prefix == '\0' ? (char *)string : NULL; } char const * ring_connection_status_as_string(TpConnectionStatus st) { switch ((int)st) { case TP_INTERNAL_CONNECTION_STATUS_NEW: return "new"; case TP_CONNECTION_STATUS_CONNECTED: return "connected"; case TP_CONNECTION_STATUS_CONNECTING: return "connecting"; case TP_CONNECTION_STATUS_DISCONNECTED: return "disconnected"; } return ""; } char * ring_normalize_isdn(char const *s) { int i, j; char *isdn = g_strdup(s ? s : ""); for (i = 0, j = 0; isdn[i]; i++) { switch (isdn[i]) { case ' ': case '.': case '(': case ')': case '-': continue; case '+': if (j != 0) { g_free(isdn); return NULL; } /* FALLTHROUGH */ case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': if (i != j) isdn[j] = isdn[i]; j++; break; default: g_free(isdn); return NULL; } } if (i != j) isdn[j] = '\0'; return isdn; } int ring_str_has_token(char const *string, char const *token) { char **strv; int i, found; if (!string || !token) return 0; if (g_ascii_strcasecmp(string, token) == 0) return 1; strv = g_strsplit_set(string, " \t\r\n,;", G_MAXINT); if (strv == NULL) return 0; for (i = 0; strv[i]; i++) { if (g_ascii_strcasecmp(string, token) == 0) break; } found = strv[i] != NULL; g_strfreev(strv); return found; } #define DEBUG_FLAG RING_DEBUG_CONNECTION #include gboolean ring_properties_satisfy(GHashTable *requested_properties, GHashTable *fixed_properties, char const * const *allowed) { GHashTableIter i[1]; gpointer keyp, valuep; for (g_hash_table_iter_init(i, fixed_properties); g_hash_table_iter_next(i, &keyp, &valuep);) { GValue *fixed = valuep; gpointer requestedp = g_hash_table_lookup(requested_properties, keyp); if (!requestedp) { DEBUG("** expecting %s with %s", (char *)keyp, g_type_name(G_VALUE_TYPE(fixed))); return 0; } GValue *requested = requestedp; if (G_VALUE_HOLDS(fixed, G_TYPE_UINT)) { guint64 u = 0; gint64 i = 0; switch (G_VALUE_TYPE(requested)) { case G_TYPE_UCHAR: u = g_value_get_uchar(requested); break; case G_TYPE_UINT: u = g_value_get_uint(requested); break; case G_TYPE_UINT64: u = g_value_get_uint64(requested); break; case G_TYPE_INT: i = g_value_get_int(requested); u = (guint64)i; break; case G_TYPE_INT64: i = g_value_get_int64(requested); u = (guint64)i; break; } if (i < 0) { DEBUG("*** expecting %u for %s, got %lld", g_value_get_uint(fixed), (char *)keyp, (long long)i); return 0; } if ((guint64)g_value_get_uint(fixed) == u) continue; DEBUG("*** expecting %u for %s, got %llu", g_value_get_uint(fixed), (char *)keyp, (unsigned long long)u); return 0; } if (!G_VALUE_HOLDS(requested, G_VALUE_TYPE(fixed))) { DEBUG("*** expecting type %s for %s, got type %s", g_type_name(G_VALUE_TYPE(fixed)), (char *)keyp, g_type_name(G_VALUE_TYPE(requested))); } else if (G_VALUE_HOLDS(requested, G_TYPE_BOOLEAN)) { if (g_value_get_boolean(fixed) == g_value_get_boolean(requested)) continue; DEBUG("*** expecting %u for %s, got %u", g_value_get_boolean(fixed), (char *)keyp, g_value_get_boolean(requested)); } else if (G_VALUE_HOLDS (requested, G_TYPE_STRING)) { char const *fixed_string = g_value_get_string (fixed); char const *requested_string = g_value_get_string (requested); if (!tp_strdiff (fixed_string, requested_string)) continue; if (tp_strdiff (keyp, "org.freedesktop.Telepathy.Channel.ChannelType")) { DEBUG ("*** expecting \"%s\" for %s, got \"%s\"", fixed_string, (char *)keyp, requested_string); } } else { g_warning ("*** fixed-properties contains %s ***", G_VALUE_TYPE_NAME (fixed)); } return 0; } for (g_hash_table_iter_init(i, requested_properties); g_hash_table_iter_next(i, &keyp, &valuep);) { if (g_hash_table_lookup(fixed_properties, keyp)) continue; char const *name = keyp; if (!tp_strv_contains(allowed, name)) { if (DEBUGGING) { char *value = g_strdup_value_contents(valuep); DEBUG("Unknown property %s=%s in request", name, value); g_free(value); } return 0; } } return 1; } GHashTable * ring_channel_add_properties(gpointer obj, GHashTable *hash, char const *interface, char const *member, ...) { va_list ap; va_start(ap, member); if (hash == NULL) hash = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, (GDestroyNotify)tp_g_value_slice_free); for (;;) { char *key = g_strdup_printf("%s.%s", interface, member); GValue *value = g_slice_new0(GValue); tp_dbus_properties_mixin_get(obj, interface, member, value, NULL); g_assert(G_IS_VALUE(value)); g_hash_table_insert(hash, key, value); interface = va_arg(ap, char const *); if (interface == NULL) break; member = va_arg(ap, char const *); } va_end(ap); return hash; } /** Return internal error to a pending DBus method call */ void ring_method_return_internal_error(gpointer _context) { GError error = { TP_ERRORS, TP_ERROR_DISCONNECTED, "Internal error - request canceled" }; dbus_g_method_return_error((DBusGMethodInvocation *)_context, &error); } /** Emit MembersChanged(Detailed) signal */ gboolean ring_util_group_change_members(gpointer obj, TpIntSet *add, TpIntSet *del, TpIntSet *local_pending, TpIntSet *remote_pending, char const *key, /* gtype */ /* value */ /* actor, G_TYPE_UINT, value, */ /* change-reason, G_TYPE_UINT, value */ /* message, G_TYPE_STRING, value */ /* error, G_TYPE_STRING, value */ /* debug-message, G_TYPE_STRING, value */ ...) { GHashTable *details; va_list ap; gboolean emitted; details = g_hash_table_new_full( g_str_hash, g_str_equal, NULL, (GDestroyNotify) tp_g_value_slice_free); for (va_start(ap, key); key; key = va_arg(ap, char const *)) { GType type = va_arg(ap, GType); GValue value[1], *copy = NULL; g_value_init(memset(value, 0, (sizeof value)), type); if (type == G_TYPE_UINT) { g_value_set_uint(value, va_arg(ap, guint)); } else if (type == G_TYPE_STRING) { g_value_take_string(copy = value, va_arg(ap, char *)); } else { DEBUG("detail \"%s\" with unknown type %s", key, g_type_name(type)); break; } if (g_str_equal(key, "")) { /* empty name, skip the value */ continue; } g_value_copy(value, copy = tp_g_value_slice_new(type)); g_hash_table_insert(details, (gpointer)key, copy); } va_end(ap); emitted = tp_group_mixin_change_members_detailed(G_OBJECT(obj), add, del, local_pending, remote_pending, details); g_hash_table_destroy(details); return emitted; } /* Map group change reason to string */ char const * ring_util_reason_name(TpChannelGroupChangeReason reason) { switch (reason) { case TP_CHANNEL_GROUP_CHANGE_REASON_NONE: return "NONE"; case TP_CHANNEL_GROUP_CHANGE_REASON_OFFLINE: return "OFFLINE"; case TP_CHANNEL_GROUP_CHANGE_REASON_KICKED: return "KICKED"; case TP_CHANNEL_GROUP_CHANGE_REASON_BUSY: return "BUSY"; case TP_CHANNEL_GROUP_CHANGE_REASON_INVITED: return "INVITED"; case TP_CHANNEL_GROUP_CHANGE_REASON_BANNED: return "BANNED"; case TP_CHANNEL_GROUP_CHANGE_REASON_ERROR: return "ERROR"; case TP_CHANNEL_GROUP_CHANGE_REASON_INVALID_CONTACT: return "INVALID_CONTACT"; case TP_CHANNEL_GROUP_CHANGE_REASON_NO_ANSWER: return "NO_ANSWER"; case TP_CHANNEL_GROUP_CHANGE_REASON_RENAMED: return "RENAMED"; case TP_CHANNEL_GROUP_CHANGE_REASON_PERMISSION_DENIED: return "PERMISSION_DENIED"; case TP_CHANNEL_GROUP_CHANGE_REASON_SEPARATED: return "SEPARATED"; } return ""; } /* ---------------------------------------------------------------------- */ /* Map modem causetype and cause to reason */ TpChannelGroupChangeReason ring_channel_group_release_reason(guint causetype, guint cause) { if (causetype == MODEM_CALL_CAUSE_TYPE_NETWORK) { /* 3GPP TS 22.001 F.4 */ switch (cause) { case MODEM_CALL_NET_ERROR_NORMAL: return TP_CHANNEL_GROUP_CHANGE_REASON_NONE; case MODEM_CALL_NET_ERROR_USER_BUSY: return TP_CHANNEL_GROUP_CHANGE_REASON_BUSY; case MODEM_CALL_NET_ERROR_NUMBER_CHANGED: return TP_CHANNEL_GROUP_CHANGE_REASON_INVALID_CONTACT; case MODEM_CALL_NET_ERROR_RESP_TO_STATUS: case MODEM_CALL_NET_ERROR_NORMAL_UNSPECIFIED: return TP_CHANNEL_GROUP_CHANGE_REASON_NONE; case MODEM_CALL_NET_ERROR_NO_CHANNEL: case MODEM_CALL_NET_ERROR_TEMPORARY_FAILURE: case MODEM_CALL_NET_ERROR_CONGESTION: case MODEM_CALL_NET_ERROR_CHANNEL_NA: case MODEM_CALL_NET_ERROR_QOS_NA: case MODEM_CALL_NET_ERROR_BC_NA: /* Congestion */ return TP_CHANNEL_GROUP_CHANGE_REASON_ERROR; default: return TP_CHANNEL_GROUP_CHANGE_REASON_ERROR; } } else { switch (cause) { case MODEM_CALL_ERROR_NO_ERROR: case MODEM_CALL_ERROR_RELEASE_BY_USER: return TP_CHANNEL_GROUP_CHANGE_REASON_NONE; case MODEM_CALL_ERROR_NO_CALL: case MODEM_CALL_ERROR_CALL_ACTIVE: case MODEM_CALL_ERROR_NO_CALL_ACTIVE: return TP_CHANNEL_GROUP_CHANGE_REASON_ERROR; case MODEM_CALL_ERROR_BUSY_USER_REQUEST: return TP_CHANNEL_GROUP_CHANGE_REASON_BUSY; case MODEM_CALL_ERROR_EMERGENCY: case MODEM_CALL_ERROR_DTMF_SEND_ONGOING: return TP_CHANNEL_GROUP_CHANGE_REASON_ERROR; case MODEM_CALL_ERROR_BLACKLIST_BLOCKED: case MODEM_CALL_ERROR_BLACKLIST_DELAYED: return TP_CHANNEL_GROUP_CHANGE_REASON_PERMISSION_DENIED; case MODEM_CALL_ERROR_CHANNEL_LOSS: case MODEM_CALL_ERROR_NO_SERVICE: case MODEM_CALL_ERROR_NO_COVERAGE: return TP_CHANNEL_GROUP_CHANGE_REASON_ERROR; case MODEM_CALL_ERROR_ERROR_REQUEST: case MODEM_CALL_ERROR_INVALID_CALL_MODE: case MODEM_CALL_ERROR_CODE_REQUIRED: case MODEM_CALL_ERROR_NOT_ALLOWED: case MODEM_CALL_ERROR_DTMF_ERROR: case MODEM_CALL_ERROR_FDN_NOT_OK: case MODEM_CALL_ERROR_EMERGENCY_FAILURE: return TP_CHANNEL_GROUP_CHANGE_REASON_ERROR; case MODEM_CALL_ERROR_TOO_LONG_ADDRESS: case MODEM_CALL_ERROR_INVALID_ADDRESS: return TP_CHANNEL_GROUP_CHANGE_REASON_INVALID_CONTACT; case MODEM_CALL_ERROR_NO_SIM: case MODEM_CALL_ERROR_CS_INACTIVE: default: return TP_CHANNEL_GROUP_CHANGE_REASON_ERROR; } } return TP_CHANNEL_GROUP_CHANGE_REASON_ERROR; } TpChannelGroupChangeReason ring_channel_group_error_reason(GError *error) { if (error == NULL) return TP_CHANNEL_GROUP_CHANGE_REASON_ERROR; if (error->domain == MODEM_CALL_ERRORS) return ring_channel_group_release_reason(MODEM_CALL_CAUSE_TYPE_REMOTE, error->code); if (error->domain == MODEM_CALL_NET_ERRORS) return ring_channel_group_release_reason(MODEM_CALL_CAUSE_TYPE_NETWORK, error->code); return TP_CHANNEL_GROUP_CHANGE_REASON_ERROR; } /* ---------------------------------------------------------------------- */ /* Initial media stuff */ gboolean tp_asv_get_initial_audio (GHashTable *properties, gboolean default_value) { GValue *value = g_hash_table_lookup (properties, TP_IFACE_CHANNEL_TYPE_STREAMED_MEDIA ".InitialAudio"); if (value && G_VALUE_HOLDS_BOOLEAN (value)) return g_value_get_boolean (value); else return default_value; } gboolean tp_asv_get_initial_video (GHashTable *properties, gboolean default_value) { GValue *value = g_hash_table_lookup (properties, TP_IFACE_CHANNEL_TYPE_STREAMED_MEDIA ".InitialVideo"); if (value && G_VALUE_HOLDS_BOOLEAN (value)) return g_value_get_boolean (value); else return default_value; } telepathy-ring-2.3.24-37ad7111424725a5446e82dac516d7cab88149c7/src/ring-util.h000066400000000000000000000064411251541261300246330ustar00rootroot00000000000000/* -util.c - Miscellaneous utility functions * * Copyright (C) 2007-2010 Nokia Corporation * @author Pekka Pessi * @author Tom Swindell * This work is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This work is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this work; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __RING_UTIL_H__ #define __RING_UTIL_H__ #include "telepathy-glib/util.h" #include "telepathy-glib/enums.h" #include "telepathy-glib/util.h" #include "telepathy-glib/intset.h" G_BEGIN_DECLS #define RING_STR_EMPTY(s) (s == NULL || s[0] == '\0') char *ring_str_starts_with(char const *string, char const *prefix); char *ring_str_starts_with_case(char const *string, char const *prefix); int ring_str_has_token(char const *string, char const *token); gboolean ring_properties_satisfy(GHashTable *requested_properties, GHashTable *fixed_properties, char const * const * allowed); char const *ring_connection_status_as_string(TpConnectionStatus st); char *ring_normalize_isdn(gchar const *s); GHashTable *ring_channel_add_properties(gpointer obj, GHashTable *hash, char const *interface, char const *member, ...) G_GNUC_NULL_TERMINATED; void ring_method_return_internal_error(gpointer _context); gpointer ring_network_normalization_context(void); GValueArray *ring_contact_capability_new(guint handle, char const *channel_type, guint generic, guint specific); void ring_contact_capability_free(gpointer value); gboolean ring_util_group_change_members(gpointer object, TpIntSet *add, TpIntSet *del, TpIntSet *local_pending, TpIntSet *remote_pending, char const *key, /* gtype */ /* value */ /* actor, G_TYPE_UINT, value, */ /* change-reason, G_TYPE_UINT, value */ /* message, G_TYPE_STRING, value */ /* error, G_TYPE_STRING, value */ /* debug-message, G_TYPE_STRING, value */ ...) G_GNUC_NULL_TERMINATED; char const *ring_util_reason_name(TpChannelGroupChangeReason reason); /* initial-members */ typedef struct { char const **odata; guint len; } RingInitialMembers; TpChannelGroupChangeReason ring_channel_group_release_reason( guint causetype, guint cause); TpChannelGroupChangeReason ring_channel_group_error_reason(GError *); static inline void ring_signal_disconnect (gpointer object, gulong id[1]) { if (*id && object && g_signal_handler_is_connected (object, *id)) g_signal_handler_disconnect (object, *id); *id = 0; } #define tp_asv_get_initial_audio ring_tp_asv_get_initial_audio #define tp_asv_get_initial_video ring_tp_asv_get_initial_video gboolean tp_asv_get_initial_audio (GHashTable *asv, gboolean default_value); gboolean tp_asv_get_initial_video (GHashTable *asv, gboolean default_value); G_END_DECLS #endif /* #ifndef __RING_UTIL_H__*/ telepathy-ring-2.3.24-37ad7111424725a5446e82dac516d7cab88149c7/src/ring.manager000066400000000000000000000020201251541261300250300ustar00rootroot00000000000000[ConnectionManager] BusName=org.freedesktop.Telepathy.ConnectionManager.ring ObjectPath=/org/freedesktop/Telepathy/ConnectionManager/ring [Protocol tel] param-org.freedesktop.Telepathy.Connection.Interface.Cellular.IMSI=s dbus-property param-org.freedesktop.Telepathy.Connection.Interface.Cellular.MessageServiceCentre=s dbus-property # SMS validity period in seconds # Range from 300 ... 38102400 (5 minutes to 63 weeks) param-org.freedesktop.Telepathy.Connection.Interface.Cellular.MessageValidityPeriod=u dbus-property default-org.freedesktop.Telepathy.Connection.Interface.Cellular.MessageValidityPeriod=0 param-org.freedesktop.Telepathy.Connection.Interface.Cellular.MessageReducedCharacterSet=b dbus-property default-org.freedesktop.Telepathy.Connection.Interface.Cellular.MessageReducedCharacterSet=false param-org.freedesktop.Telepathy.Connection.Interface.Anonymity.AnonymityModes=u dbus-property default-org.freedesktop.Telepathy.Connection.Interface.Anonymity.AnonymityModes=0 # Deprecated param-account=s param-password=s telepathy-ring-2.3.24-37ad7111424725a5446e82dac516d7cab88149c7/src/telepathy-ring.c000066400000000000000000000103001251541261300256350ustar00rootroot00000000000000/* * telepathy-ring.c - Telepathy-ring connection manager * * Copyright (C) 2007-2010 Nokia Corporation * @author Pekka Pessi * * This work is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This work is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this work; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "config.h" #define DEBUG_FLAG RING_DEBUG_CONNECTION #include "ring-debug.h" #include "ring-connection-manager.h" #include #include #include #include #include #include #include #include #include #include #include #if HAVE_LIBMLOCKNICE #include #endif static TpBaseConnectionManager * telepathy_ring_connection_manager_new(void) { return (TpBaseConnectionManager *) g_object_new(RING_TYPE_CONNECTION_MANAGER, NULL); } char * readfile(char *file) { int fd = open(file, O_RDONLY); char buffer[1024]; ssize_t n; size_t ws, nws; if (fd == -1) return NULL; n = read(fd, buffer, (sizeof buffer) - 1); close(fd); if (n < 0) return NULL; buffer[n] = '\0'; ws = strspn(buffer, " \t\v\r\n"); nws = strcspn(buffer + ws, " \t\v\r\n"); buffer[ws + nws] = '\0'; return g_strdup(buffer + ws); } #define STATEDIR "/var/lib/telepathy-ring/" int main (int argc, char** argv) { ring_debug_set_flags_from_env(); char const *ring_realtime = getenv("RING_REALTIME"); if (!ring_realtime) ring_realtime = readfile(STATEDIR "realtime"); if (ring_realtime) { struct sched_param sp = { .sched_priority = strtoul(ring_realtime, NULL, 0) }; int priority_min = sched_get_priority_min(SCHED_FIFO); if (sp.sched_priority < priority_min) sp.sched_priority = priority_min; int priority_max = sched_get_priority_max(SCHED_FIFO); if (sp.sched_priority > priority_max) sp.sched_priority = priority_max; if (sched_setscheduler(0, SCHED_FIFO, &sp) < 0) DEBUG("sched_setscheduler(RING_REALTIME=%s): %s", ring_realtime, strerror(errno)); } char const *ring_memlock = getenv("RING_MEMLOCK"); if (!ring_memlock) ring_memlock = readfile(STATEDIR "memlock"); if (ring_memlock) { struct rlimit rl[1]; char *rest = NULL; rlim_t rlim = strtoul(ring_memlock, &rest, 0); switch (*rest) { case 'g': case 'G': rlim *= 1024; /* FALLTHROUGH */ case 'm': case 'M': rlim *= 1024; /* FALLTHROUGH */ case 'k': case 'K': rlim *= 1024; } if (rlim == 0) rlim = 1024 * 1024 * 256; rl->rlim_cur = rlim; rl->rlim_max = rlim; if (setrlimit(RLIMIT_MEMLOCK, rl) < 0) { DEBUG("setrlimit(): %s", strerror(errno)); ring_memlock = NULL; } } /* Drop privileges */ #if HAVE_GETRESUID { uid_t ruid, euid, suid; if (getresuid(&ruid, &euid, &suid) == -1) DEBUG("%s(): %s", "getresuid", strerror(errno)); else if (ruid == euid && ruid == suid) ; else if (setresuid(ruid, ruid, ruid) == -1) { DEBUG("%s(): %s", "setresuid", strerror(errno)); exit(2); } } #else { uid_t ruid = getuid(); if (ruid != geteuid()) if (setreuid(ruid, ruid) == -1) { DEBUG("%s(): %s", "setreuid", strerror(errno)); exit(2); } } #endif if (ring_memlock) { if (mlockall(MCL_FUTURE) < 0) DEBUG("mlockall(): %s", strerror(errno)); #if HAVE_LIBMLOCKNICE else if (mln_lock_data() < 0) DEBUG("mln_lock_data(): %s", strerror(errno)); #endif } return tp_run_connection_manager( "telepathy-ring", PACKAGE_VERSION, telepathy_ring_connection_manager_new, argc, argv); } telepathy-ring-2.3.24-37ad7111424725a5446e82dac516d7cab88149c7/src/tests/000077500000000000000000000000001251541261300237055ustar00rootroot00000000000000telepathy-ring-2.3.24-37ad7111424725a5446e82dac516d7cab88149c7/src/tests/test-ring-util.c000066400000000000000000000106571251541261300267510ustar00rootroot00000000000000/* * test-ring-util.c - Test cases for ring utility functions * * Copyright (C) 2009 Nokia Corporation * @author Pekka Pessi * * This work is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This work is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this work; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "config.h" #include #include #include "test-ring.h" #include #include #include static void setup(void) { g_type_init(); (void)dbus_g_bus_get(DBUS_BUS_SYSTEM, NULL); } static void teardown(void) { } START_TEST(test_normalize_isdn) { gchar *s; s = ring_normalize_isdn("(1) 2.3-4"); fail_unless(s && strcmp(s, "1234") == 0); s = ring_normalize_isdn("1+2"); fail_unless(s == NULL); s = ring_normalize_isdn("+123"); fail_unless(s && strcmp(s, "+123") == 0); s = ring_normalize_isdn("+12345678901234567890"); fail_unless(s && strcmp(s, "+12345678901234567890") == 0); s = ring_normalize_isdn("12A"); fail_unless(s == NULL); s = ring_normalize_isdn("(.)(.)"); fail_unless(s && strcmp(s, "") == 0); } END_TEST START_TEST(test_str_starts_with) { gchar *s; s = ring_str_starts_with_case("humppa", "HU"); fail_if(s == NULL); fail_if(strcmp("mppa", s)); } END_TEST START_TEST(test_str_has_token) { fail_if(ring_str_has_token("no-priv", "priv")); } END_TEST static GHashTable * some_fixed_properties(void) { GHashTable *hash; gchar const *key; GValue *value; hash = g_hash_table_new(g_str_hash, g_str_equal); key = "uint"; value = tp_g_value_slice_new(G_TYPE_UINT); g_value_set_uint(value, 13); g_hash_table_insert(hash, (gpointer)key, value); key = "string"; value = tp_g_value_slice_new(G_TYPE_STRING); g_value_set_static_string(value, "value"); g_hash_table_insert(hash, (gpointer)key, value); key = "boolean"; value = tp_g_value_slice_new(G_TYPE_BOOLEAN); g_value_set_boolean(value, TRUE); g_hash_table_insert(hash, (gpointer)key, value); return hash; } START_TEST(test_properties_satisfy) { gchar const * const extra[] = { "foo", "bar", "baz", NULL }; GHashTable *fixed = some_fixed_properties(); GHashTable *props = some_fixed_properties(); fail_unless(ring_properties_satisfy(props, fixed, extra)); gchar const *key; GValue *value; key = "foo"; value = tp_g_value_slice_new(G_TYPE_STRING); g_value_set_static_string(value, "foo-value"); g_hash_table_insert(props, (gpointer)key, value); fail_unless(ring_properties_satisfy(props, fixed, extra)); fail_if(ring_properties_satisfy(fixed, props, extra)); key = "uint"; value = tp_g_value_slice_new(G_TYPE_UCHAR); g_value_set_uchar(value, 13); g_hash_table_insert(props, (gpointer)key, value); fail_unless(ring_properties_satisfy(props, fixed, extra)); value = tp_g_value_slice_new(G_TYPE_UINT64); g_value_set_uint64(value, 13); g_hash_table_insert(props, (gpointer)key, value); fail_unless(ring_properties_satisfy(props, fixed, extra)); value = tp_g_value_slice_new(G_TYPE_INT64); g_value_set_int64(value, 13); g_hash_table_insert(props, (gpointer)key, value); fail_unless(ring_properties_satisfy(props, fixed, extra)); value = tp_g_value_slice_new(G_TYPE_INT); g_value_set_int(value, 13); g_hash_table_insert(props, (gpointer)key, value); fail_unless(ring_properties_satisfy(props, fixed, extra)); value = tp_g_value_slice_new(G_TYPE_UINT); g_value_set_uint(value, 0xfffffffe); } END_TEST static TCase * ring_util_tcase(void) { TCase *tc = tcase_create("Test for ring-util"); tcase_add_checked_fixture(tc, setup, teardown); tcase_add_test(tc, test_normalize_isdn); tcase_add_test(tc, test_str_starts_with); tcase_add_test(tc, test_str_has_token); tcase_add_test(tc, test_properties_satisfy); tcase_set_timeout(tc, 5); return tc; } struct test_cases ring_tcases[] = { DECLARE_TEST_CASE(ring_util_tcase), LAST_TEST_CASE }; telepathy-ring-2.3.24-37ad7111424725a5446e82dac516d7cab88149c7/src/tests/test-ring.c000066400000000000000000000031431251541261300257660ustar00rootroot00000000000000/* * test-ring.c - A check-based test runner for telepathy-ring * * Copyright (C) 2008 Nokia Corporation * @author Pekka Pessi * * This work is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This work is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this work; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "config.h" #include #include "test-ring.h" #include "ring-debug.h" #include #include #include #include int main(int argc, char *argv[]) { struct common_args *args; int failed = 0; Suite *suite = suite_create("Unit tests for telepathy-ring"); SRunner *runner; ring_debug_set_flags_from_env(); args = parse_common_args(argc, argv); filter_add_tcases(suite, ring_tcases, args->tests); runner = srunner_create(suite); if (args->xml) srunner_set_xml(runner, args->xml); srunner_run_all(runner, CK_ENV); failed = srunner_ntests_failed(runner); free_common_args(args); srunner_free(runner); exit(failed ? EXIT_FAILURE : EXIT_SUCCESS); } telepathy-ring-2.3.24-37ad7111424725a5446e82dac516d7cab88149c7/src/tests/test-ring.h000066400000000000000000000017631251541261300260010ustar00rootroot00000000000000/* * test-ring.h - A check-based test runner for telepathy-ring * * Copyright (C) 2008 Nokia Corporation * @author Pekka Pessi * * This work is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This work is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this work; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef TEST_RING_H #define TEST_RING_H #include extern struct test_cases ring_tcases[]; #endif telepathy-ring-2.3.24-37ad7111424725a5446e82dac516d7cab88149c7/src/util.c000066400000000000000000000351501251541261300236700ustar00rootroot00000000000000/* * util.c - Source for Ring utility functions * Copyright (C) 2006-2007 Collabora Ltd. * Copyright (C) 2006-2007 Nokia Corporation * @author Robert McQueen * @author Simon McVittie * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "config.h" #include "util.h" #include #include #include #include #include #include #define DEBUG_FLAG RING_DEBUG_JID #include "ring-connection.h" gchar * sha1_hex (const gchar *bytes, guint len) { gchar *hex = g_compute_checksum_for_string (G_CHECKSUM_SHA1, bytes, len); guint i; for (i = 0; i < SHA1_HASH_SIZE * 2; i++) { g_assert (hex[i] != '\0'); hex[i] = g_ascii_tolower (hex[i]); } g_assert (hex[SHA1_HASH_SIZE * 2] == '\0'); return hex; } void sha1_bin (const gchar *bytes, guint len, guchar out[SHA1_HASH_SIZE]) { GChecksum *checksum = g_checksum_new (G_CHECKSUM_SHA1); gsize out_len = SHA1_HASH_SIZE; g_assert (g_checksum_type_get_length (G_CHECKSUM_SHA1) == SHA1_HASH_SIZE); g_checksum_update (checksum, (const guchar *) bytes, len); g_checksum_get_digest (checksum, out, &out_len); g_assert (out_len == SHA1_HASH_SIZE); g_checksum_free (checksum); } /** ring_generate_id: * * RFC4122 version 4 compliant random UUIDs generator. * * Returns: A string with RFC41122 version 4 random UUID, must be freed with * g_free(). */ gchar * ring_generate_id (void) { GRand *grand; gchar *str; struct { guint32 time_low; guint16 time_mid; guint16 time_hi_and_version; guint8 clock_seq_hi_and_rsv; guint8 clock_seq_low; guint16 node_hi; guint32 node_low; } uuid; /* Fill with random. Every new GRand are seede with 128 bit read from * /dev/urandom (or the current time on non-unix systems). This makes the * random source good enough for our usage, but may not be suitable for all * situation outside Ring. */ grand = g_rand_new (); uuid.time_low = g_rand_int (grand); uuid.time_mid = (guint16) g_rand_int_range (grand, 0, G_MAXUINT16); uuid.time_hi_and_version = (guint16) g_rand_int_range (grand, 0, G_MAXUINT16); uuid.clock_seq_hi_and_rsv = (guint8) g_rand_int_range (grand, 0, G_MAXUINT8); uuid.clock_seq_low = (guint8) g_rand_int_range (grand, 0, G_MAXUINT8); uuid.node_hi = (guint16) g_rand_int_range (grand, 0, G_MAXUINT16); uuid.node_low = g_rand_int (grand); g_rand_free (grand); /* Set the two most significant bits (bits 6 and 7) of the * clock_seq_hi_and_rsv to zero and one, respectively. */ uuid.clock_seq_hi_and_rsv = (uuid.clock_seq_hi_and_rsv & 0x3F) | 0x80; /* Set the four most significant bits (bits 12 through 15) of the * time_hi_and_version field to 4 */ uuid.time_hi_and_version = (uuid.time_hi_and_version & 0x0fff) | 0x4000; str = g_strdup_printf ("%08x-%04x-%04x-%02x%02x-%04x%08x", uuid.time_low, uuid.time_mid, uuid.time_hi_and_version, uuid.clock_seq_hi_and_rsv, uuid.clock_seq_low, uuid.node_hi, uuid.node_low); return str; } /** * ring_get_room_handle_from_jid: * @room_repo: The %TP_HANDLE_TYPE_ROOM handle repository * @jid: A JID * * Given a JID seen in the from="" attribute on a stanza, work out whether * it's something to do with a MUC, and if so, return its handle. * * Returns: The handle of the MUC, if the JID refers to either a MUC * we're in, or a contact's channel-specific JID inside a MUC. * Returns 0 if the JID is either invalid, or nothing to do with a * known MUC (typically this will mean it's the global JID of a contact). */ TpHandle ring_get_room_handle_from_jid (TpHandleRepoIface *room_repo, const gchar *jid) { TpHandle handle; gchar *room; room = ring_remove_resource (jid); if (room == NULL) return 0; handle = tp_handle_lookup (room_repo, room, NULL, NULL); g_free (room); return handle; } #define INVALID_HANDLE(e, f, ...) \ G_STMT_START { \ DEBUG (f, ##__VA_ARGS__); \ g_set_error (e, TP_ERROR, TP_ERROR_INVALID_HANDLE, f, ##__VA_ARGS__);\ } G_STMT_END gchar * ring_remove_resource (const gchar *jid) { char *slash = strchr (jid, '/'); gchar *buf; if (slash == NULL) return g_strdup (jid); /* The user and domain parts can't contain '/', assuming it's valid */ buf = g_malloc (slash - jid + 1); strncpy (buf, jid, slash - jid); buf[slash - jid] = '\0'; return buf; } gchar * ring_encode_jid ( const gchar *node, const gchar *domain, const gchar *resource) { gchar *tmp, *ret; g_return_val_if_fail (domain != NULL, NULL); if (node != NULL && resource != NULL) tmp = g_strdup_printf ("%s@%s/%s", node, domain, resource); else if (node != NULL) tmp = g_strdup_printf ("%s@%s", node, domain); else if (resource != NULL) tmp = g_strdup_printf ("%s/%s", domain, resource); else tmp = g_strdup (domain); ret = g_utf8_normalize (tmp, -1, G_NORMALIZE_NFKC); g_free (tmp); return ret; } typedef struct { GObject *instance; GObject *user_data; gulong handler_id; } WeakHandlerCtx; static WeakHandlerCtx * whc_new (GObject *instance, GObject *user_data) { WeakHandlerCtx *ctx = g_slice_new0 (WeakHandlerCtx); ctx->instance = instance; ctx->user_data = user_data; return ctx; } static void whc_free (WeakHandlerCtx *ctx) { g_slice_free (WeakHandlerCtx, ctx); } static void user_data_destroyed_cb (gpointer, GObject *); static void instance_destroyed_cb (gpointer ctx_, GObject *where_the_instance_was) { WeakHandlerCtx *ctx = ctx_; /* No need to disconnect the signal here, the instance has gone away. */ g_object_weak_unref (ctx->user_data, user_data_destroyed_cb, ctx); whc_free (ctx); } static void user_data_destroyed_cb (gpointer ctx_, GObject *where_the_user_data_was) { WeakHandlerCtx *ctx = ctx_; g_signal_handler_disconnect (ctx->instance, ctx->handler_id); g_object_weak_unref (ctx->instance, instance_destroyed_cb, ctx); whc_free (ctx); } /** * ring_signal_connect_weak: * @instance: the instance to connect to. * @detailed_signal: a string of the form "signal-name::detail". * @c_handler: the GCallback to connect. * @user_data: an object to pass as data to c_handler calls. * * Connects a #GCallback function to a signal for a particular object, as if * with g_signal_connect(). Additionally, arranges for the signal handler to be * disconnected if @user_data is destroyed. * * This is intended to be a convenient way for objects to use themselves as * user_data for callbacks without having to explicitly disconnect all the * handlers in their finalizers. */ void ring_signal_connect_weak (gpointer instance, const gchar *detailed_signal, GCallback c_handler, GObject *user_data) { GObject *instance_obj = G_OBJECT (instance); WeakHandlerCtx *ctx = whc_new (instance_obj, user_data); ctx->handler_id = g_signal_connect (instance, detailed_signal, c_handler, user_data); g_object_weak_ref (instance_obj, instance_destroyed_cb, ctx); g_object_weak_ref (user_data, user_data_destroyed_cb, ctx); } typedef struct { GSourceFunc function; GObject *object; guint source_id; } WeakIdleCtx; static void idle_weak_ref_notify (gpointer data, GObject *dead_object) { g_source_remove (GPOINTER_TO_UINT (data)); } static void idle_removed (gpointer data) { WeakIdleCtx *ctx = (WeakIdleCtx *) data; g_slice_free (WeakIdleCtx, ctx); } static gboolean idle_callback (gpointer data) { WeakIdleCtx *ctx = (WeakIdleCtx *) data; if (ctx->function ((gpointer) ctx->object)) { return TRUE; } else { g_object_weak_unref ( ctx->object, idle_weak_ref_notify, GUINT_TO_POINTER (ctx->source_id)); return FALSE; } } /* Like g_idle_add(), but cancel the callback if the provided object is * finalized. */ guint ring_idle_add_weak (GSourceFunc function, GObject *object) { WeakIdleCtx *ctx; ctx = g_slice_new0 (WeakIdleCtx); ctx->function = function; ctx->object = object; ctx->source_id = g_idle_add_full ( G_PRIORITY_DEFAULT_IDLE, idle_callback, ctx, idle_removed); g_object_weak_ref ( object, idle_weak_ref_notify, GUINT_TO_POINTER (ctx->source_id)); return ctx->source_id; } GPtrArray * ring_g_ptr_array_copy (GPtrArray *source) { GPtrArray *ret = g_ptr_array_sized_new (source->len); guint i; for (i = 0; i < source->len; i++) g_ptr_array_add (ret, g_ptr_array_index (source, i)); return ret; } gchar * ring_peer_to_jid (RingConnection *conn, TpHandle peer, const gchar *resource) { TpHandleRepoIface *repo = tp_base_connection_get_handles ( TP_BASE_CONNECTION (conn), TP_HANDLE_TYPE_CONTACT); const gchar *target = tp_handle_inspect (repo, peer); if (resource == NULL) return g_strdup (target); return g_strdup_printf ("%s/%s", target, resource); } /* Like wocky_enum_from_nick, but for GFlagsValues instead. */ gboolean ring_flag_from_nick (GType flag_type, const gchar *nick, guint *value) { GFlagsClass *klass = g_type_class_ref (flag_type); GFlagsValue *flag_value; g_return_val_if_fail (klass != NULL, FALSE); g_return_val_if_fail (value != NULL, FALSE); flag_value = g_flags_get_value_by_nick (klass, nick); g_type_class_unref (klass); if (flag_value != NULL) { *value = flag_value->value; return TRUE; } else { return FALSE; } } /** * ring_simple_async_succeed_or_fail_in_idle: * @self: the source object for an asynchronous function * @callback: a callback to call when @todo things have been done * @user_data: user data for the callback * @source_tag: the source tag for a #GSimpleAsyncResult * @error: (allow-none): %NULL to indicate success, or an error on failure * * Create a new #GSimpleAsyncResult and schedule it to call its callback * in an idle. If @error is %NULL, report success with * tp_simple_async_report_success_in_idle(); if @error is non-%NULL, * use g_simple_async_report_gerror_in_idle(). */ void ring_simple_async_succeed_or_fail_in_idle (gpointer self, GAsyncReadyCallback callback, gpointer user_data, gpointer source_tag, const GError *error) { if (error == NULL) { tp_simple_async_report_success_in_idle (self, callback, user_data, source_tag); } else { /* not const-correct yet: GNOME #622004 */ g_simple_async_report_gerror_in_idle (self, callback, user_data, (GError *) error); } } /** * ring_simple_async_countdown_new: * @self: the source object for an asynchronous function * @callback: a callback to call when @todo things have been done * @user_data: user data for the callback * @source_tag: the source tag for a #GSimpleAsyncResult * @todo: number of things to do before calling @callback (at least 1) * * Create a new #GSimpleAsyncResult that will call its callback when a number * of asynchronous operations have happened. * * An internal counter is initialized to @todo, incremented with * ring_simple_async_countdown_inc() or decremented with * ring_simple_async_countdown_dec(). * * When that counter reaches zero, if an error has been set with * g_simple_async_result_set_from_error() or similar, the operation fails; * otherwise, it succeeds. * * The caller must not use the operation result functions, such as * g_simple_async_result_get_op_res_gssize() - this async result is only * suitable for "void" async methods which return either success or a #GError, * i.e. the same signature as g_async_initable_init_async(). * * Returns: (transfer full): a counter */ GSimpleAsyncResult * ring_simple_async_countdown_new (gpointer self, GAsyncReadyCallback callback, gpointer user_data, gpointer source_tag, gssize todo) { GSimpleAsyncResult *simple; g_return_val_if_fail (todo >= 1, NULL); simple = g_simple_async_result_new (self, callback, user_data, source_tag); /* We (ab)use the op_res member as a count of things to do. When * it reaches zero, the operation completes with any error that has been * set, or with success. */ g_simple_async_result_set_op_res_gssize (simple, todo); /* we keep one extra reference as long as the counter is nonzero */ g_object_ref (simple); return simple; } /** * ring_simple_async_countdown_inc: * @simple: a result created by ring_simple_async_countdown_new() * * Increment the counter in @simple, indicating that an additional async * operation has been started. An additional call to * ring_simple_async_countdown_dec() will be needed to make @simple * call its callback. */ void ring_simple_async_countdown_inc (GSimpleAsyncResult *simple) { gssize todo = g_simple_async_result_get_op_res_gssize (simple); g_return_if_fail (todo >= 1); g_simple_async_result_set_op_res_gssize (simple, todo + 1); } /** * ring_simple_async_countdown_dec: * @simple: a result created by ring_simple_async_countdown_new() * * Decrement the counter in @simple. If the number of things to do has * reached zero, schedule @simple to call its callback in an idle, then * unref it. * * When one of the asynchronous operations needed for @simple succeeds, * this should be signalled by a call to this function. * * When one of the asynchronous operations needed for @simple fails, * this should be signalled by a call to g_simple_async_result_set_from_error() * (or one of the similar functions), followed by a call to this function. * If more than one async operation fails in this way, the #GError from the * last failure will be used. */ void ring_simple_async_countdown_dec (GSimpleAsyncResult *simple) { gssize todo = g_simple_async_result_get_op_res_gssize (simple); g_simple_async_result_set_op_res_gssize (simple, --todo); if (todo <= 0) { g_simple_async_result_complete_in_idle (simple); g_object_unref (simple); } } telepathy-ring-2.3.24-37ad7111424725a5446e82dac516d7cab88149c7/src/util.h000066400000000000000000000070511251541261300236740ustar00rootroot00000000000000/* * util.h - Headers for Ring utility functions * Copyright (C) 2006 Collabora Ltd. * Copyright (C) 2006 Nokia Corporation * @author Robert McQueen * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __GABBLE_UTIL_H__ #define __GABBLE_UTIL_H__ #include #include #include #include "ring-connection.h" /* Guarantees that the resulting hash is in lower-case */ gchar *sha1_hex (const gchar *bytes, guint len); /* A SHA1 digest is 20 bytes long */ #define SHA1_HASH_SIZE 20 void sha1_bin (const gchar *bytes, guint len, guchar out[SHA1_HASH_SIZE]); gchar *ring_generate_id (void); G_GNUC_WARN_UNUSED_RESULT gchar *ring_encode_jid (const gchar *node, const gchar *domain, const gchar *resource); gchar *ring_remove_resource (const gchar *jid); gchar *ring_normalize_room (TpHandleRepoIface *repo, const gchar *jid, gpointer context, GError **error); TpHandle ring_get_room_handle_from_jid (TpHandleRepoIface *room_repo, const gchar *jid); void ring_signal_connect_weak (gpointer instance, const gchar *detailed_signal, GCallback c_handler, GObject *user_data); guint ring_idle_add_weak (GSourceFunc function, GObject *object); GPtrArray *ring_g_ptr_array_copy (GPtrArray *source); gchar * ring_peer_to_jid (RingConnection *conn, TpHandle peer, const gchar *resource); gboolean ring_flag_from_nick (GType flag_type, const gchar *nick, guint *value); void ring_simple_async_succeed_or_fail_in_idle (gpointer self, GAsyncReadyCallback callback, gpointer user_data, gpointer source_tag, const GError *error); GSimpleAsyncResult *ring_simple_async_countdown_new (gpointer self, GAsyncReadyCallback callback, gpointer user_data, gpointer source_tag, gssize todo); void ring_simple_async_countdown_inc (GSimpleAsyncResult *simple); void ring_simple_async_countdown_dec (GSimpleAsyncResult *simple); /* Boilerplate for telling servers which implement XEP-0079 not to store these * messages for delivery later. Include it in your call to wocky_stanza_build() * like so: * * wocky_stanza_build (WOCKY_STANZA_TYPE_MESSAGE, WOCKY_STANZA_SUB_TYPE_NONE, * NULL, jid, * '(', "close", * ':', NS_TUBES, * '@', "tube", id_str, * ')', * RING_AMP_DO_NOT_STORE_SPEC, * NULL); * * Every 1000th user will win a Marshall amplifier! */ #define RING_AMP_DO_NOT_STORE_SPEC \ '(', "amp", \ ':', NS_AMP, \ '(', "rule", \ '@', "condition", "deliver-at", \ '@', "value", "stored", \ '@', "action", "error", \ ')', \ '(', "rule", \ '@', "condition", "match-resource", \ '@', "value", "exact", \ '@', "action", "error", \ ')', \ ')' #endif /* __GABBLE_UTIL_H__ */ telepathy-ring-2.3.24-37ad7111424725a5446e82dac516d7cab88149c7/tests/000077500000000000000000000000001251541261300231165ustar00rootroot00000000000000telepathy-ring-2.3.24-37ad7111424725a5446e82dac516d7cab88149c7/tests/Makefile.am000066400000000000000000000005261251541261300251550ustar00rootroot00000000000000SUBDIRS = twisted AM_CFLAGS = $(ERROR_CFLAGS) \ @GLIB_CFLAGS@ @DBUS_CFLAGS@ \ -I$(top_srcdir) @CHECK_CFLAGS@ dist_noinst_SCRIPTS = ring-monitor noinst_LTLIBRARIES = libtestcommon.la libtestcommon_la_SOURCES = \ test-common.c test-common.h tools_dir = $(top_srcdir)/tools MOSTLYCLEANFILES = $(BUILT_SOURCES) test_DATA = tests.xml telepathy-ring-2.3.24-37ad7111424725a5446e82dac516d7cab88149c7/tests/README000066400000000000000000000030631251541261300240000ustar00rootroot00000000000000To run all tests: make check == Twisted tests == To run all Twisted tests: make check-twisted To run an individual Twisted test: make -C tests/twisted check-twisted TWISTED_TESTS=connect/connect-success.py or: cd tests/twisted sh tools/with-session-bus.sh --config-file=tools/servicedir-uninstalled/tmp-session-bus.conf \ -- python connect/test-success.py To run with debug information: make -C tests/twisted check-twisted TWISTED_TESTS=connect/test-success.py \ CHECK_TWISTED_VERBOSE=1 or: cd tests/twisted sh tools/with-session-bus.sh --config-file=tools/servicedir-uninstalled/tmp-session-bus.conf \ -- python connect/test-success.py -v To debug an individual test you can set one of the following env variable: * RING_TEST_VALGRIND : to run Ring inside valgrind. The report is added to tools/ring-testing.log. export RING_TEST_VALGRIND=1 * RING_TEST_REFDBG : to run Ring inside refdbg. The report is written to tools/refdbg.log. You can change RING_WRAPPER to use an alternative refdbg and change REFDBG_OPTIONS to set your own parameters. Example: export RING_TEST_REFDBG=1 export RING_WRAPPER="/path/to/refdbg" export REFDBG_OPTIONS="btnum=16" * RING_WRAPPER="nemiver" : to run Ring inside the graphical debugger nemiver. You'll be able to set up breakpoints; then hit the "continue" button to launch Ring. * RING_TEST_BUSTLE : to run bustle-dbus-monitor in the temporary bus daemon. The logs are saved to tools/*bustle-logs. export RING_TEST_BUSTLE=1 telepathy-ring-2.3.24-37ad7111424725a5446e82dac516d7cab88149c7/tests/ring-monitor000077500000000000000000000002301251541261300254630ustar00rootroot00000000000000#! /bin/sh exec dbus-monitor "$@" sender=org.freedesktop.Telepathy.ConnectionManager.ring destination=org.freedesktop.Telepathy.ConnectionManager.ring telepathy-ring-2.3.24-37ad7111424725a5446e82dac516d7cab88149c7/tests/test-common.c000066400000000000000000000044141251541261300255320ustar00rootroot00000000000000/* * Copyright (C) 2009 Nokia Corporation. All rights reserved. * @author Andres Salomon */ #include "config.h" #include "test-common.h" #include #include #include #include #include #include static void usage(int err, const char *argv0) { fprintf(err == EXIT_FAILURE ? stderr : stdout, "Usage: %s [args]\n" "[args]:\n" " -h display this help menu\n" " -o XML log (check format)\n" " -t which test case to run\n" " (-t may be listed multiple times)\n" "\n", argv0); exit(err); } struct common_args *parse_common_args(int argc, char *const argv[]) { int opt, all_tests = 0; struct common_args *args; args = calloc(1, sizeof(*args)); if (!args) return NULL; while ((opt = getopt(argc, argv, "ho:t:")) != -1) { switch (opt) { case 'h': usage(EXIT_SUCCESS, argv[0]); break; case 'o': args->xml = strdup(optarg); break; case 't': /* check for magic strings that tell us to run all tests */ if (strcmp(optarg, "*") == 0 || strcmp(optarg, "all") == 0) { all_tests = 1; if (args->tests) { g_slist_foreach(args->tests, (GFunc) g_free, NULL); g_slist_free(args->tests); args->tests = NULL; } } if (all_tests) break; /* only running some tests; pay attention to which ones */ args->tests = g_slist_prepend(args->tests, strdup(optarg)); break; default: usage(EXIT_FAILURE, argv[0]); } } return args; } void free_common_args(struct common_args *args) { if (args->xml) free(args->xml); g_slist_foreach(args->tests, (GFunc) g_free, NULL); g_slist_free(args->tests); free(args); } void filter_add_tcases(Suite *suite, struct test_cases *tests, GSList *filter) { struct test_cases *i; for (i = tests; i->tcase_callback != NULL; i++) { if ((!filter && i->default_on) || g_slist_find_custom(filter, i->tcase_name, (GCompareFunc) g_ascii_strcasecmp)) suite_add_tcase(suite, i->tcase_callback()); else printf("skipping test %s\n", i->tcase_name); } } telepathy-ring-2.3.24-37ad7111424725a5446e82dac516d7cab88149c7/tests/test-common.h000066400000000000000000000020021251541261300255260ustar00rootroot00000000000000#ifndef _TEST_COMMON_H #define _TEST_COMMON_H #include #include struct common_args { char *xml; /* file to save XML check output to */ GSList *tests; /* list of tests to run; * empty list means run all tests */ }; struct common_args *parse_common_args(int argc, char *const argv[]); void free_common_args(struct common_args *args); typedef TCase *(*tcase_func)(void); struct test_cases { tcase_func tcase_callback; const char *tcase_name; int default_on; }; #define DECLARE_TEST_CASE(tc_func) \ { .tcase_name = (#tc_func), .tcase_callback = (tc_func), .default_on = 1 } #define DECLARE_TEST_CASE_OFF_BY_DEFAULT(tc_func) \ { .tcase_name = (#tc_func), .tcase_callback = (tc_func), .default_on = 0 } #define LAST_TEST_CASE \ { .tcase_name = NULL, .tcase_callback = NULL } void filter_add_tcases(Suite *suite, struct test_cases *tests, GSList *filter); #endif telepathy-ring-2.3.24-37ad7111424725a5446e82dac516d7cab88149c7/tests/tests.xml000066400000000000000000000013711251541261300250040ustar00rootroot00000000000000 Telepathy-Ring unit tests Telepathy-Ring modem tests /opt/tests/telepathy-ring/test-modem Telepathy-Ring ring tests /opt/tests/telepathy-ring/test-ring telepathy-ring-2.3.24-37ad7111424725a5446e82dac516d7cab88149c7/tests/tp-monitor000077500000000000000000000050261251541261300251570ustar00rootroot00000000000000#!/bin/sh filter=" interface='com.Nokia.Telepathy.Channel.Interface.DialStrings' interface='com.Nokia.Telepathy.Connection.Interface.Forwarding' interface='com.nokia.Telepathy.Channel.Interface.Emergency' interface='com.nokia.Telepathy.Connection.Interface.Emergency' interface='com.nokia.Telepathy.Connection.Interface.StoredMessages' interface='org.freedesktop.Telepathy.Channel.Interface.Conference.DRAFT' interface='org.freedesktop.Telepathy.Channel.Interface.MergeableConference.DRAFT' interface='org.freedesktop.Telepathy.Channel.Interface.Splittable.DRAFT' interface='org.freedesktop.Telepathy.Channel' interface='org.freedesktop.Telepathy.Channel.Interface.ChatState' interface='org.freedesktop.Telepathy.Channel.Interface.DTMF' interface='org.freedesktop.Telepathy.Channel.Interface.Group' interface='org.freedesktop.Telepathy.Channel.Interface.Hold' interface='org.freedesktop.Telepathy.Channel.Interface.Hold' interface='org.freedesktop.Telepathy.Channel.Interface.MediaSignalling' interface='org.freedesktop.Telepathy.Channel.Interface.Messages' interface='org.freedesktop.Telepathy.Channel.Interface.Password' interface='org.freedesktop.Telepathy.Channel.Interface.Transfer' interface='org.freedesktop.Telepathy.Channel.Type.ContactList' interface='org.freedesktop.Telepathy.Channel.Type.ContactSearch' interface='org.freedesktop.Telepathy.Channel.Type.RoomList' interface='org.freedesktop.Telepathy.Channel.Type.StreamedMedia' interface='org.freedesktop.Telepathy.Channel.Type.Text' interface='org.freedesktop.Telepathy.Channel.Type.Tubes' interface='org.freedesktop.Telepathy.ChannelHandler' interface='org.freedesktop.Telepathy.Connection' interface='org.freedesktop.Telepathy.Connection.Interface.Aliasing' interface='org.freedesktop.Telepathy.Connection.Interface.Avatars' interface='org.freedesktop.Telepathy.Connection.Interface.Capabilities' interface='org.freedesktop.Telepathy.Connection.Interface.ContactInfo' interface='org.freedesktop.Telepathy.Connection.Interface.Forwarding' interface='org.freedesktop.Telepathy.Connection.Interface.Presence' interface='org.freedesktop.Telepathy.Connection.Interface.Privacy' interface='org.freedesktop.Telepathy.Connection.Interface.Renaming' interface='org.freedesktop.Telepathy.Connection.Interface.Requests' interface='org.freedesktop.Telepathy.ConnectionManager' interface='org.freedesktop.Telepathy.Media.SessionHandler' interface='org.freedesktop.Telepathy.Media.StreamHandler' interface='org.freedesktop.Telepathy.MissionControl' interface='org.freedesktop.Telepathy.Properties' " dbus-monitor "$@" $filter telepathy-ring-2.3.24-37ad7111424725a5446e82dac516d7cab88149c7/tests/twisted/000077500000000000000000000000001251541261300246015ustar00rootroot00000000000000telepathy-ring-2.3.24-37ad7111424725a5446e82dac516d7cab88149c7/tests/twisted/Makefile.am000066400000000000000000000027121251541261300266370ustar00rootroot00000000000000TWISTED_TESTS = \ cm/protocol.py \ connect/connect-success.py \ $(NULL) config.py: Makefile $(AM_V_GEN) { \ echo "PACKAGE_STRING = \"$(PACKAGE_STRING)\""; \ } > $@ if WANT_TWISTED_TESTS check-local: check-twisted CHECK_TWISTED_SLEEP=0 check-twisted: $(BUILT_SOURCES) $(MAKE) -C tools if test "x$(CHECK_TWISTED_SLEEP)" = x0; then \ ring_test_sleep= ; \ else \ ring_test_sleep=--sleep=$(CHECK_TWISTED_SLEEP); \ fi; \ RING_TEST_UNINSTALLED=1 \ RING_ABS_TOP_SRCDIR=@abs_top_srcdir@ \ RING_ABS_TOP_BUILDDIR=@abs_top_builddir@ \ RING_TEST_SLEEP=$$ring_test_sleep \ ./run-test.sh "$(TWISTED_TESTS)" ring-twisted-tests.list: Makefile $(AM_V_GEN)echo $(TWISTED_TESTS) > $@ BUILT_SOURCES = \ config.py \ ring-twisted-tests.list \ run-test.sh \ $(NULL) # We don't really use ringtestsdir yet - we only support uninstalled testing # so far - but I'm substituting it to keep the script more similar to Gabble's. # ${pkglibexecdir}/tests is what GNOME's InstalledTests goal recommends. run-test.sh: run-test.sh.in Makefile $(AM_V_GEN)sed \ -e 's![@]ringtestsdir[@]!${pkglibexecdir}/tests!' \ -e 's![@]TEST_PYTHON[@]!$(TEST_PYTHON)!' \ < $< > $@.tmp && \ chmod +x $@.tmp && \ mv $@.tmp $@ endif EXTRA_DIST = \ $(TWISTED_TESTS) \ run-test.sh.in \ servicetest.py \ ringtest.py \ constants.py \ $(NULL) CLEANFILES = \ $(BUILT_SOURCES) \ ring-[1-9]*.log \ *.pyc \ */*.pyc \ $(NULL) SUBDIRS = tools telepathy-ring-2.3.24-37ad7111424725a5446e82dac516d7cab88149c7/tests/twisted/cm/000077500000000000000000000000001251541261300252005ustar00rootroot00000000000000telepathy-ring-2.3.24-37ad7111424725a5446e82dac516d7cab88149c7/tests/twisted/cm/protocol.py000066400000000000000000000030311251541261300274100ustar00rootroot00000000000000""" Test Ring's o.fd.T.Protocol implementation """ import dbus from servicetest import unwrap, tp_path_prefix, assertEquals, assertContains from ringtest import exec_test import constants as cs def test(q, bus, conn): cm = bus.get_object(cs.CM + '.ring', tp_path_prefix + '/ConnectionManager/ring') cm_iface = dbus.Interface(cm, cs.CM) cm_prop_iface = dbus.Interface(cm, cs.PROPERTIES_IFACE) protocols = unwrap(cm_prop_iface.Get(cs.CM, 'Protocols')) assertEquals(set(['tel']), set(protocols.keys())) protocol_names = unwrap(cm_iface.ListProtocols()) assertEquals(set(['tel']), set(protocol_names)) cm_params = cm_iface.GetParameters('tel') local_props = protocols['tel'] local_params = local_props[cs.PROTOCOL + '.Parameters'] assertEquals(cm_params, local_params) proto = bus.get_object(cm.bus_name, cm.object_path + '/tel') proto_prop_iface = dbus.Interface(proto, cs.PROPERTIES_IFACE) proto_props = unwrap(proto_prop_iface.GetAll(cs.PROTOCOL)) for key in ['Parameters', 'Interfaces', 'ConnectionInterfaces', 'RequestableChannelClasses', u'VCardField', u'EnglishName', u'Icon']: a = local_props[cs.PROTOCOL + '.' + key] b = proto_props[key] assertEquals(a, b) assertEquals('tel', proto_props['VCardField']) assertEquals('Mobile Telephony', proto_props['EnglishName']) assertEquals('im-tel', proto_props['Icon']) assertContains(cs.CONN_IFACE_REQUESTS, proto_props['ConnectionInterfaces']) if __name__ == '__main__': exec_test(test) telepathy-ring-2.3.24-37ad7111424725a5446e82dac516d7cab88149c7/tests/twisted/connect/000077500000000000000000000000001251541261300262325ustar00rootroot00000000000000connect-success.py000066400000000000000000000012171251541261300316250ustar00rootroot00000000000000telepathy-ring-2.3.24-37ad7111424725a5446e82dac516d7cab88149c7/tests/twisted/connect """ Test connecting to a server. """ from ringtest import exec_test from servicetest import EventPattern, call_async import constants as cs def test(q, bus, conn): conn.Connect() q.expect('dbus-signal', signal='StatusChanged', args=[cs.CONN_STATUS_CONNECTING, cs.CSR_REQUESTED]) q.expect('dbus-signal', signal='StatusChanged', args=[cs.CONN_STATUS_CONNECTED, cs.CSR_REQUESTED]) call_async(q, conn, 'Disconnect') q.expect_many( EventPattern('dbus-signal', signal='StatusChanged', args=[2, cs.CSR_REQUESTED]), EventPattern('dbus-return', method='Disconnect')) if __name__ == '__main__': exec_test(test) telepathy-ring-2.3.24-37ad7111424725a5446e82dac516d7cab88149c7/tests/twisted/constants.py000066400000000000000000000510751251541261300271770ustar00rootroot00000000000000# Copyright (C) 2009 Nokia Corporation # Copyright (C) 2009-2013 Collabora Ltd. # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA # 02110-1301 USA """ Some handy constants for other tests to share and enjoy. """ from dbus import PROPERTIES_IFACE, INTROSPECTABLE_IFACE PREFIX = "org.freedesktop.Telepathy" PATH_PREFIX = '/' + PREFIX.replace('.', '/') tp_name_prefix = PREFIX tp_path_prefix = PATH_PREFIX CM = PREFIX + ".ConnectionManager" HT_NONE = 0 HT_CONTACT = 1 HT_ROOM = 2 HT_LIST = 3 HT_GROUP = 4 CHANNEL = PREFIX + ".Channel" CHANNEL_IFACE_CALL_STATE = CHANNEL + ".Interface.CallState" CHANNEL_IFACE_CHAT_STATE = CHANNEL + '.Interface.ChatState' CHANNEL_IFACE_DESTROYABLE = CHANNEL + ".Interface.Destroyable" CHANNEL_IFACE_DTMF = CHANNEL + ".Interface.DTMF" CHANNEL_IFACE_GROUP = CHANNEL + ".Interface.Group" CHANNEL_IFACE_HOLD = CHANNEL + ".Interface.Hold" CHANNEL_IFACE_MEDIA_SIGNALLING = CHANNEL + ".Interface.MediaSignalling" CHANNEL_IFACE_MESSAGES = CHANNEL + ".Interface.Messages" CHANNEL_IFACE_PASSWORD = CHANNEL + ".Interface.Password" CHANNEL_IFACE_TUBE = CHANNEL + ".Interface.Tube" CHANNEL_IFACE_SASL_AUTH = CHANNEL + ".Interface.SASLAuthentication" CHANNEL_IFACE_CONFERENCE = CHANNEL + '.Interface.Conference' CHANNEL_IFACE_ROOM = CHANNEL + '.Interface.Room2' CHANNEL_IFACE_ROOM_CONFIG = CHANNEL + '.Interface.RoomConfig1' CHANNEL_IFACE_SUBJECT = CHANNEL + '.Interface.Subject2' CHANNEL_IFACE_FILE_TRANSFER_METADATA = CHANNEL + '.Interface.FileTransfer.Metadata' CHANNEL_IFACE_SMS = CHANNEL + '.Interface.SMS' CHANNEL_TYPE_CALL = CHANNEL + ".Type.Call1" CHANNEL_TYPE_CONTACT_LIST = CHANNEL + ".Type.ContactList" CHANNEL_TYPE_CONTACT_SEARCH = CHANNEL + ".Type.ContactSearch" CHANNEL_TYPE_TEXT = CHANNEL + ".Type.Text" CHANNEL_TYPE_TUBES = CHANNEL + ".Type.Tubes" CHANNEL_TYPE_STREAM_TUBE = CHANNEL + ".Type.StreamTube" CHANNEL_TYPE_DBUS_TUBE = CHANNEL + ".Type.DBusTube" CHANNEL_TYPE_TEXT = CHANNEL + ".Type.Text" CHANNEL_TYPE_FILE_TRANSFER = CHANNEL + ".Type.FileTransfer" CHANNEL_TYPE_ROOM_LIST = CHANNEL + ".Type.RoomList" CHANNEL_TYPE_SERVER_AUTHENTICATION = \ CHANNEL + ".Type.ServerAuthentication" CHANNEL_TYPE_SERVER_TLS_CONNECTION = \ CHANNEL + ".Type.ServerTLSConnection" TP_AWKWARD_PROPERTIES = PREFIX + ".Properties" PROPERTY_FLAG_READ = 1 PROPERTY_FLAG_WRITE = 2 PROPERTY_FLAGS_RW = PROPERTY_FLAG_READ | PROPERTY_FLAG_WRITE CHANNEL_TYPE = CHANNEL + '.ChannelType' TARGET_HANDLE_TYPE = CHANNEL + '.TargetHandleType' TARGET_HANDLE = CHANNEL + '.TargetHandle' TARGET_ID = CHANNEL + '.TargetID' REQUESTED = CHANNEL + '.Requested' INITIATOR_HANDLE = CHANNEL + '.InitiatorHandle' INITIATOR_ID = CHANNEL + '.InitiatorID' INTERFACES = CHANNEL + '.Interfaces' CALL_CONTENTS = CHANNEL_TYPE_CALL + '.Contents' CALL_CALL_STATE_DETAILS = CHANNEL_TYPE_CALL + '.CallStateDetails' CALL_CALL_STATE = CHANNEL_TYPE_CALL + '.CallState' CALL_CALL_FLAGS = CHANNEL_TYPE_CALL + '.CallFlags' CALL_CALL_STATE_REASON = CHANNEL_TYPE_CALL + '.CallStateReason' CALL_HARDWARE_STREAMING = CHANNEL_TYPE_CALL + '.HardwareStreaming' CALL_CALL_MEMBERS = CHANNEL_TYPE_CALL + '.CallMembers' CALL_MEMBER_IDENTIFIERS = CHANNEL_TYPE_CALL + '.MemberIdentifiers' CALL_INITIAL_TRANSPORT = CHANNEL_TYPE_CALL + '.InitialTransport' CALL_INITIAL_AUDIO = CHANNEL_TYPE_CALL + '.InitialAudio' CALL_INITIAL_AUDIO_NAME = CHANNEL_TYPE_CALL + '.InitialAudioName' CALL_INITIAL_VIDEO = CHANNEL_TYPE_CALL + '.InitialVideo' CALL_INITIAL_VIDEO_NAME = CHANNEL_TYPE_CALL + '.InitialVideoName' CALL_MUTABLE_CONTENTS = CHANNEL_TYPE_CALL + '.MutableContents' CALL_CONTENT = PREFIX + '.Call1.Content' CALL_CONTENT_IFACE_MEDIA = CALL_CONTENT + '.Interface.Media' CALL_CONTENT_IFACE_DTMF = CALL_CONTENT + '.Interface.DTMF' CALL_CONTENT_MEDIA_DESCRIPTION = CALL_CONTENT + '.MediaDescription' CALL_STREAM = PREFIX + '.Call1.Stream' CALL_STREAM_IFACE_MEDIA = CALL_STREAM + '.Interface.Media' CALL_STREAM_ENDPOINT = CALL_STREAM + '.Endpoint' CALL_MEDIA_TYPE_AUDIO = 0 CALL_MEDIA_TYPE_VIDEO = 1 CALL_CONTENT_PACKETIZATION_RTP = 0 CALL_CONTENT_PACKETIZATION_RAW = 1 CALL_CONTENT_PACKETIZATION_MSN_WEBCAM = 2 CALL_STREAM_TRANSPORT_UNKNOWN = 0 CALL_STREAM_TRANSPORT_RAW_UDP = 1 CALL_STREAM_TRANSPORT_ICE = 2 CALL_STREAM_TRANSPORT_GTALK_P2P = 3 CALL_STREAM_TRANSPORT_WLM_2009 = 4 CALL_STREAM_TRANSPORT_SHM = 5 CALL_STREAM_TRANSPORT_MULTICAST = 6 CALL_STATE_UNKNOWN = 0 CALL_STATE_PENDING_INITIATOR = 1 CALL_STATE_INITIALISING = 2 CALL_STATE_INITIALISED = 3 CALL_STATE_ACCEPTED = 4 CALL_STATE_ACTIVE = 5 CALL_STATE_ENDED = 6 CALL_FLAG_LOCALLY_HELD = 1 CALL_FLAG_LOCALLY_RINGING = 2 CALL_FLAG_LOCALLY_QUEUED = 4 CALL_FLAG_FORWARDED = 8 CALL_FLAG_CLEARING = 16 CALL_MEMBER_FLAG_RINGING = 1 CALL_MEMBER_FLAG_HELD = 2 CALL_DISPOSITION_NONE = 0 CALL_DISPOSITION_INITIAL = 1 CALL_SENDING_STATE_NONE = 0 CALL_SENDING_STATE_PENDING_SEND = 1 CALL_SENDING_STATE_SENDING = 2 CALL_SENDING_STATE_PENDING_STOP_SENDING = 3 CALL_STREAM_FLOW_STATE_STOPPED = 0 CALL_STREAM_FLOW_STATE_PENDING_START = 1 CALL_STREAM_FLOW_STATE_PENDING_STOP = 2 CALL_STREAM_FLOW_STATE_STARTED = 3 CALL_STREAM_ENDPOINT_STATE_CONNECTING = 0 CALL_STREAM_ENDPOINT_STATE_PROVISIONALLY_CONNECTED = 1 CALL_STREAM_ENDPOINT_STATE_FULLY_CONNECTED = 2 CALL_STREAM_ENDPOINT_STATE_EXHAUSTED_CANDIDATES = 3 CALL_STREAM_ENDPOINT_STATE_FAILED = 4 CALL_STREAM_CANDIDATE_TYPE_HOST = 1 CALL_STREAM_CANDIDATE_TYPE_SERVER_REFLEXIVE = 2 CALL_STREAM_CANDIDATE_TYPE_RELAY = 4 CALL_STATE_CHANGE_REASON_UNKNOWN = 0 CALL_STATE_CHANGE_REASON_PROGRESS_MADE = 1 CALL_STATE_CHANGE_REASON_USER_REQUESTED = 2 CALL_STATE_CHANGE_REASON_FORWARDED = 3 CALL_STATE_CHANGE_REASON_REJECTED = 4 CALL_STATE_CHANGE_REASON_NO_ANSWER = 5 CALL_STATE_CHANGE_REASON_INVALID_CONTACT = 6 CALL_STATE_CHANGE_REASON_PERMISSION_DENIED = 7 CALL_STATE_CHANGE_REASON_BUSY = 8 CALL_STATE_CHANGE_REASON_INTERNAL_ERROR = 9 CALL_STATE_CHANGE_REASON_SERVICE_ERROR = 10 CALL_STATE_CHANGE_REASON_NETWORK_ERROR = 11 CALL_STATE_CHANGE_REASON_MEDIA_ERROR = 12 CALL_STATE_CHANGE_REASON_CONNECTIVITY_ERROR = 13 CALL_STREAM_COMPONENT_UNKNOWN = 0 CALL_STREAM_COMPONENT_DATA = 1 CALL_STREAM_COMPONENT_CONTROL = 2 SUBSCRIPTION_STATE_UNKNOWN = 0 SUBSCRIPTION_STATE_NO = 1 SUBSCRIPTION_STATE_REMOVED_REMOTELY = 2 SUBSCRIPTION_STATE_ASK = 3 SUBSCRIPTION_STATE_YES = 4 CONTACT_LIST_STATE_NONE = 0 CONTACT_LIST_STATE_WAITING = 1 CONTACT_LIST_STATE_FAILURE = 2 CONTACT_LIST_STATE_SUCCESS = 3 CONN = PREFIX + ".Connection" CONN_IFACE_AVATARS = CONN + '.Interface.Avatars' CONN_IFACE_ALIASING = CONN + '.Interface.Aliasing' CONN_IFACE_CAPS = CONN + '.Interface.Capabilities' CONN_IFACE_CONTACTS = CONN + '.Interface.Contacts' CONN_IFACE_CONTACT_CAPS = CONN + '.Interface.ContactCapabilities' CONN_IFACE_CONTACT_INFO = CONN + ".Interface.ContactInfo" CONN_IFACE_PRESENCE = CONN + '.Interface.Presence' CONN_IFACE_SIMPLE_PRESENCE = CONN + '.Interface.SimplePresence' CONN_IFACE_REQUESTS = CONN + '.Interface.Requests' CONN_IFACE_LOCATION = CONN + '.Interface.Location' CONN_IFACE_GABBLE_DECLOAK = CONN + '.Interface.Gabble.Decloak' CONN_IFACE_MAIL_NOTIFICATION = CONN + '.Interface.MailNotification' CONN_IFACE_CONTACT_LIST = CONN + '.Interface.ContactList' CONN_IFACE_CONTACT_GROUPS = CONN + '.Interface.ContactGroups' CONN_IFACE_CLIENT_TYPES = CONN + '.Interface.ClientTypes' CONN_IFACE_POWER_SAVING = CONN + '.Interface.PowerSaving' CONN_IFACE_CONTACT_BLOCKING = CONN + '.Interface.ContactBlocking' CONN_IFACE_ADDRESSING = CONN + '.Interface.Addressing1' CONN_IFACE_SERVICE_POINT = CONN + '.Interface.ServicePoint' ATTR_ALIAS = CONN_IFACE_ALIASING + '/alias' ATTR_AVATAR_TOKEN = CONN_IFACE_AVATARS + '/token' ATTR_CLIENT_TYPES = CONN_IFACE_CLIENT_TYPES + '/client-types' ATTR_CONTACT_CAPABILITIES = CONN_IFACE_CONTACT_CAPS + '/capabilities' ATTR_CONTACT_ID = CONN + '/contact-id' ATTR_CONTACT_INFO = CONN_IFACE_CONTACT_INFO + '/info' ATTR_GROUPS = CONN_IFACE_CONTACT_GROUPS + '/groups' ATTR_LOCATION = CONN_IFACE_LOCATION + '/location' ATTR_PRESENCE = CONN_IFACE_SIMPLE_PRESENCE + '/presence' ATTR_PUBLISH = CONN_IFACE_CONTACT_LIST + '/publish' ATTR_SUBSCRIBE = CONN_IFACE_CONTACT_LIST + '/subscribe' STREAM_HANDLER = PREFIX + '.Media.StreamHandler' ERROR = PREFIX + '.Error' INVALID_ARGUMENT = ERROR + '.InvalidArgument' NOT_IMPLEMENTED = ERROR + '.NotImplemented' NOT_AVAILABLE = ERROR + '.NotAvailable' PERMISSION_DENIED = ERROR + '.PermissionDenied' OFFLINE = ERROR + '.Offline' NOT_CAPABLE = ERROR + '.NotCapable' CONNECTION_REFUSED = ERROR + '.ConnectionRefused' CONNECTION_FAILED = ERROR + '.ConnectionFailed' CONNECTION_LOST = ERROR + '.ConnectionLost' CANCELLED = ERROR + '.Cancelled' NOT_YOURS = ERROR + '.NotYours' DISCONNECTED = ERROR + '.Disconnected' REGISTRATION_EXISTS = ERROR + '.RegistrationExists' AUTHENTICATION_FAILED = ERROR + '.AuthenticationFailed' CONNECTION_REPLACED = ERROR + '.ConnectionReplaced' ALREADY_CONNECTED = ERROR + '.AlreadyConnected' NETWORK_ERROR = ERROR + '.NetworkError' NOT_YET = ERROR + '.NotYet' INVALID_HANDLE = ERROR + '.InvalidHandle' CERT_UNTRUSTED = ERROR + '.Cert.Untrusted' SERVICE_BUSY = ERROR + '.ServiceBusy' SERVICE_CONFUSED = ERROR + '.ServiceConfused' SOFTWARE_UPGRADE_REQUIRED = ERROR + '.SoftwareUpgradeRequired' BANNED = ERROR + '.Channel.Banned' DBUS_ERROR_UNKNOWN_METHOD = 'org.freedesktop.DBus.Error.UnknownMethod' DBUS_ERROR_NO_REPLY = 'org.freedesktop.DBus.Error.NoReply' TUBE_PARAMETERS = CHANNEL_IFACE_TUBE + '.Parameters' TUBE_STATE = CHANNEL_IFACE_TUBE + '.State' STREAM_TUBE_SERVICE = CHANNEL_TYPE_STREAM_TUBE + '.Service' DBUS_TUBE_SERVICE_NAME = CHANNEL_TYPE_DBUS_TUBE + '.ServiceName' DBUS_TUBE_DBUS_NAMES = CHANNEL_TYPE_DBUS_TUBE + '.DBusNames' DBUS_TUBE_SUPPORTED_ACCESS_CONTROLS = CHANNEL_TYPE_DBUS_TUBE + '.SupportedAccessControls' STREAM_TUBE_SUPPORTED_SOCKET_TYPES = CHANNEL_TYPE_STREAM_TUBE + '.SupportedSocketTypes' CONFERENCE_INITIAL_CHANNELS = CHANNEL_IFACE_CONFERENCE + '.InitialChannels' CONFERENCE_INITIAL_INVITEE_HANDLES = CHANNEL_IFACE_CONFERENCE + '.InitialInviteeHandles' CONFERENCE_INITIAL_INVITEE_IDS = CHANNEL_IFACE_CONFERENCE + '.InitialInviteeIDs' CONTACT_SEARCH_ASK = CHANNEL_TYPE_CONTACT_SEARCH + '.AvailableSearchKeys' CONTACT_SEARCH_SERVER = CHANNEL_TYPE_CONTACT_SEARCH + '.Server' CONTACT_SEARCH_STATE = CHANNEL_TYPE_CONTACT_SEARCH + '.SearchState' SEARCH_NOT_STARTED = 0 SEARCH_IN_PROGRESS = 1 SEARCH_MORE_AVAILABLE = 2 SEARCH_COMPLETED = 3 SEARCH_FAILED = 4 TUBE_CHANNEL_STATE_LOCAL_PENDING = 0 TUBE_CHANNEL_STATE_REMOTE_PENDING = 1 TUBE_CHANNEL_STATE_OPEN = 2 TUBE_CHANNEL_STATE_NOT_OFFERED = 3 MEDIA_STREAM_TYPE_AUDIO = 0 MEDIA_STREAM_TYPE_VIDEO = 1 MEDIA_STREAM_BASE_PROTO_UDP = 0 MEDIA_STREAM_BASE_PROTO_TCP = 1 MEDIA_STREAM_TRANSPORT_TYPE_LOCAL = 0 MEDIA_STREAM_TRANSPORT_TYPE_DERIVED = 1 MEDIA_STREAM_TRANSPORT_TYPE_RELAY = 2 SOCKET_ADDRESS_TYPE_UNIX = 0 SOCKET_ADDRESS_TYPE_ABSTRACT_UNIX = 1 SOCKET_ADDRESS_TYPE_IPV4 = 2 SOCKET_ADDRESS_TYPE_IPV6 = 3 SOCKET_ACCESS_CONTROL_LOCALHOST = 0 SOCKET_ACCESS_CONTROL_PORT = 1 SOCKET_ACCESS_CONTROL_NETMASK = 2 SOCKET_ACCESS_CONTROL_CREDENTIALS = 3 TUBE_STATE_LOCAL_PENDING = 0 TUBE_STATE_REMOTE_PENDING = 1 TUBE_STATE_OPEN = 2 TUBE_STATE_NOT_OFFERED = 3 TUBE_TYPE_DBUS = 0 TUBE_TYPE_STREAM = 1 MEDIA_STREAM_DIRECTION_NONE = 0 MEDIA_STREAM_DIRECTION_SEND = 1 MEDIA_STREAM_DIRECTION_RECEIVE = 2 MEDIA_STREAM_DIRECTION_BIDIRECTIONAL = 3 MEDIA_STREAM_PENDING_LOCAL_SEND = 1 MEDIA_STREAM_PENDING_REMOTE_SEND = 2 MEDIA_STREAM_TYPE_AUDIO = 0 MEDIA_STREAM_TYPE_VIDEO = 1 MEDIA_STREAM_STATE_DISCONNECTED = 0 MEDIA_STREAM_STATE_CONNECTING = 1 MEDIA_STREAM_STATE_CONNECTED = 2 MEDIA_STREAM_DIRECTION_NONE = 0 MEDIA_STREAM_DIRECTION_SEND = 1 MEDIA_STREAM_DIRECTION_RECEIVE = 2 MEDIA_STREAM_DIRECTION_BIDIRECTIONAL = 3 FT_STATE_NONE = 0 FT_STATE_PENDING = 1 FT_STATE_ACCEPTED = 2 FT_STATE_OPEN = 3 FT_STATE_COMPLETED = 4 FT_STATE_CANCELLED = 5 FT_STATE_CHANGE_REASON_NONE = 0 FT_STATE_CHANGE_REASON_REQUESTED = 1 FT_STATE_CHANGE_REASON_LOCAL_STOPPED = 2 FT_STATE_CHANGE_REASON_REMOTE_STOPPED = 3 FT_STATE_CHANGE_REASON_LOCAL_ERROR = 4 FT_STATE_CHANGE_REASON_REMOTE_ERROR = 5 FILE_HASH_TYPE_NONE = 0 FILE_HASH_TYPE_MD5 = 1 FILE_HASH_TYPE_SHA1 = 2 FILE_HASH_TYPE_SHA256 = 3 FT_STATE = CHANNEL_TYPE_FILE_TRANSFER + '.State' FT_CONTENT_TYPE = CHANNEL_TYPE_FILE_TRANSFER + '.ContentType' FT_FILENAME = CHANNEL_TYPE_FILE_TRANSFER + '.Filename' FT_SIZE = CHANNEL_TYPE_FILE_TRANSFER + '.Size' FT_CONTENT_HASH_TYPE = CHANNEL_TYPE_FILE_TRANSFER + '.ContentHashType' FT_CONTENT_HASH = CHANNEL_TYPE_FILE_TRANSFER + '.ContentHash' FT_DESCRIPTION = CHANNEL_TYPE_FILE_TRANSFER + '.Description' FT_DATE = CHANNEL_TYPE_FILE_TRANSFER + '.Date' FT_AVAILABLE_SOCKET_TYPES = CHANNEL_TYPE_FILE_TRANSFER + '.AvailableSocketTypes' FT_TRANSFERRED_BYTES = CHANNEL_TYPE_FILE_TRANSFER + '.TransferredBytes' FT_INITIAL_OFFSET = CHANNEL_TYPE_FILE_TRANSFER + '.InitialOffset' FT_FILE_COLLECTION = CHANNEL_TYPE_FILE_TRANSFER + '.FUTURE.FileCollection' FT_URI = CHANNEL_TYPE_FILE_TRANSFER + '.URI' FT_SERVICE_NAME = CHANNEL_IFACE_FILE_TRANSFER_METADATA + '.ServiceName' FT_METADATA = CHANNEL_IFACE_FILE_TRANSFER_METADATA + '.Metadata' GF_CAN_ADD = 1 GF_CAN_REMOVE = 2 GF_CAN_RESCIND = 4 GF_MESSAGE_ADD = 8 GF_MESSAGE_REMOVE = 16 GF_MESSAGE_ACCEPT = 32 GF_MESSAGE_REJECT = 64 GF_MESSAGE_RESCIND = 128 GF_CHANNEL_SPECIFIC_HANDLES = 256 GF_ONLY_ONE_GROUP = 512 GF_HANDLE_OWNERS_NOT_AVAILABLE = 1024 GF_PROPERTIES = 2048 GF_MEMBERS_CHANGED_DETAILED = 4096 GC_REASON_NONE = 0 GC_REASON_OFFLINE = 1 GC_REASON_KICKED = 2 GC_REASON_BUSY = 3 GC_REASON_INVITED = 4 GC_REASON_BANNED = 5 GC_REASON_ERROR = 6 GC_REASON_INVALID_CONTACT = 7 GC_REASON_NO_ANSWER = 8 GC_REASON_RENAMED = 9 GC_REASON_PERMISSION_DENIED = 10 GC_REASON_SEPARATED = 11 HS_UNHELD = 0 HS_HELD = 1 HS_PENDING_HOLD = 2 HS_PENDING_UNHOLD = 3 HSR_NONE = 0 HSR_REQUESTED = 1 HSR_RESOURCE_NOT_AVAILABLE = 2 CONN_STATUS_CONNECTED = 0 CONN_STATUS_CONNECTING = 1 CONN_STATUS_DISCONNECTED = 2 CSR_NONE_SPECIFIED = 0 CSR_REQUESTED = 1 CSR_NETWORK_ERROR = 2 CSR_AUTHENTICATION_FAILED = 3 CSR_ENCRYPTION_ERROR = 4 CSR_NAME_IN_USE = 5 CSR_CERT_NOT_PROVIDED = 6 CSR_CERT_UNTRUSTED = 7 CSR_CERT_EXPIRED = 8 CSR_CERT_NOT_ACTIVATED = 9 CSR_CERT_HOSTNAME_MISMATCH = 10 CSR_CERT_FINGERPRINT_MISMATCH = 11 CSR_CERT_SELF_SIGNED = 12 CSR_CERT_OTHER_ERROR = 13 BUDDY_INFO = 'org.laptop.Telepathy.BuddyInfo' ACTIVITY_PROPERTIES = 'org.laptop.Telepathy.ActivityProperties' CHAT_STATE_GONE = 0 CHAT_STATE_INACTIVE = 1 CHAT_STATE_ACTIVE = 2 CHAT_STATE_PAUSED = 3 CHAT_STATE_COMPOSING = 4 # Channel_Media_Capabilities MEDIA_CAP_AUDIO = 1 MEDIA_CAP_VIDEO = 2 MEDIA_CAP_STUN = 4 MEDIA_CAP_GTALKP2P = 8 MEDIA_CAP_ICEUDP = 16 MEDIA_CAP_IMMUTABLE_STREAMS = 32 CLIENT = PREFIX + '.Client' PRESENCE_UNSET = 0 PRESENCE_OFFLINE = 1 PRESENCE_AVAILABLE = 2 PRESENCE_AWAY = 3 PRESENCE_EXTENDED_AWAY = 4 PRESENCE_HIDDEN = 5 PRESENCE_BUSY = 6 PRESENCE_UNKNOWN = 7 PRESENCE_ERROR = 8 CONTACT_INFO_FLAG_CAN_SET = 1 CONTACT_INFO_FLAG_PUSH = 2 CONTACT_INFO_FIELD_FLAG_PARAMETERS_EXACT = 1 CONTACT_INFO_FIELD_FLAG_OVERWRITTEN_BY_NICKNAME = 2 # Channel_Interface_SaslAuthentication SASL_STATUS_NOT_STARTED = 0 SASL_STATUS_IN_PROGRESS = 1 SASL_STATUS_SERVER_SUCCEEDED = 2 SASL_STATUS_CLIENT_ACCEPTED = 3 SASL_STATUS_SUCCEEDED = 4 SASL_STATUS_SERVER_FAILED = 5 SASL_STATUS_CLIENT_FAILED = 6 SASL_ABORT_REASON_INVALID_CHALLENGE = 0 SASL_ABORT_REASON_USER_ABORT = 1 AUTH_METHOD = CHANNEL_TYPE_SERVER_AUTHENTICATION + ".AuthenticationMethod" SASL_AVAILABLE_MECHANISMS = CHANNEL_IFACE_SASL_AUTH + ".AvailableMechanisms" SASL_STATUS = CHANNEL_IFACE_SASL_AUTH + ".SASLStatus" SASL_ERROR = CHANNEL_IFACE_SASL_AUTH + ".SASLError" SASL_ERROR_DETAILS = CHANNEL_IFACE_SASL_AUTH + ".SASLErrorDetails" SASL_CONTEXT = CHANNEL_IFACE_SASL_AUTH + ".SASLContext" SASL_AUTHORIZATION_IDENTITY = CHANNEL_IFACE_SASL_AUTH + ".AuthorizationIdentity" SASL_DEFAULT_REALM = CHANNEL_IFACE_SASL_AUTH + ".DefaultRealm" SASL_DEFAULT_USERNAME = CHANNEL_IFACE_SASL_AUTH + ".DefaultUsername" # Channel_Type_ServerTLSConnection TLS_CERT_PATH = CHANNEL_TYPE_SERVER_TLS_CONNECTION + ".ServerCertificate" TLS_HOSTNAME = CHANNEL_TYPE_SERVER_TLS_CONNECTION + ".Hostname" TLS_REFERENCE_IDENTITIES = \ CHANNEL_TYPE_SERVER_TLS_CONNECTION + ".ReferenceIdentities" # Connection.Interface.Location LOCATION_FEATURE_CAN_SET = 1 # Channel.Type.Text MT_NORMAL = 0 MT_ACTION = 1 MT_NOTICE = 2 MT_AUTO_REPLY = 3 MT_DELIVERY_REPORT = 4 class MessageFlag(object): TRUNCATED = 1 NON_TEXT_CONTENT = 2 SCROLLBACK = 4 RESCUED = 8 class SendError(object): UNKNOWN = 0 OFFLINE = 1 INVALID_CONTACT = 2 PERMISSION_DENIED = 3 TOO_LONG = 4 NOT_IMPLEMENTED = 5 PROTOCOL = PREFIX + '.Protocol' PROTOCOL_IFACE_PRESENCES = PROTOCOL + '.Interface.Presence' PROTOCOL_IFACE_ADDRESSING = PROTOCOL + '.Interface.Addressing' PROTOCOL_IFACE_AVATARS = PROTOCOL + '.Interface.Avatars' PARAM_REQUIRED = 1 PARAM_REGISTER = 2 PARAM_HAS_DEFAULT = 4 PARAM_SECRET = 8 PARAM_DBUS_PROPERTY = 16 AUTHENTICATION = PREFIX + '.Authentication' AUTH_TLS_CERT = AUTHENTICATION + ".TLSCertificate" TLS_CERT_STATE_PENDING = 0 TLS_CERT_STATE_ACCEPTED = 1 TLS_CERT_STATE_REJECTED = 2 TLS_REJECT_REASON_UNKNOWN = 0 TLS_REJECT_REASON_UNTRUSTED = 1 # Channel.Interface.Messages MESSAGE_PART_SUPPORT_FLAGS = CHANNEL_IFACE_MESSAGES + '.MessagePartSupportFlags' DELIVERY_REPORTING_SUPPORT = CHANNEL_IFACE_MESSAGES + '.DeliveryReportingSupport' SUPPORTED_CONTENT_TYPES = CHANNEL_IFACE_MESSAGES + '.SupportedContentTypes' MSG_SENDING_FLAGS_REPORT_DELIVERY = 1 MSG_SENDING_FLAGS_REPORT_READ = 2 MSG_SENDING_FLAGS_REPORT_DELETED = 4 DELIVERY_REPORTING_SUPPORT_FLAGS_RECEIVE_FAILURES = 1 DELIVERY_REPORTING_SUPPORT_FLAGS_RECEIVE_SUCCESSES = 2 DELIVERY_REPORTING_SUPPORT_FLAGS_RECEIVE_READ = 4 DELIVERY_REPORTING_SUPPORT_FLAGS_RECEIVE_DELETED = 8 DELIVERY_STATUS_UNKNOWN = 0 DELIVERY_STATUS_DELIVERED = 1 DELIVERY_STATUS_TEMPORARILY_FAILED = 2 DELIVERY_STATUS_PERMANENTLY_FAILED = 3 DELIVERY_STATUS_ACCEPTED = 4 DELIVERY_STATUS_READ = 5 DELIVERY_STATUS_DELETED = 6 MEDIA_STREAM_ERROR_UNKNOWN = 0 MEDIA_STREAM_ERROR_EOS = 1 MEDIA_STREAM_ERROR_CODEC_NEGOTIATION_FAILED = 2 MEDIA_STREAM_ERROR_CONNECTION_FAILED = 3 MEDIA_STREAM_ERROR_NETWORK_ERROR = 4 MEDIA_STREAM_ERROR_NO_CODECS = 5 MEDIA_STREAM_ERROR_INVALID_CM_BEHAVIOR = 6 MEDIA_STREAM_ERROR_MEDIA_ERROR = 7 PASSWORD_FLAG_PROVIDE = 8 # Channel.Interface.Room ROOM_NAME = CHANNEL_IFACE_ROOM + '.RoomName' ROOM_SERVER = CHANNEL_IFACE_ROOM + '.Server' # Channel.Interface.Subject SUBJECT = CHANNEL_IFACE_ROOM + '.Subject' SUBJECT_PRESENT = 1 SUBJECT_CAN_SET = 2 DEBUG_IFACE = PREFIX + '.Debug' DEBUG_PATH = PATH_PREFIX + '/debug' SERVICE_POINT_TYPE_NONE = 0 SERVICE_POINT_TYPE_EMERGENCY = 1 SERVICE_POINT_TYPE_COUNSELING = 2 CLIENT = PREFIX + '.Client' CLIENT_PATH = PATH_PREFIX + '/Client' OBSERVER = PREFIX + '.Client.Observer' APPROVER = PREFIX + '.Client.Approver' HANDLER = PREFIX + '.Client.Handler' CLIENT_IFACE_REQUESTS = CLIENT + '.Interface.Requests' ACCOUNT = PREFIX + '.Account' ACCOUNT_IFACE_AVATAR = ACCOUNT + '.Interface.Avatar' ACCOUNT_IFACE_ADDRESSING = ACCOUNT + '.Interface.Addressing' ACCOUNT_IFACE_HIDDEN = ACCOUNT + '.Interface.Hidden.DRAFT1' ACCOUNT_IFACE_NOKIA_CONDITIONS = 'com.nokia.Account.Interface.Conditions' ACCOUNT_PATH_PREFIX = PATH_PREFIX + '/Account/' AM = PREFIX + '.AccountManager' AM_IFACE_HIDDEN = AM + '.Interface.Hidden.DRAFT1' AM_PATH = PATH_PREFIX + '/AccountManager' CR = PREFIX + '.ChannelRequest' CDO = PREFIX + '.ChannelDispatchOperation' CD = PREFIX + '.ChannelDispatcher' CD_IFACE_OP_LIST = PREFIX + '.ChannelDispatcher.Interface.OperationList' CD_PATH = PATH_PREFIX + '/ChannelDispatcher' CD_REDISPATCH = CD + '.Interface.Redispatch.DRAFT' MC = PREFIX + '.MissionControl5' MC_PATH = PATH_PREFIX + '/MissionControl5' DTMF_CURRENTLY_SENDING_TONES = CHANNEL_IFACE_DTMF + '.CurrentlySendingTones' DTMF_INITIAL_TONES = CHANNEL_IFACE_DTMF + '.InitialTones' DTMF_DEFERRED_TONES = CHANNEL_IFACE_DTMF + '.DeferredTones' TESTDOT = PREFIX + ".Test." TESTSLASH = PATH_PREFIX + "/Test/" TEST_DBUS_ACCOUNT_SERVICE = TESTDOT + "DBusAccountService" TEST_DBUS_ACCOUNT_SERVICE_PATH = TESTSLASH + "DBusAccountService" TEST_DBUS_ACCOUNT_SERVICE_IFACE = TEST_DBUS_ACCOUNT_SERVICE TEST_DBUS_ACCOUNT_PLUGIN_PATH = TESTSLASH + "DBusAccountPlugin" TEST_DBUS_ACCOUNT_PLUGIN_IFACE = TESTDOT + "DBusAccountPlugin" # Channel.Interface.SMS SMS_FLASH = CHANNEL_IFACE_SMS + '.Flash' SMS_CHANNEL = CHANNEL_IFACE_SMS + '.SMSChannel' telepathy-ring-2.3.24-37ad7111424725a5446e82dac516d7cab88149c7/tests/twisted/ringtest.py000066400000000000000000000065511251541261300270210ustar00rootroot00000000000000 """ Infrastructure code for testing Ring """ import os import sys import dbus import servicetest import time from servicetest import (unwrap, Event) from twisted.internet import reactor def install_colourer(): def red(s): return '\x1b[31m%s\x1b[0m' % s def green(s): return '\x1b[32m%s\x1b[0m' % s patterns = { 'handled': green, 'not handled': red, } class Colourer: def __init__(self, fh, patterns): self.fh = fh self.patterns = patterns def write(self, s): f = self.patterns.get(s, lambda x: x) self.fh.write(f(s)) sys.stdout = Colourer(sys.stdout, patterns) return sys.stdout class Simulator(object): def __init__(self): self.bus = dbus.SystemBus() try: manager = dbus.Interface(self.bus.get_object('org.ofono', '/'), dbus_interface='org.ofono.Manager') except dbus.exceptions.DBusException: print " Ofono needs to be running to execute tests" os._exit(1) def make_connection(bus, event_func, params=None): default_params = { 'modem': dbus.ObjectPath('/phonesim'), } if params: default_params.update(params) return servicetest.make_connection(bus, event_func, 'ring', 'tel', default_params) def exec_test_deferred (funs, params, protocol=None, timeout=None): colourer = None if sys.stdout.isatty(): colourer = install_colourer() queue = servicetest.IteratingEventQueue(timeout) queue.verbose = ( os.environ.get('CHECK_TWISTED_VERBOSE', '') != '' or '-v' in sys.argv) bus = dbus.SessionBus() sim = Simulator() bus.add_signal_receiver( lambda *args, **kw: queue.append( Event('dbus-signal', path=unwrap(kw['path']), signal=kw['member'], args=map(unwrap, args), interface=kw['interface'])), None, # signal name None, # interface None, path_keyword='path', member_keyword='member', interface_keyword='interface', byte_arrays=True ) try: for f in funs: conn = make_connection(bus, queue.append, params) f(queue, bus, conn) except Exception: import traceback traceback.print_exc() try: if colourer: sys.stdout = colourer.fh reactor.crash() # force Disconnect in case the test crashed and didn't disconnect # properly. We need to call this async because the BaseIRCServer # class must do something in response to the Disconnect call and if we # call it synchronously, we're blocking ourself from responding to the # quit method. servicetest.call_async(queue, conn, 'Disconnect') if 'RING_TEST_REFDBG' in os.environ: # we have to wait for the timeout so the process is properly # exited and refdbg can generate its report time.sleep(5.5) except dbus.DBusException: pass def exec_tests(funs, params=None, protocol=None, timeout=None): reactor.callWhenRunning (exec_test_deferred, funs, params, protocol, timeout) reactor.run() def exec_test(fun, params=None, protocol=None, timeout=None): exec_tests([fun], params, protocol, timeout) telepathy-ring-2.3.24-37ad7111424725a5446e82dac516d7cab88149c7/tests/twisted/run-test.sh.in000066400000000000000000000030661251541261300273300ustar00rootroot00000000000000#!/bin/sh if test "x$RING_TEST_UNINSTALLED" = x; then script_fullname=`readlink -e "@ringtestsdir@/twisted/run-test.sh"` if [ `readlink -e "$0"` != "$script_fullname" ] ; then echo "This script is meant to be installed at $script_fullname" >&2 exit 1 fi test_src="@ringtestsdir@" test_build="@ringtestsdir@" config_file="@ringtestsdir@/twisted/tools/servicedir/tmp-session-bus.conf" PYTHONPATH="@ringtestsdir@/twisted" export PYTHONPATH RING_TWISTED_PATH="@ringtestsdir@/twisted" export RING_TWISTED_PATH else if test -z "$RING_ABS_TOP_SRCDIR"; then echo "RING_ABS_TOP_SRCDIR must be set" >&2 exit 1 fi if test -z "$RING_ABS_TOP_BUILDDIR"; then echo "RING_ABS_TOP_BUILDDIR must be set" >&2 exit 1 fi test_src="${RING_ABS_TOP_SRCDIR}/tests" test_build="${RING_ABS_TOP_BUILDDIR}/tests" config_file="${test_build}/twisted/tools/tmp-session-bus.conf" PYTHONPATH="${test_src}/twisted:${test_build}/twisted" export PYTHONPATH RING_TWISTED_PATH="${test_src}/twisted" export RING_TWISTED_PATH fi if [ -n "$1" ] ; then list="$1" else list=$(cat "${test_build}"/twisted/ring-twisted-tests.list) fi any_failed=0 for i in $list ; do echo "Testing $i ..." sh "${test_src}/twisted/tools/with-session-bus.sh" \ ${RING_TEST_SLEEP} \ --config-file="${config_file}" \ -- \ @TEST_PYTHON@ -u "${test_src}/twisted/$i" e=$? case "$e" in (0) echo "PASS: $i" ;; (77) echo "SKIP: $i" ;; (*) any_failed=1 echo "FAIL: $i ($e)" ;; esac done exit $any_failed telepathy-ring-2.3.24-37ad7111424725a5446e82dac516d7cab88149c7/tests/twisted/servicetest.py000066400000000000000000000670621251541261300275260ustar00rootroot00000000000000# Copyright (C) 2009 Nokia Corporation # Copyright (C) 2009-2013 Collabora Ltd. # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA # 02110-1301 USA """ Infrastructure code for testing Telepathy services. """ from twisted.internet import glib2reactor from twisted.internet.protocol import Protocol, Factory, ClientFactory glib2reactor.install() import sys import time import os import pprint import unittest import dbus import dbus.lowlevel from dbus.mainloop.glib import DBusGMainLoop DBusGMainLoop(set_as_default=True) from twisted.internet import reactor import constants as cs tp_name_prefix = cs.PREFIX tp_path_prefix = cs.PATH_PREFIX class DictionarySupersetOf (object): """Utility class for expecting "a dictionary with at least these keys".""" def __init__(self, dictionary): self._dictionary = dictionary def __repr__(self): return "DictionarySupersetOf(%s)" % self._dictionary def __eq__(self, other): """would like to just do: return set(other.items()).issuperset(self._dictionary.items()) but it turns out that this doesn't work if you have another dict nested in the values of your dicts""" try: for k,v in self._dictionary.items(): if k not in other or other[k] != v: return False return True except TypeError: # other is not iterable return False class Event(object): def __init__(self, type, **kw): self.__dict__.update(kw) self.type = type (self.subqueue, self.subtype) = type.split ("-", 1) def __str__(self): return '\n'.join([ str(type(self)) ] + format_event(self)) def format_event(event): ret = ['- type %s' % event.type] for key in sorted(dir(event)): if key != 'type' and not key.startswith('_'): ret.append('- %s: %s' % ( key, pprint.pformat(getattr(event, key)))) if key == 'error': ret.append('%s' % getattr(event, key)) return ret class EventPattern: def __init__(self, type, **properties): self.type = type self.predicate = None if 'predicate' in properties: self.predicate = properties['predicate'] del properties['predicate'] self.properties = properties (self.subqueue, self.subtype) = type.split ("-", 1) def __repr__(self): properties = dict(self.properties) if self.predicate is not None: properties['predicate'] = self.predicate return '%s(%r, **%r)' % ( self.__class__.__name__, self.type, properties) def match(self, event): if event.type != self.type: return False for key, value in self.properties.iteritems(): try: if getattr(event, key) != value: return False except AttributeError: return False if self.predicate is None or self.predicate(event): return True return False class TimeoutError(Exception): pass class ForbiddenEventOccurred(Exception): def __init__(self, event): Exception.__init__(self) self.event = event def __str__(self): return '\n' + '\n'.join(format_event(self.event)) class BaseEventQueue: """Abstract event queue base class. Implement the wait() method to have something that works. """ def __init__(self, timeout=None): self.verbose = False self.forbidden_events = set() self.event_queues = {} if timeout is None: self.timeout = 5 else: self.timeout = timeout def log(self, s): if self.verbose: print s def log_queues(self, queues): self.log ("Waiting for event on: %s" % ", ".join(queues)) def log_event(self, event): self.log('got event:') if self.verbose: map(self.log, format_event(event)) def forbid_events(self, patterns): """ Add patterns (an iterable of EventPattern) to the set of forbidden events. If a forbidden event occurs during an expect or expect_many, the test will fail. """ self.forbidden_events.update(set(patterns)) def unforbid_events(self, patterns): """ Remove 'patterns' (an iterable of EventPattern) from the set of forbidden events. These must be the same EventPattern pointers that were passed to forbid_events. """ self.forbidden_events.difference_update(set(patterns)) def unforbid_all(self): """ Remove all patterns from the set of forbidden events. """ self.forbidden_events.clear() def _check_forbidden(self, event): for e in self.forbidden_events: if e.match(event): raise ForbiddenEventOccurred(event) def expect(self, type, **kw): """ Waits for an event matching the supplied pattern to occur, and returns it. For example, to await a D-Bus signal with particular arguments: e = q.expect('dbus-signal', signal='Badgers', args=["foo", 42]) """ pattern = EventPattern(type, **kw) t = time.time() while True: try: event = self.wait([pattern.subqueue]) except TimeoutError: self.log('timeout') self.log('still expecting:') self.log(' - %r' % pattern) raise self._check_forbidden(event) if pattern.match(event): self.log('handled, took %0.3f ms' % ((time.time() - t) * 1000.0) ) self.log('') return event self.log('not handled') self.log('') def expect_many(self, *patterns): """ Waits for events matching all of the supplied EventPattern instances to return, and returns a list of events in the same order as the patterns they matched. After a pattern is successfully matched, it is not considered for future events; if more than one unsatisfied pattern matches an event, the first "wins". Note that the expected events may occur in any order. If you're expecting a series of events in a particular order, use repeated calls to expect() instead. This method is useful when you're awaiting a number of events which may happen in any order. For instance, in telepathy-gabble, calling a D-Bus method often causes a value to be returned immediately, as well as a query to be sent to the server. Since these events may reach the test in either order, the following is incorrect and will fail if the IQ happens to reach the test first: ret = q.expect('dbus-return', method='Foo') query = q.expect('stream-iq', query_ns=ns.FOO) The following would be correct: ret, query = q.expect_many( EventPattern('dbus-return', method='Foo'), EventPattern('stream-iq', query_ns=ns.FOO), ) """ ret = [None] * len(patterns) t = time.time() while None in ret: try: queues = set() for i, pattern in enumerate(patterns): if ret[i] is None: queues.add(pattern.subqueue) event = self.wait(queues) except TimeoutError: self.log('timeout') self.log('still expecting:') for i, pattern in enumerate(patterns): if ret[i] is None: self.log(' - %r' % pattern) raise self._check_forbidden(event) for i, pattern in enumerate(patterns): if ret[i] is None and pattern.match(event): self.log('handled, took %0.3f ms' % ((time.time() - t) * 1000.0) ) self.log('') ret[i] = event break else: self.log('not handled') self.log('') return ret def demand(self, type, **kw): pattern = EventPattern(type, **kw) event = self.wait([pattern.subqueue]) if pattern.match(event): self.log('handled') self.log('') return event self.log('not handled') raise RuntimeError('expected %r, got %r' % (pattern, event)) def queues_available(self, queues): if queues == None: return self.event_queues.keys() else: available = self.event_queues.keys() return filter(lambda x: x in available, queues) def pop_next(self, queue): events = self.event_queues[queue] e = events.pop(0) if not events: self.event_queues.pop (queue) return e def append(self, event): self.log ("Adding to queue") self.log_event (event) self.event_queues[event.subqueue] = \ self.event_queues.get(event.subqueue, []) + [event] class IteratingEventQueue(BaseEventQueue): """Event queue that works by iterating the Twisted reactor.""" def __init__(self, timeout=None): BaseEventQueue.__init__(self, timeout) self._dbus_method_impls = [] self._buses = [] # a message filter which will claim we handled everything self._dbus_dev_null = \ lambda bus, message: dbus.lowlevel.HANDLER_RESULT_HANDLED def wait(self, queues=None): stop = [False] def later(): stop[0] = True delayed_call = reactor.callLater(self.timeout, later) self.log_queues(queues) qa = self.queues_available(queues) while not qa and (not stop[0]): reactor.iterate(0.01) qa = self.queues_available(queues) if qa: delayed_call.cancel() e = self.pop_next (qa[0]) self.log_event (e) return e else: raise TimeoutError def add_dbus_method_impl(self, cb, bus=None, **kwargs): if bus is None: bus = self._buses[0] self._dbus_method_impls.append( (EventPattern('dbus-method-call', **kwargs), cb)) def dbus_emit(self, path, iface, name, *a, **k): bus = k.pop('bus', self._buses[0]) assert 'signature' in k, k message = dbus.lowlevel.SignalMessage(path, iface, name) message.append(*a, **k) bus.send_message(message) def dbus_return(self, in_reply_to, *a, **k): bus = k.pop('bus', self._buses[0]) assert 'signature' in k, k reply = dbus.lowlevel.MethodReturnMessage(in_reply_to) reply.append(*a, **k) bus.send_message(reply) def dbus_raise(self, in_reply_to, name, message=None, bus=None): if bus is None: bus = self._buses[0] reply = dbus.lowlevel.ErrorMessage(in_reply_to, name, message) bus.send_message(reply) def attach_to_bus(self, bus): if not self._buses: # first-time setup self._dbus_filter_bound_method = self._dbus_filter self._buses.append(bus) # Only subscribe to messages on the first bus connection (assumed to # be the shared session bus connection used by the simulated connection # manager and most of the test suite), not on subsequent bus # connections (assumed to represent extra clients). # # When we receive a method call on the other bus connections, ignore # it - the eavesdropping filter installed on the first bus connection # will see it too. # # This is highly counter-intuitive, but it means our messages are in # a guaranteed order (we don't have races between messages arriving on # various connections). if len(self._buses) > 1: bus.add_message_filter(self._dbus_dev_null) return try: # for dbus > 1.5 bus.add_match_string("eavesdrop=true,type='signal'") except dbus.DBusException: bus.add_match_string("type='signal'") bus.add_match_string("type='method_call'") else: bus.add_match_string("eavesdrop=true,type='method_call'") bus.add_message_filter(self._dbus_filter_bound_method) bus.add_signal_receiver( lambda *args, **kw: self.append( Event('dbus-signal', path=unwrap(kw['path']), signal=kw['member'], args=map(unwrap, args), interface=kw['interface'])), None, None, None, path_keyword='path', member_keyword='member', interface_keyword='interface', byte_arrays=True, ) def cleanup(self): if self._buses: self._buses[0].remove_message_filter(self._dbus_filter_bound_method) for bus in self._buses[1:]: bus.remove_message_filter(self._dbus_dev_null) self._buses = [] self._dbus_method_impls = [] def _dbus_filter(self, bus, message): if isinstance(message, dbus.lowlevel.MethodCallMessage): destination = message.get_destination() sender = message.get_sender() if (destination == 'org.freedesktop.DBus' or sender == self._buses[0].get_unique_name()): # suppress reply and don't make an Event return dbus.lowlevel.HANDLER_RESULT_HANDLED e = Event('dbus-method-call', message=message, interface=message.get_interface(), path=message.get_path(), raw_args=message.get_args_list(byte_arrays=True), args=map(unwrap, message.get_args_list(byte_arrays=True)), destination=str(destination), method=message.get_member(), sender=message.get_sender(), handled=False) for pair in self._dbus_method_impls: pattern, cb = pair if pattern.match(e): cb(e) e.handled = True break self.append(e) return dbus.lowlevel.HANDLER_RESULT_HANDLED return dbus.lowlevel.HANDLER_RESULT_NOT_YET_HANDLED class TestEventQueue(BaseEventQueue): def __init__(self, events): BaseEventQueue.__init__(self) for e in events: self.append (e) def wait(self, queues = None): qa = self.queues_available(queues) if qa: return self.pop_next (qa[0]) else: raise TimeoutError class EventQueueTest(unittest.TestCase): def test_expect(self): queue = TestEventQueue([Event('test-foo'), Event('test-bar')]) assert queue.expect('test-foo').type == 'test-foo' assert queue.expect('test-bar').type == 'test-bar' def test_expect_many(self): queue = TestEventQueue([Event('test-foo'), Event('test-bar')]) bar, foo = queue.expect_many( EventPattern('test-bar'), EventPattern('test-foo')) assert bar.type == 'test-bar' assert foo.type == 'test-foo' def test_expect_many2(self): # Test that events are only matched against patterns that haven't yet # been matched. This tests a regression. queue = TestEventQueue([Event('test-foo', x=1), Event('test-foo', x=2)]) foo1, foo2 = queue.expect_many( EventPattern('test-foo'), EventPattern('test-foo')) assert foo1.type == 'test-foo' and foo1.x == 1 assert foo2.type == 'test-foo' and foo2.x == 2 def test_expect_queueing(self): queue = TestEventQueue([Event('foo-test', x=1), Event('foo-test', x=2)]) queue.append(Event('bar-test', x=1)) queue.append(Event('bar-test', x=2)) queue.append(Event('baz-test', x=1)) queue.append(Event('baz-test', x=2)) for x in xrange(1,2): e = queue.expect ('baz-test') assertEquals (x, e.x) e = queue.expect ('bar-test') assertEquals (x, e.x) e = queue.expect ('foo-test') assertEquals (x, e.x) def test_timeout(self): queue = TestEventQueue([]) self.assertRaises(TimeoutError, queue.expect, 'test-foo') def test_demand(self): queue = TestEventQueue([Event('test-foo'), Event('test-bar')]) foo = queue.demand('test-foo') assert foo.type == 'test-foo' def test_demand_fail(self): queue = TestEventQueue([Event('test-foo'), Event('test-bar')]) self.assertRaises(RuntimeError, queue.demand, 'test-bar') def unwrap(x): """Hack to unwrap D-Bus values, so that they're easier to read when printed.""" if isinstance(x, list): return map(unwrap, x) if isinstance(x, tuple): return tuple(map(unwrap, x)) if isinstance(x, dict): return dict([(unwrap(k), unwrap(v)) for k, v in x.iteritems()]) if isinstance(x, dbus.Boolean): return bool(x) for t in [unicode, str, long, int, float]: if isinstance(x, t): return t(x) return x def call_async(test, proxy, method, *args, **kw): """Call a D-Bus method asynchronously and generate an event for the resulting method return/error.""" def reply_func(*ret): test.append(Event('dbus-return', method=method, value=unwrap(ret))) def error_func(err): test.append(Event('dbus-error', method=method, error=err, name=err.get_dbus_name(), message=str(err))) method_proxy = getattr(proxy, method) kw.update({'reply_handler': reply_func, 'error_handler': error_func}) method_proxy(*args, **kw) def sync_dbus(bus, q, proxy): # Dummy D-Bus method call. We can't use DBus.Peer.Ping() because libdbus # replies to that message immediately, rather than handing it up to # dbus-glib and thence the application, which means that Ping()ing the # application doesn't ensure that it's processed all D-Bus messages prior # to our ping. call_async(q, dbus.Interface(proxy, 'org.freedesktop.Telepathy.Tests'), 'DummySyncDBus') q.expect('dbus-error', method='DummySyncDBus') class ProxyWrapper: def __init__(self, object, default, others={}): self.object = object self.default_interface = dbus.Interface(object, default) self.Properties = dbus.Interface(object, dbus.PROPERTIES_IFACE) self.TpProperties = \ dbus.Interface(object, tp_name_prefix + '.Properties') self.interfaces = dict([ (name, dbus.Interface(object, iface)) for name, iface in others.iteritems()]) def __getattr__(self, name): if name in self.interfaces: return self.interfaces[name] if name in self.object.__dict__: return getattr(self.object, name) return getattr(self.default_interface, name) class ConnWrapper(ProxyWrapper): def inspect_contact_sync(self, handle): return self.inspect_contacts_sync([handle])[0] def inspect_contacts_sync(self, handles): h2asv = self.Contacts.GetContactAttributes(handles, [], True) ret = [] for h in handles: ret.append(h2asv[h][cs.ATTR_CONTACT_ID]) return ret def get_contact_handle_sync(self, identifier): return self.Contacts.GetContactByID(identifier, [])[0] def get_contact_handles_sync(self, ids): return [self.get_contact_handle_sync(i) for i in ids] def wrap_connection(conn): return ConnWrapper(conn, tp_name_prefix + '.Connection', dict([ (name, tp_name_prefix + '.Connection.Interface.' + name) for name in ['Aliasing', 'Avatars', 'Capabilities', 'Contacts', 'SimplePresence', 'Requests']] + [('Peer', 'org.freedesktop.DBus.Peer'), ('ContactCapabilities', cs.CONN_IFACE_CONTACT_CAPS), ('ContactInfo', cs.CONN_IFACE_CONTACT_INFO), ('Location', cs.CONN_IFACE_LOCATION), ('Future', tp_name_prefix + '.Connection.FUTURE'), ('MailNotification', cs.CONN_IFACE_MAIL_NOTIFICATION), ('ContactList', cs.CONN_IFACE_CONTACT_LIST), ('ContactGroups', cs.CONN_IFACE_CONTACT_GROUPS), ('ContactBlocking', cs.CONN_IFACE_CONTACT_BLOCKING), ('PowerSaving', cs.CONN_IFACE_POWER_SAVING), ('Addressing', cs.CONN_IFACE_ADDRESSING), ('ClientTypes', cs.CONN_IFACE_CLIENT_TYPES), ])) class ChannelWrapper(ProxyWrapper): def send_msg_sync(self, txt): message = [ { 'message-type': cs.MT_NORMAL, }, { 'content-type': 'text/plain', 'content': txt }] self.Messages.SendMessage(message, 0) def wrap_channel(chan, type_, extra=None): interfaces = { type_: tp_name_prefix + '.Channel.Type.' + type_, 'Channel': cs.CHANNEL, 'Group': cs.CHANNEL_IFACE_GROUP, 'Hold': cs.CHANNEL_IFACE_HOLD, 'Messages': cs.CHANNEL_IFACE_MESSAGES, 'RoomConfig1': cs.CHANNEL_IFACE_ROOM_CONFIG, 'ChatState': cs.CHANNEL_IFACE_CHAT_STATE, 'Destroyable': cs.CHANNEL_IFACE_DESTROYABLE, 'Password': cs.CHANNEL_IFACE_PASSWORD, } if extra: interfaces.update(dict([ (name, tp_name_prefix + '.Channel.Interface.' + name) for name in extra])) return ChannelWrapper(chan, tp_name_prefix + '.Channel', interfaces) def wrap_content(chan, extra=None): interfaces = { 'DTMF': cs.CALL_CONTENT_IFACE_DTMF, 'Media': cs.CALL_CONTENT_IFACE_MEDIA, } if extra: interfaces.update(dict([ (name, tp_name_prefix + '.Call1.Content.Interface.' + name) for name in extra])) return ProxyWrapper(chan, tp_name_prefix + '.Call1.Content', interfaces) def make_connection(bus, event_func, name, proto, params): cm = bus.get_object( tp_name_prefix + '.ConnectionManager.%s' % name, tp_path_prefix + '/ConnectionManager/%s' % name, introspect=False) cm_iface = dbus.Interface(cm, tp_name_prefix + '.ConnectionManager') connection_name, connection_path = cm_iface.RequestConnection( proto, dbus.Dictionary(params, signature='sv')) conn = wrap_connection(bus.get_object(connection_name, connection_path)) return conn def make_channel_proxy(conn, path, iface): bus = dbus.SessionBus() chan = bus.get_object(conn.object.bus_name, path) chan = dbus.Interface(chan, tp_name_prefix + '.' + iface) return chan # block_reading can be used if the test want to choose when we start to read # data from the socket. class EventProtocol(Protocol): def __init__(self, queue=None, block_reading=False): self.queue = queue self.block_reading = block_reading def dataReceived(self, data): if self.queue is not None: self.queue.append(Event('socket-data', protocol=self, data=data)) def sendData(self, data): self.transport.write(data) def connectionMade(self): if self.block_reading: self.transport.stopReading() def connectionLost(self, reason=None): if self.queue is not None: self.queue.append(Event('socket-disconnected', protocol=self)) class EventProtocolFactory(Factory): def __init__(self, queue, block_reading=False): self.queue = queue self.block_reading = block_reading def _create_protocol(self): return EventProtocol(self.queue, self.block_reading) def buildProtocol(self, addr): proto = self._create_protocol() self.queue.append(Event('socket-connected', protocol=proto)) return proto class EventProtocolClientFactory(EventProtocolFactory, ClientFactory): pass def watch_tube_signals(q, tube): def got_signal_cb(*args, **kwargs): q.append(Event('tube-signal', path=kwargs['path'], signal=kwargs['member'], args=map(unwrap, args), tube=tube)) tube.add_signal_receiver(got_signal_cb, path_keyword='path', member_keyword='member', byte_arrays=True) def pretty(x): return pprint.pformat(unwrap(x)) def assertEquals(expected, value): if expected != value: raise AssertionError( "expected:\n%s\ngot:\n%s" % (pretty(expected), pretty(value))) def assertSameSets(expected, value): exp_set = set(expected) val_set = set(value) if exp_set != val_set: raise AssertionError( "expected contents:\n%s\ngot:\n%s" % ( pretty(exp_set), pretty(val_set))) def assertNotEquals(expected, value): if expected == value: raise AssertionError( "expected something other than:\n%s" % pretty(value)) def assertContains(element, value): if element not in value: raise AssertionError( "expected:\n%s\nin:\n%s" % (pretty(element), pretty(value))) def assertDoesNotContain(element, value): if element in value: raise AssertionError( "expected:\n%s\nnot in:\n%s" % (pretty(element), pretty(value))) def assertLength(length, value): if len(value) != length: raise AssertionError("expected: length %d, got length %d:\n%s" % ( length, len(value), pretty(value))) def assertFlagsSet(flags, value): masked = value & flags if masked != flags: raise AssertionError( "expected flags %u, of which only %u are set in %u" % ( flags, masked, value)) def assertFlagsUnset(flags, value): masked = value & flags if masked != 0: raise AssertionError( "expected none of flags %u, but %u are set in %u" % ( flags, masked, value)) def assertDBusError(name, error): if error.get_dbus_name() != name: raise AssertionError( "expected DBus error named:\n %s\ngot:\n %s\n(with message: %s)" % (name, error.get_dbus_name(), error.message)) def install_colourer(): def red(s): return '\x1b[31m%s\x1b[0m' % s def green(s): return '\x1b[32m%s\x1b[0m' % s patterns = { 'handled': green, 'not handled': red, } class Colourer: def __init__(self, fh, patterns): self.fh = fh self.patterns = patterns def write(self, s): for p, f in self.patterns.items(): if s.startswith(p): self.fh.write(f(p) + s[len(p):]) return self.fh.write(s) sys.stdout = Colourer(sys.stdout, patterns) return sys.stdout # this is just to shut up unittest. class DummyStream(object): def write(self, s): if 'CHECK_TWISTED_VERBOSE' in os.environ: print s, def flush(self): pass if __name__ == '__main__': stream = DummyStream() runner = unittest.TextTestRunner(stream=stream) unittest.main(testRunner=runner) telepathy-ring-2.3.24-37ad7111424725a5446e82dac516d7cab88149c7/tests/twisted/tools/000077500000000000000000000000001251541261300257415ustar00rootroot00000000000000telepathy-ring-2.3.24-37ad7111424725a5446e82dac516d7cab88149c7/tests/twisted/tools/Makefile.am000066400000000000000000000020221251541261300277710ustar00rootroot00000000000000exec-with-log.sh: exec-with-log.sh.in sed -e "s|[@]abs_top_builddir[@]|@abs_top_builddir@|g" \ -e "s|[@]abs_top_srcdir[@]|@abs_top_srcdir@|g" $< > $@ chmod +x $@ %.conf: %.conf.in sed -e "s|[@]abs_top_builddir[@]|@abs_top_builddir@|g" $< > $@ # We don't use the full filename for the .in because > 99 character filenames # in tarballs are non-portable (and automake 1.8 doesn't let us build # non-archaic tarballs) org.freedesktop.Telepathy.ConnectionManager.%.service: %.service.in sed -e "s|[@]abs_top_builddir[@]|@abs_top_builddir@|g" $< > $@ # D-Bus service file for testing service_in_files = ring.service.in service_files = org.freedesktop.Telepathy.ConnectionManager.ring.service # D-Bus config file for testing conf_in_files = tmp-session-bus.conf.in conf_files = $(conf_in_files:.conf.in=.conf) BUILT_SOURCES = $(service_files) $(conf_files) exec-with-log.sh EXTRA_DIST = \ $(service_in_files) \ $(conf_in_files) \ exec-with-log.sh.in \ with-session-bus.sh CLEANFILES = \ $(BUILT_SOURCES) \ ring-testing.log exec-with-log.sh.in000066400000000000000000000020501251541261300312740ustar00rootroot00000000000000telepathy-ring-2.3.24-37ad7111424725a5446e82dac516d7cab88149c7/tests/twisted/tools#!/bin/sh cd "@abs_top_builddir@/tests/twisted/tools" export RING_DEBUG=all RING_HTFU=seriously G_MESSAGES_DEBUG=all export G_MESSAGES_DEBUG ulimit -c unlimited exec >> ring-testing.log 2>&1 if test -n "$RING_TEST_VALGRIND"; then export G_DEBUG=${G_DEBUG:+"${G_DEBUG},"}gc-friendly export G_SLICE=always-malloc RING_WRAPPER="valgrind --leak-check=full --num-callers=20" RING_WRAPPER="$RING_WRAPPER --show-reachable=yes" RING_WRAPPER="$RING_WRAPPER --gen-suppressions=all" RING_WRAPPER="$RING_WRAPPER --child-silent-after-fork=yes" RING_WRAPPER="$RING_WRAPPER --suppressions=@abs_top_srcdir@/tests/twisted/tools/tp-glib.supp" elif test -n "$RING_TEST_REFDBG"; then if test -z "$REFDBG_OPTIONS" ; then export REFDBG_OPTIONS="btnum=10" fi if test -z "$RING_WRAPPER" ; then RING_WRAPPER="refdbg" fi fi export G_DEBUG=fatal-warnings" ${G_DEBUG}" exec @abs_top_builddir@/libtool --mode=execute $RING_WRAPPER @abs_top_builddir@/src/telepathy-ring telepathy-ring-2.3.24-37ad7111424725a5446e82dac516d7cab88149c7/tests/twisted/tools/ring.service.in000066400000000000000000000002031251541261300306620ustar00rootroot00000000000000[D-BUS Service] Name=org.freedesktop.Telepathy.ConnectionManager.ring Exec=@abs_top_builddir@/tests/twisted/tools/exec-with-log.sh tmp-session-bus.conf.in000066400000000000000000000016061251541261300322110ustar00rootroot00000000000000telepathy-ring-2.3.24-37ad7111424725a5446e82dac516d7cab88149c7/tests/twisted/tools session unix:tmpdir=/tmp @abs_top_builddir@/tests/twisted/tools with-session-bus.sh000066400000000000000000000060301251541261300314400ustar00rootroot00000000000000telepathy-ring-2.3.24-37ad7111424725a5446e82dac516d7cab88149c7/tests/twisted/tools#!/bin/sh # with-session-bus.sh - run a program with a temporary D-Bus session daemon # # The canonical location of this program is the telepathy-glib tools/ # directory, please synchronize any changes with that copy. # # Copyright (C) 2007-2008 Collabora Ltd. # # Copying and distribution of this file, with or without modification, # are permitted in any medium without royalty provided the copyright # notice and this notice are preserved. set -e me=with-session-bus dbus_daemon_args="--print-address=5 --print-pid=6 --fork" sleep=0 usage () { echo "usage: $me [options] -- program [program_options]" >&2 echo "Requires write access to the current directory." >&2 echo "" >&2 echo "If \$WITH_SESSION_BUS_FORK_DBUS_MONITOR is set, fork dbus-monitor" >&2 echo "with the arguments in \$WITH_SESSION_BUS_FORK_DBUS_MONITOR_OPT." >&2 echo "The output of dbus-monitor is saved in $me-.dbus-monitor-logs" >&2 exit 2 } while test "z$1" != "z--"; do case "$1" in --sleep=*) sleep="$1" sleep="${sleep#--sleep=}" shift ;; --session) dbus_daemon_args="$dbus_daemon_args --session" shift ;; --config-file=*) # FIXME: assumes config file doesn't contain any special characters dbus_daemon_args="$dbus_daemon_args $1" shift ;; --also-for-system) with_system_bus=1 shift ;; *) usage ;; esac done shift if test "z$1" = "z"; then usage; fi exec 5> $me-$$.address exec 6> $me-$$.pid cleanup () { pid=`head -n1 $me-$$.pid` if test -n "$pid" ; then if [ -n "$CHECK_TWISTED_VERBOSE" ] || [ -n "$VERBOSE_TESTS" ]; then echo "Killing temporary bus daemon: $pid" >&2 fi kill -INT "$pid" fi rm -f $me-$$.address rm -f $me-$$.pid } trap cleanup INT HUP TERM dbus-daemon $dbus_daemon_args if [ -n "$CHECK_TWISTED_VERBOSE" ] || [ -n "$VERBOSE_TESTS" ]; then { echo -n "Temporary bus daemon is "; cat $me-$$.address; } >&2 { echo -n "Temporary bus daemon PID is "; head -n1 $me-$$.pid; } >&2 fi e=0 # These might be non-null when run from e.g. gnome-terminal 3.8, which uses # an activatable service for its windows; we don't want to inherit them either unset DBUS_STARTER_ADDRESS unset DBUS_STARTER_BUS_TYPE DBUS_SESSION_BUS_ADDRESS="`cat $me-$$.address`" export DBUS_SESSION_BUS_ADDRESS if [ -n "$with_system_bus" ] ; then DBUS_SYSTEM_BUS_ADDRESS="$DBUS_SESSION_BUS_ADDRESS" export DBUS_SYSTEM_BUS_ADDRESS fi if [ -n "$WITH_SESSION_BUS_FORK_DBUS_MONITOR" ] ; then DBUS_MONITOR_LOG_FILE="$me-$$.dbus-monitor-logs" echo "Running dbus-monitor $WITH_SESSION_BUS_FORK_DBUS_MONITOR_OPT" >&2 echo "Its output will be in $DBUS_MONITOR_LOG_FILE" >&2 dbus-monitor $WITH_SESSION_BUS_FORK_DBUS_MONITOR_OPT \ > $DBUS_MONITOR_LOG_FILE 2>&1 & fi if [ -n "$RING_TEST_BUSTLE" ]; then BUSTLE_LOG_FILE="tools/$me-$$.bustle-logs" echo "Running bustle-dbus-monitor; log file $BUSTLE_LOG_FILE" >&2 bustle-dbus-monitor > $BUSTLE_LOG_FILE 2>&1 & fi "$@" || e=$? if test $sleep != 0; then sleep $sleep fi trap - INT HUP TERM cleanup exit $e telepathy-ring-2.3.24-37ad7111424725a5446e82dac516d7cab88149c7/tests/unitcheck.py000066400000000000000000000006131251541261300254450ustar00rootroot00000000000000from unittest import * class CheckResult (TestResult): successes = () # Immutable def addSuccess(self, test): if not self.successes: self.successes = [] self.successes.append(test) class _CheckCase: def defaultTestResult(self): return CheckResult() class CheckCase (_CheckCase, TestCase): pass class FunctionCheckCase (_CheckCase, FunctionTestCase): pass telepathy-ring-2.3.24-37ad7111424725a5446e82dac516d7cab88149c7/tools/000077500000000000000000000000001251541261300231145ustar00rootroot00000000000000telepathy-ring-2.3.24-37ad7111424725a5446e82dac516d7cab88149c7/tools/Makefile.am000066400000000000000000000026411251541261300251530ustar00rootroot00000000000000abs_top_builddir = @abs_top_builddir@ noinst_SCRIPTS = telepathy-glib-env telepathy-glib-env: telepathy-glib-env.in Makefile $(AM_V_GEN)sed -e 's![@]abs_top_builddir[@]!$(abs_top_builddir)!' $< > $@ $(AM_V_at)chmod +x $@ EXTRA_DIST = \ c-constants-gen.py \ check-coding-style.mk \ check-c-style.sh \ check-misc.sh \ check-whitespace.sh \ doc-generator.xsl \ glib-client-gen.py \ glib-client-marshaller-gen.py \ glib-errors-enum-body-gen.py \ glib-errors-enum-header-gen.py \ glib-ginterface-gen.py \ glib-gtypes-generator.py \ glib-interfaces-gen.py \ glib-signals-marshal-gen.py \ gobject-foo.py \ identity.xsl \ libglibcodegen.py \ libtpcodegen.py \ make-version-script.py \ telepathy-glib-env.in \ with-session-bus.sh \ xincludator.py CLEANFILES = libtpcodegen.pyc libtpcodegen.pyo libglibcodegen.pyc libglibcodegen.pyo $(noinst_SCRIPTS) all: $(EXTRA_DIST) libglibcodegen.py: libtpcodegen.py touch $@ c-constants-gen.py: libglibcodegen.py touch $@ glib-client-marshaller-gen.py: libglibcodegen.py touch $@ glib-errors-enum-body-gen.py: libglibcodegen.py touch $@ glib-errors-enum-header-gen.py: libglibcodegen.py touch $@ glib-ginterface-gen.py: libglibcodegen.py touch $@ glib-gtypes-generator.py: libglibcodegen.py touch $@ glib-interfaces-gen.py: libglibcodegen.py touch $@ glib-signals-marshal-gen.py: libglibcodegen.py touch $@ telepathy-ring-2.3.24-37ad7111424725a5446e82dac516d7cab88149c7/tools/c-constants-gen.py000066400000000000000000000121201251541261300264650ustar00rootroot00000000000000#!/usr/bin/python from sys import argv, stdout, stderr import xml.dom.minidom from libglibcodegen import NS_TP, get_docstring, \ get_descendant_text, get_by_path class Generator(object): def __init__(self, prefix, dom, output_base): self.prefix = prefix + '_' self.spec = get_by_path(dom, "spec")[0] self.__header = open(output_base + '.h', 'w') self.__docs = open(output_base + '-gtk-doc.h', 'w') def __call__(self): self.do_header() self.do_body() self.do_footer() def write(self, code): self.__header.write(code.encode('utf-8')) def d(self, code): self.__docs.write(code.encode('utf-8')) # Header def do_header(self): self.write('/* Generated from ') self.write(get_descendant_text(get_by_path(self.spec, 'title'))) version = get_by_path(self.spec, "version") if version: self.write(', version ' + get_descendant_text(version)) self.write('\n\n') for copyright in get_by_path(self.spec, 'copyright'): self.write(get_descendant_text(copyright)) self.write('\n') self.write(get_descendant_text(get_by_path(self.spec, 'license'))) self.write('\n') self.write(get_descendant_text(get_by_path(self.spec, 'docstring'))) self.write(""" */ #ifdef __cplusplus extern "C" { #endif \n""") # Body def do_body(self): for elem in self.spec.getElementsByTagNameNS(NS_TP, '*'): if elem.localName == 'flags': self.do_flags(elem) elif elem.localName == 'enum': self.do_enum(elem) def do_flags(self, flags): name = flags.getAttribute('plural') or flags.getAttribute('name') value_prefix = flags.getAttribute('singular') or \ flags.getAttribute('value-prefix') or \ flags.getAttribute('name') self.d("""\ /** * %s: """ % (self.prefix + name).replace('_', '')) for flag in get_by_path(flags, 'flag'): self.do_gtkdoc(flag, value_prefix) self.d(' *\n') docstrings = get_by_path(flags, 'docstring') if docstrings: self.d("""\ * * """ % get_descendant_text(docstrings).replace('\n', ' ')) self.d("""\ * Bitfield/set of flags generated from the Telepathy specification. */ """) self.write("typedef enum {\n") for flag in get_by_path(flags, 'flag'): self.do_val(flag, value_prefix) self.write("""\ } %s; """ % (self.prefix + name).replace('_', '')) def do_enum(self, enum): name = enum.getAttribute('singular') or enum.getAttribute('name') value_prefix = enum.getAttribute('singular') or \ enum.getAttribute('value-prefix') or \ enum.getAttribute('name') name_plural = enum.getAttribute('plural') or \ enum.getAttribute('name') + 's' self.d("""\ /** * %s: """ % (self.prefix + name).replace('_', '')) vals = get_by_path(enum, 'enumvalue') for val in vals: self.do_gtkdoc(val, value_prefix) self.d(' *\n') docstrings = get_by_path(enum, 'docstring') if docstrings: self.d("""\ * * """ % get_descendant_text(docstrings).replace('\n', ' ')) self.d("""\ * Bitfield/set of flags generated from the Telepathy specification. */ """) self.write("typedef enum {\n") for val in vals: self.do_val(val, value_prefix) self.write("} %s;\n" % (self.prefix + name).replace('_', '')) self.d("""\ /** * NUM_%(upper-plural)s: * * 1 higher than the highest valid value of #%(mixed-name)s. */ """ % {'mixed-name' : (self.prefix + name).replace('_', ''), 'upper-plural' : (self.prefix + name_plural).upper(), 'last-val' : vals[-1].getAttribute('value')}) self.write("""\ #define NUM_%(upper-plural)s (%(last-val)s+1) """ % {'mixed-name' : (self.prefix + name).replace('_', ''), 'upper-plural' : (self.prefix + name_plural).upper(), 'last-val' : vals[-1].getAttribute('value')}) def do_val(self, val, value_prefix): name = val.getAttribute('name') suffix = val.getAttribute('suffix') use_name = (self.prefix + value_prefix + '_' + \ (suffix or name)).upper() assert not (name and suffix) or name == suffix, \ 'Flag/enumvalue name %s != suffix %s' % (name, suffix) self.write(' %s = %s,\n' % (use_name, val.getAttribute('value'))) def do_gtkdoc(self, node, value_prefix): self.d(' * @') self.d((self.prefix + value_prefix + '_' + node.getAttribute('suffix')).upper()) self.d(': \n') # Footer def do_footer(self): self.write(""" #ifdef __cplusplus } #endif """) if __name__ == '__main__': argv = argv[1:] Generator(argv[0], xml.dom.minidom.parse(argv[1]), argv[2])() telepathy-ring-2.3.24-37ad7111424725a5446e82dac516d7cab88149c7/tools/check-c-style.sh000066400000000000000000000032201251541261300261000ustar00rootroot00000000000000#!/bin/sh fail=0 ( . "${tools_dir}"/check-misc.sh ) || fail=$? if grep -n '^ *GError *\*[[:alpha:]_][[:alnum:]_]* *;' "$@" then echo "^^^ The above files contain uninitialized GError*s - they should be" echo " initialized to NULL" fail=1 fi # The first regex finds function calls like foo() (as opposed to foo ()). # It attempts to ignore string constants (may cause false negatives). # The second and third ignore block comments (gtkdoc uses foo() as markup). # The fourth ignores cpp so you can # #define foo(bar) (_real_foo (__FUNC__, bar)) (cpp insists on foo() style). if grep -n '^[^"]*[[:lower:]](' "$@" \ | grep -v '^[-[:alnum:]_./]*:[[:digit:]]*: *\*' \ | grep -v '^[-[:alnum:]_./]*:[[:digit:]]*: */\*' \ | grep -v '^[-[:alnum:]_./]*:[[:digit:]]*: *#' then echo "^^^ Our coding style is to use function calls like foo (), not foo()" fail=1 fi if grep -En '[(][[:alnum:]_]+ ?\*[)][(]?[[:alpha:]_]' "$@"; then echo "^^^ Our coding style is to have a space between a cast and the " echo " thing being cast" fail=1 fi # this only spots casts if grep -En '[(][[:alnum:]_]+\*+[)]' "$@"; then echo "^^^ Our coding style is to have a space before the * of pointer types" echo " (regex 1)" fail=1 fi # ... and this only spots variable declarations and function return types if grep -En '^ *(static |const |)* *[[:alnum:]_]+\*+([[:alnum:]_]|;|$)' \ "$@"; then echo "^^^ Our coding style is to have a space before the * of pointer types" echo " (regex 2)" fail=1 fi if test -n "$CHECK_FOR_LONG_LINES" then if egrep -n '.{80,}' "$@" then echo "^^^ The above files contain long lines" fail=1 fi fi exit $fail telepathy-ring-2.3.24-37ad7111424725a5446e82dac516d7cab88149c7/tools/check-coding-style.mk000066400000000000000000000007641251541261300271300ustar00rootroot00000000000000check-coding-style: @fail=0; \ if test -n "$(check_misc_sources)"; then \ tools_dir=$(top_srcdir)/tools \ sh $(top_srcdir)/tools/check-misc.sh \ $(addprefix $(srcdir)/,$(check_misc_sources)) || fail=1; \ fi; \ if test -n "$(check_c_sources)"; then \ tools_dir=$(top_srcdir)/tools \ sh $(top_srcdir)/tools/check-c-style.sh \ $(addprefix $(srcdir)/,$(check_c_sources)) || fail=1; \ fi;\ if test yes = "$(ENABLE_CODING_STYLE_CHECKS)"; then \ exit "$$fail";\ else \ exit 0;\ fi telepathy-ring-2.3.24-37ad7111424725a5446e82dac516d7cab88149c7/tools/check-misc.sh000066400000000000000000000003601251541261300254550ustar00rootroot00000000000000#!/bin/sh fail=0 ( . "${tools_dir}"/check-whitespace.sh ) || fail=$? if egrep '(Free\s*Software\s*Foundation.*02139|02111-1307)' "$@" then echo "^^^ The above files contain the FSF's old address in GPL headers" fail=1 fi exit $fail telepathy-ring-2.3.24-37ad7111424725a5446e82dac516d7cab88149c7/tools/check-whitespace.sh000066400000000000000000000003231251541261300266550ustar00rootroot00000000000000#!/bin/sh fail=0 if grep -n ' $' "$@" then echo "^^^ The above files contain unwanted trailing spaces" fail=1 fi if grep -n ' ' "$@" then echo "^^^ The above files contain tabs" fail=1 fi exit $fail telepathy-ring-2.3.24-37ad7111424725a5446e82dac516d7cab88149c7/tools/doc-generator.xsl000066400000000000000000001205671251541261300264100ustar00rootroot00000000000000 . ERR: cannot find D-Bus interface, method, signal or property called ' ' ERR: Cannot use tp:member-ref when not in an <interface> ERR: interface has no signal/method/property called

Added in version .

Changed in version :

Changed in version

Deprecated since version .

Errors

Generic types

Simple types

Enumerated types:

Sets of flags:

Structure types

Mapping types

Types defined elsewhere

This interface is and is likely to cause havoc to your API/ABI if bindings are generated. Don't include it in libraries that care about compatibility.

Implementations of this interface must also implement:

Methods:

Interface has no methods.

Signals:

Interface has no signals.

Telepathy Properties:

Accessed using the Telepathy Properties interface.

Interface has no Telepathy properties.

D-Bus core Properties:

Accessed using the org.freedesktop.DBus.Properties interface.

Interface has no D-Bus core properties.

ERR: missing @name on a tp:flags type ERR: missing @type on tp:flags type

=
(Undocumented)
ERR: missing @name on a tp:enum type ERR: missing @type on tp:enum type

=
(Undocumented)
ERR: property does not have an interface as parent ERR: missing @name on a property of ERR: missing @type on property : ' '
, read-only write-only read/write ERR: unknown or missing value for @access on property : ' '

− a{ : }

In bindings that need a separate name, arrays of should be called .

Members

ERR: missing @name on a tp:simple-type ERR: missing @type on tp:simple-type

ERR: missing @name on a tp:external-type ERR: missing @type on tp:external-type
Defined by:
− ( , ) − a{ }

− ( : , )

In bindings that need a separate name, arrays of should be called .

Arrays of don't generally make sense.

Members

ERR: method does not have an interface as parent ERR: missing @name on a method of ERR: an arg of method has no type ERR: an 'in' arg of method has no name ERR: an arg of method has direction neither 'in' nor 'out'

( : , ) → , nothing

Parameters

Returns

Possible errors

( ) a{ } ERR: Unable to find type ' ' ERR: tp:type ' ' has D-Bus type ' ' but has been used with type=' ' ( )
(undocumented)
(generic description) (Undocumented.)
ERR: signal does not have an interface as parent ERR: missing @name on a signal of ERR: an arg of signal has no type ERR: an arg of signal has no name INFO: an arg of signal has unnecessary direction 'in' ERR: an arg of signal has direction other than 'in'

( : , )

Parameters

<xsl:value-of select="tp:title"/> <xsl:if test="tp:version"> <xsl:text> version </xsl:text> <xsl:value-of select="tp:version"/> </xsl:if>

Version

Interfaces

Index

Index of interfaces

Index of types

Stray text: {{{ }}} Unrecognised element: { }
telepathy-ring-2.3.24-37ad7111424725a5446e82dac516d7cab88149c7/tools/glib-client-gen.py000066400000000000000000001340371251541261300264360ustar00rootroot00000000000000#!/usr/bin/python # glib-client-gen.py: "I Can't Believe It's Not dbus-binding-tool" # # Generate GLib client wrappers from the Telepathy specification. # The master copy of this program is in the telepathy-glib repository - # please make any changes there. # # Copyright (C) 2006-2008 Collabora Ltd. # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA import sys import os.path import xml.dom.minidom from getopt import gnu_getopt from libglibcodegen import Signature, type_to_gtype, cmp_by_name, \ get_docstring, xml_escape, get_deprecated NS_TP = "http://telepathy.freedesktop.org/wiki/DbusSpec#extensions-v0" class Generator(object): def __init__(self, dom, prefix, basename, opts): self.dom = dom self.__header = [] self.__body = [] self.__docs = [] self.prefix_lc = prefix.lower() self.prefix_uc = prefix.upper() self.prefix_mc = prefix.replace('_', '') self.basename = basename self.group = opts.get('--group', None) self.iface_quark_prefix = opts.get('--iface-quark-prefix', None) self.tp_proxy_api = tuple(map(int, opts.get('--tp-proxy-api', '0').split('.'))) self.proxy_cls = opts.get('--subclass', 'TpProxy') + ' *' self.proxy_arg = opts.get('--subclass', 'void') + ' *' self.proxy_assert = opts.get('--subclass-assert', 'TP_IS_PROXY') self.proxy_doc = ('A #%s or subclass' % opts.get('--subclass', 'TpProxy')) if self.proxy_arg == 'void *': self.proxy_arg = 'gpointer ' self.generate_reentrant = ('--generate-reentrant' in opts or '--deprecate-reentrant' in opts) self.deprecate_reentrant = opts.get('--deprecate-reentrant', None) self.deprecation_attribute = opts.get('--deprecation-attribute', 'G_GNUC_DEPRECATED') def h(self, s): if isinstance(s, unicode): s = s.encode('utf-8') self.__header.append(s) def b(self, s): if isinstance(s, unicode): s = s.encode('utf-8') self.__body.append(s) def d(self, s): if isinstance(s, unicode): s = s.encode('utf-8') self.__docs.append(s) def get_iface_quark(self): assert self.iface_dbus is not None assert self.iface_uc is not None if self.iface_quark_prefix is None: return 'g_quark_from_static_string (\"%s\")' % self.iface_dbus else: return '%s_%s' % (self.iface_quark_prefix, self.iface_uc) def do_signal(self, iface, signal): iface_lc = iface.lower() member = signal.getAttribute('name') member_lc = signal.getAttribute('tp:name-for-bindings') if member != member_lc.replace('_', ''): raise AssertionError('Signal %s tp:name-for-bindings (%s) does ' 'not match' % (member, member_lc)) member_lc = member_lc.lower() member_uc = member_lc.upper() arg_count = 0 args = [] out_args = [] for arg in signal.getElementsByTagName('arg'): name = arg.getAttribute('name') type = arg.getAttribute('type') tp_type = arg.getAttribute('tp:type') if not name: name = 'arg%u' % arg_count arg_count += 1 else: name = 'arg_%s' % name info = type_to_gtype(type) args.append((name, info, tp_type, arg)) callback_name = ('%s_%s_signal_callback_%s' % (self.prefix_lc, iface_lc, member_lc)) collect_name = ('_%s_%s_collect_args_of_%s' % (self.prefix_lc, iface_lc, member_lc)) invoke_name = ('_%s_%s_invoke_callback_for_%s' % (self.prefix_lc, iface_lc, member_lc)) # Example: # # typedef void (*tp_cli_connection_signal_callback_new_channel) # (TpConnection *proxy, const gchar *arg_object_path, # const gchar *arg_channel_type, guint arg_handle_type, # guint arg_handle, gboolean arg_suppress_handler, # gpointer user_data, GObject *weak_object); self.d('/**') self.d(' * %s:' % callback_name) self.d(' * @proxy: The proxy on which %s_%s_connect_to_%s ()' % (self.prefix_lc, iface_lc, member_lc)) self.d(' * was called') for arg in args: name, info, tp_type, elt = arg ctype, gtype, marshaller, pointer = info self.d(' * @%s: %s' % (name, xml_escape(get_docstring(elt) or '(Undocumented)'))) self.d(' * @user_data: User-supplied data') self.d(' * @weak_object: User-supplied weakly referenced object') self.d(' *') self.d(' * Represents the signature of a callback for the signal %s.' % member) self.d(' */') self.d('') self.h('typedef void (*%s) (%sproxy,' % (callback_name, self.proxy_cls)) for arg in args: name, info, tp_type, elt = arg ctype, gtype, marshaller, pointer = info const = pointer and 'const ' or '' self.h(' %s%s%s,' % (const, ctype, name)) self.h(' gpointer user_data, GObject *weak_object);') if args: self.b('static void') self.b('%s (DBusGProxy *proxy G_GNUC_UNUSED,' % collect_name) for arg in args: name, info, tp_type, elt = arg ctype, gtype, marshaller, pointer = info const = pointer and 'const ' or '' self.b(' %s%s%s,' % (const, ctype, name)) self.b(' TpProxySignalConnection *sc)') self.b('{') self.b(' GValueArray *args = g_value_array_new (%d);' % len(args)) self.b(' GValue blank = { 0 };') self.b(' guint i;') self.b('') self.b(' g_value_init (&blank, G_TYPE_INT);') self.b('') self.b(' for (i = 0; i < %d; i++)' % len(args)) self.b(' g_value_array_append (args, &blank);') self.b('') for i, arg in enumerate(args): name, info, tp_type, elt = arg ctype, gtype, marshaller, pointer = info self.b(' g_value_unset (args->values + %d);' % i) self.b(' g_value_init (args->values + %d, %s);' % (i, gtype)) if gtype == 'G_TYPE_STRING': self.b(' g_value_set_string (args->values + %d, %s);' % (i, name)) elif marshaller == 'BOXED': self.b(' g_value_set_boxed (args->values + %d, %s);' % (i, name)) elif gtype == 'G_TYPE_UCHAR': self.b(' g_value_set_uchar (args->values + %d, %s);' % (i, name)) elif gtype == 'G_TYPE_BOOLEAN': self.b(' g_value_set_boolean (args->values + %d, %s);' % (i, name)) elif gtype == 'G_TYPE_INT': self.b(' g_value_set_int (args->values + %d, %s);' % (i, name)) elif gtype == 'G_TYPE_UINT': self.b(' g_value_set_uint (args->values + %d, %s);' % (i, name)) elif gtype == 'G_TYPE_INT64': self.b(' g_value_set_int (args->values + %d, %s);' % (i, name)) elif gtype == 'G_TYPE_UINT64': self.b(' g_value_set_uint64 (args->values + %d, %s);' % (i, name)) elif gtype == 'G_TYPE_DOUBLE': self.b(' g_value_set_double (args->values + %d, %s);' % (i, name)) else: assert False, ("Don't know how to put %s in a GValue" % gtype) self.b('') self.b(' tp_proxy_signal_connection_v0_take_results (sc, args);') self.b('}') self.b('static void') self.b('%s (TpProxy *tpproxy,' % invoke_name) self.b(' GError *error G_GNUC_UNUSED,') self.b(' GValueArray *args,') self.b(' GCallback generic_callback,') self.b(' gpointer user_data,') self.b(' GObject *weak_object)') self.b('{') self.b(' %s callback =' % callback_name) self.b(' (%s) generic_callback;' % callback_name) self.b('') self.b(' if (callback != NULL)') self.b(' callback (g_object_ref (tpproxy),') # FIXME: factor out into a function for i, arg in enumerate(args): name, info, tp_type, elt = arg ctype, gtype, marshaller, pointer = info if marshaller == 'BOXED': self.b(' g_value_get_boxed (args->values + %d),' % i) elif gtype == 'G_TYPE_STRING': self.b(' g_value_get_string (args->values + %d),' % i) elif gtype == 'G_TYPE_UCHAR': self.b(' g_value_get_uchar (args->values + %d),' % i) elif gtype == 'G_TYPE_BOOLEAN': self.b(' g_value_get_boolean (args->values + %d),' % i) elif gtype == 'G_TYPE_UINT': self.b(' g_value_get_uint (args->values + %d),' % i) elif gtype == 'G_TYPE_INT': self.b(' g_value_get_int (args->values + %d),' % i) elif gtype == 'G_TYPE_UINT64': self.b(' g_value_get_uint64 (args->values + %d),' % i) elif gtype == 'G_TYPE_INT64': self.b(' g_value_get_int64 (args->values + %d),' % i) elif gtype == 'G_TYPE_DOUBLE': self.b(' g_value_get_double (args->values + %d),' % i) else: assert False, "Don't know how to get %s from a GValue" % gtype self.b(' user_data,') self.b(' weak_object);') self.b('') if len(args) > 0: self.b(' g_value_array_free (args);') else: self.b(' if (args != NULL)') self.b(' g_value_array_free (args);') self.b('') self.b(' g_object_unref (tpproxy);') self.b('}') # Example: # # TpProxySignalConnection * # tp_cli_connection_connect_to_new_channel # (TpConnection *proxy, # tp_cli_connection_signal_callback_new_channel callback, # gpointer user_data, # GDestroyNotify destroy); # # destroy is invoked when the signal becomes disconnected. This # is either because the signal has been disconnected explicitly # by the user, because the TpProxy has become invalid and # emitted the 'invalidated' signal, or because the weakly referenced # object has gone away. self.d('/**') self.d(' * %s_%s_connect_to_%s:' % (self.prefix_lc, iface_lc, member_lc)) self.d(' * @proxy: %s' % self.proxy_doc) self.d(' * @callback: Callback to be called when the signal is') self.d(' * received') self.d(' * @user_data: User-supplied data for the callback') self.d(' * @destroy: Destructor for the user-supplied data, which') self.d(' * will be called when this signal is disconnected, or') self.d(' * before this function returns %NULL') self.d(' * @weak_object: A #GObject which will be weakly referenced; ') self.d(' * if it is destroyed, this callback will automatically be') self.d(' * disconnected') self.d(' * @error: If not %NULL, used to raise an error if %NULL is') self.d(' * returned') self.d(' *') self.d(' * Connect a handler to the signal %s.' % member) self.d(' *') self.d(' * %s' % xml_escape(get_docstring(signal) or '(Undocumented)')) self.d(' *') self.d(' * Returns: a #TpProxySignalConnection containing all of the') self.d(' * above, which can be used to disconnect the signal; or') self.d(' * %NULL if the proxy does not have the desired interface') self.d(' * or has become invalid.') self.d(' */') self.d('') self.h('TpProxySignalConnection *%s_%s_connect_to_%s (%sproxy,' % (self.prefix_lc, iface_lc, member_lc, self.proxy_arg)) self.h(' %s callback,' % callback_name) self.h(' gpointer user_data,') self.h(' GDestroyNotify destroy,') self.h(' GObject *weak_object,') self.h(' GError **error);') self.h('') self.b('TpProxySignalConnection *') self.b('%s_%s_connect_to_%s (%sproxy,' % (self.prefix_lc, iface_lc, member_lc, self.proxy_arg)) self.b(' %s callback,' % callback_name) self.b(' gpointer user_data,') self.b(' GDestroyNotify destroy,') self.b(' GObject *weak_object,') self.b(' GError **error)') self.b('{') self.b(' GType expected_types[%d] = {' % (len(args) + 1)) for arg in args: name, info, tp_type, elt = arg ctype, gtype, marshaller, pointer = info self.b(' %s,' % gtype) self.b(' G_TYPE_INVALID };') self.b('') self.b(' g_return_val_if_fail (%s (proxy), NULL);' % self.proxy_assert) self.b(' g_return_val_if_fail (callback != NULL, NULL);') self.b('') self.b(' return tp_proxy_signal_connection_v0_new ((TpProxy *) proxy,') self.b(' %s, \"%s\",' % (self.get_iface_quark(), member)) self.b(' expected_types,') if args: self.b(' G_CALLBACK (%s),' % collect_name) else: self.b(' NULL, /* no args => no collector function */') self.b(' %s,' % invoke_name) self.b(' G_CALLBACK (callback), user_data, destroy,') self.b(' weak_object, error);') self.b('}') self.b('') def do_method(self, iface, method): iface_lc = iface.lower() member = method.getAttribute('name') member_lc = method.getAttribute('tp:name-for-bindings') if member != member_lc.replace('_', ''): raise AssertionError('Method %s tp:name-for-bindings (%s) does ' 'not match' % (member, member_lc)) member_lc = member_lc.lower() member_uc = member_lc.upper() in_count = 0 ret_count = 0 in_args = [] out_args = [] for arg in method.getElementsByTagName('arg'): name = arg.getAttribute('name') direction = arg.getAttribute('direction') type = arg.getAttribute('type') tp_type = arg.getAttribute('tp:type') if direction != 'out': if not name: name = 'in%u' % in_count in_count += 1 else: name = 'in_%s' % name else: if not name: name = 'out%u' % ret_count ret_count += 1 else: name = 'out_%s' % name info = type_to_gtype(type) if direction != 'out': in_args.append((name, info, tp_type, arg)) else: out_args.append((name, info, tp_type, arg)) # Async reply callback type # Example: # void (*tp_cli_properties_interface_callback_for_get_properties) # (TpProxy *proxy, # const GPtrArray *out0, # const GError *error, # gpointer user_data, # GObject *weak_object); self.d('/**') self.d(' * %s_%s_callback_for_%s:' % (self.prefix_lc, iface_lc, member_lc)) self.d(' * @proxy: the proxy on which the call was made') for arg in out_args: name, info, tp_type, elt = arg ctype, gtype, marshaller, pointer = info self.d(' * @%s: Used to return an \'out\' argument if @error is ' '%%NULL: %s' % (name, xml_escape(get_docstring(elt) or '(Undocumented)'))) self.d(' * @error: %NULL on success, or an error on failure') self.d(' * @user_data: user-supplied data') self.d(' * @weak_object: user-supplied object') self.d(' *') self.d(' * Signature of the callback called when a %s method call' % member) self.d(' * succeeds or fails.') deprecated = method.getElementsByTagName('tp:deprecated') if deprecated: d = deprecated[0] self.d(' *') self.d(' * Deprecated: %s' % xml_escape(get_deprecated(d))) self.d(' */') self.d('') callback_name = '%s_%s_callback_for_%s' % (self.prefix_lc, iface_lc, member_lc) self.h('typedef void (*%s) (%sproxy,' % (callback_name, self.proxy_cls)) for arg in out_args: name, info, tp_type, elt = arg ctype, gtype, marshaller, pointer = info const = pointer and 'const ' or '' self.h(' %s%s%s,' % (const, ctype, name)) self.h(' const GError *error, gpointer user_data,') self.h(' GObject *weak_object);') self.h('') # Async callback implementation invoke_callback = '_%s_%s_invoke_callback_%s' % (self.prefix_lc, iface_lc, member_lc) collect_callback = '_%s_%s_collect_callback_%s' % (self.prefix_lc, iface_lc, member_lc) # The callback called by dbus-glib; this ends the call and collects # the results into a GValueArray. self.b('static void') self.b('%s (DBusGProxy *proxy,' % collect_callback) self.b(' DBusGProxyCall *call,') self.b(' gpointer user_data)') self.b('{') self.b(' GError *error = NULL;') if len(out_args) > 0: self.b(' GValueArray *args;') self.b(' GValue blank = { 0 };') self.b(' guint i;') for arg in out_args: name, info, tp_type, elt = arg ctype, gtype, marshaller, pointer = info # "We handle variants specially; the caller is expected to # have already allocated storage for them". Thanks, # dbus-glib... if gtype == 'G_TYPE_VALUE': self.b(' GValue *%s = g_new0 (GValue, 1);' % name) else: self.b(' %s%s;' % (ctype, name)) self.b('') self.b(' dbus_g_proxy_end_call (proxy, call, &error,') for arg in out_args: name, info, tp_type, elt = arg ctype, gtype, marshaller, pointer = info if gtype == 'G_TYPE_VALUE': self.b(' %s, %s,' % (gtype, name)) else: self.b(' %s, &%s,' % (gtype, name)) self.b(' G_TYPE_INVALID);') if len(out_args) == 0: self.b(' tp_proxy_pending_call_v0_take_results (user_data, error,' 'NULL);') else: self.b('') self.b(' if (error != NULL)') self.b(' {') self.b(' tp_proxy_pending_call_v0_take_results (user_data, error,') self.b(' NULL);') for arg in out_args: name, info, tp_type, elt = arg ctype, gtype, marshaller, pointer = info if gtype == 'G_TYPE_VALUE': self.b(' g_free (%s);' % name) self.b(' return;') self.b(' }') self.b('') self.b(' args = g_value_array_new (%d);' % len(out_args)) self.b(' g_value_init (&blank, G_TYPE_INT);') self.b('') self.b(' for (i = 0; i < %d; i++)' % len(out_args)) self.b(' g_value_array_append (args, &blank);') for i, arg in enumerate(out_args): name, info, tp_type, elt = arg ctype, gtype, marshaller, pointer = info self.b('') self.b(' g_value_unset (args->values + %d);' % i) self.b(' g_value_init (args->values + %d, %s);' % (i, gtype)) if gtype == 'G_TYPE_STRING': self.b(' g_value_take_string (args->values + %d, %s);' % (i, name)) elif marshaller == 'BOXED': self.b(' g_value_take_boxed (args->values + %d, %s);' % (i, name)) elif gtype == 'G_TYPE_UCHAR': self.b(' g_value_set_uchar (args->values + %d, %s);' % (i, name)) elif gtype == 'G_TYPE_BOOLEAN': self.b(' g_value_set_boolean (args->values + %d, %s);' % (i, name)) elif gtype == 'G_TYPE_INT': self.b(' g_value_set_int (args->values + %d, %s);' % (i, name)) elif gtype == 'G_TYPE_UINT': self.b(' g_value_set_uint (args->values + %d, %s);' % (i, name)) elif gtype == 'G_TYPE_INT64': self.b(' g_value_set_int (args->values + %d, %s);' % (i, name)) elif gtype == 'G_TYPE_UINT64': self.b(' g_value_set_uint (args->values + %d, %s);' % (i, name)) elif gtype == 'G_TYPE_DOUBLE': self.b(' g_value_set_double (args->values + %d, %s);' % (i, name)) else: assert False, ("Don't know how to put %s in a GValue" % gtype) self.b(' tp_proxy_pending_call_v0_take_results (user_data, ' 'NULL, args);') self.b('}') self.b('static void') self.b('%s (TpProxy *self,' % invoke_callback) self.b(' GError *error,') self.b(' GValueArray *args,') self.b(' GCallback generic_callback,') self.b(' gpointer user_data,') self.b(' GObject *weak_object)') self.b('{') self.b(' %s callback = (%s) generic_callback;' % (callback_name, callback_name)) self.b('') self.b(' if (error != NULL)') self.b(' {') self.b(' callback ((%s) self,' % self.proxy_cls) for arg in out_args: name, info, tp_type, elt = arg ctype, gtype, marshaller, pointer = info if marshaller == 'BOXED' or pointer: self.b(' NULL,') elif gtype == 'G_TYPE_DOUBLE': self.b(' 0.0,') else: self.b(' 0,') self.b(' error, user_data, weak_object);') self.b(' g_error_free (error);') self.b(' return;') self.b(' }') self.b(' callback ((%s) self,' % self.proxy_cls) # FIXME: factor out into a function for i, arg in enumerate(out_args): name, info, tp_type, elt = arg ctype, gtype, marshaller, pointer = info if marshaller == 'BOXED': self.b(' g_value_get_boxed (args->values + %d),' % i) elif gtype == 'G_TYPE_STRING': self.b(' g_value_get_string (args->values + %d),' % i) elif gtype == 'G_TYPE_UCHAR': self.b(' g_value_get_uchar (args->values + %d),' % i) elif gtype == 'G_TYPE_BOOLEAN': self.b(' g_value_get_boolean (args->values + %d),' % i) elif gtype == 'G_TYPE_UINT': self.b(' g_value_get_uint (args->values + %d),' % i) elif gtype == 'G_TYPE_INT': self.b(' g_value_get_int (args->values + %d),' % i) elif gtype == 'G_TYPE_UINT64': self.b(' g_value_get_uint64 (args->values + %d),' % i) elif gtype == 'G_TYPE_INT64': self.b(' g_value_get_int64 (args->values + %d),' % i) elif gtype == 'G_TYPE_DOUBLE': self.b(' g_value_get_double (args->values + %d),' % i) else: assert False, "Don't know how to get %s from a GValue" % gtype self.b(' error, user_data, weak_object);') self.b('') if len(out_args) > 0: self.b(' g_value_array_free (args);') else: self.b(' if (args != NULL)') self.b(' g_value_array_free (args);') self.b('}') self.b('') # Async stub # Example: # TpProxyPendingCall * # tp_cli_properties_interface_call_get_properties # (gpointer proxy, # gint timeout_ms, # const GArray *in_properties, # tp_cli_properties_interface_callback_for_get_properties callback, # gpointer user_data, # GDestroyNotify *destructor); self.h('TpProxyPendingCall *%s_%s_call_%s (%sproxy,' % (self.prefix_lc, iface_lc, member_lc, self.proxy_arg)) self.h(' gint timeout_ms,') self.d('/**') self.d(' * %s_%s_call_%s:' % (self.prefix_lc, iface_lc, member_lc)) self.d(' * @proxy: the #TpProxy') self.d(' * @timeout_ms: the timeout in milliseconds, or -1 to use the') self.d(' * default') for arg in in_args: name, info, tp_type, elt = arg ctype, gtype, marshaller, pointer = info self.d(' * @%s: Used to pass an \'in\' argument: %s' % (name, xml_escape(get_docstring(elt) or '(Undocumented)'))) self.d(' * @callback: called when the method call succeeds or fails;') self.d(' * may be %NULL to make a "fire and forget" call with no ') self.d(' * reply tracking') self.d(' * @user_data: user-supplied data passed to the callback;') self.d(' * must be %NULL if @callback is %NULL') self.d(' * @destroy: called with the user_data as argument, after the') self.d(' * call has succeeded, failed or been cancelled;') self.d(' * must be %NULL if @callback is %NULL') self.d(' * @weak_object: If not %NULL, a #GObject which will be ') self.d(' * weakly referenced; if it is destroyed, this call ') self.d(' * will automatically be cancelled. Must be %NULL if ') self.d(' * @callback is %NULL') self.d(' *') self.d(' * Start a %s method call.' % member) self.d(' *') self.d(' * %s' % xml_escape(get_docstring(method) or '(Undocumented)')) self.d(' *') self.d(' * Returns: a #TpProxyPendingCall representing the call in') self.d(' * progress. It is borrowed from the object, and will become') self.d(' * invalid when the callback is called, the call is') self.d(' * cancelled or the #TpProxy becomes invalid.') deprecated = method.getElementsByTagName('tp:deprecated') if deprecated: d = deprecated[0] self.d(' *') self.d(' * Deprecated: %s' % xml_escape(get_deprecated(d))) self.d(' */') self.d('') self.b('TpProxyPendingCall *\n%s_%s_call_%s (%sproxy,' % (self.prefix_lc, iface_lc, member_lc, self.proxy_arg)) self.b(' gint timeout_ms,') for arg in in_args: name, info, tp_type, elt = arg ctype, gtype, marshaller, pointer = info const = pointer and 'const ' or '' self.h(' %s%s%s,' % (const, ctype, name)) self.b(' %s%s%s,' % (const, ctype, name)) self.h(' %s callback,' % callback_name) self.h(' gpointer user_data,') self.h(' GDestroyNotify destroy,') self.h(' GObject *weak_object);') self.h('') self.b(' %s callback,' % callback_name) self.b(' gpointer user_data,') self.b(' GDestroyNotify destroy,') self.b(' GObject *weak_object)') self.b('{') self.b(' GError *error = NULL;') self.b(' GQuark interface = %s;' % self.get_iface_quark()) self.b(' DBusGProxy *iface;') self.b('') self.b(' g_return_val_if_fail (%s (proxy), NULL);' % self.proxy_assert) self.b(' g_return_val_if_fail (callback != NULL || ' 'user_data == NULL, NULL);') self.b(' g_return_val_if_fail (callback != NULL || ' 'destroy == NULL, NULL);') self.b(' g_return_val_if_fail (callback != NULL || ' 'weak_object == NULL, NULL);') self.b('') self.b(' iface = tp_proxy_borrow_interface_by_id (') self.b(' (TpProxy *) proxy,') self.b(' interface, &error);') self.b('') self.b(' if (iface == NULL)') self.b(' {') self.b(' if (callback != NULL)') self.b(' callback (proxy,') for arg in out_args: name, info, tp_type, elt = arg ctype, gtype, marshaller, pointer = info if pointer: self.b(' NULL,') else: self.b(' 0,') self.b(' error, user_data, weak_object);') self.b('') self.b(' if (destroy != NULL)') self.b(' destroy (user_data);') self.b('') self.b(' g_error_free (error);') self.b(' return NULL;') self.b(' }') self.b('') self.b(' if (callback == NULL)') self.b(' {') self.b(' dbus_g_proxy_call_no_reply (iface, "%s",' % member) for arg in in_args: name, info, tp_type, elt = arg ctype, gtype, marshaller, pointer = info const = pointer and 'const ' or '' self.b(' %s, %s,' % (gtype, name)) self.b(' G_TYPE_INVALID);') self.b(' return NULL;') self.b(' }') self.b(' else') self.b(' {') self.b(' TpProxyPendingCall *data;') self.b('') self.b(' data = tp_proxy_pending_call_v0_new ((TpProxy *) proxy,') self.b(' interface, "%s", iface,' % member) self.b(' %s,' % invoke_callback) self.b(' G_CALLBACK (callback), user_data, destroy,') self.b(' weak_object, FALSE);') self.b(' tp_proxy_pending_call_v0_take_pending_call (data,') self.b(' dbus_g_proxy_begin_call_with_timeout (iface,') self.b(' "%s",' % member) self.b(' %s,' % collect_callback) self.b(' data,') self.b(' tp_proxy_pending_call_v0_completed,') self.b(' timeout_ms,') for arg in in_args: name, info, tp_type, elt = arg ctype, gtype, marshaller, pointer = info const = pointer and 'const ' or '' self.b(' %s, %s,' % (gtype, name)) self.b(' G_TYPE_INVALID));') self.b('') self.b(' return data;') self.b(' }') self.b('}') self.b('') if self.generate_reentrant: self.do_method_reentrant(method, iface_lc, member, member_lc, in_args, out_args, collect_callback) # leave a gap for the end of the method self.d('') self.b('') self.h('') def do_method_reentrant(self, method, iface_lc, member, member_lc, in_args, out_args, collect_callback): # Reentrant blocking calls # Example: # gboolean tp_cli_properties_interface_run_get_properties # (gpointer proxy, # gint timeout_ms, # const GArray *in_properties, # GPtrArray **out0, # GError **error, # GMainLoop **loop); self.b('typedef struct {') self.b(' GMainLoop *loop;') self.b(' GError **error;') for arg in out_args: name, info, tp_type, elt = arg ctype, gtype, marshaller, pointer = info self.b(' %s*%s;' % (ctype, name)) self.b(' unsigned success:1;') self.b(' unsigned completed:1;') self.b('} _%s_%s_run_state_%s;' % (self.prefix_lc, iface_lc, member_lc)) reentrant_invoke = '_%s_%s_finish_running_%s' % (self.prefix_lc, iface_lc, member_lc) self.b('static void') self.b('%s (TpProxy *self G_GNUC_UNUSED,' % reentrant_invoke) self.b(' GError *error,') self.b(' GValueArray *args,') self.b(' GCallback unused G_GNUC_UNUSED,') self.b(' gpointer user_data G_GNUC_UNUSED,') self.b(' GObject *unused2 G_GNUC_UNUSED)') self.b('{') self.b(' _%s_%s_run_state_%s *state = user_data;' % (self.prefix_lc, iface_lc, member_lc)) self.b('') self.b(' state->success = (error == NULL);') self.b(' state->completed = TRUE;') self.b(' g_main_loop_quit (state->loop);') self.b('') self.b(' if (error != NULL)') self.b(' {') self.b(' if (state->error != NULL)') self.b(' *state->error = error;') self.b(' else') self.b(' g_error_free (error);') self.b('') self.b(' return;') self.b(' }') self.b('') for i, arg in enumerate(out_args): name, info, tp_type, elt = arg ctype, gtype, marshaller, pointer = info self.b(' if (state->%s != NULL)' % name) if marshaller == 'BOXED': self.b(' *state->%s = g_value_dup_boxed (' 'args->values + %d);' % (name, i)) elif marshaller == 'STRING': self.b(' *state->%s = g_value_dup_string ' '(args->values + %d);' % (name, i)) elif marshaller in ('UCHAR', 'BOOLEAN', 'INT', 'UINT', 'INT64', 'UINT64', 'DOUBLE'): self.b(' *state->%s = g_value_get_%s (args->values + %d);' % (name, marshaller.lower(), i)) else: assert False, "Don't know how to copy %s" % gtype self.b('') if len(out_args) > 0: self.b(' g_value_array_free (args);') else: self.b(' if (args != NULL)') self.b(' g_value_array_free (args);') self.b('}') self.b('') if self.deprecate_reentrant: self.h('#ifndef %s' % self.deprecate_reentrant) self.h('gboolean %s_%s_run_%s (%sproxy,' % (self.prefix_lc, iface_lc, member_lc, self.proxy_arg)) self.h(' gint timeout_ms,') self.d('/**') self.d(' * %s_%s_run_%s:' % (self.prefix_lc, iface_lc, member_lc)) self.d(' * @proxy: %s' % self.proxy_doc) self.d(' * @timeout_ms: Timeout in milliseconds, or -1 for default') for arg in in_args: name, info, tp_type, elt = arg ctype, gtype, marshaller, pointer = info self.d(' * @%s: Used to pass an \'in\' argument: %s' % (name, xml_escape(get_docstring(elt) or '(Undocumented)'))) for arg in out_args: name, info, tp_type, elt = arg ctype, gtype, marshaller, pointer = info self.d(' * @%s: Used to return an \'out\' argument if %%TRUE is ' 'returned: %s' % (name, xml_escape(get_docstring(elt) or '(Undocumented)'))) self.d(' * @error: If not %NULL, used to return errors if %FALSE ') self.d(' * is returned') self.d(' * @loop: If not %NULL, set before re-entering ') self.d(' * the main loop, to point to a #GMainLoop ') self.d(' * which can be used to cancel this call with ') self.d(' * g_main_loop_quit(), causing a return of ') self.d(' * %FALSE with @error set to %TP_DBUS_ERROR_CANCELLED') self.d(' *') self.d(' * Call the method %s and run the main loop' % member) self.d(' * until it returns. Before calling this method, you must') self.d(' * add a reference to any borrowed objects you need to keep,') self.d(' * and generally ensure that everything is in a consistent') self.d(' * state.') self.d(' *') self.d(' * %s' % xml_escape(get_docstring(method) or '(Undocumented)')) self.d(' *') self.d(' * Returns: TRUE on success, FALSE and sets @error on error') deprecated = method.getElementsByTagName('tp:deprecated') if deprecated: d = deprecated[0] self.d(' *') self.d(' * Deprecated: %s' % xml_escape(get_deprecated(d))) self.d(' */') self.d('') self.b('gboolean\n%s_%s_run_%s (%sproxy,' % (self.prefix_lc, iface_lc, member_lc, self.proxy_arg)) self.b(' gint timeout_ms,') for arg in in_args: name, info, tp_type, elt = arg ctype, gtype, marshaller, pointer = info const = pointer and 'const ' or '' self.h(' %s%s%s,' % (const, ctype, name)) self.b(' %s%s%s,' % (const, ctype, name)) for arg in out_args: name, info, tp_type, elt = arg ctype, gtype, marshaller, pointer = info self.h(' %s*%s,' % (ctype, name)) self.b(' %s*%s,' % (ctype, name)) self.h(' GError **error,') if self.deprecate_reentrant: self.h(' GMainLoop **loop) %s;' % self.deprecation_attribute) self.h('#endif /* not %s */' % self.deprecate_reentrant) else: self.h(' GMainLoop **loop);') self.h('') self.b(' GError **error,') self.b(' GMainLoop **loop)') self.b('{') self.b(' DBusGProxy *iface;') self.b(' GQuark interface = %s;' % self.get_iface_quark()) self.b(' TpProxyPendingCall *pc;') self.b(' _%s_%s_run_state_%s state = {' % (self.prefix_lc, iface_lc, member_lc)) self.b(' NULL /* loop */, error,') for arg in out_args: name, info, tp_type, elt = arg self.b(' %s,' % name) self.b(' FALSE /* completed */, FALSE /* success */ };') self.b('') self.b(' g_return_val_if_fail (%s (proxy), FALSE);' % self.proxy_assert) self.b('') self.b(' iface = tp_proxy_borrow_interface_by_id') self.b(' ((TpProxy *) proxy, interface, error);') self.b('') self.b(' if (iface == NULL)') self.b(' return FALSE;') self.b('') self.b(' state.loop = g_main_loop_new (NULL, FALSE);') self.b('') self.b(' pc = tp_proxy_pending_call_v0_new ((TpProxy *) proxy,') self.b(' interface, "%s", iface,' % member) self.b(' %s,' % reentrant_invoke) self.b(' NULL, &state, NULL, NULL, TRUE);') self.b('') self.b(' if (loop != NULL)') self.b(' *loop = state.loop;') self.b('') self.b(' tp_proxy_pending_call_v0_take_pending_call (pc,') self.b(' dbus_g_proxy_begin_call_with_timeout (iface,') self.b(' "%s",' % member) self.b(' %s,' % collect_callback) self.b(' pc,') self.b(' tp_proxy_pending_call_v0_completed,') self.b(' timeout_ms,') for arg in in_args: name, info, tp_type, elt = arg ctype, gtype, marshaller, pointer = info const = pointer and 'const ' or '' self.b(' %s, %s,' % (gtype, name)) self.b(' G_TYPE_INVALID));') self.b('') self.b(' if (!state.completed)') self.b(' g_main_loop_run (state.loop);') self.b('') self.b(' if (!state.completed)') self.b(' tp_proxy_pending_call_cancel (pc);') self.b('') self.b(' if (loop != NULL)') self.b(' *loop = NULL;') self.b('') self.b(' g_main_loop_unref (state.loop);') self.b('') self.b(' return state.success;') self.b('}') self.b('') def do_signal_add(self, signal): marshaller_items = [] gtypes = [] for i in signal.getElementsByTagName('arg'): name = i.getAttribute('name') type = i.getAttribute('type') info = type_to_gtype(type) # type, GType, STRING, is a pointer gtypes.append(info[1]) self.b(' dbus_g_proxy_add_signal (proxy, "%s",' % signal.getAttribute('name')) for gtype in gtypes: self.b(' %s,' % gtype) self.b(' G_TYPE_INVALID);') def do_interface(self, node): ifaces = node.getElementsByTagName('interface') assert len(ifaces) == 1 iface = ifaces[0] name = node.getAttribute('name').replace('/', '') self.iface = name self.iface_lc = name.lower() self.iface_uc = name.upper() self.iface_mc = name.replace('_', '') self.iface_dbus = iface.getAttribute('name') signals = node.getElementsByTagName('signal') methods = node.getElementsByTagName('method') if signals: self.b('static inline void') self.b('%s_add_signals_for_%s (DBusGProxy *proxy)' % (self.prefix_lc, name.lower())) self.b('{') if self.tp_proxy_api >= (0, 7, 6): self.b(' if (!tp_proxy_dbus_g_proxy_claim_for_signal_adding ' '(proxy))') self.b(' return;') for signal in signals: self.do_signal_add(signal) self.b('}') self.b('') self.b('') for signal in signals: self.do_signal(name, signal) for method in methods: self.do_method(name, method) self.iface_dbus = None def __call__(self): self.h('G_BEGIN_DECLS') self.h('') self.b('/* We don\'t want gtkdoc scanning this file, it\'ll get') self.b(' * confused by seeing function definitions, so mark it as: */') self.b('/**/') self.b('') nodes = self.dom.getElementsByTagName('node') nodes.sort(cmp_by_name) for node in nodes: self.do_interface(node) if self.group is not None: self.b('/*') self.b(' * %s_%s_add_signals:' % (self.prefix_lc, self.group)) self.b(' * @self: the #TpProxy') self.b(' * @quark: a quark whose string value is the interface') self.b(' * name whose signals should be added') self.b(' * @proxy: the D-Bus proxy to which to add the signals') self.b(' * @unused: not used for anything') self.b(' *') self.b(' * Tell dbus-glib that @proxy has the signatures of all') self.b(' * signals on the given interface, if it\'s one we') self.b(' * support.') self.b(' *') self.b(' * This function should be used as a signal handler for') self.b(' * #TpProxy::interface-added.') self.b(' */') self.b('static void') self.b('%s_%s_add_signals (TpProxy *self G_GNUC_UNUSED,' % (self.prefix_lc, self.group)) self.b(' guint quark,') self.b(' DBusGProxy *proxy,') self.b(' gpointer unused G_GNUC_UNUSED)') self.b('{') for node in nodes: iface = node.getElementsByTagName('interface')[0] self.iface_dbus = iface.getAttribute('name') signals = node.getElementsByTagName('signal') if not signals: continue name = node.getAttribute('name').replace('/', '').lower() self.iface_uc = name.upper() self.b(' if (quark == %s)' % self.get_iface_quark()) self.b(' %s_add_signals_for_%s (proxy);' % (self.prefix_lc, name)) self.b('}') self.b('') self.h('G_END_DECLS') self.h('') open(self.basename + '.h', 'w').write('\n'.join(self.__header)) open(self.basename + '-body.h', 'w').write('\n'.join(self.__body)) open(self.basename + '-gtk-doc.h', 'w').write('\n'.join(self.__docs)) def types_to_gtypes(types): return [type_to_gtype(t)[1] for t in types] if __name__ == '__main__': options, argv = gnu_getopt(sys.argv[1:], '', ['group=', 'subclass=', 'subclass-assert=', 'iface-quark-prefix=', 'tp-proxy-api=', 'generate-reentrant', 'deprecate-reentrant=', 'deprecation-attribute=']) opts = {} for option, value in options: opts[option] = value dom = xml.dom.minidom.parse(argv[0]) Generator(dom, argv[1], argv[2], opts)() telepathy-ring-2.3.24-37ad7111424725a5446e82dac516d7cab88149c7/tools/glib-client-marshaller-gen.py000066400000000000000000000027241251541261300305630ustar00rootroot00000000000000#!/usr/bin/python import sys import xml.dom.minidom from string import ascii_letters, digits from libglibcodegen import signal_to_marshal_name NS_TP = "http://telepathy.freedesktop.org/wiki/DbusSpec#extensions-v0" class Generator(object): def __init__(self, dom, prefix): self.dom = dom self.marshallers = {} self.prefix = prefix def do_signal(self, signal): marshaller = signal_to_marshal_name(signal, self.prefix) assert '__' in marshaller rhs = marshaller.split('__', 1)[1].split('_') self.marshallers[marshaller] = rhs def __call__(self): signals = self.dom.getElementsByTagName('signal') for signal in signals: self.do_signal(signal) print 'void' print '%s_register_dbus_glib_marshallers (void)' % self.prefix print '{' all = self.marshallers.keys() all.sort() for marshaller in all: rhs = self.marshallers[marshaller] print ' dbus_g_object_register_marshaller (%s,' % marshaller print ' G_TYPE_NONE, /* return */' for type in rhs: print ' G_TYPE_%s,' % type.replace('VOID', 'NONE') print ' G_TYPE_INVALID);' print '}' def types_to_gtypes(types): return [type_to_gtype(t)[1] for t in types] if __name__ == '__main__': argv = sys.argv[1:] dom = xml.dom.minidom.parse(argv[0]) Generator(dom, argv[1])() telepathy-ring-2.3.24-37ad7111424725a5446e82dac516d7cab88149c7/tools/glib-errors-enum-body-gen.py000066400000000000000000000033521251541261300303640ustar00rootroot00000000000000#!/usr/bin/python import sys import xml.dom.minidom from libglibcodegen import NS_TP, camelcase_to_upper, get_docstring, \ get_descendant_text class Generator(object): def __init__(self, dom): self.dom = dom self.errors = self.dom.getElementsByTagNameNS(NS_TP, 'errors')[0] def do_header(self): print '/* Generated from the Telepathy spec\n' copyrights = self.errors.getElementsByTagNameNS(NS_TP, 'copyright') for copyright in copyrights: print get_descendant_text(copyright) license = self.errors.getElementsByTagNameNS(NS_TP, 'license')[0] print '\n' + get_descendant_text(license) + '\n*/' def do_enum_values(self): for error in self.errors.getElementsByTagNameNS(NS_TP, 'error'): print '' nick = error.getAttribute('name').replace(' ', '') name = camelcase_to_upper(nick.replace('.', '')) ns = error.parentNode.getAttribute('namespace') enum = 'TP_ERROR_' + name print ' /* ' + ns + '.' + name print ' ' + get_docstring(error) print ' */' print ' { %s, "%s", "%s" },' % (enum, enum, nick) def do_get_type(self): print """ #include <_gen/telepathy-errors.h> GType tp_error_get_type (void) { static GType etype = 0; if (G_UNLIKELY (etype == 0)) { static const GEnumValue values[] = {""" self.do_enum_values() print """\ }; etype = g_enum_register_static ("TpError", values); } return etype; } """ def __call__(self): self.do_header() self.do_get_type() if __name__ == '__main__': argv = sys.argv[1:] Generator(xml.dom.minidom.parse(argv[0]))() telepathy-ring-2.3.24-37ad7111424725a5446e82dac516d7cab88149c7/tools/glib-errors-enum-header-gen.py000066400000000000000000000040521251541261300306550ustar00rootroot00000000000000#!/usr/bin/python import sys import xml.dom.minidom from libglibcodegen import NS_TP, camelcase_to_upper, get_docstring, \ get_descendant_text class Generator(object): def __init__(self, dom): self.dom = dom self.errors = self.dom.getElementsByTagNameNS(NS_TP, 'errors')[0] def do_header(self): print '/* Generated from the Telepathy spec\n' copyrights = self.errors.getElementsByTagNameNS(NS_TP, 'copyright') for copyright in copyrights: print get_descendant_text(copyright) license = self.errors.getElementsByTagNameNS(NS_TP, 'license')[0] print '\n' + get_descendant_text(license) + '\n*/' def do_gtkdoc(self): for error in self.errors.getElementsByTagNameNS(NS_TP, 'error'): ns = error.parentNode.getAttribute('namespace') nick = error.getAttribute('name').replace(' ', '') enum = 'TP_ERROR_' + camelcase_to_upper(nick.replace('.', '')) print ' * @' + enum + ': ' + ns + '.' + nick + ':' print ' * ' + get_docstring(error) + ' ' def do_enumnames(self): for error in self.errors.getElementsByTagNameNS(NS_TP, 'error'): nick = error.getAttribute('name').replace(' ', '') enum = 'TP_ERROR_' + camelcase_to_upper(nick.replace('.', '')) print ' ' + enum + ',' def do_get_type(self): print """ #include G_BEGIN_DECLS GType tp_error_get_type (void); /** * TP_TYPE_ERROR: * * The GType of the Telepathy error enumeration. */ #define TP_TYPE_ERROR (tp_error_get_type()) """ def do_enum(self): print """\ /** * TpError:""" self.do_gtkdoc() print """\ * * Enumerated type representing the Telepathy D-Bus errors. */ typedef enum {""" self.do_enumnames() print """\ } TpError; G_END_DECLS""" def __call__(self): self.do_header() self.do_get_type() self.do_enum() if __name__ == '__main__': argv = sys.argv[1:] Generator(xml.dom.minidom.parse(argv[0]))() telepathy-ring-2.3.24-37ad7111424725a5446e82dac516d7cab88149c7/tools/glib-ginterface-gen.py000066400000000000000000000727151251541261300272730ustar00rootroot00000000000000#!/usr/bin/python # glib-ginterface-gen.py: service-side interface generator # # Generate dbus-glib 0.x service GInterfaces from the Telepathy specification. # The master copy of this program is in the telepathy-glib repository - # please make any changes there. # # Copyright (C) 2006, 2007 Collabora Limited # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA import sys import os.path import xml.dom.minidom from libglibcodegen import Signature, type_to_gtype, cmp_by_name, \ NS_TP, dbus_gutils_wincaps_to_uscore, \ signal_to_marshal_name, method_to_glue_marshal_name NS_TP = "http://telepathy.freedesktop.org/wiki/DbusSpec#extensions-v0" class Generator(object): def __init__(self, dom, prefix, basename, signal_marshal_prefix, headers, end_headers, not_implemented_func, allow_havoc): self.dom = dom self.__header = [] self.__body = [] self.__docs = [] assert prefix.endswith('_') assert not signal_marshal_prefix.endswith('_') # The main_prefix, sub_prefix thing is to get: # FOO_ -> (FOO_, _) # FOO_SVC_ -> (FOO_, _SVC_) # but # FOO_BAR/ -> (FOO_BAR_, _) # FOO_BAR/SVC_ -> (FOO_BAR_, _SVC_) if '/' in prefix: main_prefix, sub_prefix = prefix.upper().split('/', 1) prefix = prefix.replace('/', '_') else: main_prefix, sub_prefix = prefix.upper().split('_', 1) self.MAIN_PREFIX_ = main_prefix + '_' self._SUB_PREFIX_ = '_' + sub_prefix self.Prefix_ = prefix self.Prefix = prefix.replace('_', '') self.prefix_ = prefix.lower() self.PREFIX_ = prefix.upper() self.basename = basename self.signal_marshal_prefix = signal_marshal_prefix self.headers = headers self.end_headers = end_headers self.not_implemented_func = not_implemented_func self.allow_havoc = allow_havoc def h(self, s): if isinstance(s, unicode): s = s.encode('utf-8') self.__header.append(s) def b(self, s): if isinstance(s, unicode): s = s.encode('utf-8') self.__body.append(s) def d(self, s): if isinstance(s, unicode): s = s.encode('utf-8') self.__docs.append(s) def do_node(self, node): node_name = node.getAttribute('name').replace('/', '') node_name_mixed = self.node_name_mixed = node_name.replace('_', '') node_name_lc = self.node_name_lc = node_name.lower() node_name_uc = self.node_name_uc = node_name.upper() interfaces = node.getElementsByTagName('interface') assert len(interfaces) == 1, interfaces interface = interfaces[0] self.iface_name = interface.getAttribute('name') tmp = interface.getAttribute('tp:implement-service') if tmp == "no": return tmp = interface.getAttribute('tp:causes-havoc') if tmp and not self.allow_havoc: raise AssertionError('%s is %s' % (self.iface_name, tmp)) self.b('static const DBusGObjectInfo _%s%s_object_info;' % (self.prefix_, node_name_lc)) self.b('') methods = interface.getElementsByTagName('method') signals = interface.getElementsByTagName('signal') properties = interface.getElementsByTagName('property') # Don't put properties in dbus-glib glue glue_properties = [] self.b('struct _%s%sClass {' % (self.Prefix, node_name_mixed)) self.b(' GTypeInterface parent_class;') for method in methods: self.b(' %s %s;' % self.get_method_impl_names(method)) self.b('};') self.b('') if signals: self.b('enum {') for signal in signals: self.b(' %s,' % self.get_signal_const_entry(signal)) self.b(' N_%s_SIGNALS' % node_name_uc) self.b('};') self.b('static guint %s_signals[N_%s_SIGNALS] = {0};' % (node_name_lc, node_name_uc)) self.b('') self.b('static void %s%s_base_init (gpointer klass);' % (self.prefix_, node_name_lc)) self.b('') self.b('GType') self.b('%s%s_get_type (void)' % (self.prefix_, node_name_lc)) self.b('{') self.b(' static GType type = 0;') self.b('') self.b(' if (G_UNLIKELY (type == 0))') self.b(' {') self.b(' static const GTypeInfo info = {') self.b(' sizeof (%s%sClass),' % (self.Prefix, node_name_mixed)) self.b(' %s%s_base_init, /* base_init */' % (self.prefix_, node_name_lc)) self.b(' NULL, /* base_finalize */') self.b(' NULL, /* class_init */') self.b(' NULL, /* class_finalize */') self.b(' NULL, /* class_data */') self.b(' 0,') self.b(' 0, /* n_preallocs */') self.b(' NULL /* instance_init */') self.b(' };') self.b('') self.b(' type = g_type_register_static (G_TYPE_INTERFACE,') self.b(' "%s%s", &info, 0);' % (self.Prefix, node_name_mixed)) self.b(' }') self.b('') self.b(' return type;') self.b('}') self.b('') self.d('/**') self.d(' * %s%s:' % (self.Prefix, node_name_mixed)) self.d(' *') self.d(' * Dummy typedef representing any implementation of this ' 'interface.') self.d(' */') self.h('typedef struct _%s%s %s%s;' % (self.Prefix, node_name_mixed, self.Prefix, node_name_mixed)) self.h('') self.d('/**') self.d(' * %s%sClass:' % (self.Prefix, node_name_mixed)) self.d(' *') self.d(' * The class of %s%s.' % (self.Prefix, node_name_mixed)) if methods: self.d(' *') self.d(' * In a full implementation of this interface (i.e. all') self.d(' * methods implemented), the interface initialization') self.d(' * function used in G_IMPLEMENT_INTERFACE() would') self.d(' * typically look like this:') self.d(' *') self.d(' * ') self.d(' * static void') self.d(' * implement_%s (gpointer klass,' % self.node_name_lc) self.d(' * gpointer unused G_GNUC_UNUSED)') self.d(' * {') self.d(' * #define IMPLEMENT(x) %s%s_implement_##x (\\' % (self.prefix_, self.node_name_lc)) self.d(' * klass, my_object_##x)') for method in methods: class_member_name = method.getAttribute('tp:name-for-bindings') class_member_name = class_member_name.lower() self.d(' * IMPLEMENT (%s);' % class_member_name) self.d(' * #undef IMPLEMENT') self.d(' * }') self.d(' * ') else: self.d(' * This interface has no D-Bus methods, so an') self.d(' * implementation can typically pass %NULL to') self.d(' * G_IMPLEMENT_INTERFACE() as the interface') self.d(' * initialization function.') self.d(' */') self.d('') self.h('typedef struct _%s%sClass %s%sClass;' % (self.Prefix, node_name_mixed, self.Prefix, node_name_mixed)) self.h('') self.h('GType %s%s_get_type (void);' % (self.prefix_, node_name_lc)) gtype = self.current_gtype = \ self.MAIN_PREFIX_ + 'TYPE' + self._SUB_PREFIX_ + node_name_uc classname = self.Prefix + node_name_mixed self.h('#define %s \\\n (%s%s_get_type ())' % (gtype, self.prefix_, node_name_lc)) self.h('#define %s%s(obj) \\\n' ' (G_TYPE_CHECK_INSTANCE_CAST((obj), %s, %s))' % (self.PREFIX_, node_name_uc, gtype, classname)) self.h('#define %sIS%s%s(obj) \\\n' ' (G_TYPE_CHECK_INSTANCE_TYPE((obj), %s))' % (self.MAIN_PREFIX_, self._SUB_PREFIX_, node_name_uc, gtype)) self.h('#define %s%s_GET_CLASS(obj) \\\n' ' (G_TYPE_INSTANCE_GET_INTERFACE((obj), %s, %sClass))' % (self.PREFIX_, node_name_uc, gtype, classname)) self.h('') self.h('') base_init_code = [] for method in methods: self.do_method(method) for signal in signals: base_init_code.extend(self.do_signal(signal)) self.b('static inline void') self.b('%s%s_base_init_once (gpointer klass G_GNUC_UNUSED)' % (self.prefix_, node_name_lc)) self.b('{') if properties: self.b(' static TpDBusPropertiesMixinPropInfo properties[%d] = {' % (len(properties) + 1)) for m in properties: access = m.getAttribute('access') assert access in ('read', 'write', 'readwrite') if access == 'read': flags = 'TP_DBUS_PROPERTIES_MIXIN_FLAG_READ' elif access == 'write': flags = 'TP_DBUS_PROPERTIES_MIXIN_FLAG_WRITE' else: flags = ('TP_DBUS_PROPERTIES_MIXIN_FLAG_READ | ' 'TP_DBUS_PROPERTIES_MIXIN_FLAG_WRITE') self.b(' { 0, %s, "%s", 0, NULL, NULL }, /* %s */' % (flags, m.getAttribute('type'), m.getAttribute('name'))) self.b(' { 0, 0, NULL, 0, NULL, NULL }') self.b(' };') self.b(' static TpDBusPropertiesMixinIfaceInfo interface =') self.b(' { 0, properties, NULL, NULL };') self.b('') self.b(' dbus_g_object_type_install_info (%s%s_get_type (),' % (self.prefix_, node_name_lc)) self.b(' &_%s%s_object_info);' % (self.prefix_, node_name_lc)) self.b('') if properties: self.b(' interface.dbus_interface = g_quark_from_static_string ' '("%s");' % self.iface_name) for i, m in enumerate(properties): self.b(' properties[%d].name = g_quark_from_static_string ("%s");' % (i, m.getAttribute('name'))) self.b(' properties[%d].type = %s;' % (i, type_to_gtype(m.getAttribute('type'))[1])) self.b(' tp_svc_interface_set_dbus_properties_info (%s, &interface);' % self.current_gtype) self.b('') for s in base_init_code: self.b(s) self.b('}') self.b('static void') self.b('%s%s_base_init (gpointer klass)' % (self.prefix_, node_name_lc)) self.b('{') self.b(' static gboolean initialized = FALSE;') self.b('') self.b(' if (!initialized)') self.b(' {') self.b(' initialized = TRUE;') self.b(' %s%s_base_init_once (klass);' % (self.prefix_, node_name_lc)) self.b(' }') # insert anything we need to do per implementation here self.b('}') self.h('') self.b('static const DBusGMethodInfo _%s%s_methods[] = {' % (self.prefix_, node_name_lc)) method_blob, offsets = self.get_method_glue(methods) for method, offset in zip(methods, offsets): self.do_method_glue(method, offset) if len(methods) == 0: # empty arrays are a gcc extension, so put in a dummy member self.b(" { NULL, NULL, 0 }") self.b('};') self.b('') self.b('static const DBusGObjectInfo _%s%s_object_info = {' % (self.prefix_, node_name_lc)) self.b(' 0,') # version self.b(' _%s%s_methods,' % (self.prefix_, node_name_lc)) self.b(' %d,' % len(methods)) self.b('"' + method_blob.replace('\0', '\\0') + '",') self.b('"' + self.get_signal_glue(signals).replace('\0', '\\0') + '",') self.b('"' + self.get_property_glue(glue_properties).replace('\0', '\\0') + '",') self.b('};') self.b('') self.node_name_mixed = None self.node_name_lc = None self.node_name_uc = None def get_method_glue(self, methods): info = [] offsets = [] for method in methods: offsets.append(len(''.join(info))) info.append(self.iface_name + '\0') info.append(method.getAttribute('name') + '\0') info.append('A\0') # async counter = 0 for arg in method.getElementsByTagName('arg'): out = arg.getAttribute('direction') == 'out' name = arg.getAttribute('name') if not name: assert out name = 'arg%u' % counter counter += 1 info.append(name + '\0') if out: info.append('O\0') else: info.append('I\0') if out: info.append('F\0') # not const info.append('N\0') # not error or return info.append(arg.getAttribute('type') + '\0') info.append('\0') return ''.join(info) + '\0', offsets def do_method_glue(self, method, offset): lc_name = method.getAttribute('tp:name-for-bindings') if method.getAttribute('name') != lc_name.replace('_', ''): raise AssertionError('Method %s tp:name-for-bindings (%s) does ' 'not match' % (method.getAttribute('name'), lc_name)) lc_name = lc_name.lower() marshaller = method_to_glue_marshal_name(method, self.signal_marshal_prefix) wrapper = self.prefix_ + self.node_name_lc + '_' + lc_name self.b(" { (GCallback) %s, %s, %d }," % (wrapper, marshaller, offset)) def get_signal_glue(self, signals): info = [] for signal in signals: info.append(self.iface_name) info.append(signal.getAttribute('name')) return '\0'.join(info) + '\0\0' # the implementation can be the same get_property_glue = get_signal_glue def get_method_impl_names(self, method): dbus_method_name = method.getAttribute('name') class_member_name = method.getAttribute('tp:name-for-bindings') if dbus_method_name != class_member_name.replace('_', ''): raise AssertionError('Method %s tp:name-for-bindings (%s) does ' 'not match' % (dbus_method_name, class_member_name)) class_member_name = class_member_name.lower() stub_name = (self.prefix_ + self.node_name_lc + '_' + class_member_name) return (stub_name + '_impl', class_member_name + '_cb') def do_method(self, method): assert self.node_name_mixed is not None in_class = [] # Examples refer to Thing.DoStuff (su) -> ii # DoStuff dbus_method_name = method.getAttribute('name') # do_stuff class_member_name = method.getAttribute('tp:name-for-bindings') if dbus_method_name != class_member_name.replace('_', ''): raise AssertionError('Method %s tp:name-for-bindings (%s) does ' 'not match' % (dbus_method_name, class_member_name)) class_member_name = class_member_name.lower() # void tp_svc_thing_do_stuff (TpSvcThing *, const char *, guint, # DBusGMethodInvocation *); stub_name = (self.prefix_ + self.node_name_lc + '_' + class_member_name) # typedef void (*tp_svc_thing_do_stuff_impl) (TpSvcThing *, # const char *, guint, DBusGMethodInvocation); impl_name = stub_name + '_impl' # void tp_svc_thing_return_from_do_stuff (DBusGMethodInvocation *, # gint, gint); ret_name = (self.prefix_ + self.node_name_lc + '_return_from_' + class_member_name) # Gather arguments in_args = [] out_args = [] for i in method.getElementsByTagName('arg'): name = i.getAttribute('name') direction = i.getAttribute('direction') or 'in' dtype = i.getAttribute('type') assert direction in ('in', 'out') if name: name = direction + '_' + name elif direction == 'in': name = direction + str(len(in_args)) else: name = direction + str(len(out_args)) ctype, gtype, marshaller, pointer = type_to_gtype(dtype) if pointer: ctype = 'const ' + ctype struct = (ctype, name) if direction == 'in': in_args.append(struct) else: out_args.append(struct) # Implementation type declaration (in header, docs separated) self.d('/**') self.d(' * %s:' % impl_name) self.d(' * @self: The object implementing this interface') for (ctype, name) in in_args: self.d(' * @%s: %s (FIXME, generate documentation)' % (name, ctype)) self.d(' * @context: Used to return values or throw an error') self.d(' *') self.d(' * The signature of an implementation of the D-Bus method') self.d(' * %s on interface %s.' % (dbus_method_name, self.iface_name)) self.d(' */') self.h('typedef void (*%s) (%s%s *self,' % (impl_name, self.Prefix, self.node_name_mixed)) for (ctype, name) in in_args: self.h(' %s%s,' % (ctype, name)) self.h(' DBusGMethodInvocation *context);') # Class member (in class definition) in_class.append(' %s %s;' % (impl_name, class_member_name)) # Stub definition (in body only - it's static) self.b('static void') self.b('%s (%s%s *self,' % (stub_name, self.Prefix, self.node_name_mixed)) for (ctype, name) in in_args: self.b(' %s%s,' % (ctype, name)) self.b(' DBusGMethodInvocation *context)') self.b('{') self.b(' %s impl = (%s%s_GET_CLASS (self)->%s_cb);' % (impl_name, self.PREFIX_, self.node_name_uc, class_member_name)) self.b('') self.b(' if (impl != NULL)') tmp = ['self'] + [name for (ctype, name) in in_args] + ['context'] self.b(' {') self.b(' (impl) (%s);' % ',\n '.join(tmp)) self.b(' }') self.b(' else') self.b(' {') if self.not_implemented_func: self.b(' %s (context);' % self.not_implemented_func) else: self.b(' GError e = { DBUS_GERROR, ') self.b(' DBUS_GERROR_UNKNOWN_METHOD,') self.b(' "Method not implemented" };') self.b('') self.b(' dbus_g_method_return_error (context, &e);') self.b(' }') self.b('}') self.b('') # Implementation registration (in both header and body) self.h('void %s%s_implement_%s (%s%sClass *klass, %s impl);' % (self.prefix_, self.node_name_lc, class_member_name, self.Prefix, self.node_name_mixed, impl_name)) self.d('/**') self.d(' * %s%s_implement_%s:' % (self.prefix_, self.node_name_lc, class_member_name)) self.d(' * @klass: A class whose instances implement this interface') self.d(' * @impl: A callback used to implement the %s D-Bus method' % dbus_method_name) self.d(' *') self.d(' * Register an implementation for the %s method in the vtable' % dbus_method_name) self.d(' * of an implementation of this interface. To be called from') self.d(' * the interface init function.') self.d(' */') self.b('void') self.b('%s%s_implement_%s (%s%sClass *klass, %s impl)' % (self.prefix_, self.node_name_lc, class_member_name, self.Prefix, self.node_name_mixed, impl_name)) self.b('{') self.b(' klass->%s_cb = impl;' % class_member_name) self.b('}') self.b('') # Return convenience function (static inline, in header) self.d('/**') self.d(' * %s:' % ret_name) self.d(' * @context: The D-Bus method invocation context') for (ctype, name) in out_args: self.d(' * @%s: %s (FIXME, generate documentation)' % (name, ctype)) self.d(' *') self.d(' * Return successfully by calling dbus_g_method_return().') self.d(' * This inline function exists only to provide type-safety.') self.d(' */') self.d('') tmp = (['DBusGMethodInvocation *context'] + [ctype + name for (ctype, name) in out_args]) self.h('static inline') self.h('/* this comment is to stop gtkdoc realising this is static */') self.h(('void %s (' % ret_name) + (',\n '.join(tmp)) + ');') self.h('static inline void') self.h(('%s (' % ret_name) + (',\n '.join(tmp)) + ')') self.h('{') tmp = ['context'] + [name for (ctype, name) in out_args] self.h(' dbus_g_method_return (' + ',\n '.join(tmp) + ');') self.h('}') self.h('') return in_class def get_signal_const_entry(self, signal): assert self.node_name_uc is not None return ('SIGNAL_%s_%s' % (self.node_name_uc, signal.getAttribute('name'))) def do_signal(self, signal): assert self.node_name_mixed is not None in_base_init = [] # for signal: Thing::StuffHappened (s, u) # we want to emit: # void tp_svc_thing_emit_stuff_happened (gpointer instance, # const char *arg0, guint arg1); dbus_name = signal.getAttribute('name') ugly_name = signal.getAttribute('tp:name-for-bindings') if dbus_name != ugly_name.replace('_', ''): raise AssertionError('Signal %s tp:name-for-bindings (%s) does ' 'not match' % (dbus_name, ugly_name)) stub_name = (self.prefix_ + self.node_name_lc + '_emit_' + ugly_name.lower()) const_name = self.get_signal_const_entry(signal) # Gather arguments args = [] for i in signal.getElementsByTagName('arg'): name = i.getAttribute('name') dtype = i.getAttribute('type') tp_type = i.getAttribute('tp:type') if name: name = 'arg_' + name else: name = 'arg' + str(len(args)) ctype, gtype, marshaller, pointer = type_to_gtype(dtype) if pointer: ctype = 'const ' + ctype struct = (ctype, name, gtype) args.append(struct) tmp = (['gpointer instance'] + [ctype + name for (ctype, name, gtype) in args]) self.h(('void %s (' % stub_name) + (',\n '.join(tmp)) + ');') # FIXME: emit docs self.d('/**') self.d(' * %s:' % stub_name) self.d(' * @instance: The object implementing this interface') for (ctype, name, gtype) in args: self.d(' * @%s: %s (FIXME, generate documentation)' % (name, ctype)) self.d(' *') self.d(' * Type-safe wrapper around g_signal_emit to emit the') self.d(' * %s signal on interface %s.' % (dbus_name, self.iface_name)) self.d(' */') self.b('void') self.b(('%s (' % stub_name) + (',\n '.join(tmp)) + ')') self.b('{') self.b(' g_assert (instance != NULL);') self.b(' g_assert (G_TYPE_CHECK_INSTANCE_TYPE (instance, %s));' % (self.current_gtype)) tmp = (['instance', '%s_signals[%s]' % (self.node_name_lc, const_name), '0'] + [name for (ctype, name, gtype) in args]) self.b(' g_signal_emit (' + ',\n '.join(tmp) + ');') self.b('}') self.b('') signal_name = dbus_gutils_wincaps_to_uscore(dbus_name).replace('_', '-') self.d('/**') self.d(' * %s%s::%s:' % (self.Prefix, self.node_name_mixed, signal_name)) for (ctype, name, gtype) in args: self.d(' * @%s: %s (FIXME, generate documentation)' % (name, ctype)) self.d(' *') self.d(' * The %s D-Bus signal is emitted whenever ' 'this GObject signal is.' % dbus_name) self.d(' */') self.d('') in_base_init.append(' %s_signals[%s] =' % (self.node_name_lc, const_name)) in_base_init.append(' g_signal_new ("%s",' % signal_name) in_base_init.append(' G_OBJECT_CLASS_TYPE (klass),') in_base_init.append(' G_SIGNAL_RUN_LAST|G_SIGNAL_DETAILED,') in_base_init.append(' 0,') in_base_init.append(' NULL, NULL,') in_base_init.append(' %s,' % signal_to_marshal_name(signal, self.signal_marshal_prefix)) in_base_init.append(' G_TYPE_NONE,') tmp = ['%d' % len(args)] + [gtype for (ctype, name, gtype) in args] in_base_init.append(' %s);' % ',\n '.join(tmp)) in_base_init.append('') return in_base_init def have_properties(self, nodes): for node in nodes: interface = node.getElementsByTagName('interface')[0] if interface.getElementsByTagName('property'): return True return False def __call__(self): nodes = self.dom.getElementsByTagName('node') nodes.sort(cmp_by_name) self.h('#include ') self.h('#include ') if self.have_properties(nodes): self.h('#include ') self.h('') self.h('G_BEGIN_DECLS') self.h('') self.b('#include "%s.h"' % self.basename) self.b('') for header in self.headers: self.b('#include %s' % header) self.b('') for node in nodes: self.do_node(node) self.h('') self.h('G_END_DECLS') self.b('') for header in self.end_headers: self.b('#include %s' % header) self.h('') self.b('') open(self.basename + '.h', 'w').write('\n'.join(self.__header)) open(self.basename + '.c', 'w').write('\n'.join(self.__body)) open(self.basename + '-gtk-doc.h', 'w').write('\n'.join(self.__docs)) def cmdline_error(): print """\ usage: gen-ginterface [OPTIONS] xmlfile Prefix_ options: --include='' (may be repeated) --include='"header.h"' (ditto) --include-end='"header.h"' (ditto) Include extra headers in the generated .c file --signal-marshal-prefix='prefix' Use the given prefix on generated signal marshallers (default is prefix.lower()). --filename='BASENAME' Set the basename for the output files (default is prefix.lower() + 'ginterfaces') --not-implemented-func='symbol' Set action when methods not implemented in the interface vtable are called. symbol must have signature void symbol (DBusGMethodInvocation *context) and return some sort of "not implemented" error via dbus_g_method_return_error (context, ...) """ sys.exit(1) if __name__ == '__main__': from getopt import gnu_getopt options, argv = gnu_getopt(sys.argv[1:], '', ['filename=', 'signal-marshal-prefix=', 'include=', 'include-end=', 'allow-unstable', 'not-implemented-func=']) try: prefix = argv[1] except IndexError: cmdline_error() basename = prefix.lower() + 'ginterfaces' signal_marshal_prefix = prefix.lower().rstrip('_') headers = [] end_headers = [] not_implemented_func = '' allow_havoc = False for option, value in options: if option == '--filename': basename = value elif option == '--signal-marshal-prefix': signal_marshal_prefix = value elif option == '--include': if value[0] not in '<"': value = '"%s"' % value headers.append(value) elif option == '--include-end': if value[0] not in '<"': value = '"%s"' % value end_headers.append(value) elif option == '--not-implemented-func': not_implemented_func = value elif option == '--allow-unstable': allow_havoc = True try: dom = xml.dom.minidom.parse(argv[0]) except IndexError: cmdline_error() Generator(dom, prefix, basename, signal_marshal_prefix, headers, end_headers, not_implemented_func, allow_havoc)() telepathy-ring-2.3.24-37ad7111424725a5446e82dac516d7cab88149c7/tools/glib-gtypes-generator.py000066400000000000000000000303521251541261300277030ustar00rootroot00000000000000#!/usr/bin/python # Generate GLib GInterfaces from the Telepathy specification. # The master copy of this program is in the telepathy-glib repository - # please make any changes there. # # Copyright (C) 2006, 2007 Collabora Limited # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA import sys import xml.dom.minidom from libglibcodegen import escape_as_identifier, \ get_docstring, \ NS_TP, \ Signature, \ type_to_gtype, \ xml_escape def types_to_gtypes(types): return [type_to_gtype(t)[1] for t in types] class GTypesGenerator(object): def __init__(self, dom, output, mixed_case_prefix): self.dom = dom self.Prefix = mixed_case_prefix self.PREFIX_ = self.Prefix.upper() + '_' self.prefix_ = self.Prefix.lower() + '_' self.header = open(output + '.h', 'w') self.body = open(output + '-body.h', 'w') self.docs = open(output + '-gtk-doc.h', 'w') for f in (self.header, self.body, self.docs): f.write('/* Auto-generated, do not edit.\n *\n' ' * This file may be distributed under the same terms\n' ' * as the specification from which it was generated.\n' ' */\n\n') # keys are e.g. 'sv', values are the key escaped self.need_mappings = {} # keys are the contents of the struct (e.g. 'sssu'), values are the # key escaped self.need_structs = {} # keys are the contents of the struct (e.g. 'sssu'), values are the # key escaped self.need_struct_arrays = {} # keys are the contents of the array (unlike need_struct_arrays!), # values are the key escaped self.need_other_arrays = {} def h(self, code): self.header.write(code.encode("utf-8")) def c(self, code): self.body.write(code.encode("utf-8")) def d(self, code): self.docs.write(code.encode('utf-8')) def do_mapping_header(self, mapping): members = mapping.getElementsByTagNameNS(NS_TP, 'member') assert len(members) == 2 impl_sig = ''.join([elt.getAttribute('type') for elt in members]) esc_impl_sig = escape_as_identifier(impl_sig) name = (self.PREFIX_ + 'HASH_TYPE_' + mapping.getAttribute('name').upper()) impl = self.prefix_ + 'type_dbus_hash_' + esc_impl_sig docstring = get_docstring(mapping) or '(Undocumented)' self.d('/**\n * %s:\n *\n' % name) self.d(' * %s\n' % xml_escape(docstring)) self.d(' *\n') self.d(' * This macro expands to a call to a function\n') self.d(' * that returns the #GType of a #GHashTable\n') self.d(' * appropriate for representing a D-Bus\n') self.d(' * dictionary of signature\n') self.d(' * a{%s}.\n' % impl_sig) self.d(' *\n') key, value = members self.d(' * Keys (D-Bus type %s,\n' % key.getAttribute('type')) tp_type = key.getAttributeNS(NS_TP, 'type') if tp_type: self.d(' * type %s,\n' % tp_type) self.d(' * named %s):\n' % key.getAttribute('name')) docstring = get_docstring(key) or '(Undocumented)' self.d(' * %s\n' % xml_escape(docstring)) self.d(' *\n') self.d(' * Values (D-Bus type %s,\n' % value.getAttribute('type')) tp_type = value.getAttributeNS(NS_TP, 'type') if tp_type: self.d(' * type %s,\n' % tp_type) self.d(' * named %s):\n' % value.getAttribute('name')) docstring = get_docstring(value) or '(Undocumented)' self.d(' * %s\n' % xml_escape(docstring)) self.d(' *\n') self.d(' */\n') self.h('#define %s (%s ())\n\n' % (name, impl)) self.need_mappings[impl_sig] = esc_impl_sig array_name = mapping.getAttribute('array-name') if array_name: gtype_name = self.PREFIX_ + 'ARRAY_TYPE_' + array_name.upper() contents_sig = 'a{' + impl_sig + '}' esc_contents_sig = escape_as_identifier(contents_sig) impl = self.prefix_ + 'type_dbus_array_of_' + esc_contents_sig self.d('/**\n * %s:\n\n' % gtype_name) self.d(' * Expands to a call to a function\n') self.d(' * that returns the #GType of a #GPtrArray\n') self.d(' * of #%s.\n' % name) self.d(' */\n\n') self.h('#define %s (%s ())\n\n' % (gtype_name, impl)) self.need_other_arrays[contents_sig] = esc_contents_sig def do_struct_header(self, struct): members = struct.getElementsByTagNameNS(NS_TP, 'member') impl_sig = ''.join([elt.getAttribute('type') for elt in members]) esc_impl_sig = escape_as_identifier(impl_sig) name = (self.PREFIX_ + 'STRUCT_TYPE_' + struct.getAttribute('name').upper()) impl = self.prefix_ + 'type_dbus_struct_' + esc_impl_sig docstring = struct.getElementsByTagNameNS(NS_TP, 'docstring') if docstring: docstring = docstring[0].toprettyxml() if docstring.startswith(''): docstring = docstring[14:] if docstring.endswith('\n'): docstring = docstring[:-16] if docstring.strip() in ('', ''): docstring = '(Undocumented)' else: docstring = '(Undocumented)' self.d('/**\n * %s:\n\n' % name) self.d(' * %s\n' % xml_escape(docstring)) self.d(' *\n') self.d(' * This macro expands to a call to a function\n') self.d(' * that returns the #GType of a #GValueArray\n') self.d(' * appropriate for representing a D-Bus struct\n') self.d(' * with signature (%s).\n' % impl_sig) self.d(' *\n') for i, member in enumerate(members): self.d(' * Member %d (D-Bus type ' '%s,\n' % (i, member.getAttribute('type'))) tp_type = member.getAttributeNS(NS_TP, 'type') if tp_type: self.d(' * type %s,\n' % tp_type) self.d(' * named %s):\n' % member.getAttribute('name')) docstring = get_docstring(member) or '(Undocumented)' self.d(' * %s\n' % xml_escape(docstring)) self.d(' *\n') self.d(' */\n\n') self.h('#define %s (%s ())\n\n' % (name, impl)) array_name = struct.getAttribute('array-name') if array_name != '': array_name = (self.PREFIX_ + 'ARRAY_TYPE_' + array_name.upper()) impl = self.prefix_ + 'type_dbus_array_' + esc_impl_sig self.d('/**\n * %s:\n\n' % array_name) self.d(' * Expands to a call to a function\n') self.d(' * that returns the #GType of a #GPtrArray\n') self.d(' * of #%s.\n' % name) self.d(' */\n\n') self.h('#define %s (%s ())\n\n' % (array_name, impl)) self.need_struct_arrays[impl_sig] = esc_impl_sig self.need_structs[impl_sig] = esc_impl_sig def __call__(self): mappings = self.dom.getElementsByTagNameNS(NS_TP, 'mapping') structs = self.dom.getElementsByTagNameNS(NS_TP, 'struct') for mapping in mappings: self.do_mapping_header(mapping) for sig in self.need_mappings: self.h('GType %stype_dbus_hash_%s (void);\n\n' % (self.prefix_, self.need_mappings[sig])) self.c('GType\n%stype_dbus_hash_%s (void)\n{\n' % (self.prefix_, self.need_mappings[sig])) self.c(' static GType t = 0;\n\n') self.c(' if (G_UNLIKELY (t == 0))\n') # FIXME: translate sig into two GTypes items = tuple(Signature(sig)) gtypes = types_to_gtypes(items) self.c(' t = dbus_g_type_get_map ("GHashTable", ' '%s, %s);\n' % (gtypes[0], gtypes[1])) self.c(' return t;\n') self.c('}\n\n') for struct in structs: self.do_struct_header(struct) for sig in self.need_structs: self.h('GType %stype_dbus_struct_%s (void);\n\n' % (self.prefix_, self.need_structs[sig])) self.c('GType\n%stype_dbus_struct_%s (void)\n{\n' % (self.prefix_, self.need_structs[sig])) self.c(' static GType t = 0;\n\n') self.c(' if (G_UNLIKELY (t == 0))\n') self.c(' t = dbus_g_type_get_struct ("GValueArray",\n') items = tuple(Signature(sig)) gtypes = types_to_gtypes(items) for gtype in gtypes: self.c(' %s,\n' % gtype) self.c(' G_TYPE_INVALID);\n') self.c(' return t;\n') self.c('}\n\n') for sig in self.need_struct_arrays: self.h('GType %stype_dbus_array_%s (void);\n\n' % (self.prefix_, self.need_struct_arrays[sig])) self.c('GType\n%stype_dbus_array_%s (void)\n{\n' % (self.prefix_, self.need_struct_arrays[sig])) self.c(' static GType t = 0;\n\n') self.c(' if (G_UNLIKELY (t == 0))\n') self.c(' t = dbus_g_type_get_collection ("GPtrArray", ' '%stype_dbus_struct_%s ());\n' % (self.prefix_, self.need_struct_arrays[sig])) self.c(' return t;\n') self.c('}\n\n') for sig in self.need_other_arrays: self.h('GType %stype_dbus_array_of_%s (void);\n\n' % (self.prefix_, self.need_other_arrays[sig])) self.c('GType\n%stype_dbus_array_of_%s (void)\n{\n' % (self.prefix_, self.need_other_arrays[sig])) self.c(' static GType t = 0;\n\n') self.c(' if (G_UNLIKELY (t == 0))\n') if sig[:2] == 'a{' and sig[-1:] == '}': # array of mappings self.c(' t = dbus_g_type_get_collection (' '"GPtrArray", ' '%stype_dbus_hash_%s ());\n' % (self.prefix_, escape_as_identifier(sig[2:-1]))) elif sig[:2] == 'a(' and sig[-1:] == ')': # array of arrays of struct self.c(' t = dbus_g_type_get_collection (' '"GPtrArray", ' '%stype_dbus_array_%s ());\n' % (self.prefix_, escape_as_identifier(sig[2:-1]))) elif sig[:1] == 'a': # array of arrays of non-struct self.c(' t = dbus_g_type_get_collection (' '"GPtrArray", ' '%stype_dbus_array_of_%s ());\n' % (self.prefix_, escape_as_identifier(sig[1:]))) else: raise AssertionError("array of '%s' not supported" % sig) self.c(' return t;\n') self.c('}\n\n') if __name__ == '__main__': argv = sys.argv[1:] dom = xml.dom.minidom.parse(argv[0]) GTypesGenerator(dom, argv[1], argv[2])() telepathy-ring-2.3.24-37ad7111424725a5446e82dac516d7cab88149c7/tools/glib-interfaces-gen.py000066400000000000000000000141021251541261300272710ustar00rootroot00000000000000#!/usr/bin/python from sys import argv, stdout, stderr import xml.dom.minidom from libglibcodegen import NS_TP, get_docstring, \ get_descendant_text, get_by_path class Generator(object): def __init__(self, prefix, implfile, declfile, dom): self.prefix = prefix + '_' assert declfile.endswith('.h') docfile = declfile[:-2] + '-gtk-doc.h' self.impls = open(implfile, 'w') self.decls = open(declfile, 'w') self.docs = open(docfile, 'w') self.spec = get_by_path(dom, "spec")[0] def h(self, code): self.decls.write(code.encode('utf-8')) def c(self, code): self.impls.write(code.encode('utf-8')) def d(self, code): self.docs.write(code.encode('utf-8')) def __call__(self): for f in self.h, self.c: self.do_header(f) self.do_body() # Header def do_header(self, f): f('/* Generated from: ') f(get_descendant_text(get_by_path(self.spec, 'title'))) version = get_by_path(self.spec, "version") if version: f(' version ' + get_descendant_text(version)) f('\n\n') for copyright in get_by_path(self.spec, 'copyright'): f(get_descendant_text(copyright)) f('\n') f('\n') f(get_descendant_text(get_by_path(self.spec, 'license'))) f(get_descendant_text(get_by_path(self.spec, 'docstring'))) f(""" */ """) # Body def do_body(self): for iface in self.spec.getElementsByTagName('interface'): self.do_iface(iface) def do_iface(self, iface): parent_name = get_by_path(iface, '../@name') self.d("""\ /** * %(IFACE_DEFINE)s: * * The interface name "%(name)s" */ """ % {'IFACE_DEFINE' : (self.prefix + 'IFACE_' + \ parent_name).upper().replace('/', ''), 'name' : iface.getAttribute('name')}) self.h(""" #define %(IFACE_DEFINE)s \\ "%(name)s" """ % {'IFACE_DEFINE' : (self.prefix + 'IFACE_' + \ parent_name).upper().replace('/', ''), 'name' : iface.getAttribute('name')}) self.d(""" /** * %(IFACE_QUARK_DEFINE)s: * * Expands to a call to a function that returns a quark for the interface \ name "%(name)s" */ """ % {'IFACE_QUARK_DEFINE' : (self.prefix + 'IFACE_QUARK_' + \ parent_name).upper().replace('/', ''), 'iface_quark_func' : (self.prefix + 'iface_quark_' + \ parent_name).lower().replace('/', ''), 'name' : iface.getAttribute('name')}) self.h(""" #define %(IFACE_QUARK_DEFINE)s \\ (%(iface_quark_func)s ()) GQuark %(iface_quark_func)s (void); """ % {'IFACE_QUARK_DEFINE' : (self.prefix + 'IFACE_QUARK_' + \ parent_name).upper().replace('/', ''), 'iface_quark_func' : (self.prefix + 'iface_quark_' + \ parent_name).lower().replace('/', ''), 'name' : iface.getAttribute('name')}) self.c("""\ GQuark %(iface_quark_func)s (void) { static GQuark quark = 0; if (G_UNLIKELY (quark == 0)) { quark = g_quark_from_static_string ("%(name)s"); } return quark; } """ % {'iface_quark_func' : (self.prefix + 'iface_quark_' + \ parent_name).lower().replace('/', ''), 'name' : iface.getAttribute('name')}) for prop in iface.getElementsByTagNameNS(None, 'property'): self.d(""" /** * %(IFACE_PREFIX)s_%(PROP_UC)s: * * The fully-qualified property name "%(name)s.%(prop)s" */ """ % {'IFACE_PREFIX' : (self.prefix + 'PROP_' + \ parent_name).upper().replace('/', ''), 'PROP_UC': prop.getAttributeNS(NS_TP, "name-for-bindings").upper(), 'name' : iface.getAttribute('name'), 'prop' : prop.getAttribute('name'), }) self.h(""" #define %(IFACE_PREFIX)s_%(PROP_UC)s \\ "%(name)s.%(prop)s" """ % {'IFACE_PREFIX' : (self.prefix + 'PROP_' + \ parent_name).upper().replace('/', ''), 'PROP_UC': prop.getAttributeNS(NS_TP, "name-for-bindings").upper(), 'name' : iface.getAttribute('name'), 'prop' : prop.getAttribute('name'), }) for prop in iface.getElementsByTagNameNS(NS_TP, 'contact-attribute'): self.d(""" /** * %(TOKEN_PREFIX)s_%(TOKEN_UC)s: * * The fully-qualified contact attribute token name "%(name)s/%(prop)s" */ """ % {'TOKEN_PREFIX' : (self.prefix + 'TOKEN_' + \ parent_name).upper().replace('/', ''), 'TOKEN_UC': prop.getAttributeNS(None, "name").upper().replace("-", "_").replace(".", "_"), 'name' : iface.getAttribute('name'), 'prop' : prop.getAttribute('name'), }) self.h(""" #define %(TOKEN_PREFIX)s_%(TOKEN_UC)s \\ "%(name)s/%(prop)s" """ % {'TOKEN_PREFIX' : (self.prefix + 'TOKEN_' + \ parent_name).upper().replace('/', ''), 'TOKEN_UC': prop.getAttributeNS(None, "name").upper().replace("-", "_").replace(".", "_"), 'name' : iface.getAttribute('name'), 'prop' : prop.getAttribute('name'), }) for prop in iface.getElementsByTagNameNS(NS_TP, 'hct'): if (prop.getAttribute('is-family') != "yes"): self.d(""" /** * %(TOKEN_PREFIX)s_%(TOKEN_UC)s: * * The fully-qualified capability token name "%(name)s/%(prop)s" */ """ % {'TOKEN_PREFIX' : (self.prefix + 'TOKEN_' + \ parent_name).upper().replace('/', ''), 'TOKEN_UC': prop.getAttributeNS(None, "name").upper().replace("-", "_").replace(".", "_"), 'name' : iface.getAttribute('name'), 'prop' : prop.getAttribute('name'), }) self.h(""" #define %(TOKEN_PREFIX)s_%(TOKEN_UC)s \\ "%(name)s/%(prop)s" """ % {'TOKEN_PREFIX' : (self.prefix + 'TOKEN_' + \ parent_name).upper().replace('/', ''), 'TOKEN_UC': prop.getAttributeNS(None, "name").upper().replace("-", "_").replace(".", "_"), 'name' : iface.getAttribute('name'), 'prop' : prop.getAttribute('name'), }) if __name__ == '__main__': argv = argv[1:] Generator(argv[0], argv[1], argv[2], xml.dom.minidom.parse(argv[3]))() telepathy-ring-2.3.24-37ad7111424725a5446e82dac516d7cab88149c7/tools/glib-signals-marshal-gen.py000066400000000000000000000025411251541261300302370ustar00rootroot00000000000000#!/usr/bin/python import sys import xml.dom.minidom from string import ascii_letters, digits from libglibcodegen import signal_to_marshal_name, method_to_glue_marshal_name class Generator(object): def __init__(self, dom): self.dom = dom self.marshallers = {} def do_method(self, method): marshaller = method_to_glue_marshal_name(method, 'PREFIX') assert '__' in marshaller rhs = marshaller.split('__', 1)[1].split('_') self.marshallers[marshaller] = rhs def do_signal(self, signal): marshaller = signal_to_marshal_name(signal, 'PREFIX') assert '__' in marshaller rhs = marshaller.split('__', 1)[1].split('_') self.marshallers[marshaller] = rhs def __call__(self): methods = self.dom.getElementsByTagName('method') for method in methods: self.do_method(method) signals = self.dom.getElementsByTagName('signal') for signal in signals: self.do_signal(signal) all = self.marshallers.keys() all.sort() for marshaller in all: rhs = self.marshallers[marshaller] if not marshaller.startswith('g_cclosure'): print 'VOID:' + ','.join(rhs) if __name__ == '__main__': argv = sys.argv[1:] dom = xml.dom.minidom.parse(argv[0]) Generator(dom)() telepathy-ring-2.3.24-37ad7111424725a5446e82dac516d7cab88149c7/tools/gobject-foo.py000066400000000000000000000053531251541261300256720ustar00rootroot00000000000000#!/usr/bin/python # gobject-foo.py: generate standard GObject type macros etc. # # The master copy of this program is in the telepathy-glib repository - # please make any changes there. # # Copyright (C) 2007 Collabora Ltd. # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA def gobject_header(head, tail, as_interface=False): out = [] o = out.append name = head + '_' + tail MixedCase = name.replace('_', '') lower_case = name.lower() UPPER_CASE = name.upper() gtype = head.upper() + '_TYPE_' + tail.upper() o("typedef struct _%s %s;" % (MixedCase, MixedCase)) o("typedef struct _%sClass %sClass;" % (MixedCase, MixedCase)) o("typedef struct _%sPrivate %sPrivate;" % (MixedCase, MixedCase)) o("") o("GType %s_get_type (void);" % lower_case) o("") o("#define %s \\" % gtype) o(" (%s_get_type ())" % lower_case) o("#define %s(obj) \\" % UPPER_CASE) o(" (G_TYPE_CHECK_INSTANCE_CAST ((obj), %s, \\" % gtype) o(" %s))" % MixedCase) if not as_interface: o("#define %s_CLASS(klass) \\" % UPPER_CASE) o(" (G_TYPE_CHECK_CLASS_CAST ((klass), %s, \\" % gtype) o(" %sClass))" % MixedCase) o("#define %s_IS_%s(obj) \\" % (head.upper(), tail.upper())) o(" (G_TYPE_CHECK_INSTANCE_TYPE ((obj), %s))" % gtype) if not as_interface: o("#define %s_IS_%s_CLASS(klass) \\" % (head.upper(), tail.upper())) o(" (G_TYPE_CHECK_CLASS_TYPE ((klass), %s))" % gtype) o("#define %s_GET_CLASS(obj) \\" % UPPER_CASE) o(" (G_TYPE_INSTANCE_GET_CLASS ((obj), %s, \\" % gtype) o(" %sClass))" % MixedCase) return out if __name__ == '__main__': import sys from getopt import gnu_getopt options, argv = gnu_getopt(sys.argv[1:], '', ['interface']) as_interface = False for opt, val in options: if opt == '--interface': as_interface = True head, tail = argv print '\n'.join(gobject_header(head, tail, as_interface=as_interface)) telepathy-ring-2.3.24-37ad7111424725a5446e82dac516d7cab88149c7/tools/identity.xsl000066400000000000000000000003351251541261300254760ustar00rootroot00000000000000 telepathy-ring-2.3.24-37ad7111424725a5446e82dac516d7cab88149c7/tools/libglibcodegen.py000066400000000000000000000145631251541261300264300ustar00rootroot00000000000000"""Library code for GLib/D-Bus-related code generation. The master copy of this library is in the telepathy-glib repository - please make any changes there. """ # Copyright (C) 2006-2008 Collabora Limited # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA from libtpcodegen import NS_TP, \ Signature, \ cmp_by_name, \ escape_as_identifier, \ get_by_path, \ get_descendant_text, \ get_docstring, \ xml_escape, \ get_deprecated def dbus_gutils_wincaps_to_uscore(s): """Bug-for-bug compatible Python port of _dbus_gutils_wincaps_to_uscore which gets sequences of capital letters wrong in the same way. (e.g. in Telepathy, SendDTMF -> send_dt_mf) """ ret = '' for c in s: if c >= 'A' and c <= 'Z': length = len(ret) if length > 0 and (length < 2 or ret[length-2] != '_'): ret += '_' ret += c.lower() else: ret += c return ret def signal_to_marshal_type(signal): """ return a list of strings indicating the marshalling type for this signal. """ mtype=[] for i in signal.getElementsByTagName("arg"): name =i.getAttribute("name") type = i.getAttribute("type") mtype.append(type_to_gtype(type)[2]) return mtype _glib_marshallers = ['VOID', 'BOOLEAN', 'CHAR', 'UCHAR', 'INT', 'STRING', 'UINT', 'LONG', 'ULONG', 'ENUM', 'FLAGS', 'FLOAT', 'DOUBLE', 'STRING', 'PARAM', 'BOXED', 'POINTER', 'OBJECT', 'UINT_POINTER'] def signal_to_marshal_name(signal, prefix): mtype = signal_to_marshal_type(signal) if len(mtype): name = '_'.join(mtype) else: name = 'VOID' if name in _glib_marshallers: return 'g_cclosure_marshal_VOID__' + name else: return prefix + '_marshal_VOID__' + name def method_to_glue_marshal_name(method, prefix): mtype = [] for i in method.getElementsByTagName("arg"): if i.getAttribute("direction") != "out": type = i.getAttribute("type") mtype.append(type_to_gtype(type)[2]) mtype.append('POINTER') name = '_'.join(mtype) if name in _glib_marshallers: return 'g_cclosure_marshal_VOID__' + name else: return prefix + '_marshal_VOID__' + name def type_to_gtype(s): if s == 'y': #byte return ("guchar ", "G_TYPE_UCHAR","UCHAR", False) elif s == 'b': #boolean return ("gboolean ", "G_TYPE_BOOLEAN","BOOLEAN", False) elif s == 'n': #int16 return ("gint ", "G_TYPE_INT","INT", False) elif s == 'q': #uint16 return ("guint ", "G_TYPE_UINT","UINT", False) elif s == 'i': #int32 return ("gint ", "G_TYPE_INT","INT", False) elif s == 'u': #uint32 return ("guint ", "G_TYPE_UINT","UINT", False) elif s == 'x': #int64 return ("gint64 ", "G_TYPE_INT64","INT64", False) elif s == 't': #uint64 return ("guint64 ", "G_TYPE_UINT64","UINT64", False) elif s == 'd': #double return ("gdouble ", "G_TYPE_DOUBLE","DOUBLE", False) elif s == 's': #string return ("gchar *", "G_TYPE_STRING", "STRING", True) elif s == 'g': #signature - FIXME return ("gchar *", "DBUS_TYPE_G_SIGNATURE", "STRING", True) elif s == 'o': #object path return ("gchar *", "DBUS_TYPE_G_OBJECT_PATH", "BOXED", True) elif s == 'v': #variant return ("GValue *", "G_TYPE_VALUE", "BOXED", True) elif s == 'as': #array of strings return ("gchar **", "G_TYPE_STRV", "BOXED", True) elif s == 'ay': #byte array return ("GArray *", "dbus_g_type_get_collection (\"GArray\", G_TYPE_UCHAR)", "BOXED", True) elif s == 'au': #uint array return ("GArray *", "DBUS_TYPE_G_UINT_ARRAY", "BOXED", True) elif s == 'ai': #int array return ("GArray *", "DBUS_TYPE_G_INT_ARRAY", "BOXED", True) elif s == 'ax': #int64 array return ("GArray *", "DBUS_TYPE_G_INT64_ARRAY", "BOXED", True) elif s == 'at': #uint64 array return ("GArray *", "DBUS_TYPE_G_UINT64_ARRAY", "BOXED", True) elif s == 'ad': #double array return ("GArray *", "DBUS_TYPE_G_DOUBLE_ARRAY", "BOXED", True) elif s == 'ab': #boolean array return ("GArray *", "DBUS_TYPE_G_BOOLEAN_ARRAY", "BOXED", True) elif s == 'ao': #object path array return ("GPtrArray *", 'dbus_g_type_get_collection ("GPtrArray",' ' DBUS_TYPE_G_OBJECT_PATH)', "BOXED", True) elif s == 'a{ss}': #hash table of string to string return ("GHashTable *", "DBUS_TYPE_G_STRING_STRING_HASHTABLE", "BOXED", False) elif s[:2] == 'a{': #some arbitrary hash tables if s[2] not in ('y', 'b', 'n', 'q', 'i', 'u', 's', 'o', 'g'): raise Exception, "can't index a hashtable off non-basic type " + s first = type_to_gtype(s[2]) second = type_to_gtype(s[3:-1]) return ("GHashTable *", "(dbus_g_type_get_map (\"GHashTable\", " + first[1] + ", " + second[1] + "))", "BOXED", False) elif s[:2] in ('a(', 'aa'): # array of structs or arrays, recurse gtype = type_to_gtype(s[1:])[1] return ("GPtrArray *", "(dbus_g_type_get_collection (\"GPtrArray\", "+gtype+"))", "BOXED", True) elif s[:1] == '(': #struct gtype = "(dbus_g_type_get_struct (\"GValueArray\", " for subsig in Signature(s[1:-1]): gtype = gtype + type_to_gtype(subsig)[1] + ", " gtype = gtype + "G_TYPE_INVALID))" return ("GValueArray *", gtype, "BOXED", True) # we just don't know .. raise Exception, "don't know the GType for " + s telepathy-ring-2.3.24-37ad7111424725a5446e82dac516d7cab88149c7/tools/libtpcodegen.py000066400000000000000000000147221251541261300261330ustar00rootroot00000000000000"""Library code for language-independent D-Bus-related code generation. The master copy of this library is in the telepathy-glib repository - please make any changes there. """ # Copyright (C) 2006-2008 Collabora Limited # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA from string import ascii_letters, digits NS_TP = "http://telepathy.freedesktop.org/wiki/DbusSpec#extensions-v0" _ASCII_ALNUM = ascii_letters + digits def cmp_by_name(node1, node2): return cmp(node1.getAttributeNode("name").nodeValue, node2.getAttributeNode("name").nodeValue) def escape_as_identifier(identifier): """Escape the given string to be a valid D-Bus object path or service name component, using a reversible encoding to ensure uniqueness. The reversible encoding is as follows: * The empty string becomes '_' * Otherwise, each non-alphanumeric character is replaced by '_' plus two lower-case hex digits; the same replacement is carried out on the first character, if it's a digit """ # '' -> '_' if not identifier: return '_' # A bit of a fast path for strings which are already OK. # We deliberately omit '_' because, for reversibility, that must also # be escaped. if (identifier.strip(_ASCII_ALNUM) == '' and identifier[0] in ascii_letters): return identifier # The first character may not be a digit if identifier[0] not in ascii_letters: ret = ['_%02x' % ord(identifier[0])] else: ret = [identifier[0]] # Subsequent characters may be digits or ASCII letters for c in identifier[1:]: if c in _ASCII_ALNUM: ret.append(c) else: ret.append('_%02x' % ord(c)) return ''.join(ret) def get_by_path(element, path): branches = path.split('/') branch = branches[0] # Is the current branch an attribute, if so, return the attribute value if branch[0] == '@': return element.getAttribute(branch[1:]) # Find matching children for the branch children = [] if branch == '..': children.append(element.parentNode) else: for x in element.childNodes: if x.localName == branch: children.append(x) ret = [] # If this is not the last path element, recursively gather results from # children if len(branches) > 1: for x in children: add = get_by_path(x, '/'.join(branches[1:])) if isinstance(add, list): ret += add else: return add else: ret = children return ret def get_docstring(element): docstring = None for x in element.childNodes: if x.namespaceURI == NS_TP and x.localName == 'docstring': docstring = x if docstring is not None: docstring = docstring.toxml().replace('\n', ' ').strip() if docstring.startswith(''): docstring = docstring[14:].lstrip() if docstring.endswith(''): docstring = docstring[:-15].rstrip() if docstring in ('', ''): docstring = '' return docstring def get_deprecated(element): text = [] for x in element.childNodes: if hasattr(x, 'data'): text.append(x.data.replace('\n', ' ').strip()) else: # This caters for tp:dbus-ref elements, but little else. if x.childNodes and hasattr(x.childNodes[0], 'data'): text.append(x.childNodes[0].data.replace('\n', ' ').strip()) return ' '.join(text) def get_descendant_text(element_or_elements): if not element_or_elements: return '' if isinstance(element_or_elements, list): return ''.join(map(get_descendant_text, element_or_elements)) parts = [] for x in element_or_elements.childNodes: if x.nodeType == x.TEXT_NODE: parts.append(x.nodeValue) elif x.nodeType == x.ELEMENT_NODE: parts.append(get_descendant_text(x)) else: pass return ''.join(parts) class _SignatureIter: """Iterator over a D-Bus signature. Copied from dbus-python 0.71 so we can run genginterface in a limited environment with only Python (like Scratchbox). """ def __init__(self, string): self.remaining = string def next(self): if self.remaining == '': raise StopIteration signature = self.remaining block_depth = 0 block_type = None end = len(signature) for marker in range(0, end): cur_sig = signature[marker] if cur_sig == 'a': pass elif cur_sig == '{' or cur_sig == '(': if block_type == None: block_type = cur_sig if block_type == cur_sig: block_depth = block_depth + 1 elif cur_sig == '}': if block_type == '{': block_depth = block_depth - 1 if block_depth == 0: end = marker break elif cur_sig == ')': if block_type == '(': block_depth = block_depth - 1 if block_depth == 0: end = marker break else: if block_depth == 0: end = marker break end = end + 1 self.remaining = signature[end:] return Signature(signature[0:end]) class Signature(str): """A string, iteration over which is by D-Bus single complete types rather than characters. """ def __iter__(self): return _SignatureIter(self) def xml_escape(s): s = s.replace('&', '&').replace("'", ''').replace('"', '"') return s.replace('<', '<').replace('>', '>') telepathy-ring-2.3.24-37ad7111424725a5446e82dac516d7cab88149c7/tools/make-version-script.py000066400000000000000000000140761251541261300274000ustar00rootroot00000000000000#!/usr/bin/python """Construct a GNU ld or Debian dpkg version-script from a set of RFC822-style symbol lists. Usage: make-version-script.py [--symbols SYMBOLS] [--unreleased-version VER] [--dpkg "LIBRARY.so.0 LIBRARY0 #MINVER#"] [--dpkg-build-depends-package LIBRARY-dev] [FILES...] Each FILE starts with RFC822-style headers "Version:" (the name of the symbol version, e.g. FOO_1.2.3) and "Extends:" (either the previous version, or "-" if this is the first version). Next there is a blank line, then a list of C symbols one per line. Comments (lines starting with whitespace + "#") are allowed and ignored. If --symbols is given, SYMBOLS lists the symbols actually exported by the library (one per line). If --unreleased-version is given, any symbols in SYMBOLS but not in FILES are assigned to that version; otherwise, any such symbols cause an error. If --dpkg is given, produce a Debian dpkg-gensymbols file instead of a GNU ld version-script. The argument to --dpkg is the first line of the resulting symbols file, and --dpkg-build-depends-package can optionally be used to set the Build-Depends-Package field. This script originates in telepathy-glib - please send us any changes that are needed. """ # Copyright (C) 2008-2010 Collabora Ltd. # Copyright (C) 2008 Nokia Corporation # # Copying and distribution of this file, with or without modification, # are permitted in any medium without royalty provided the copyright # notice and this notice are preserved. import sys from getopt import gnu_getopt def e(format, *args): sys.stderr.write((format + '\n') % args) def main(abifiles, symbols=None, unreleased_version=None, dpkg=False, dpkg_first_line=None, dpkg_build_depends_package=None): gnuld = not dpkg symbol_set = None if symbols is not None: symbol_set = open(symbols, 'r').readlines() symbol_set = map(str.strip, symbol_set) symbol_set = set(symbol_set) versioned_symbols = set() dpkg_symbols = [] dpkg_versions = [] if dpkg: assert dpkg_first_line is not None print dpkg_first_line if dpkg_build_depends_package is not None: print "* Build-Depends-Package: %s" % dpkg_build_depends_package for filename in abifiles: lines = open(filename, 'r').readlines() version = None extends = None release = None for i, line in enumerate(lines): line = line.strip() if line.startswith('#'): continue elif not line: # the transition betwen headers and symbols cut = i + 1 break elif line.lower().startswith('version:'): line = line[8:].strip() version = line continue elif line.lower().startswith('extends:'): line = line[8:].strip() extends = line continue elif line.lower().startswith('release:'): release = line[8:].strip() continue else: e('Could not understand line in %s header: %s', filename, line) raise SystemExit(1) else: e('No symbols in %s', filename) raise SystemExit(1) if version is None: e('No Versions: header in %s', filename) raise SystemExit(1) if extends is None: e('No Extends: header in %s', filename) raise SystemExit(1) if release is None and dpkg: e('No Release: header in %s', filename) raise SystemExit(1) if dpkg: dpkg_versions.append('%s@%s %s' % (version, version, release)) lines = lines[cut:] if gnuld: print "%s {" % version print " global:" for symbol in lines: symbol = symbol.strip() if symbol.startswith('#'): continue if gnuld: print " %s;" % symbol elif dpkg: dpkg_symbols.append('%s@%s %s' % (symbol, version, release)) if symbol in versioned_symbols: raise AssertionError('Symbol %s is in version %s and an ' 'earlier version' % (symbol, version)) versioned_symbols.add(symbol) if gnuld: if extends == '-': print " local:" print " *;" print "};" else: print "} %s;" % extends print if dpkg: dpkg_symbols.sort() dpkg_versions.sort() for x in dpkg_versions: print " %s" % x for x in dpkg_symbols: print " %s" % x if symbol_set is not None: missing = versioned_symbols - symbol_set if missing: e('These symbols have disappeared:') for symbol in missing: e(' %s', symbol) raise SystemExit(1) unreleased = symbol_set - versioned_symbols if unreleased: if unreleased_version is None: e('Unversioned symbols are not allowed in releases:') for symbol in unreleased: e(' %s', symbol) raise SystemExit(1) if gnuld: print "%s {" % unreleased_version print " global:" for symbol in unreleased: print " %s;" % symbol print "} %s;" % version if __name__ == '__main__': options, argv = gnu_getopt (sys.argv[1:], '', ['symbols=', 'unreleased-version=', 'dpkg=', 'dpkg-build-depends-package=']) opts = {'dpkg': False} for option, value in options: if option == '--dpkg': opts['dpkg'] = True opts['dpkg_first_line'] = value else: opts[option.lstrip('-').replace('-', '_')] = value main(argv, **opts) telepathy-ring-2.3.24-37ad7111424725a5446e82dac516d7cab88149c7/tools/telepathy-glib-env.in000066400000000000000000000004371251541261300271500ustar00rootroot00000000000000#!/bin/sh abs_top_builddir="@abs_top_builddir@" export abs_top_builddir LD_LIBRARY_PATH="${abs_top_builddir}/telepathy-glib/.libs${LD_LIBRARY_PATH:+":${LD_LIBRARY_PATH}"}" export LD_LIBRARY_PATH G_DEBUG="fatal_criticals,fatal_warnings${G_DEBUG:+",${G_DEBUG}"}" export G_DEBUG exec "$@" telepathy-ring-2.3.24-37ad7111424725a5446e82dac516d7cab88149c7/tools/with-session-bus.sh000066400000000000000000000042741251541261300267020ustar00rootroot00000000000000#!/bin/sh # with-session-bus.sh - run a program with a temporary D-Bus session daemon # # The canonical location of this program is the telepathy-glib tools/ # directory, please synchronize any changes with that copy. # # Copyright (C) 2007-2008 Collabora Ltd. # # Copying and distribution of this file, with or without modification, # are permitted in any medium without royalty provided the copyright # notice and this notice are preserved. set -e me=with-session-bus dbus_daemon_args="--print-address=5 --print-pid=6 --fork" sleep=0 usage () { echo "usage: $me [options] -- program [program_options]" >&2 echo "Requires write access to the current directory." >&2 echo "" >&2 echo "If \$WITH_SESSION_BUS_FORK_DBUS_MONITOR is set, fork dbus-monitor" >&2 echo "with the arguments in \$WITH_SESSION_BUS_FORK_DBUS_MONITOR_OPT." >&2 echo "The output of dbus-monitor is saved in $me-.dbus-monitor-logs" >&2 exit 2 } while test "z$1" != "z--"; do case "$1" in --sleep=*) sleep="$1" sleep="${sleep#--sleep=}" shift ;; --session) dbus_daemon_args="$dbus_daemon_args --session" shift ;; --config-file=*) # FIXME: assumes config file doesn't contain any special characters dbus_daemon_args="$dbus_daemon_args $1" shift ;; *) usage ;; esac done shift if test "z$1" = "z"; then usage; fi exec 5> $me-$$.address exec 6> $me-$$.pid cleanup () { pid=`head -n1 $me-$$.pid` if test -n "$pid" ; then echo "Killing temporary bus daemon: $pid" >&2 kill -INT "$pid" fi rm -f $me-$$.address rm -f $me-$$.pid } trap cleanup INT HUP TERM dbus-daemon $dbus_daemon_args { echo -n "Temporary bus daemon is "; cat $me-$$.address; } >&2 { echo -n "Temporary bus daemon PID is "; head -n1 $me-$$.pid; } >&2 e=0 DBUS_SESSION_BUS_ADDRESS="`cat $me-$$.address`" export DBUS_SESSION_BUS_ADDRESS if [ -n "$WITH_SESSION_BUS_FORK_DBUS_MONITOR" ] ; then echo -n "Forking dbus-monitor $WITH_SESSION_BUS_FORK_DBUS_MONITOR_OPT" >&2 dbus-monitor $WITH_SESSION_BUS_FORK_DBUS_MONITOR_OPT \ > $me-$$.dbus-monitor-logs 2>&1 & fi "$@" || e=$? if test $sleep != 0; then sleep $sleep fi trap - INT HUP TERM cleanup exit $e telepathy-ring-2.3.24-37ad7111424725a5446e82dac516d7cab88149c7/tools/xincludator.py000066400000000000000000000024131251541261300260220ustar00rootroot00000000000000#!/usr/bin/python from sys import argv, stdout, stderr import codecs, locale import os import xml.dom.minidom stdout = codecs.getwriter('utf-8')(stdout) NS_XI = 'http://www.w3.org/2001/XInclude' def xincludate(dom, base, dropns = []): remove_attrs = [] for i in xrange(dom.documentElement.attributes.length): attr = dom.documentElement.attributes.item(i) if attr.prefix == 'xmlns': if attr.localName in dropns: remove_attrs.append(attr) else: dropns.append(attr.localName) for attr in remove_attrs: dom.documentElement.removeAttributeNode(attr) for include in dom.getElementsByTagNameNS(NS_XI, 'include'): href = include.getAttribute('href') # FIXME: assumes Unixy paths filename = os.path.join(os.path.dirname(base), href) subdom = xml.dom.minidom.parse(filename) xincludate(subdom, filename, dropns) if './' in href: subdom.documentElement.setAttribute('xml:base', href) include.parentNode.replaceChild(subdom.documentElement, include) if __name__ == '__main__': argv = argv[1:] dom = xml.dom.minidom.parse(argv[0]) xincludate(dom, argv[0]) xml = dom.toxml() stdout.write(xml) stdout.write('\n')